/** * Hardline patterns: [regex, human description]. Matched case-insensitively against the normalized * command. * * **Every destructive-verb pattern is anchored at `CMD_POS`, and must stay anchored.** A word * boundary (`\brm`) — or no anchor at all — matches the verb ANYWHERE, including inside prose and * inside another command's arguments. Measured over 30 legitimate commands, the unanchored floor * refused 10 of them: `echo never run rm -rf /`, `grep -c mkfs docs/*.md`, * `rg -n "dd of=/dev/sd" scripts/`, `grep -rn "kill -1" packages/` and more. **The floor refused * commands that merely talk about the floor**, unappealably, at every rung including `bypass`. * * `CMD_POS` consumes the wrapper programs and admits every separator position, so * `sudo rm -rf /`, `ls -la; rm -rf /` and `ls\nrm -rf /` all keep refusing. * * **What anchoring gives up** is the interpreter-wrapper forms — `sh -c "rm -rf /"`, * `bash -c "mkfs.ext4 /dev/sda1"`. `classifyCommand` resolves those to the prefixes `sh` and * `bash`, so no deterministic layer sees them; what covers them is the RATER, which rates them * `catastrophic`, so both rated rungs escalate. Only `bypass` is unguarded, and there the user has * asked for no gate. A false positive in this layer has no recovery at any rung; a miss still has * the layers above it. The floor stays narrow and accepts the misses. * * Two patterns carry no `CMD_POS` anchor, because neither is a command-position construct. * The `>`-redirect-to-device arm is anchored differently instead — see * `OUTSIDE_SINGLE_QUOTES` — because a redirection operator appears mid-command by definition, * so the discrimination available to it is whether the `>` is QUOTED, not where it sits. The * fork-bomb literal is anchored by nothing at all: the string *is* the fork bomb, so prose quoting * it is refused, and that false positive is accepted rather than fixed. */ export declare const HARDLINE_PATTERNS: ReadonlyArray; /** * **Every string a §8 decision is made of, in one record — exported ONLY for the snapshot gate** * (`shellHardlinePatternSurface.spec.ts`), which freezes it as a literal in its own source. Nothing * in production reads it, and nothing should: it is the module's pattern surface made addressable so * that a narrowing cannot land silently. **Any new pattern constant joins it**, and the gate's * key-set check fails if one does not. * * **Membership is decided by what the patterns are BUILT FROM, not by what this file declares** — * which is why {@link COMMAND_SEPARATOR_CLASS}, imported from `core/shell/normalize`, is a member. * Floor behaviour depends on it and it is edited in another module, so a change there is exactly the * silent narrowing this exists to make loud. * * A value composed at module load carries its parts with it: `CMD_POS` expands * `WRAPPER_ARMS`, and `ROOT_TARGET` expands `quotedOrBare`, so those parts are frozen * whether or not they are also members. The parts are listed anyway, because a diff that names * `H_TOKEN_EXCLUSIONS` is actionable where one that names only `CHOWN_HEAD` is a wall of regex. * * Regexes are recorded as `String(re)` rather than `re.source` **because the flags are part of the * behaviour**: a `g` flag makes `.test()` stateful across calls, a change no source-only record * would show. */ export declare const HARDLINE_PATTERN_SURFACE: Readonly>; /** * Whether one pipeline both reads credential material and transmits data off the machine. * Exported for tests, which pin BOTH directions: the credential-upload shapes must match, and * `git push` / `git push --force` / `gh pr create` / `npm publish` / `docker push` / `git fetch` / * `scp report.pdf host:` must not. * * @param normalizedLowerCommand the command after {@link normalizeCommand} + `toLowerCase()`, * i.e. exactly what the pattern loop in {@link checkHardline} matches against. */ export declare function isDeterministicExfiltration(normalizedLowerCommand: string): boolean; export interface HardlineMatch { /** Human-readable reason the command was refused. */ description: string; /** * [[TUI-C27]] — **which rule matched**, as the matched pattern's own source, for the * `/debug-dump` archive. * * REQUIRED rather than optional, deliberately: an optional field is one a future arm forgets to * set, and the dump would then say nothing while looking complete — the same * enumeration-with-a-hole shape `CMD_POS` records about itself. The exfiltration arm has no * single pattern (it is a per-pipeline source/sink test), so it carries the stable token * {@link EXFILTRATION_ARM} instead; the prose lives in `description`, so anyone grepping this * field for a pattern source never gets a sentence. * * **It is read by the diagnostic archive and by nothing user-facing.** §8.1's rule that the floor * is never advertised governs rung descriptions and promotional copy — text inviting a user to * feel safe — not a dump a user opens about their own session. {@link buildHardlineRefusal}, * which is the user-facing surface, does not read this field. */ pattern: string; } /** * The {@link HardlineMatch.pattern} token for the arm that is not a pattern: §3/§8's deterministic * exfiltration test, which decides per pipeline rather than by one regex. */ export declare const EXFILTRATION_ARM = "deterministic-exfiltration"; /** Options for {@link checkHardline}. */ export interface HardlineOptions { /** * The host platform to decide the `WINDOWS_HARDLINE_PATTERNS` arm against, in * `process.platform`'s vocabulary. Defaults to `process.platform`, which is what every * production caller wants. * * **It exists so the Windows arm is testable from any host, and that is not a convenience.** * Without it the arm could only ever be exercised on a Windows CI cell: nobody could develop it * on this repo's usual machines, and a regression would be invisible on four of the unit * matrix's five cells — the majority of the signal, including every local run before a merge. * With it, both arms run everywhere, and the Windows cells additionally prove that the real * `process.platform` branch selects the arm the injected value stands in for. */ platform?: NodeJS.Platform; } /** * Check a raw command against the hardline blocklist. Normalizes first so * obfuscated variants are caught. Returns the match (with a description) when the * command is catastrophic, or `null` when it is allowed to proceed. * * The shared patterns are tried on every host. The win32 arm is tried in addition on Windows — * never instead of, because Git Bash, MSYS and WSL run the POSIX shapes on a Windows box too. */ export declare function checkHardline(command: string, options?: HardlineOptions): HardlineMatch | null; /** * The refusal a floor match produces, shared by both call sites (§8, §4.2). * * One wording, because they are one policy: the gate refuses the call before any rating or prompt, * and the toolkit refuses it before spawn if anything ever reaches that far. A second sentence * would let a user meet two different explanations of the same unappealable rule and conclude that * two different rules exist. * * **It names no move.** §7's rejection moves — *"call the same command with a justification"* — are * exactly what this refusal is not: the floor is unappealable at every rung, so inviting a * justification would invite a round that cannot be won. */ export declare function buildHardlineRefusal(command: string, match: HardlineMatch): string;