/** * Parses destructive removal commands * * Catches not a "scary word" but a concrete disaster: removing the root, the * home directory, or a system tree — including through a symlink that the * command's text alone doesn't reveal. * * `rm -rf link` removes the link itself on both BusyBox and coreutils, * leaving the target intact. `rm -rf link/` (and `link/*`) behaves * differently: BusyBox still removes just the link, but coreutils empties * the target through it. That's why a trailing slash makes a path dangerous * while a bare `rm -rf link` does not: paths with a trailing slash are * resolved before the target is judged. * * This module only parses the string, with no calls to the server: pure * functions are easy to cover with tests, and the network is added one layer up. */ /** Confirmation marker for a deliberate override — the same device as the reboot hook's */ export declare const CONFIRMATION_MARKER = "# CONFIRMED-DESTRUCTIVE"; /** What a target actually means */ export type TargetVerdict = 'root' | 'system' | 'home' | 'safe'; /** A removal target found in the command */ export interface RemovalTarget { /** The argument exactly as written in the command */ raw: string; /** * The path without a trailing slash and without a trailing `*` — the part * that needs resolving. For `/var/www/data/` this is `/var/www/data`. */ path: string; /** * Whether the target's contents are affected rather than the link itself: * a path with a trailing slash or with `/*`. Only in that case is a symlink dangerous. */ followsLink: boolean; /** Expanded by the server: a variable, a substitution, a glob — nothing to parse here */ expandable: boolean; } /** Whether the command carries the explicit confirmation marker */ export declare function isConfirmed(command: string): boolean; /** * Find recursive-removal targets across the whole command. * * An empty list means "no recursive rm in the command", not "everything is * safe": the command could have been in an unrecognized form, which shows up * as the `expandable` flag on the targets that were found. */ export declare function findRemovalTargets(command: string): RemovalTarget[]; /** * What a path means when read literally. * * `home` is the home directory from the server's profile. An empty string * means "unknown": then `~` is taken as home by how it's written, since * there's nothing to compare it against. */ export declare function classifyTarget(path: string, home?: string): TargetVerdict; /** Verdict for one command */ export interface DestructiveVerdict { blocked: boolean; /** Human-readable explanation: what exactly was stopped, and why */ reason?: string; /** Targets whose fate the text alone can't settle — they need resolving on the server */ needsResolution: RemovalTarget[]; } /** * Inspect a command from its text alone. * * Returns either a ready-made refusal, or a list of targets that need * resolving on the server: a symlink is only visible from there. */ export declare function inspectCommand(command: string, home?: string): DestructiveVerdict; /** * Build the refusal the way the agent will read it. * * The text must say three things: the command was NOT run, why, and how to * run it deliberately — otherwise the agent starts looking for a way around it. */ export declare function blockedMessage(command: string, reason: string): string;