github kenryu42/cc-safety-net v2.6.1

5 hours ago

The analyzer now reads inside Windows cmd /c and powershell -Command wrappers, closing a gap where a recursive delete nested in either one was allowed. This release also fixes a hook crash on long interpreter string literals, clears a batch of standard-mode secret-protection false positives, and ships a Cursor plugin.

Highlights

  • Added analysis of commands nested in Windows shell wrappers. In v2.6.0 forms such as cmd /c "rmdir /s /q C:\" and powershell -Command "Remove-Item -Recurse -Force C:\" were allowed; they are now blocked as catastrophic hard stops, in every permission mode. (#226, #227)
  • Fixed the v2.6.0 hook failing closed with "command analysis failed unexpectedly" on commands containing a very long string literal, such as a python3 - <<'EOF' script that edits source files. (#224)
  • Added a Cursor plugin, so CC Safety Net can be installed from Cursor's Customize screen instead of the npx user hook. (#230)

Added

  • Added the cmd.recursive-delete-* rule family for rmdir/rd/del/erase with /s inside a cmd /c, cmd /k, or Git Bash //c body. Targets are judged by the existing recursive-delete rules against the outer working directory, and every other piece of the body is analyzed as a command of the outer shell, so a rm -rf / nested in a cmd body is also denied. Deletes that stay inside the current directory remain allowed. (#226)
  • Added the powershell.nested-recursive-delete-unread rule. A powershell/pwsh invocation in the one shape the analyzer reads — optional -NoProfile, -NonInteractive, -NoLogo, or -ExecutionPolicy, then -Command/-c with literal script words — is analyzed as PowerShell; any other form that names a delete command and a recursive switch is blocked instead of allowed. -EncodedCommand is refused as an unverifiable shell source. (#227)

Changed

  • Reduced standard-mode secret-protection false positives. Words outside string literals in Python and JavaScript code are no longer treated as paths; f-strings and untagged JS templates are masked as literals; and when code holds an access marker, Python and JavaScript literals are judged by position, so a collection element, dict key or value, subscript key, or in/==/!= operand counts as data rather than a path. (#219, #220)
  • Relaxed standard mode for names that are created or counted rather than read: a mkdir or touch operand, wc with only count options, grep/rg limited to names, counts, or exit status, a for list word whose variable reaches no reader, tailscale cert --cert-file/--key-file outputs, and the input of openssl x509 -in. Each relaxation turns off wherever the name could still reach a reader — a later pipe, a coprocess, a command or process substitution, or an interpreter execution call — and none of them relax home-directory paths, coding-CLI credential paths, or configured deny paths. (#222, #225)
  • Relaxed a literal that only names the file a write creates (Path(...).write_text, open(p, 'w'), writeFile/appendFile/Bun.write) like an output redirection, and only where no such file exists. (#225)
  • Exempted .env.tpl in every mode, alongside .env.example, .env.sample, and .env.template. Home-directory rules now run before the template exemptions, so ~/.ssh/.env.tpl stays protected. (#222)
  • Redesigned the local policy GUI (cc-safety-net gui) for readability: tidier Protections toolbar, disclosures and card headers, visible ends of long paths, fewer repeated labels, and clearer Activity row actions. Activity and Protections filters are now kept in the page address, so Back, reload, and sidebar links behave as expected. (#229)
  • Simplified the false-positive report: the title is prefilled from the blocked command, and the explanation of what you were doing is now optional in both the GUI form and the GitHub issue template. (#229)

Fixed

  • Fixed rm -rf inside a project being denied as rm.recursive-force-outside-cwd after cd /d/<project> on Windows with Git Bash. The POSIX cd operand is now MSYS-normalized before resolving, matching the behavior of cd D:/<project>. (#223, #228)
  • Fixed path-variable expansion giving up on an unclosed unsupported ${, which left the rest of the value unexpanded. Later supported variables now expand, and an excessive number of unclosed expansions is refused. (#226)
  • Fixed uv run python - <<EOF bodies being walked as shell words instead of analyzed as Python, in every mode. (#225)

Breaking Changes

  • A recursive delete handed to powershell or pwsh in any form other than literal -Command script words is now denied in every permission mode, including -File, a positional script, -WorkingDirectory, and an outer-expanded word.
    • Migration: Pass the script as a single -Command with literal paths, after at most -NoProfile, -NonInteractive, -NoLogo, or -ExecutionPolicy.
  • A cmd /c or cmd /k recursive delete whose quoting can reach cmd as \", or whose target uses a \\?\ or \\.\ namespace path, is now denied in every permission mode, because cmd does not treat \" as an escape and the target can split down to the drive root.
    • Migration: Pass each path as its own quoted argument, or use Remove-Item -LiteralPath.
  • A cmd body containing ^, %, !, a directory-change word, or a delete the bounded reader cannot parse is refused as an unverifiable shell source, which asks the user where the host can prompt.
    • Migration: Write the delete with a literal path and no escape or expansion characters, or run it manually.

Don't miss a new cc-safety-net release

NewReleases is sending notifications on new releases.