/** * OpenCode permission deployment helpers shared by the audit-code and * remediate-code install flows (postinstall scripts and the remediate-code * `ensure` command). Two named scopes exist: * * - **Global top-level scope** — the `permission` block at the root of * `~/.config/opencode/opencode.json`. This scope must never seed broad * allows: no `bash["*"] = "allow"` and no forced * `external_directory["*"] = "allow"`. It also actively migrates away * previously deployed broad rules whose value exactly matches the * historically managed value (`"allow"`); a matching value is treated as * tool-managed and removed even if it happened to be user-authored * (accepted safer failure direction). Any non-matching value (e.g. `"ask"`, * `"deny"`, or a different rule shape) is left completely untouched. * - **Agent scope** — a per-agent `permission` block (e.g. * `agent.auditor.permission`). This scope keeps the * broad-allow-with-denylist deployment unchanged. */ /** The broad value the deploy helpers historically wrote ("tool-managed"). */ export declare const OPENCODE_MANAGED_BROAD_VALUE = "allow"; type PermissionRule = Record; /** Returns a copy of `rules` without its `"*"` wildcard entry. */ export declare function withoutOpenCodeWildcard(rules: PermissionRule): PermissionRule; /** * Agent-scope rule merge: generated rules seed the entry, existing * non-wildcard user entries win over generated ones, and managed rules always * win (including a managed wildcard when the caller provides one — pass * `withoutOpenCodeWildcard(rules)` to let an existing wildcard survive). */ export declare function mergeOpenCodeAgentPermissionRule(existingRule: unknown, generatedRule: unknown, managedRules?: PermissionRule): unknown; /** * Global-scope rule merge: never seeds a wildcard. An existing wildcard is * preserved verbatim unless it exactly matches the historically managed broad * value (`"allow"`), in which case it is removed as migration cleanup. * Non-wildcard rules merge like the agent scope (existing wins over * generated; managed wins over existing). */ export declare function mergeOpenCodeGlobalPermissionRule(existingRule: unknown, generatedRule: PermissionRule, managedRules?: PermissionRule): PermissionRule; /** * Global-scope `external_directory` migration: the deploy helpers * historically forced `external_directory: { "*": "allow" }` at the top * level. That broad rule is no longer seeded; when an existing entry's * wildcard exactly matches the historically managed value it is removed. * Returns `undefined` when the whole entry should be dropped from the * permission block; any non-matching value is returned untouched. */ export declare function migrateOpenCodeGlobalExternalDirectory(existingRule: unknown): unknown; /** * Deterministic, order-stable union of every agent's bash rule set into one * top-level privilege ceiling. The result is a plain object whose keys are * emitted in a stable, content-derived order (`"*"` first, then the remaining * keys sorted lexicographically) so re-deriving it never churns the artifact's * content hash. * * Semantics, per command key `K`: * - Wildcard `"*"`: the broadest wildcard across all agents (allow > ask > * deny). If no agent sets one, it defaults to `"ask"`. * - A non-wildcard `K` any agent explicitly `allow`s → ceiling `allow` (at * least one agent needs it, so the ceiling must permit it). * - Otherwise a `K` any agent explicitly `deny`s → ceiling `deny` (a shared * deny — e.g. `rm *`, `audit-code synthesize*` — survives at the ceiling). * - A `K` no agent mentions explicitly is omitted (covered by the wildcard). * * This is a true privilege ceiling: it introduces no command no agent needs, * and every agent's own rules remain a subset of it. Each agent block still * carries its own (possibly more restrictive) wildcard + denies, so widening * the ceiling to `allow` never silently grants a read-only agent another * agent's mutating commands — least-privilege is enforced per-agent. */ export declare function unionOpenCodeBashCeiling(agentBashRuleSets: ReadonlyArray): PermissionRule; /** * Compose the top-level bash block an installer should write: the managed union * ceiling of all agents (emitted in its stable, content-derived order — `"*"` * first, then sorted keys) followed by any user-authored top-level keys the * union does not cover (appended in sorted order, non-clobber). The result is * therefore order-stable regardless of which installer ran last: the managed * portion is always the same sorted union, so re-running either installer in * any order is byte-idempotent and never churns the artifact's content hash. */ export declare function composeOpenCodeBashCeiling(existingTopBash: Record | null | undefined, agentBashRuleSets: Array | null | undefined>): PermissionRule; /** * A single subset/no-unneeded/least-privilege violation found by * {@link verifyOpenCodeBashCeiling}. */ export interface OpenCodeCeilingViolation { kind: "agent_not_subset" | "ceiling_unneeded_command" | "ceiling_value_mismatch"; key: string; detail: string; } /** * Reframed INV-RCI-16 verifier. Confirms that `topLevelBash` is exactly the * union ceiling of `agentBashRuleSets`, which mechanically enforces all three * reframed properties in one check: * * 1. **Subset** — every agent's explicit rule is reflected in the ceiling at a * value at least as broad, so no agent can run a command the ceiling denies. * 2. **No unneeded command** — the ceiling introduces no command key that no * agent needs (extra top-level keys are flagged). * 3. **Least-privilege deny** — a shared `deny` (e.g. `rm *`) is preserved at * the ceiling rather than being widened away by another agent's wildcard * `allow`; each agent block keeps its own denies so a broad ceiling wildcard * never grants an agent a command it must not run. * * Mutually key-aware by construction: it consumes the full agent-rule-set list, * so it accepts each installer's keys in the shared block — either installer can * regenerate the file and this verifier greenlights the same state. * * By default the "no unneeded command" property is enforced strictly: a * top-level key no agent needs is a violation. This is the right check for the * fully tool-generated committed asset. Pass `allowExtraTopLevelKeys: true` to * relax it for a real user config, where a user may add their own top-level * bash rules that the installer preserves (non-clobber) — those must not count * as tool-introduced unneeded commands. * * Returns an empty array when the ceiling is valid; otherwise one entry per * violation. Callers throw/report as they see fit. */ export declare function verifyOpenCodeBashCeiling(topLevelBash: Record | null | undefined, agentBashRuleSets: Array | null | undefined>, options?: { allowExtraTopLevelKeys?: boolean; }): OpenCodeCeilingViolation[]; export {}; //# sourceMappingURL=opencodePermissions.d.ts.map