import type { ByteSource } from '../../io/types.ts'; import type { Ask, CommandContext, CommandRule, AdmissionRules, Deny } from '../../policy/index.ts'; import type { TSNodeLike } from '../../shell/types.ts'; import { PathSpec, type Refusal } from '../../types.ts'; import type { EntryGate } from '../../types.ts'; import type { Namespace } from '../mount/namespace/namespace.ts'; import type { MountRegistry } from '../mount/registry.ts'; import type { Session } from '../session/session.ts'; import { type Word } from './inner_lines.ts'; /** * What the command plane prints when a line does not get to run: 127 * for a word the session cannot see, 126 for a whole-command refusal * or an unanswered ask, the operand code (1, tar 2) for an * operand-scoped refusal. `refusal` is the record the result carries * beside stderr, null on the 127 row, which must not say the word * names anything. Mirrors the Python `Refused`. */ export interface Refused { readonly stderr: Uint8Array; readonly exitCode: number; readonly refusal: Refusal | null; } /** * A command the gate let through, and what its own I/O may touch. * * The gate judged the paths the line names; a walk below them reaches * entries no rule has seen, so the dispatcher binds this to the session * context for the command's run and the commands tier asks it before each * read, write or listing (`EntryGate`). The paths the gate already judged * pass, since the line was admitted on them; every other entry is judged * by `ioRefusal` under the same precedence the gate applied to the line, * and a refusal is the op door's `PolicyDenied` (EACCES, the reason, the * path), which every command renders as GNU's `Permission denied`. * `granted` holds the ask rules the line runs under a grant for: the one * the door answered for this line, and the session's standing ones. */ export declare class Admitted implements EntryGate { readonly rules: AdmissionRules | null; readonly tokens: readonly string[]; readonly judged: ReadonlySet; readonly granted: readonly CommandRule[]; readonly scoped: boolean; constructor(init: { rules: AdmissionRules | null; tokens: readonly string[]; judged: ReadonlySet; granted: readonly CommandRule[]; scoped: boolean; }); check(virtual: string): void; } /** * The paths a path-pattern guard reads for a line: the operands as * typed and the values of path-valued flags, then, for a command that * follows links, the targets they resolve to. `cat /data/link` reads * `/data/secret`, so a rule protecting the target has to see it, and a * command-scoped rule never runs at the op door where the resolved path * would otherwise be checked. The follow policy is the command's own * (`followsLastComponent`: rm, mv, ln, stat, tar ... act on the link * itself, `-L` turns following back on), the same one the router * applies to the operands before the handler runs, so a rule sees * exactly the path the command will touch. A loop is left to that later * step to report; here the typed paths stand. Then the operand a bare * `ls`/`find`/`du`/`tree`/`grep -r` implies, the working directory, * which the executor injects after the gate and which a rule on that * directory has to see. Last come the statement's redirect targets: * `cat < /data/secret` reads the file and `echo x > /data/secret` * truncates it, on the shell's own fds outside the admitted command's * gate window, so the admission is the one place a rule can see them. A * redirect always dereferences (the shell opens the target), so its * link targets ride along whatever the command's own follow policy * says. */ export declare function policyScopes(name: string, args: readonly string[], operands: readonly (string | PathSpec)[], namespace: Namespace | null, cwd: string, implied?: PathSpec | null, redirects?: readonly PathSpec[]): PathSpec[]; /** * The command plane's admission of one command: visibility, then the * policy chain, then the approval door. The one gate every command * class passes through, in the tree (`runArgv`, once the words are * expanded) and for a line a runtime takes whole (`admitLine`, per * parsed command). A word the session's allow lists do not install is * bash's "command not found" before any admission hook, so an unlisted * tool never leaks a deny reason; a path the session cannot see is * dropped before any hook, so a rule never names it and the door * answers ENOENT; a Deny renders in the outcome table's voice; an Ask * is answered by the door from the session's grants or the host. * `agentId` is the agent the line is attributed to, for an approval * request; `stdin` decides whether a bare `rg` reads the working * directory. */ /** * One command's literal words, classified the way the runtime would * classify them, so the gate and the run name the same paths. */ export declare function classifiedWords(name: string, args: readonly string[], session: Session, registry: MountRegistry): (string | PathSpec)[]; /** * The paths a statement's redirect targets name. * * Shared by admission and by the dry run, because a rule reads a * redirect the same way in both: a target only the runtime can expand * names no path here, and one that is not path-shaped is not a file. */ export declare function redirectPaths(words: readonly Word[], registry: MountRegistry, cwd: string): PathSpec[]; /** * Everything the gate decides about one command before anything is * spent on it: visibility, the classified context, and the policy * chain's answer. * * Split out of `admit` so a dry run can have the answer without the * consequences. Nothing here records a request, consumes a grant or * reaches the host, which is what makes it safe for `explain`; * `admit` adds exactly those and renders. * * `definedFn` is the caller vouching the head word is a shell function * defined by run time. The provision walk vouches for a function its * own script defines — the run stores that definition in the session * before the call, a dry run keeps it in plan state, where neither * `commandVisible` nor `isTool` can see it. The word is then judged * exactly as the run would judge it: exempt from the allow lists * unless a builtin shadows it (`SHELL_NAMES`), and `ctx.tool` false * accordingly. */ export declare function gate(name: string, args: readonly string[], operands: readonly (string | PathSpec)[], session: Session, registry: MountRegistry, namespace: Namespace | null, agentId?: string, stdin?: ByteSource | null, redirects?: readonly PathSpec[], definedFn?: boolean): Promise; export declare function admit(name: string, args: readonly string[], operands: readonly (string | PathSpec)[], session: Session, registry: MountRegistry, namespace: Namespace | null, agentId?: string, stdin?: ByteSource | null, redirects?: readonly PathSpec[], signal?: AbortSignal): Promise; /** * Admit every command of a line a runtime takes whole. A whole line is * a command like any other, but the runtime does the expanding, so the * gate reads the line as typed: each command is admitted on its literal * words (quotes removed, escapes resolved, a path-shaped word a path, an * installed CLI's verb path walked), and the first refusal is the * line's. A word only the runtime can expand (`$cmd`, `"$p"`, `$(...)`, * `{a,b}`) is refused wherever a rule in force would have read it: as * the command name whenever the session has any command rule, as an * argument when a rule reads that command's arguments (a pattern with a * token after the name, a path-scoped or mount-scoped rule). The words * that run other words (`eval`, `sh -c`, `xargs`, `env` ... see * `innerLines`) have those lines admitted in turn, and a line the gate * cannot read at all (a sourced file, a script, `eval "$p"`) is refused * under any command rule. A statement's redirect targets are read as * words of its command, so `cat < /data/secret` is judged on the file * it opens. A command a path or mount rule reads is refused outright * when its I/O would pass the judged words — a walk (`find`, * `grep -r`, `tar -c`) or a glob only the runtime expands — because * every line executor acts outside the entry gate (a remote sandbox's * own disk, a host process), so a walk the gate cannot follow does not * run; a runtime whose I/O rides the dispatcher could relax this by * carrying the gate. With no rule in force nothing is refused on this * account: the words are admitted as typed, which is all a coded * policy ever saw. `reparse` parses the text a word runs (`eval`, * `sh -c`) the way the line reader parsed the line. */ export declare function admitLine(root: TSNodeLike, session: Session, registry: MountRegistry, namespace: Namespace | null, agentId: string, reparse: (line: string) => TSNodeLike, signal?: AbortSignal): Promise; /** * The redirect targets of the statement holding a command, as the gate * reads its words: the raw text and the literal it names, null when * only the runtime can expand it (refused wherever a rule reads the * command's arguments, like any other word). Heredoc and herestring * bodies are content, not paths, and a numeric target is an fd * duplication; neither names a file. * * A redirect binds to one command, and which one is a question about * the tree rather than the statement: `a && b > f` and `a | b > f` both * parse as a redirected_statement wrapping the whole list, so reading * only its first child answered `a` and left `b`, the command bash * actually opens the file for, with no target at all. The walk climbs * the last-command chain instead, which is bash's own rule for a list * and a pipeline. A compound (`{ }`, a loop, a subshell) redirects * every command inside it, which is not a chain, so none is claimed * here and the op door judges the write. */ export declare function statementRedirects(node: TSNodeLike, home: string | null): Word[]; //# sourceMappingURL=admission.d.ts.map