vigiles API reference
    Preparing search index...

    Interface CommandView

    An AST-backed view of a Bash command — the author never writes a regex.

    interface CommandView {
        raw: string;
        isSideEffecting(): boolean;
        pipesToShell(): boolean;
        runs(program: string, opts?: { force?: boolean }): boolean;
        touches(prefixes: readonly string[]): boolean;
        writesTo(prefixes: readonly string[]): boolean;
        writeTargets(prefixes: readonly string[]): readonly string[];
    }
    Index

    Properties

    raw: string

    Methods

    • True iff the command is provably side-effecting (bash-effects classifier).

      Returns boolean

    • True iff the command pipes into a BARE shell interpreter (curl … | sh, … | bash -s) — the remote-code-execution shape. High-signal: a shell leaf WITH a script-file argument (sh deploy.sh) is NOT flagged; only a shell reading from stdin is, which only happens downstream of a pipe.

      Returns boolean

    • True iff a leaf command runs program (e.g. "git push"), optionally with --force/-f.

      Parameters

      • program: string
      • Optionalopts: { force?: boolean }

      Returns boolean

    • True iff a leaf command MENTIONS a path under one of the prefixes (e.g. ~/.ssh, .env) — the secret-read / sensitive-path matcher. Sees the path however the command is wrapped (cd x && cat ~/.ssh/id_rsa).

      MENTIONS, not writes. grep -c x notes/S.md touches notes — so does rm -rf notes. Pairing this with isSideEffecting, which classifies the WHOLE command line, does NOT recover the difference: a plain read whose line happens to be side-effecting for an unrelated reason (grep -c x notes/S.md 2>/dev/null) matches both and gets blocked. To gate WRITES to a directory, use writesTo; conflating the two is the trap.

      OVER-INCLUSIVE ON PURPOSE, AND THE OPPOSITE WAY ROUND FROM PathView.under. This is a DENYLIST primitive — every caller spells touches(secret) ? deny() : allow() — so a miss is an ALLOW, not silence. An unprovable answer is therefore reported as a MATCH: a token that could name something under the prefix under some root or some cd matches, and the gate blocks. See prefixVerdict for the three answers and matchesPrefix for the two biases.

      Parameters

      • prefixes: readonly string[]

      Returns boolean

    • True iff a leaf command CREATES OR MODIFIES a file under one of the prefixes — the "don't let Bash write here" matcher, and the precise counterpart to touches. Two sources, both AST-backed:

      • Redirection targets — cmd > f, cmd >> f, cmd >| f, cmd &> f. This is the single most common write shape, and it lives on the statement, not in any argv.
      • File-writing programs, at the argv positions that actually name the file written: sed -i, cp/mv/install (destination), tee, dd of=, truncate, shred.

      A path merely READ never matches — cat a/paper.md, grep x a/paper.md, cp a/paper.md /tmp/x (the source is read, /tmp/x is the write) are all false for writesTo(["a"]). Quoting is handled by the parser, so a write QUOTED inside another command does not match: in echo 'echo y > a/paper.md' > /tmp/note.txt the only real target is /tmp/note.txt.

      Deletion is a different question and is deliberately NOT reported here — pair with runs("rm") if a gate cares about removal too.

      Exactly writeTargets(prefixes).length > 0, and implemented as that — reach for writeTargets when the gate needs to know WHICH file.

      Parameters

      • prefixes: readonly string[]

      Returns boolean

    • The write targets of this command that fall under one of the prefixes — the "WHICH file is written" counterpart to writesTo. Same two AST-backed sources (redirection targets + file-writing programs' argv positions), same denylist bias (an undecidable placement is INCLUDED).

      Spelling is as-written after normalization — quote-unwrapped, $HOME-canonicalized, and resolved against the leaf's own chdir wrapper (env -C dir sed -i x reports dir/x) — in order of appearance, exact duplicates collapsed. Filter by basename/suffix; never re-match the prefixes by hand, because hand-rolled prefix matching is the exact source of the trailing-slash, absolute-path and root-blindness defects this vocabulary exists to remove.

      An empty array ⇔ writesTo(prefixes) === false, so the natural writeTargets(P).some(pred) needs no emptiness check to behave correctly.

      prefixes is REQUIRED and there is no unfiltered overload: the raw list does not cross the API boundary, because a consumer holding it has to re-implement the matching that prefixVerdict exists to own.

      Parameters

      • prefixes: readonly string[]

      Returns readonly string[]