Skip to content

Fix shell write bypass of write-only denies#2

Merged
rblaine95 merged 3 commits into
masterfrom
fix/rules-guard-write-bypass
Jul 1, 2026
Merged

Fix shell write bypass of write-only denies#2
rblaine95 merged 3 commits into
masterfrom
fix/rules-guard-write-bypass

Conversation

@rblaine95

@rblaine95 rblaine95 commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Summary

Closes a security gap in rules-guard surfaced by a CodeRabbit review: a path protected only by a Write(...)/Edit(...) rule (no matching Read(...) deny) could be modified through a shell or eval command, because the command/code path-token scan checked read-class denies only.

Finding 1 (critical) — code fix

decide's token scan for SHELL_FIELDS + CODE_FIELDS passed includeWrite = false, so writeDeny was never consulted for shell/code. Example: the embedded Edit(~/.bashrc) deny did not stop echo ... >> ~/.bashrc.

  • index.ts — the token scan now passes includeWrite = true. Shell and code execution can both read and write and the scan can't tell which, so it now enforces read- and write-class denies. Comment rewritten to document the reasoning and the trade-off.
  • Trade-off (intended): a read-allowed-but-write-denied template (.env.example) is now conservatively blocked in shell context. The direct read tool still permits it (read-class only), so the template-allow goal is preserved; only the ambiguous shell path is tightened.
  • index.test.ts — new regression test: shell (echo >>) and eval (open(...,'w')) writes to a Write-only-denied path both block. The existing cat .env.example shell test flips to true to match the conservative behavior.

Finding 3 (minor) — stronger assertion

  • index.test.tsloadPolicyEntries test now asserts not.toContain("123") instead of not.toContain(123 as unknown as string), so an accidental stringification of an invalid non-string entry would be caught.

Finding 2 (non-hermetic live-POLICY test) was intentionally out of scope for this PR.

Verification

  • bun test — 56 pass, 0 fail, 100.00% funcs / 100.00% lines, exit 0.
  • bun typecheck — clean.
  • bunx biome check — clean.
  • coderabbit review --agent on the diff — 0 findings.
  • Pre-commit hk hooks pass.

Summary by CodeRabbit

  • Bug Fixes
    • Strengthened security checks for commands and code snippets that reference paths, preventing some write operations from bypassing restrictions.
    • Improved handling of template-like paths so blocked write targets are now treated more conservatively.
    • Updated policy loading behavior so non-string deny entries are excluded consistently.

The command/code path-token scan in `decide` only checked read-class
denies (`includeWrite = false`), so a path protected by a
`Write(...)`/`Edit(...)` rule with no matching `Read(...)` deny could
still be modified through a shell or eval command. The embedded
`Edit(~/.bashrc)` deny, for instance, did not stop a shell append to
that file.

Shell and code execution can both read and write, and the token scan
cannot tell which, so it now checks read- and write-class denies. A
read-allowed but write-denied template (`.env.example`) is
conservatively blocked in shell context; the read tool still permits
it directly.

Also tighten a `loadPolicyEntries` test to assert the string "123" is
absent, so an accidental stringification of an invalid non-string
entry would be caught.

Glory to the Omnissiah
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@rblaine95, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 21 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0846a448-b08b-4bd7-bfbc-6471b099dd31

📥 Commits

Reviewing files that changed from the base of the PR and between 33202fc and 9e12e79.

📒 Files selected for processing (3)
  • extensions/rules-guard/README.md
  • extensions/rules-guard/index.test.ts
  • extensions/rules-guard/index.ts
📝 Walkthrough

Walkthrough

The decide() function's shell/code path-token scan now checks both read-class and write-class deny globs instead of only read-class, closing a bypass where write denies could be circumvented via token-based path detection. Corresponding tests were added or adjusted to reflect this conservative blocking behavior.

Changes

Write-Class Deny Detection Fix

Layer / File(s) Summary
Enable write-class checking in token scan
extensions/rules-guard/index.ts
The includeWrite argument for the path-token verdict call is switched from false to true, and the inline documentation is updated to describe checking both read and write class denies during the shell/code token scan.
Tests for conservative write-deny blocking
extensions/rules-guard/index.test.ts
A shell-template read test is replaced with an assertion that shell references to Write-denied templates are blocked; a new adversarial bypass test verifies bash and eval writes to a Write-only-denied path are blocked; a settings-merge assertion is adjusted to compare against the string "123".

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant Decide as decide()
    participant TokenScan as Path-token scan
    participant Policy as Deny policy globs

    Caller->>Decide: decide("bash", { command })
    Decide->>TokenScan: scan command for path tokens
    TokenScan->>Policy: check read-class deny globs
    TokenScan->>Policy: check write-class deny globs (new)
    Policy-->>TokenScan: match found (write deny)
    TokenScan-->>Decide: verdict block:true
    Decide-->>Caller: { block: true }
Loading

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely captures the main security fix: blocking shell write bypasses of write-only denies.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

rblaine95 added 2 commits July 1, 2026 13:56
Broaden `redactText`'s `SECRET_OUTPUT` list from a handful of shapes to
cover the common credential formats a tool might echo, as defense in
depth on `tool_result`.

- Widen the GitHub token pattern from `ghp_` to every prefix the `gh`
  CLI writes (`gho_`, `ghu_`, `ghs_`, `ghr_`).
- Detect AWS secret access keys, but only in context (an
  `aws_secret_access_key`-style label plus value), since a bare 40-char
  base64 string is indistinguishable from a git SHA.
- Redact credentials embedded in a connection URL
  (`scheme://user:password@host/...`), including the host, port, path,
  and any query-string secret, so a leaked DSN is fully masked.
- Add Stripe, GitLab, Slack app tokens and webhooks, Google API/OAuth,
  npm, PyPI, SendGrid, DigitalOcean, Shopify, Twilio, Discord, and JWT.

Bare high-entropy strings, git SHAs, and UUIDs are deliberately left
untouched to keep false positives out of normal tool output.

Add positive tests for every new shape, negative tests guarding the
false-positive cases, and document the full list as a bullet list in
the extension `README.md`.

Glory to the Omnissiah
Add the complementary regression assertion flagged by CodeRabbit to the
`decide` finding-1 test: alongside verifying that shell and `eval`
writes to an `Edit(...)`-only-denied path are blocked, assert that a
plain read of the same path is still permitted when no matching read
deny exists. This locks in the read/write class asymmetry the
write-bypass fix depends on.

Glory to the Omnissiah
Repository owner deleted a comment from coderabbitai Bot Jul 1, 2026
@rblaine95 rblaine95 merged commit 0d780ee into master Jul 1, 2026
2 checks passed
@rblaine95 rblaine95 deleted the fix/rules-guard-write-bypass branch July 1, 2026 12:26
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.

1 participant