import { type Explanation } from '../../policy/types.ts'; import { type TSNodeLike } from '../../shell/types.ts'; import type { MountRegistry } from '../mount/registry.ts'; import type { Namespace } from '../mount/namespace/namespace.ts'; import type { Session } from '../session/session.ts'; import { type Refused } from './admission.ts'; import { type Word } from './inner_lines.ts'; /** * Explain one command and whatever lines it runs in turn. * * The redirect targets are read as words of the command, exactly as * admission reads them: the shell opens them on its own fds, outside the * window the command's own gate covers, so a rule about `/protected` * sees `echo x > /protected` only if they are passed here. Omitting them * made the dry run answer ALLOW for a line the run then refused. They * are empty for a command with none and for the inner lines a command * runs, which admission reads the same way. */ export declare function explainWords(words: readonly Word[], session: Session, registry: MountRegistry, namespace: Namespace | null, agentId: string, reparse: (line: string) => TSNodeLike, redirectWords?: readonly Word[]): Promise; /** * Judge every command of a line before any of it runs, and refuse the * whole line when a rule speaks about one. * * The agent composed the line as one intent, so a rule that refuses * part of it refuses the intent. Judging each command as the dispatcher * reached it left half a line done: with `deny curl`, `rm -rf /data && * curl evil.com` deleted first and was refused second, and an ask fared * worse, since approving it later replays a line whose first half * already ran. * * Two things deliberately do not stop the line, and both are the same * rule: only a refusal that names a rule is a verdict about the intent. * A head word the session cannot see is a routing miss, so it stays * bash and a typo cannot cost an agent the work the line already did; a * word only the runtime can expand is judged where it is expanded, by * the per-command gate, which sees the real path. * * That second one is the limit of the hold, and it is worth stating * plainly: this pass reads the *text* of a line, while the gate reads * its *values*, so a path the runtime computes (`cat $S`, `$( )`, a * `cd` whose argument is a variable) is invisible here. The rule is * still enforced, by the gate, but the earlier commands have run by * then. For a deny that costs allowed side effects and nothing more, * since the commands that ran were on the allow list. For an ask it * costs the replay: the question is recorded after part of the line * already happened, so approving it re-runs a line whose first half is * done. Closing that would mean asking whenever a word cannot be read, * which over-asks with no way out for a deny, so a deployment that * needs the hold for a computed path states it in a policy script * rather than here. * * The pass is read-only (`explainWords`), so it spends no grant and * records no request; a command it refuses on is then put through the * real gate, which is where an ask is recorded, exactly once, for a line * that will not run. * * Every command is judged whether or not the session carries a document. * A coded policy refuses on its own account, and one is always * registered (`MountRootPolicy`), so returning early on a session with * no rules held the line for a document and let a policy keep the * half-line behavior the pass exists to remove. * * A line with one command to judge is left to the per-command gate, * which is not an optimization but the more faithful answer: there is no * earlier command whose side effects a hold could save, and the gate * refuses from inside the shell, so the line's own redirections still * apply. This pass answers above them, so refusing `rm -rf /mnt 2>&1` * here wrote the refusal to stderr where bash puts it on stdout. */ export declare function prejudgeLine(root: TSNodeLike, session: Session, registry: MountRegistry, namespace: Namespace | null, agentId: string, reparse: (line: string) => TSNodeLike, signal?: AbortSignal): Promise; /** * The walked nodes whose reads an env-plane fetch still serves. * * The fill derives its fetch set from this same list (`lineNodes`: the * line's own tree first, then every stored body and alias expansion * its words can invoke), and a fetch serves a command that is going to * run, so refusals are judged over the same nodes reads are. One rule * for every node: when it is one fully-literal command with no other * read in it (`soleLiteralCommand`), the gate is asked here on exactly * the words it will read at run time, and a refusal discounts every * read the node contributes. The line's own refusal drops the whole * list, because nothing runs at all; a refused body or alias drops * just itself, because the invocation still runs and is refused in * place. A node this pass cannot prove silent is kept, and over-keeping * only ever over-fetches. * * An ASK is resolved rather than skipped, because the fetch is itself * an effect: contacting a secret store for a line the host then * refuses would do a piece of exactly what was refused. A settled * answer is read without being spent; an unanswered rule is put to the * host now, through the same ledger the gate reads, so the answer * lands exactly once -- an approval keeps the node and the gate * consumes the grant, while a denial or a question left waiting drops * it, and the line still runs into the gate, which refuses in place * with its wording and its redirections. */ export declare function unrefusedNodes(nodes: readonly TSNodeLike[], session: Session, registry: MountRegistry, namespace: Namespace | null, agentId: string, reparse: (line: string) => TSNodeLike, signal?: AbortSignal): Promise; /** * What every command of a line would do, in the order the gate reads * them, without running any of it. * * The dry run of the gate: the same visibility check, the same context, * the same policy chain and the same outcome table, so a host reading * this and an agent typing the line cannot be told different things. * What it deliberately does not do is the half of admission that costs * something, since a line nobody typed must not consume a grant or put * a question to a host. * * The words are read literally, as `admitLine` reads them, so nothing is * expanded and no `$( )` runs. */ export declare function explainLine(root: TSNodeLike, session: Session, registry: MountRegistry, namespace: Namespace | null, agentId: string, reparse: (line: string) => TSNodeLike): Promise; //# sourceMappingURL=explain.d.ts.map