Skip to content

Add test to ensure the BatBadBut mitigation handles trailing . and space safely #23363

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 26, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions test/standalone/windows_bat_args/test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,31 @@ pub fn main() anyerror!void {
try testExec(allocator, &.{ "\"hello^\"world\"", "hello &echo oh no >file.txt" }, null);
try testExec(allocator, &.{"&whoami.exe"}, null);

// Ensure that trailing space and . characters can't lead to unexpected bat/cmd script execution.
// In many Windows APIs (including CreateProcess), trailing space and . characters are stripped
// from paths, so if a path with trailing . and space character(s) is passed directly to
// CreateProcess, then it could end up executing a batch/cmd script that naive extension detection
// would not flag as .bat/.cmd.
//
// Note that we expect an error here, though, which *is* a valid mitigation, but also an implementation detail.
// This error is caused by the use of a wildcard with NtQueryDirectoryFile to optimize PATHEXT searching. That is,
// the trailing characters in the app name will lead to a FileNotFound error as the wildcard-appended path will not
// match any real paths on the filesystem (e.g. `foo.bat .. *` will not match `foo.bat`; only `foo.bat*` will).
//
// This being an error matches the behavior of running a command via the command line of cmd.exe, too:
//
// > "args1.bat .. "
// '"args1.bat .. "' is not recognized as an internal or external command,
// operable program or batch file.
try std.testing.expectError(error.FileNotFound, testExecBat(allocator, "args1.bat .. ", &.{"abc"}, null));
const absolute_with_trailing = blk: {
const absolute_path = try std.fs.realpathAlloc(allocator, "args1.bat");
defer allocator.free(absolute_path);
break :blk try std.mem.concat(allocator, u8, &.{ absolute_path, " .. " });
};
defer allocator.free(absolute_with_trailing);
try std.testing.expectError(error.FileNotFound, testExecBat(allocator, absolute_with_trailing, &.{"abc"}, null));

var env = env: {
var env = try std.process.getEnvMap(allocator);
errdefer env.deinit();
Expand Down
Loading