Summary
The POSIX permission check in readPatternFile incorrectly rejects .backupignore files on Windows. Windows does not implement POSIX permission bits, so (stat.mode & 0o022) !== 0 always evaluates to true, causing all .backupignore files to be skipped with the warning:
⚠️ --exclude-file …\.backupignore: group/world writable — skipping for security
This breaks two unit tests on Windows CI shard 2.
Reproduction
Consistently reproduces on checks-windows shard 2 in CI. The two failing tests in src/infra/backup-exclude.test.ts:
rejects too-long pattern from .backupignore (not just CLI) — the .backupignore file is silently skipped before the too-long pattern validation runs
.backupignore auto-loaded from stateDir when present — patterns are never loaded because the file is rejected as "world writable"
Root Cause
src/infra/backup-exclude.ts:123-127:
if (opts.permissionCheck) {
const isGroupOrWorldWritable = (fileStat.mode & 0o022) !== 0;
if (isGroupOrWorldWritable) {
throw new ExcludeFileError(filePath, "group/world writable — skipping for security");
}
}
On Windows, Node.js fs.stat().mode does not report real POSIX permission bits. The group/world-writable bits are typically set (or meaningless), so this check always triggers. The permission check is called for .backupignore auto-loading (permissionCheck: true at line 179) but not for --exclude-file (which uses throwOnError: true instead).
Possible Fix
Skip the POSIX permission check on Windows since it has no meaningful security value there:
if (opts.permissionCheck && process.platform !== "win32") {
const isGroupOrWorldWritable = (fileStat.mode & 0o022) !== 0;
if (isGroupOrWorldWritable) {
throw new ExcludeFileError(filePath, "group/world writable — skipping for security");
}
}
Alternatively, use Windows ACL checks via fs.access(filePath, fs.constants.W_OK) on Windows, though the threat model for .backupignore may not warrant that complexity.
CI Evidence
Affected Tests
Both in src/infra/backup-exclude.test.ts:
rejects too-long pattern from .backupignore (not just CLI)
.backupignore auto-loaded from stateDir when present
Summary
The POSIX permission check in
readPatternFileincorrectly rejects.backupignorefiles on Windows. Windows does not implement POSIX permission bits, so(stat.mode & 0o022) !== 0always evaluates totrue, causing all.backupignorefiles to be skipped with the warning:This breaks two unit tests on Windows CI shard 2.
Reproduction
Consistently reproduces on
checks-windowsshard 2 in CI. The two failing tests insrc/infra/backup-exclude.test.ts:rejects too-long pattern from .backupignore (not just CLI)— the.backupignorefile is silently skipped before the too-long pattern validation runs.backupignore auto-loaded from stateDir when present— patterns are never loaded because the file is rejected as "world writable"Root Cause
src/infra/backup-exclude.ts:123-127:On Windows, Node.js
fs.stat().modedoes not report real POSIX permission bits. The group/world-writable bits are typically set (or meaningless), so this check always triggers. The permission check is called for.backupignoreauto-loading (permissionCheck: trueat line 179) but not for--exclude-file(which usesthrowOnError: trueinstead).Possible Fix
Skip the POSIX permission check on Windows since it has no meaningful security value there:
Alternatively, use Windows ACL checks via
fs.access(filePath, fs.constants.W_OK)on Windows, though the threat model for.backupignoremay not warrant that complexity.CI Evidence
9a2ed86) — shard 2: 2 failuresecc2017) — shard 2: 2 failures (same root cause, pre-existing)Affected Tests
Both in
src/infra/backup-exclude.test.ts:rejects too-long pattern from .backupignore (not just CLI).backupignore auto-loaded from stateDir when present