Skip to content

fix: normalize a single backslash so protectedFilePatterns work on Windows - #593

Open
LHMQ878 wants to merge 1 commit into
Opencode-DCP:masterfrom
LHMQ878:fix/protected-patterns-windows-separator
Open

fix: normalize a single backslash so protectedFilePatterns work on Windows#593
LHMQ878 wants to merge 1 commit into
Opencode-DCP:masterfrom
LHMQ878:fix/protected-patterns-windows-separator

Conversation

@LHMQ878

@LHMQ878 LHMQ878 commented Jul 30, 2026

Copy link
Copy Markdown

Fixes #592

What was wrong

normalizePath searched for "\\\\" — in source, the two-character string \\. A path from the filesystem contains single separators, so nothing was ever replaced. The function exists solely to make Windows paths comparable to forward-slash glob patterns, and it was inert on the only platform that needs it.

protectedFilePatterns consequently protected a file on POSIX and silently failed to protect the same file on Windows:

pattern C:/repo/src/config/secrets.ts C:\repo\src\config\secrets.ts
**/secrets.ts true false
**/config/*.ts true false
C:/repo/src/** true false
**/*.ts true true

That last row is why this survived: ** compiles to .*, which spans backslashes, so a user who tests with **/*.ts sees protection working. Every pattern that names a directory or an exact file did nothing.

isFilePathProtected is the guard that keeps configured files out of pruning, consulted from compress/protected-content.ts:138, commands/sweep.ts:184/:201, strategies/deduplication.ts:60, and strategies/purge-errors.ts:62. On Windows all of them saw false for paths the user explicitly asked to protect. The failure was silent: config parses, schema validates, nothing logs.

What changed

-return input.replaceAll("\\\\", "/")
+return input.replaceAll("\\", "/")

One character. replaceAll takes a string, not a regex, so no regex-level escaping belongs there.

Because normalizePath is applied to the pattern as well as the path, a pattern written with Windows separators now also works — which is what a user copying a path out of Explorer naturally produces.

What deliberately does not change: separators are converted, not stripped. * compiles to [^/]* and must not cross a directory boundary, so src/* still does not match src\config\secrets.ts. A fix that removed backslashes instead of translating them would break that quietly, so it has a test of its own.

Verification

  • npm test: 95 pass / 0 fail (was 87 — exactly +8, the new tests).
  • With lib/protected-patterns.ts reverted to master and the new tests kept: 5 fail / 90 pass. The three tests that pass either way are the platform-neutral ones (tool-name matching, empty pattern, regex metacharacters) — they are there to pin that this change does not widen matching beyond separators.
  • npm run typecheck: clean.
  • prettier --check on both files: clean.

Tests added

tests/protected-patterns.test.ts — the module had no test file. 8 tests:

  • the same file, spelled with either separator, matches the documented pattern styles
  • a pattern written with Windows separators works against either spelling
  • * still does not cross a normalised separator — the property a strip-instead-of-convert fix would break
  • isFilePathProtected end-to-end through getFilePathsFromParameters("read", …), which is the shape the call sites actually pass
  • a genuinely unmatched path, an empty pattern list, and an empty path list all still return false
  • multiedit (nested edits[].filePath) and apply_patch (paths embedded in patchText) on Windows, since each carries paths in its own shape
  • isToolNameProtected unchanged, and empty-pattern / regex-metacharacter handling in matchesGlob

The tests build the backslash with String.fromCharCode(92) rather than writing a literal. The bug being pinned was an escaping mistake in a string literal, so the tests avoid depending on one.

…ndows

`normalizePath` searched for `"\\\\"`, which in source is the two-character
string `\`. A real Windows path contains single separators, so nothing was
replaced and the normalisation was inert on the only platform it exists for.

`protectedFilePatterns` therefore protected a file on POSIX and silently failed
to protect the same file on Windows, at all four `isFilePathProtected` call
sites (protected-content, sweep, deduplication, purge-errors). Patterns that
name a directory or an exact file did nothing; only patterns like `**/*.ts`
appeared to work, because `**` compiles to `.*` and spans backslashes anyway.

Search for the single backslash instead. Separators are converted rather than
stripped, so `*` still compiles to `[^/]*` and does not cross a directory
boundary. Because both the path and the pattern are normalised, a pattern
written with Windows separators now works too.

Adds tests/protected-patterns.test.ts; the module had no test file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

protectedFilePatterns never match on Windows: normalizePath searches for a doubled backslash

1 participant