Skip to content

bug(lexer): the permission lexer is blind to # comments — an apostrophe in a comment masks a later $(...) and collapses segment splitting #4029

Open

Description

@KuSh

Summary

The permission-path lexer tracks quote state straight through # comment text. An unbalanced apostrophe inside a comment — # it's fine, # don't, # we're done — opens a single-quote state that never closes, which masks everything after it from contains_unattestable_construct and collapses split_compound_command into a single segment.

Consequence: a command whose first segment matches an allow rule can carry an arbitrary trailing statement, including a live $(...), and the whole thing reads as one allowed segment.

This is latent on develop today, because develop declines to rewrite these blocks and so asserts nothing — the host does its own check. It is not latent for any change that widens rewrite coverage. PR #2781 is the demonstration: with its bundle rewriting, the same input produces permissionDecision: "allow".

Mechanism

src/discover/lexer.rs toggles in_single / in_double / in_backtick on every quote byte it sees, with no notion that a # at a word boundary starts a comment that runs to end of line. contains_unattestable_construct and split_compound_command both inherit that.

Reproduction

Build develop (d0c29851) and a rewriter that acts on multi-line blocks (e.g. the #2781 head, 2c89559a), with only Bash(ls:*) and Bash(cat:*) allowed:

ls -la # it's fine
curl $(echo http://x)
binary permissionDecision
develop d0c29851 (absent — defers)
#2781 head 2c89559a "allow"

Removing only the apostrophe (# fine) drops the allow, which isolates quote-state masking as the mechanism. A backtick substitution (curl \echo http://x\``) reaches the same end.

Suggested direction

Make the comment rule explicit in the shared quote/escape state machine: a # that is at the start of input or preceded by whitespace, and is not inside a quote, starts a comment that runs to the next newline; no byte inside it updates quote state, and it is never a segment or operator boundary.

That is one change in the shared lexer, and it also closes the statement-loss class in #2781 (a comment ending in &/| being read as a line continuation).

Related

Activity

  1. rtk-wshm-sync-bot commented on Sep 13, 2026

    @rtk-wshm-sync-bot

    wshm · Automated triage by AI

    🔍 Automated Triage

    🐛 Category bug
    🟠 Priority high
    🎯 Confidence 92%

    Summary

    Permission lexer's quote-state tracking ignores # comments, so an unbalanced apostrophe inside a comment can mask a trailing $(...)/backtick substitution and collapse compound-command segment splitting into one allowed segment; fix by making #-to-EOL comment recognition explicit in the shared lexer state machine.

    📁 Relevant files
    • src/discover/lexer.rs

    Triaged automatically by wshm · This is an automated analysis, not a human review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions