@scopebond/hook 0.15.0 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/managed.d.ts.map +1 -1
- package/dist/managed.js +5 -3
- package/dist/managed.js.map +1 -1
- package/dist/runtime.d.ts +3 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +3 -0
- package/dist/runtime.js.map +1 -1
- package/package.json +3 -3
package/dist/managed.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"managed.d.ts","sourceRoot":"","sources":["../src/managed.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAoC,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5E,eAAO,MAAM,gBAAgB,uBAAuB,CAAC;AACrD,eAAO,MAAM,iBAAiB,sBAAsB,CAAC;AACrD,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAE3D,eAAO,MAAM,gBAAgB,8HAA+H,CAAC;AAC7J,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAC9D,KAAK,OAAO,GAAG,oBAAoB,GAAG,sBAAsB,GAAG,eAAe,CAAC;AAC/E,KAAK,YAAY,GAAG,gBAAgB,GAAG,mBAAmB,CAAC;AAuB3D,MAAM,MAAM,WAAW,GAAG;IAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,GAAG,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AAC5G,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,yBAAyB,CAAC;IAChC,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;IAC1C,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,aAAa,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,gBAAgB,GAAG,aAAa,GAAG,cAAc,CAAC;AACtH,MAAM,MAAM,UAAU,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,eAAe,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpH,eAAO,MAAM,WAAW,GAAI,OAAO,OAAO,KAAG,MAAqE,CAAC;AAInH,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,UAAU,CA2C3J;AAuBD,oHAAoH;AACpH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"managed.d.ts","sourceRoot":"","sources":["../src/managed.ts"],"names":[],"mappings":"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"}
|
package/dist/managed.js
CHANGED
|
@@ -21,7 +21,7 @@ import { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync,
|
|
|
21
21
|
import { join } from "node:path";
|
|
22
22
|
import { createHash, randomBytes } from "node:crypto";
|
|
23
23
|
import { canonical } from "@scopebond/policy-schema/canonical";
|
|
24
|
-
import { GUARDRAIL_LOOKAHEADS, GUARDRAIL_WRITE_PATTERN, ci } from "./runtime.js";
|
|
24
|
+
import { GUARDRAIL_LOOKAHEADS, GUARDRAIL_READ_PATTERN, GUARDRAIL_WRITE_PATTERN, ci } from "./runtime.js";
|
|
25
25
|
import { compile, defaultRules, loadRules } from "./rules.js";
|
|
26
26
|
export const MANAGED_DOC_FILE = "managed-rules.json";
|
|
27
27
|
export const MANAGED_META_FILE = "managed-meta.json";
|
|
@@ -139,8 +139,10 @@ export function compileManaged(local, doc, agentKid) {
|
|
|
139
139
|
}
|
|
140
140
|
else if (clause.id === "safe-shell" && r["destructive-shell"].mode === "monitor")
|
|
141
141
|
clauses.push(allowAll(clause));
|
|
142
|
-
else if (clause.id === "protect-read" && r["secret-read"].mode === "monitor")
|
|
143
|
-
clauses.push(
|
|
142
|
+
else if (clause.id === "protect-read" && r["secret-read"].mode === "monitor") {
|
|
143
|
+
clauses.push({ ...clause, param_bounds: { path: { pattern: GUARDRAIL_READ_PATTERN } },
|
|
144
|
+
description: "Allow workspace reads, but never Scopebond's own folder, which holds this computer's key (always on). Other protected files are recorded, not blocked: set by your workspace." });
|
|
145
|
+
}
|
|
144
146
|
else if (clause.id === "protect-write" && r["ci-config-write"].mode === "monitor") {
|
|
145
147
|
clauses.push({ ...clause, param_bounds: { path: { pattern: GUARDRAIL_WRITE_PATTERN } },
|
|
146
148
|
description: "Allow workspace writes, but never to Scopebond's own settings or the agents' hook settings (always on). Other protected files are recorded, not blocked: set by your workspace." });
|
package/dist/managed.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"managed.js","sourceRoot":"","sources":["../src/managed.ts"],"names":[],"mappings":"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","sourcesContent":["// Rules managed from a Scopebond Cloud workspace.\n//\n// A connected computer keeps its own rules (`rules.json` compiled into `policy.json`) until the workspace sets rules for it.\n// From then on the workspace decides, rule by rule, whether a matching action is blocked or only recorded, and may add entries\n// to the computer's own lists (protected branches, programs, allowed sites). It never sends a pattern: this module compiles the\n// workspace's choices with the same compiler `rules apply` uses, so what decides a block lives in one place.\n//\n// A workspace may also name exact targets one rule skips: paths for the secret-read and CI-configuration rules, branches for the\n// push rule. An exclusion matches exactly (same case; a trailing `/**` covers a folder), so it is never broader than what was typed,\n// and an exclusion that would touch the always-on protection of the hook's own settings is dropped when the policy is compiled.\n//\n// What a workspace cannot change: the protection of the hook's own settings (and the settings of the agents it guards), the\n// machine key policy, fail-closed handling of anything unparseable, and the computer's own opt-in extras (`allowed_roots`,\n// `protect_remote_database`). A rule set to Monitor is still covered by an allow clause, so the action is recorded and passes;\n// leaving it out would block the whole action type, because the verifier denies an action no clause covers.\n//\n// Loading is checked and atomic: the document must be well formed, name this computer, carry a matching digest and be newer\n// than the one in force; the merged policy must build; policy.json is written through a temp file and a rename, the previous\n// one kept. Disconnecting, or a workspace that stops managing this computer, restores the computer's own rules.\n\nimport { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { canonical } from \"@scopebond/policy-schema/canonical\";\nimport { GUARDRAIL_LOOKAHEADS, GUARDRAIL_WRITE_PATTERN, ci } from \"./runtime.js\";\nimport { compile, defaultRules, loadRules, type RuleSet } from \"./rules.js\";\n\nexport const MANAGED_DOC_FILE = \"managed-rules.json\";\nexport const MANAGED_META_FILE = \"managed-meta.json\";\nexport const PREVIOUS_POLICY_FILE = \"policy.previous.json\";\n\nexport const MANAGED_RULE_IDS = [\"force-push-protected\", \"push-protected\", \"destructive-shell\", \"secret-read\", \"ci-config-write\", \"network-egress\"] as const;\nexport type ManagedRuleId = (typeof MANAGED_RULE_IDS)[number];\ntype ListKey = \"protected_branches\" | \"destructive_programs\" | \"allowed_hosts\";\ntype ExclusionKey = \"excluded_paths\" | \"excluded_branches\";\nconst LIST_OF: Partial<Record<ManagedRuleId, ListKey>> = {\n \"force-push-protected\": \"protected_branches\", \"push-protected\": \"protected_branches\",\n \"destructive-shell\": \"destructive_programs\", \"network-egress\": \"allowed_hosts\",\n};\nconst LIST_PATTERN: Record<ListKey, RegExp> = {\n protected_branches: /^[A-Za-z0-9._/*-]{1,100}$/,\n destructive_programs: /^[a-z0-9][a-z0-9._-]{0,63}$/,\n allowed_hosts: /^(?=.{1,253}$)(\\*\\.)?([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$/,\n};\n/** The exact targets a rule may skip, and the one list each rule accepts. */\nconst EXCLUSIONS_OF: Partial<Record<ManagedRuleId, ExclusionKey>> = {\n \"secret-read\": \"excluded_paths\", \"ci-config-write\": \"excluded_paths\", \"push-protected\": \"excluded_branches\",\n};\nconst EXCLUSION_PATTERN: Record<ExclusionKey, RegExp> = {\n // A relative path with forward slashes: no empty, \".\" or \"..\" segment, optionally ending in /** for a folder.\n excluded_paths: /^(?!\\/)(?!.*\\/\\/)(?!(?:.*\\/)?\\.{1,2}(?:\\/|$))[A-Za-z0-9._@+-][A-Za-z0-9._@+/-]{0,199}?(?:\\/\\*\\*)?$/,\n // One exact branch name; it starts with a letter or digit, so it can never be a push flag such as --all.\n excluded_branches: /^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$/,\n};\nconst HEX64 = /^[0-9a-f]{64}$/;\nconst OPAQUE = /^[A-Za-z0-9._:-]{1,128}$/;\n\nexport type ManagedRule = { mode: \"monitor\" | \"block\" } & Partial<Record<ListKey | ExclusionKey, string[]>>;\nexport interface ManagedDocument {\n type: \"scopebond:managed-rules\";\n version: 1;\n revision: number;\n export_id: string;\n environment_id: string;\n agent_id: string;\n installation_id: string;\n rules: Record<ManagedRuleId, ManagedRule>;\n rules_digest: string;\n}\n\nexport type RefusalReason = \"invalid_document\" | \"stale_revision\" | \"wrong_computer\" | \"unsupported\" | \"write_failed\";\nexport type Inspection = { ok: true; doc: ManagedDocument } | { ok: false; reason: RefusalReason; message: string };\n\nexport const digestRules = (rules: unknown): string => createHash(\"sha256\").update(canonical(rules)).digest(\"hex\");\n\nconst isObject = (v: unknown): v is Record<string, unknown> => typeof v === \"object\" && v !== null && !Array.isArray(v);\n\n/** Check a document from the workspace without touching disk. Pure. */\nexport function inspectManaged(raw: unknown, context: { installationId: string; currentRevision: number | null; currentDigest?: string | null }): Inspection {\n const bad = (message: string): Inspection => ({ ok: false, reason: \"invalid_document\", message });\n if (!isObject(raw) || raw.type !== \"scopebond:managed-rules\") return bad(\"this is not a Scopebond workspace rules document\");\n if (raw.version !== 1) return { ok: false, reason: \"unsupported\", message: `unsupported rules document version ${String(raw.version)}` };\n const { revision, export_id: exportId, environment_id: environmentId, agent_id: agentId, installation_id: installationId, rules, rules_digest: rulesDigest } = raw;\n if (!Number.isInteger(revision) || (revision as number) < 1 || (revision as number) > 2_147_483_647\n || typeof exportId !== \"string\" || typeof environmentId !== \"string\" || !OPAQUE.test(environmentId)\n || typeof agentId !== \"string\" || !OPAQUE.test(agentId) || typeof installationId !== \"string\" || !OPAQUE.test(installationId)\n || typeof rulesDigest !== \"string\" || !HEX64.test(rulesDigest) || !isObject(rules)) {\n return bad(\"the rules document is incomplete or malformed\");\n }\n if (installationId !== context.installationId || exportId !== `rev-${revision}-${installationId}`) {\n return { ok: false, reason: \"wrong_computer\", message: \"these rules were issued for a different computer\" };\n }\n const keys = Object.keys(rules);\n if (keys.length !== MANAGED_RULE_IDS.length || !MANAGED_RULE_IDS.every((id) => keys.includes(id))) {\n return { ok: false, reason: \"unsupported\", message: \"the rules document names rules this version does not know; update Scopebond\" };\n }\n for (const id of MANAGED_RULE_IDS) {\n const rule = rules[id];\n if (!isObject(rule) || (rule.mode !== \"monitor\" && rule.mode !== \"block\")) return bad(`rule ${id} has no valid mode`);\n for (const key of Object.keys(rule)) {\n if (key === \"mode\") continue;\n const listKey = LIST_OF[id];\n const exclusionKey = EXCLUSIONS_OF[id];\n const pattern = key === listKey ? LIST_PATTERN[listKey] : key === exclusionKey ? EXCLUSION_PATTERN[exclusionKey] : null;\n if (!pattern) return bad(`rule ${id} has an unknown setting`);\n const list = rule[key];\n if (!Array.isArray(list) || list.length > 50 || !list.every((v) => typeof v === \"string\" && pattern.test(v) && !v.includes(\"..\"))) {\n return bad(`rule ${id} has an invalid list`);\n }\n }\n }\n const network = rules[\"network-egress\"] as ManagedRule;\n if (network.mode === \"block\" && !(network.allowed_hosts?.length)) return bad(\"network blocking needs at least one allowed site\");\n if (digestRules(rules) !== rulesDigest) return bad(\"the rules do not match their digest; the document was changed or damaged\");\n // The same version with different rules is a newer document too: the workspace can change what reaches this computer without a new\n // version (an agent moved to another team, or this hook started reading a setting it did not read before). The export id binds it here.\n const sameVersionChanged = context.currentRevision !== null && revision === context.currentRevision && !!context.currentDigest && rulesDigest !== context.currentDigest;\n if (context.currentRevision !== null && (revision as number) <= context.currentRevision && !sameVersionChanged) {\n return { ok: false, reason: \"stale_revision\", message: `version ${String(revision)} is not newer than the version in force (${context.currentRevision})` };\n }\n return { ok: true, doc: raw as unknown as ManagedDocument };\n}\n\nconst union = (a: readonly string[], b: readonly string[] | undefined): string[] => [...new Set([...a, ...(b ?? [])])];\nconst branchGlob = (b: string): string => (b.endsWith(\"/*\") ? `${b.slice(0, -1)}**` : b);\nconst hostPattern = (hosts: readonly string[]): string =>\n `^(?:${hosts.map((h) => (h.startsWith(\"*.\") ? `(?:[^.]+\\\\.)+${ci(h.slice(2).replace(/\\./g, \"\\\\.\"))}` : ci(h.replace(/\\./g, \"\\\\.\")))).join(\"|\")})$`;\n\ntype Clause = Record<string, unknown> & { id: string };\n\nconst escapeRegex = (text: string): string => text.replace(/[\\\\^$.*+?()[\\]{}|/]/g, \"\\\\$&\");\n/** Exclusions that passed validation (the document check refuses the rest; this is a second line of defence). */\nconst safeExclusions = (paths: readonly string[] | undefined): string[] => (paths ?? []).filter((path) => EXCLUSION_PATTERN.excluded_paths.test(path) && !path.includes(\"..\"));\n/** An allow pattern that also allows exactly these paths (a trailing /** allows everything under the folder). The added alternative\n * carries the always-on floor and refuses any path with a \".\" or \"..\" segment, so no exclusion — however it is written — can reach\n * Scopebond's own settings or the agents' hook settings, or climb out of the folder it names. */\nconst withPathExclusions = (pattern: string, paths: readonly string[]): string => paths.length === 0 ? pattern\n : `^(?!(?:.*/)?\\\\.{1,2}(?:/|$))${GUARDRAIL_LOOKAHEADS}(?:${paths.map((p) => p.endsWith(\"/**\") ? `${escapeRegex(p.slice(0, -3))}/.+` : escapeRegex(p)).join(\"|\")})$|${pattern}`;\n/** An allow pattern for pushes that also allows exactly these branches. */\nconst withBranchExclusions = (pattern: string, branches: readonly string[]): string => branches.length === 0 ? pattern\n : `^(?:refs/heads/)?(?:${branches.map(escapeRegex).join(\"|\")})$|${pattern}`;\nconst boundPattern = (clause: Clause, key: string): string => String(((clause.param_bounds as Record<string, { pattern: string }> | undefined)?.[key])?.pattern ?? \"\");\nconst withBound = (clause: Clause, key: string, pattern: string): Clause => ({ ...clause, param_bounds: { ...(clause.param_bounds as Record<string, unknown>), [key]: { pattern } } });\n\n/** The policy this computer enforces under workspace rules: its own compiled rules, adjusted rule by rule. Pure. */\nexport function compileManaged(local: RuleSet, doc: ManagedDocument, agentKid: string): Record<string, unknown> {\n const r = doc.rules;\n const rules: RuleSet = {\n ...local,\n protected_branches: union(local.protected_branches, [...(r[\"push-protected\"].protected_branches ?? []), ...(r[\"force-push-protected\"].protected_branches ?? [])]),\n destructive_programs: union(local.destructive_programs, r[\"destructive-shell\"].destructive_programs),\n };\n const policy = compile(rules, agentKid) as { clauses: Clause[] } & Record<string, unknown>;\n const allowAll = (clause: Clause): Clause => { const { param_bounds: _dropped, ...rest } = clause; return { ...rest, mode: \"enforce\", description: `${String(clause.description ?? \"\").split(\".\")[0]}. Recorded, not blocked: set by your workspace.` }; };\n const clauses: Clause[] = [];\n for (const clause of policy.clauses) {\n if (clause.id === \"protect-branches\" && r[\"push-protected\"].mode === \"monitor\") {\n clauses.push(allowAll(clause));\n if (r[\"force-push-protected\"].mode === \"block\") {\n clauses.push({ id: \"protect-branch-history\", type: \"force_push_guard\", mode: \"enforce\", protected_refs: rules.protected_branches.map(branchGlob),\n description: \"Deny force-pushes, deletions and mirror pushes that reach a protected branch; ordinary pushes are allowed. Set by your workspace.\" });\n }\n } else if (clause.id === \"safe-shell\" && r[\"destructive-shell\"].mode === \"monitor\") clauses.push(allowAll(clause));\n else if (clause.id === \"protect-read\" && r[\"secret-read\"].mode === \"monitor\") clauses.push(allowAll(clause));\n else if (clause.id === \"protect-write\" && r[\"ci-config-write\"].mode === \"monitor\") {\n clauses.push({ ...clause, param_bounds: { path: { pattern: GUARDRAIL_WRITE_PATTERN } },\n description: \"Allow workspace writes, but never to Scopebond's own settings or the agents' hook settings (always on). Other protected files are recorded, not blocked: set by your workspace.\" });\n } else if (clause.id === \"protect-read\" && r[\"secret-read\"].excluded_paths?.length) {\n clauses.push(withBound(clause, \"path\", withPathExclusions(boundPattern(clause, \"path\"), safeExclusions(r[\"secret-read\"].excluded_paths))));\n } else if (clause.id === \"protect-write\" && r[\"ci-config-write\"].excluded_paths?.length) {\n clauses.push(withBound(clause, \"path\", withPathExclusions(boundPattern(clause, \"path\"), safeExclusions(r[\"ci-config-write\"].excluded_paths))));\n } else if (clause.id === \"protect-branches\" && r[\"push-protected\"].excluded_branches?.length) {\n clauses.push(withBound(clause, \"ref\", withBranchExclusions(boundPattern(clause, \"ref\"), r[\"push-protected\"].excluded_branches)));\n // Skipping ordinary pushes to a branch never skips history protection: force-pushes, deletions and mirror pushes stay stopped.\n if (r[\"force-push-protected\"].mode === \"block\") {\n clauses.push({ id: \"protect-branch-history\", type: \"force_push_guard\", mode: \"enforce\", protected_refs: rules.protected_branches.map(branchGlob),\n description: \"Deny force-pushes, deletions and mirror pushes that reach a protected branch. Set by your workspace.\" });\n }\n } else clauses.push(clause);\n }\n if (r[\"network-egress\"].mode === \"block\") {\n const keysAt = clauses.findIndex((c) => c.id === \"keys\");\n clauses.splice(keysAt < 0 ? clauses.length : keysAt, 0, {\n id: \"allowed-sites\", type: \"action_allowlist\", mode: \"enforce\", action_types: [\"net.fetch\"],\n param_bounds: { host: { pattern: hostPattern(r[\"network-egress\"].allowed_hosts ?? []) } },\n description: `Deny web requests to sites other than ${r[\"network-egress\"].allowed_hosts!.join(\", \")}. Set by your workspace.`,\n });\n }\n return { ...policy, policy_id: \"coding-agent\", version: doc.revision, clauses };\n}\n\n// --- on disk ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ManagedMeta {\n revision: number | null;\n rules_digest: string | null;\n export_id: string | null;\n etag: string | null;\n checked_at: string | null;\n last_ack: { revision: number; result: \"loaded\" | \"rejected\"; at: string } | null;\n last_error: string | null;\n}\nconst EMPTY_META: ManagedMeta = { revision: null, rules_digest: null, export_id: null, etag: null, checked_at: null, last_ack: null, last_error: null };\n\nexport function readMeta(dir: string): ManagedMeta {\n try { return { ...EMPTY_META, ...(JSON.parse(readFileSync(join(dir, MANAGED_META_FILE), \"utf8\")) as Partial<ManagedMeta>) }; } catch { return { ...EMPTY_META }; }\n}\nexport function writeMeta(dir: string, meta: ManagedMeta): void { writeAtomic(join(dir, MANAGED_META_FILE), `${JSON.stringify(meta, null, 2)}\\n`); }\n\n/** Whether a workspace currently manages the rules on this computer. */\nexport const isManaged = (dir: string): boolean => existsSync(join(dir, MANAGED_DOC_FILE));\n\nexport function writeAtomic(file: string, text: string): void {\n const temp = `${file}.${randomBytes(6).toString(\"hex\")}.tmp`;\n try { writeFileSync(temp, text, { mode: 0o600 }); renameSync(temp, file); }\n catch (error) { try { rmSync(temp, { force: true }); } catch { /* nothing to remove */ } throw error; }\n}\n\n/** Install a checked document: write the merged policy (previous kept) and the document. `policyBuilds` is the caller's check\n * that the gateway can load the result. */\nexport function installManaged(dir: string, doc: ManagedDocument, agentKid: string, policyBuilds: (policy: Record<string, unknown>) => boolean): { ok: true } | { ok: false; reason: RefusalReason; message: string } {\n const local = loadRules(dir) ?? defaultRules();\n const policy = compileManaged(local, doc, agentKid);\n if (!policyBuilds(policy)) return { ok: false, reason: \"unsupported\", message: \"this version of Scopebond cannot load the workspace rules; update it\" };\n try {\n mkdirSync(dir, { recursive: true });\n const policyPath = join(dir, \"policy.json\");\n if (existsSync(policyPath)) copyFileSync(policyPath, join(dir, PREVIOUS_POLICY_FILE));\n writeAtomic(policyPath, `${JSON.stringify(policy, null, 2)}\\n`);\n writeAtomic(join(dir, MANAGED_DOC_FILE), `${JSON.stringify(doc, null, 2)}\\n`);\n return { ok: true };\n } catch (error) {\n return { ok: false, reason: \"write_failed\", message: `could not write the rules (${(error as Error).name})` };\n }\n}\n\n/** Go back to this computer's own rules: recompile policy.json from rules.json and forget the workspace document. */\nexport function restoreLocal(dir: string, agentKid: string): void {\n const local = loadRules(dir) ?? defaultRules();\n const policyPath = join(dir, \"policy.json\");\n if (existsSync(policyPath)) copyFileSync(policyPath, join(dir, PREVIOUS_POLICY_FILE));\n writeAtomic(policyPath, `${JSON.stringify(compile(local, agentKid), null, 2)}\\n`);\n rmSync(join(dir, MANAGED_DOC_FILE), { force: true });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"managed.js","sourceRoot":"","sources":["../src/managed.ts"],"names":[],"mappings":"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","sourcesContent":["// Rules managed from a Scopebond Cloud workspace.\n//\n// A connected computer keeps its own rules (`rules.json` compiled into `policy.json`) until the workspace sets rules for it.\n// From then on the workspace decides, rule by rule, whether a matching action is blocked or only recorded, and may add entries\n// to the computer's own lists (protected branches, programs, allowed sites). It never sends a pattern: this module compiles the\n// workspace's choices with the same compiler `rules apply` uses, so what decides a block lives in one place.\n//\n// A workspace may also name exact targets one rule skips: paths for the secret-read and CI-configuration rules, branches for the\n// push rule. An exclusion matches exactly (same case; a trailing `/**` covers a folder), so it is never broader than what was typed,\n// and an exclusion that would touch the always-on protection of the hook's own settings is dropped when the policy is compiled.\n//\n// What a workspace cannot change: the protection of the hook's own settings (and the settings of the agents it guards), the\n// machine key policy, fail-closed handling of anything unparseable, and the computer's own opt-in extras (`allowed_roots`,\n// `protect_remote_database`). A rule set to Monitor is still covered by an allow clause, so the action is recorded and passes;\n// leaving it out would block the whole action type, because the verifier denies an action no clause covers.\n//\n// Loading is checked and atomic: the document must be well formed, name this computer, carry a matching digest and be newer\n// than the one in force; the merged policy must build; policy.json is written through a temp file and a rename, the previous\n// one kept. Disconnecting, or a workspace that stops managing this computer, restores the computer's own rules.\n\nimport { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { canonical } from \"@scopebond/policy-schema/canonical\";\nimport { GUARDRAIL_LOOKAHEADS, GUARDRAIL_READ_PATTERN, GUARDRAIL_WRITE_PATTERN, ci } from \"./runtime.js\";\nimport { compile, defaultRules, loadRules, type RuleSet } from \"./rules.js\";\n\nexport const MANAGED_DOC_FILE = \"managed-rules.json\";\nexport const MANAGED_META_FILE = \"managed-meta.json\";\nexport const PREVIOUS_POLICY_FILE = \"policy.previous.json\";\n\nexport const MANAGED_RULE_IDS = [\"force-push-protected\", \"push-protected\", \"destructive-shell\", \"secret-read\", \"ci-config-write\", \"network-egress\"] as const;\nexport type ManagedRuleId = (typeof MANAGED_RULE_IDS)[number];\ntype ListKey = \"protected_branches\" | \"destructive_programs\" | \"allowed_hosts\";\ntype ExclusionKey = \"excluded_paths\" | \"excluded_branches\";\nconst LIST_OF: Partial<Record<ManagedRuleId, ListKey>> = {\n \"force-push-protected\": \"protected_branches\", \"push-protected\": \"protected_branches\",\n \"destructive-shell\": \"destructive_programs\", \"network-egress\": \"allowed_hosts\",\n};\nconst LIST_PATTERN: Record<ListKey, RegExp> = {\n protected_branches: /^[A-Za-z0-9._/*-]{1,100}$/,\n destructive_programs: /^[a-z0-9][a-z0-9._-]{0,63}$/,\n allowed_hosts: /^(?=.{1,253}$)(\\*\\.)?([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$/,\n};\n/** The exact targets a rule may skip, and the one list each rule accepts. */\nconst EXCLUSIONS_OF: Partial<Record<ManagedRuleId, ExclusionKey>> = {\n \"secret-read\": \"excluded_paths\", \"ci-config-write\": \"excluded_paths\", \"push-protected\": \"excluded_branches\",\n};\nconst EXCLUSION_PATTERN: Record<ExclusionKey, RegExp> = {\n // A relative path with forward slashes: no empty, \".\" or \"..\" segment, optionally ending in /** for a folder.\n excluded_paths: /^(?!\\/)(?!.*\\/\\/)(?!(?:.*\\/)?\\.{1,2}(?:\\/|$))[A-Za-z0-9._@+-][A-Za-z0-9._@+/-]{0,199}?(?:\\/\\*\\*)?$/,\n // One exact branch name; it starts with a letter or digit, so it can never be a push flag such as --all.\n excluded_branches: /^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$/,\n};\nconst HEX64 = /^[0-9a-f]{64}$/;\nconst OPAQUE = /^[A-Za-z0-9._:-]{1,128}$/;\n\nexport type ManagedRule = { mode: \"monitor\" | \"block\" } & Partial<Record<ListKey | ExclusionKey, string[]>>;\nexport interface ManagedDocument {\n type: \"scopebond:managed-rules\";\n version: 1;\n revision: number;\n export_id: string;\n environment_id: string;\n agent_id: string;\n installation_id: string;\n rules: Record<ManagedRuleId, ManagedRule>;\n rules_digest: string;\n}\n\nexport type RefusalReason = \"invalid_document\" | \"stale_revision\" | \"wrong_computer\" | \"unsupported\" | \"write_failed\";\nexport type Inspection = { ok: true; doc: ManagedDocument } | { ok: false; reason: RefusalReason; message: string };\n\nexport const digestRules = (rules: unknown): string => createHash(\"sha256\").update(canonical(rules)).digest(\"hex\");\n\nconst isObject = (v: unknown): v is Record<string, unknown> => typeof v === \"object\" && v !== null && !Array.isArray(v);\n\n/** Check a document from the workspace without touching disk. Pure. */\nexport function inspectManaged(raw: unknown, context: { installationId: string; currentRevision: number | null; currentDigest?: string | null }): Inspection {\n const bad = (message: string): Inspection => ({ ok: false, reason: \"invalid_document\", message });\n if (!isObject(raw) || raw.type !== \"scopebond:managed-rules\") return bad(\"this is not a Scopebond workspace rules document\");\n if (raw.version !== 1) return { ok: false, reason: \"unsupported\", message: `unsupported rules document version ${String(raw.version)}` };\n const { revision, export_id: exportId, environment_id: environmentId, agent_id: agentId, installation_id: installationId, rules, rules_digest: rulesDigest } = raw;\n if (!Number.isInteger(revision) || (revision as number) < 1 || (revision as number) > 2_147_483_647\n || typeof exportId !== \"string\" || typeof environmentId !== \"string\" || !OPAQUE.test(environmentId)\n || typeof agentId !== \"string\" || !OPAQUE.test(agentId) || typeof installationId !== \"string\" || !OPAQUE.test(installationId)\n || typeof rulesDigest !== \"string\" || !HEX64.test(rulesDigest) || !isObject(rules)) {\n return bad(\"the rules document is incomplete or malformed\");\n }\n if (installationId !== context.installationId || exportId !== `rev-${revision}-${installationId}`) {\n return { ok: false, reason: \"wrong_computer\", message: \"these rules were issued for a different computer\" };\n }\n const keys = Object.keys(rules);\n if (keys.length !== MANAGED_RULE_IDS.length || !MANAGED_RULE_IDS.every((id) => keys.includes(id))) {\n return { ok: false, reason: \"unsupported\", message: \"the rules document names rules this version does not know; update Scopebond\" };\n }\n for (const id of MANAGED_RULE_IDS) {\n const rule = rules[id];\n if (!isObject(rule) || (rule.mode !== \"monitor\" && rule.mode !== \"block\")) return bad(`rule ${id} has no valid mode`);\n for (const key of Object.keys(rule)) {\n if (key === \"mode\") continue;\n const listKey = LIST_OF[id];\n const exclusionKey = EXCLUSIONS_OF[id];\n const pattern = key === listKey ? LIST_PATTERN[listKey] : key === exclusionKey ? EXCLUSION_PATTERN[exclusionKey] : null;\n if (!pattern) return bad(`rule ${id} has an unknown setting`);\n const list = rule[key];\n if (!Array.isArray(list) || list.length > 50 || !list.every((v) => typeof v === \"string\" && pattern.test(v) && !v.includes(\"..\"))) {\n return bad(`rule ${id} has an invalid list`);\n }\n }\n }\n const network = rules[\"network-egress\"] as ManagedRule;\n if (network.mode === \"block\" && !(network.allowed_hosts?.length)) return bad(\"network blocking needs at least one allowed site\");\n if (digestRules(rules) !== rulesDigest) return bad(\"the rules do not match their digest; the document was changed or damaged\");\n // The same version with different rules is a newer document too: the workspace can change what reaches this computer without a new\n // version (an agent moved to another team, or this hook started reading a setting it did not read before). The export id binds it here.\n const sameVersionChanged = context.currentRevision !== null && revision === context.currentRevision && !!context.currentDigest && rulesDigest !== context.currentDigest;\n if (context.currentRevision !== null && (revision as number) <= context.currentRevision && !sameVersionChanged) {\n return { ok: false, reason: \"stale_revision\", message: `version ${String(revision)} is not newer than the version in force (${context.currentRevision})` };\n }\n return { ok: true, doc: raw as unknown as ManagedDocument };\n}\n\nconst union = (a: readonly string[], b: readonly string[] | undefined): string[] => [...new Set([...a, ...(b ?? [])])];\nconst branchGlob = (b: string): string => (b.endsWith(\"/*\") ? `${b.slice(0, -1)}**` : b);\nconst hostPattern = (hosts: readonly string[]): string =>\n `^(?:${hosts.map((h) => (h.startsWith(\"*.\") ? `(?:[^.]+\\\\.)+${ci(h.slice(2).replace(/\\./g, \"\\\\.\"))}` : ci(h.replace(/\\./g, \"\\\\.\")))).join(\"|\")})$`;\n\ntype Clause = Record<string, unknown> & { id: string };\n\nconst escapeRegex = (text: string): string => text.replace(/[\\\\^$.*+?()[\\]{}|/]/g, \"\\\\$&\");\n/** Exclusions that passed validation (the document check refuses the rest; this is a second line of defence). */\nconst safeExclusions = (paths: readonly string[] | undefined): string[] => (paths ?? []).filter((path) => EXCLUSION_PATTERN.excluded_paths.test(path) && !path.includes(\"..\"));\n/** An allow pattern that also allows exactly these paths (a trailing /** allows everything under the folder). The added alternative\n * carries the always-on floor and refuses any path with a \".\" or \"..\" segment, so no exclusion — however it is written — can reach\n * Scopebond's own settings or the agents' hook settings, or climb out of the folder it names. */\nconst withPathExclusions = (pattern: string, paths: readonly string[]): string => paths.length === 0 ? pattern\n : `^(?!(?:.*/)?\\\\.{1,2}(?:/|$))${GUARDRAIL_LOOKAHEADS}(?:${paths.map((p) => p.endsWith(\"/**\") ? `${escapeRegex(p.slice(0, -3))}/.+` : escapeRegex(p)).join(\"|\")})$|${pattern}`;\n/** An allow pattern for pushes that also allows exactly these branches. */\nconst withBranchExclusions = (pattern: string, branches: readonly string[]): string => branches.length === 0 ? pattern\n : `^(?:refs/heads/)?(?:${branches.map(escapeRegex).join(\"|\")})$|${pattern}`;\nconst boundPattern = (clause: Clause, key: string): string => String(((clause.param_bounds as Record<string, { pattern: string }> | undefined)?.[key])?.pattern ?? \"\");\nconst withBound = (clause: Clause, key: string, pattern: string): Clause => ({ ...clause, param_bounds: { ...(clause.param_bounds as Record<string, unknown>), [key]: { pattern } } });\n\n/** The policy this computer enforces under workspace rules: its own compiled rules, adjusted rule by rule. Pure. */\nexport function compileManaged(local: RuleSet, doc: ManagedDocument, agentKid: string): Record<string, unknown> {\n const r = doc.rules;\n const rules: RuleSet = {\n ...local,\n protected_branches: union(local.protected_branches, [...(r[\"push-protected\"].protected_branches ?? []), ...(r[\"force-push-protected\"].protected_branches ?? [])]),\n destructive_programs: union(local.destructive_programs, r[\"destructive-shell\"].destructive_programs),\n };\n const policy = compile(rules, agentKid) as { clauses: Clause[] } & Record<string, unknown>;\n const allowAll = (clause: Clause): Clause => { const { param_bounds: _dropped, ...rest } = clause; return { ...rest, mode: \"enforce\", description: `${String(clause.description ?? \"\").split(\".\")[0]}. Recorded, not blocked: set by your workspace.` }; };\n const clauses: Clause[] = [];\n for (const clause of policy.clauses) {\n if (clause.id === \"protect-branches\" && r[\"push-protected\"].mode === \"monitor\") {\n clauses.push(allowAll(clause));\n if (r[\"force-push-protected\"].mode === \"block\") {\n clauses.push({ id: \"protect-branch-history\", type: \"force_push_guard\", mode: \"enforce\", protected_refs: rules.protected_branches.map(branchGlob),\n description: \"Deny force-pushes, deletions and mirror pushes that reach a protected branch; ordinary pushes are allowed. Set by your workspace.\" });\n }\n } else if (clause.id === \"safe-shell\" && r[\"destructive-shell\"].mode === \"monitor\") clauses.push(allowAll(clause));\n else if (clause.id === \"protect-read\" && r[\"secret-read\"].mode === \"monitor\") {\n clauses.push({ ...clause, param_bounds: { path: { pattern: GUARDRAIL_READ_PATTERN } },\n description: \"Allow workspace reads, but never Scopebond's own folder, which holds this computer's key (always on). Other protected files are recorded, not blocked: set by your workspace.\" });\n }\n else if (clause.id === \"protect-write\" && r[\"ci-config-write\"].mode === \"monitor\") {\n clauses.push({ ...clause, param_bounds: { path: { pattern: GUARDRAIL_WRITE_PATTERN } },\n description: \"Allow workspace writes, but never to Scopebond's own settings or the agents' hook settings (always on). Other protected files are recorded, not blocked: set by your workspace.\" });\n } else if (clause.id === \"protect-read\" && r[\"secret-read\"].excluded_paths?.length) {\n clauses.push(withBound(clause, \"path\", withPathExclusions(boundPattern(clause, \"path\"), safeExclusions(r[\"secret-read\"].excluded_paths))));\n } else if (clause.id === \"protect-write\" && r[\"ci-config-write\"].excluded_paths?.length) {\n clauses.push(withBound(clause, \"path\", withPathExclusions(boundPattern(clause, \"path\"), safeExclusions(r[\"ci-config-write\"].excluded_paths))));\n } else if (clause.id === \"protect-branches\" && r[\"push-protected\"].excluded_branches?.length) {\n clauses.push(withBound(clause, \"ref\", withBranchExclusions(boundPattern(clause, \"ref\"), r[\"push-protected\"].excluded_branches)));\n // Skipping ordinary pushes to a branch never skips history protection: force-pushes, deletions and mirror pushes stay stopped.\n if (r[\"force-push-protected\"].mode === \"block\") {\n clauses.push({ id: \"protect-branch-history\", type: \"force_push_guard\", mode: \"enforce\", protected_refs: rules.protected_branches.map(branchGlob),\n description: \"Deny force-pushes, deletions and mirror pushes that reach a protected branch. Set by your workspace.\" });\n }\n } else clauses.push(clause);\n }\n if (r[\"network-egress\"].mode === \"block\") {\n const keysAt = clauses.findIndex((c) => c.id === \"keys\");\n clauses.splice(keysAt < 0 ? clauses.length : keysAt, 0, {\n id: \"allowed-sites\", type: \"action_allowlist\", mode: \"enforce\", action_types: [\"net.fetch\"],\n param_bounds: { host: { pattern: hostPattern(r[\"network-egress\"].allowed_hosts ?? []) } },\n description: `Deny web requests to sites other than ${r[\"network-egress\"].allowed_hosts!.join(\", \")}. Set by your workspace.`,\n });\n }\n return { ...policy, policy_id: \"coding-agent\", version: doc.revision, clauses };\n}\n\n// --- on disk ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ManagedMeta {\n revision: number | null;\n rules_digest: string | null;\n export_id: string | null;\n etag: string | null;\n checked_at: string | null;\n last_ack: { revision: number; result: \"loaded\" | \"rejected\"; at: string } | null;\n last_error: string | null;\n}\nconst EMPTY_META: ManagedMeta = { revision: null, rules_digest: null, export_id: null, etag: null, checked_at: null, last_ack: null, last_error: null };\n\nexport function readMeta(dir: string): ManagedMeta {\n try { return { ...EMPTY_META, ...(JSON.parse(readFileSync(join(dir, MANAGED_META_FILE), \"utf8\")) as Partial<ManagedMeta>) }; } catch { return { ...EMPTY_META }; }\n}\nexport function writeMeta(dir: string, meta: ManagedMeta): void { writeAtomic(join(dir, MANAGED_META_FILE), `${JSON.stringify(meta, null, 2)}\\n`); }\n\n/** Whether a workspace currently manages the rules on this computer. */\nexport const isManaged = (dir: string): boolean => existsSync(join(dir, MANAGED_DOC_FILE));\n\nexport function writeAtomic(file: string, text: string): void {\n const temp = `${file}.${randomBytes(6).toString(\"hex\")}.tmp`;\n try { writeFileSync(temp, text, { mode: 0o600 }); renameSync(temp, file); }\n catch (error) { try { rmSync(temp, { force: true }); } catch { /* nothing to remove */ } throw error; }\n}\n\n/** Install a checked document: write the merged policy (previous kept) and the document. `policyBuilds` is the caller's check\n * that the gateway can load the result. */\nexport function installManaged(dir: string, doc: ManagedDocument, agentKid: string, policyBuilds: (policy: Record<string, unknown>) => boolean): { ok: true } | { ok: false; reason: RefusalReason; message: string } {\n const local = loadRules(dir) ?? defaultRules();\n const policy = compileManaged(local, doc, agentKid);\n if (!policyBuilds(policy)) return { ok: false, reason: \"unsupported\", message: \"this version of Scopebond cannot load the workspace rules; update it\" };\n try {\n mkdirSync(dir, { recursive: true });\n const policyPath = join(dir, \"policy.json\");\n if (existsSync(policyPath)) copyFileSync(policyPath, join(dir, PREVIOUS_POLICY_FILE));\n writeAtomic(policyPath, `${JSON.stringify(policy, null, 2)}\\n`);\n writeAtomic(join(dir, MANAGED_DOC_FILE), `${JSON.stringify(doc, null, 2)}\\n`);\n return { ok: true };\n } catch (error) {\n return { ok: false, reason: \"write_failed\", message: `could not write the rules (${(error as Error).name})` };\n }\n}\n\n/** Go back to this computer's own rules: recompile policy.json from rules.json and forget the workspace document. */\nexport function restoreLocal(dir: string, agentKid: string): void {\n const local = loadRules(dir) ?? defaultRules();\n const policyPath = join(dir, \"policy.json\");\n if (existsSync(policyPath)) copyFileSync(policyPath, join(dir, PREVIOUS_POLICY_FILE));\n writeAtomic(policyPath, `${JSON.stringify(compile(local, agentKid), null, 2)}\\n`);\n rmSync(join(dir, MANAGED_DOC_FILE), { force: true });\n}\n"]}
|
package/dist/runtime.d.ts
CHANGED
|
@@ -52,6 +52,9 @@ export declare const dir: (d: string) => string;
|
|
|
52
52
|
export declare const GUARDRAIL_WRITE_PATTERN: string;
|
|
53
53
|
/** The always-on floor as bare lookaheads, for a pattern that must also refuse these paths (the hook's own folder included). */
|
|
54
54
|
export declare const GUARDRAIL_LOOKAHEADS: string;
|
|
55
|
+
/** The read protection that is always on, whoever manages the rules: the hook's own folder, which holds this computer's
|
|
56
|
+
* signing key and connection. A workspace can record other protected reads instead of blocking them; never this one. */
|
|
57
|
+
export declare const GUARDRAIL_READ_PATTERN: string;
|
|
55
58
|
export declare const DESTRUCTIVE: string[];
|
|
56
59
|
/** Upgrade the starter-policy patterns an older hook wrote, in memory, so a user who
|
|
57
60
|
* installed an earlier version gets the current protections without re-running init.
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAOA,OAAO,EAA6C,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAgC,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAQ/E,OAAO,EAAoB,KAAK,gBAAgB,EAAsB,MAAM,oBAAoB,CAAC;AAEjG,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf;;uFAEmF;IACnF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;iFAC6E;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;sEACkE;IAClE,KAAK,CAAC,EAAE;QAAE,UAAU,EAAE,cAAc,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACvF;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,eAAe,CAAC;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB;2EACuE;IACvE,yFAAyF;IACzF,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE;YAAE,WAAW,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC7G;AAWD,eAAO,MAAM,EAAE,GAAI,GAAG,MAAM,KAAG,MAAiF,CAAC;AACjH,eAAO,MAAM,KAAK,GAAI,KAAK,MAAM,KAAG,MAAyC,CAAC;AAC9E,eAAO,MAAM,KAAK,GAAI,MAAM,MAAM,KAAG,MAAoC,CAAC;AAC1E,eAAO,MAAM,GAAG,GAAI,GAAG,MAAM,KAAG,MAAmC,CAAC;AAuBpE;uFACuF;AACvF,eAAO,MAAM,uBAAuB,QAAwC,CAAC;AAC7E,gIAAgI;AAChI,eAAO,MAAM,oBAAoB,QAAmD,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAOA,OAAO,EAA6C,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAgC,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAQ/E,OAAO,EAAoB,KAAK,gBAAgB,EAAsB,MAAM,oBAAoB,CAAC;AAEjG,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf;;uFAEmF;IACnF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;iFAC6E;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;sEACkE;IAClE,KAAK,CAAC,EAAE;QAAE,UAAU,EAAE,cAAc,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACvF;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,eAAe,CAAC;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB;2EACuE;IACvE,yFAAyF;IACzF,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE;YAAE,WAAW,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC7G;AAWD,eAAO,MAAM,EAAE,GAAI,GAAG,MAAM,KAAG,MAAiF,CAAC;AACjH,eAAO,MAAM,KAAK,GAAI,KAAK,MAAM,KAAG,MAAyC,CAAC;AAC9E,eAAO,MAAM,KAAK,GAAI,MAAM,MAAM,KAAG,MAAoC,CAAC;AAC1E,eAAO,MAAM,GAAG,GAAI,GAAG,MAAM,KAAG,MAAmC,CAAC;AAuBpE;uFACuF;AACvF,eAAO,MAAM,uBAAuB,QAAwC,CAAC;AAC7E,gIAAgI;AAChI,eAAO,MAAM,oBAAoB,QAAmD,CAAC;AACrF;yHACyH;AACzH,eAAO,MAAM,sBAAsB,QAAqC,CAAC;AAoBzE,eAAO,MAAM,WAAW,UAGvB,CAAC;AAwBF;;uFAEuF;AACvF,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAQpD;AAED;;;0BAG0B;AAC1B,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgCvE;AAED;4EAC4E;AAC5E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa;;;;IAoCnD;qFACiF;aAClE,OAAO,CAAC,IAAI,CAAC;IAO5B;;;;2DAIuD;aAC9C,IAAI;IAKb,gEAAgE;wBACtC,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IA+BpD;8EAC0E;qBACnD,MAAM,GAAG,MAAM,EAAE,YAAW;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAQ,OAAO,CAAC,QAAQ,CAAC;EAiDpG"}
|
package/dist/runtime.js
CHANGED
|
@@ -55,6 +55,9 @@ const PROTECTED_WRITE = "^" + [...GUARDRAIL_WRITE, ...CI_WRITE].join("") + ".+";
|
|
|
55
55
|
export const GUARDRAIL_WRITE_PATTERN = "^" + GUARDRAIL_WRITE.join("") + ".+";
|
|
56
56
|
/** The always-on floor as bare lookaheads, for a pattern that must also refuse these paths (the hook's own folder included). */
|
|
57
57
|
export const GUARDRAIL_LOOKAHEADS = under("\\.scopebond") + GUARDRAIL_WRITE.join("");
|
|
58
|
+
/** The read protection that is always on, whoever manages the rules: the hook's own folder, which holds this computer's
|
|
59
|
+
* signing key and connection. A workspace can record other protected reads instead of blocking them; never this one. */
|
|
60
|
+
export const GUARDRAIL_READ_PATTERN = "^" + under("\\.scopebond") + ".+";
|
|
58
61
|
const PROTECTED_READ = "^" + [
|
|
59
62
|
under("\\.scopebond"),
|
|
60
63
|
`(?!.*${ci("\\.(?:key|pem|p12|pfx|jks|keystore)")}$)`,
|
package/dist/runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["// The hook runtime: a check-only (M0) gateway built once from the enrolled\n// machine key, policy and a durable local receipt log. Each mapped action is\n// signed by the machine key, decided against policy and countersigned — locally,\n// with no HTTP server and no Cloud dependency in 0.1.\n\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { createGateway, StaticPrincipalKeyRegistry, type CloudExporter } from \"@scopebond/gateway\";\nimport { loadOrCreateAttester, openReceiptStore } from \"@scopebond/gateway/node\";\nimport { createSigner } from \"@scopebond/sdk\";\nimport type { Mapped } from \"./map.js\";\nimport { attachExporter, flushBounded, type HookConnection } from \"./cloud.js\";\nimport { explainDeny, type ExplainIntent } from \"./explain.js\";\nimport { RULES_FILE, loadRules } from \"./rules.js\";\nimport { applyRootScope } from \"./paths.js\";\nimport { withActionGroup, actionGroupId, ACTION_GROUP_PARAM } from \"./group.js\";\nimport { cliCommand } from \"./version.js\";\nimport { recordDeliveryAttempt } from \"./delivery-state.js\";\nimport { openDispatchGuard, DELEGATION_ENV } from \"@scopebond/gateway/node\";\nimport { dispatchIntentOf, type DispatchDecision, type DispatchGuard } from \"@scopebond/gateway\";\n\nexport interface RuntimeConfig {\n policyPath: string;\n keyPath: string;\n attesterPath: string;\n dbPath: string;\n /** Strict mode: an unmapped tool is policy-checked (and denied by a closed\n * allowlist) instead of observed. Fail-closed for tools with no taxonomy\n * mapping; default false (observe, matching the connector conformance vector). */\n strict?: boolean;\n /** The working directory the harness reported, used to resolve workspace roots when\n * the rule set lists `allowed_roots`. Defaults to the process directory. */\n cwd?: string;\n /** When connected to a Cloud workspace, receipts are auto-exported to the portal.\n * Export is best-effort and never changes the local decision. */\n cloud?: { connection: HookConnection; fetch?: typeof fetch; flushTimeoutMs?: number };\n}\n\nexport interface Decision {\n decision: \"allow\" | \"deny\" | \"not_evaluated\";\n reason: string;\n /** The clause that decided a deny, when the verdict named one. */\n clauseId?: string | null;\n /** The deciding receipt (the denied one, or the first allow). */\n receipt?: unknown;\n /** Every receipt produced — one per simple command in a decomposed shell call. */\n receipts?: unknown[];\n /** Each action as actually evaluated (after root scoping and grouping), with its\n * receipt: what the observation emitters bind a request digest to. */\n /** What the dispatch boundary spent or refused for this call, when one is configured. */\n dispatch?: DispatchDecision;\n dispatched?: Array<{ action: { action_type: string; params: Record<string, unknown> }; receipt?: unknown }>;\n}\n\n// The hook's own config and keys must be off-limits to the agent it governs:\n// otherwise the agent could rewrite its policy or read the signing keys and the\n// receipts stop meaning anything. These patterns are \"allow if the path does NOT\n// match a protected location\"; they run against the cwd-relative path the mapper\n// produces (and equally against an absolute one).\n//\n// Matching is case-insensitive — Windows and macOS open `.ENV` and `.env` as the\n// same file — by expanding each letter to a two-case class (policy patterns are\n// plain regular expressions with no flags). `ci()` is applied to literal text only.\nexport const ci = (s: string): string => s.replace(/[A-Za-z]/g, (c) => `[${c.toLowerCase()}${c.toUpperCase()}]`);\nexport const under = (dir: string): string => `(?!(?:.*/)?${ci(dir)}(?:/|$))`; // dir itself or anything inside it\nexport const named = (file: string): string => `(?!(?:.*/)?${ci(file)}$)`; // exactly this file name\nexport const dir = (d: string): string => `(?!(?:.*/)?${ci(d)}/?$)`; // the directory itself (a recursive read or copy)\n\n// The protected-write set, in two typed groups the catalog classifies separately:\n// guardrail/hook configuration (C02) and CI configuration (H03).\nconst GUARDRAIL_WRITE_PREV = [\n under(\"\\\\.scopebond\"), `(?!(?:.*/)?${ci(\"\\\\.claude/settings\")})`, under(\"\\\\.claude/hooks\"), under(\"\\\\.claude/agents\"),\n `(?!(?:.*/)?${ci(\"\\\\.cursor/hooks\")})`, named(\"\\\\.codex/hooks\\\\.json\"), named(\"\\\\.codex/config\\\\.toml\"), named(\"\\\\.mcp\\\\.json\"),\n under(\"\\\\.git/hooks\"), named(\"\\\\.git/config\"), under(\"\\\\.husky\"),\n];\nconst CI_WRITE_PREV = [\n under(\"\\\\.github/workflows\"), under(\"\\\\.github/actions\"), named(\"\\\\.gitlab-ci\\\\.yml\"), named(\"\\\\.gitlab-ci\\\\.yaml\"), under(\"\\\\.circleci\"),\n named(\"azure-pipelines\\\\.yml\"), named(\"Jenkinsfile\"),\n];\nconst GUARDRAIL_WRITE = [...GUARDRAIL_WRITE_PREV, named(\"\\\\.cursor/mcp\\\\.json\"), under(\"\\\\.githooks\")];\nconst CI_WRITE = [\n ...CI_WRITE_PREV,\n named(\"azure-pipelines\\\\.yaml\"), named(\"bitbucket-pipelines\\\\.yml\"), named(\"\\\\.travis\\\\.yml\"), named(\"\\\\.drone\\\\.yml\"),\n named(\"cloudbuild\\\\.yaml\"), named(\"cloudbuild\\\\.yml\"), under(\"\\\\.buildkite\"),\n];\n// What 0.8 compiled, kept so an installed policy that still carries it verbatim is\n// upgraded in memory (see LEGACY_STARTER_PATTERNS).\nconst PROTECTED_WRITE_PREV = \"^\" + [...GUARDRAIL_WRITE_PREV, ...CI_WRITE_PREV].join(\"\") + \".+\";\nconst PROTECTED_WRITE = \"^\" + [...GUARDRAIL_WRITE, ...CI_WRITE].join(\"\") + \".+\";\n/** The write protection that is always on, whoever manages the rules: the hook's own settings and those of the agents it\n * guards. A workspace can relax CI-configuration writes; it can never relax these. */\nexport const GUARDRAIL_WRITE_PATTERN = \"^\" + GUARDRAIL_WRITE.join(\"\") + \".+\";\n/** The always-on floor as bare lookaheads, for a pattern that must also refuse these paths (the hook's own folder included). */\nexport const GUARDRAIL_LOOKAHEADS = under(\"\\\\.scopebond\") + GUARDRAIL_WRITE.join(\"\");\n\nconst PROTECTED_READ = \"^\" + [\n under(\"\\\\.scopebond\"),\n `(?!.*${ci(\"\\\\.(?:key|pem|p12|pfx|jks|keystore)\")}$)`,\n // .env, .env.* — except templates whose name ends in .example/.sample/.template/.dist\n `(?!(?:.*/)?${ci(\"\\\\.env\")}(?!(?:\\\\.[^/]*)?\\\\.(?:${ci(\"example\")}|${ci(\"sample\")}|${ci(\"template\")}|${ci(\"dist\")})$)(?:\\\\.[^/]*)?$)`,\n named(\"\\\\.envrc\"),\n // Credentials outside the workspace that an agent can reach by absolute or ~ path,\n // and their directories as a whole (`cp -r ~/.ssh`, `grep -r x ~/.aws`).\n `(?!(?:.*/)?${ci(\"\\\\.ssh\")}(?:$|/(?!.*${ci(\"\\\\.pub\")}$)(?!${ci(\"known_hosts\")}$)(?!${ci(\"config\")}$)))`,\n `(?!(?:.*/)?${ci(\"\\\\.aws\")}(?:$|/(?!${ci(\"config\")}$)))`,\n named(\"\\\\.npmrc\"), named(\"\\\\.pypirc\"), named(\"\\\\.netrc\"), named(\"_netrc\"), named(\"\\\\.git-credentials\"),\n dir(\"\\\\.kube\"), named(\"\\\\.kube/config\"), dir(\"\\\\.docker\"), named(\"\\\\.docker/config\\\\.json\"),\n under(\"\\\\.config/gcloud\"), under(\"\\\\.azure\"), under(\"\\\\.gnupg\"),\n dir(\"\\\\.config/gh\"), named(\"\\\\.config/gh/hosts\\\\.yml\"), named(\"\\\\.claude/\\\\.credentials\\\\.json\"),\n].join(\"\") + \".+\";\n\n// Destructive programs, POSIX and Windows. The mapper records the program as typed,\n// so the pattern accepts any case and an executable suffix (`RM.exe`, `Remove-Item`).\nexport const DESTRUCTIVE = [\n \"rm\", \"sudo\", \"doas\", \"shutdown\", \"reboot\", \"halt\", \"poweroff\", \"mkfs\", \"dd\", \"shred\", \"truncate\", \"unlink\", \"wipe\", \"srm\",\n \"del\", \"rd\", \"rmdir\", \"erase\", \"deltree\", \"format\", \"diskpart\", \"remove-item\", \"ri\", \"clear-content\", \"clc\", \"stop-computer\", \"restart-computer\",\n];\nconst SAFE_SHELL = `^(?!(?:${DESTRUCTIVE.map(ci).join(\"|\")})(?:${ci(\"\\\\.(?:exe|cmd|bat|com|ps1)\")})?$).+`;\n\n// A push destination the starter policy refuses: main, master, release/*, the\n// \"every branch\" flags (`--all`, `--mirror`, `--branches`) and a push whose\n// destination the mapper could not read (`--unknown`: a git alias, a configured push\n// refspec, `send-pack`), in any case. `--tags` alone pushes only tags and is allowed.\nconst SAFE_REF = `^(?!(?:${ci(\"main\")}|${ci(\"master\")})$)(?!${ci(\"release\")}/)(?!-(?!-${ci(\"tags\")}$)).+`;\n\n// Patterns written by earlier starter policies (0.3–0.5), upgraded in memory when a\n// policy on disk still carries them verbatim. An operator's own edits never match\n// these strings and are left untouched.\nconst LEGACY_STARTER_PATTERNS: Record<string, string> = {\n [PROTECTED_WRITE_PREV]: PROTECTED_WRITE,\n \"^(?!(?:main|master)$)(?!release/).+\": SAFE_REF,\n \"^(?!(?:rm|sudo|shutdown|reboot|mkfs|dd|del|rd|rmdir|erase|deltree|format|Remove-Item|ri)$).+\": SAFE_SHELL,\n \"^(?!(?:rm|sudo|shutdown|reboot|mkfs|dd)$).+\": SAFE_SHELL,\n \"^(?!(?:.*/)?\\\\.scopebond/)(?!(?:.*/)?\\\\.claude/settings)(?!(?:.*/)?\\\\.cursor/hooks)(?!(?:.*/)?\\\\.codex/(?:hooks\\\\.json|config\\\\.toml)$)(?!(?:.*/)?\\\\.git/hooks/)(?!(?:.*/)?\\\\.github/workflows/)(?!(?:.*/)?\\\\.github/actions/)(?!(?:.*/)?\\\\.gitlab-ci\\\\.yml$)(?!(?:.*/)?\\\\.circleci/)(?!(?:.*/)?azure-pipelines\\\\.yml$)(?!(?:.*/)?Jenkinsfile$).+\": PROTECTED_WRITE,\n \"^(?!(?:.*/)?\\\\.scopebond/)(?!(?:.*/)?\\\\.claude/settings)(?!(?:.*/)?\\\\.cursor/hooks)(?!(?:.*/)?\\\\.git/hooks/)(?!(?:.*/)?\\\\.github/workflows/)(?!(?:.*/)?\\\\.github/actions/)(?!(?:.*/)?\\\\.gitlab-ci\\\\.yml$)(?!(?:.*/)?\\\\.circleci/)(?!(?:.*/)?azure-pipelines\\\\.yml$)(?!(?:.*/)?Jenkinsfile$).+\": PROTECTED_WRITE,\n \"^(?!(?:.*/)?\\\\.scopebond/)(?!(?:.*/)?\\\\.claude/settings)(?!(?:.*/)?\\\\.cursor/hooks)(?!(?:.*/)?\\\\.git/hooks/).+\": PROTECTED_WRITE,\n \"^(?!(?:.*/)?\\\\.scopebond/)(?!.*\\\\.key$)(?!(?:.*/)?\\\\.env(?:\\\\.(?!example$|sample$|template$)[^/]*)?$).+\": PROTECTED_READ,\n \"^(?!(?:.*/)?\\\\.scopebond/)(?!.*\\\\.key$).+\": PROTECTED_READ,\n};\n\n/** Upgrade the starter-policy patterns an older hook wrote, in memory, so a user who\n * installed an earlier version gets the current protections without re-running init.\n * Only exact legacy strings are replaced; any other pattern is the operator's own. */\nexport function upgradeStarterPolicy<T>(policy: T): T {\n const p = policy as { clauses?: Array<{ param_bounds?: Record<string, { pattern?: string }> }> };\n for (const clause of p?.clauses ?? []) {\n for (const bound of Object.values(clause?.param_bounds ?? {})) {\n if (bound && typeof bound.pattern === \"string\" && LEGACY_STARTER_PATTERNS[bound.pattern]) bound.pattern = LEGACY_STARTER_PATTERNS[bound.pattern];\n }\n }\n return policy;\n}\n\n/** The default starter policy for a coding agent: protect release branches, deny\n * destructive programs, allow workspace file access except the hook's own config\n * and keys, and trust only the enrolled machine key. Every threshold is the\n * operator's to edit. */\nexport function starterPolicy(agentKid: string): Record<string, unknown> {\n return {\n vocabulary_version: \"1.0\", policy_id: \"coding-agent\", version: 1,\n clauses: [\n {\n id: \"protect-branches\", type: \"action_allowlist\", mode: \"enforce\", action_types: [\"git.push\"],\n param_bounds: { ref: { pattern: SAFE_REF } },\n description: \"Deny pushes to main, master and release/* (any case, any refspec spelling), pushes of every branch at once (--all, --mirror) and pushes whose destination cannot be read from the command (a git alias, a configured push refspec, send-pack). A tags-only push (--tags) is allowed.\",\n },\n {\n id: \"safe-shell\", type: \"action_allowlist\", mode: \"enforce\", action_types: [\"shell.exec\"],\n param_bounds: { program: { pattern: SAFE_SHELL } },\n description: \"Deny destructive programs — POSIX (rm, sudo, doas, shutdown, reboot, mkfs, dd, shred, truncate, unlink, wipe) and Windows/PowerShell (del, rd, rmdir, erase, deltree, format, diskpart, Remove-Item, Clear-Content) — in any case and with or without .exe. An empty program (a command that could not be parsed, or whose program is only known at run time: $VAR, $(…), eval of a variable) is denied. Argument-shaped deletion (find -delete, git clean) is not a program name and is not covered here.\",\n },\n {\n id: \"protect-write\", type: \"action_allowlist\", mode: \"enforce\", action_types: [\"file.write\"],\n param_bounds: { path: { pattern: PROTECTED_WRITE } },\n description: \"Allow workspace writes, but never to the hook's policy/keys, Claude Code settings, hooks and agents, Cursor or Codex hook settings, .mcp.json and .cursor/mcp.json, git hooks (.git/hooks, .githooks) and git config, Husky hooks, or CI config (.github/workflows, .github/actions, .gitlab-ci.yml/.yaml, .circleci, azure-pipelines, bitbucket-pipelines.yml, .travis.yml, .drone.yml, cloudbuild, .buildkite, Jenkinsfile). Case-insensitive.\",\n },\n {\n id: \"protect-read\", type: \"action_allowlist\", mode: \"enforce\", action_types: [\"file.read\"],\n param_bounds: { path: { pattern: PROTECTED_READ } },\n description: \"Allow workspace reads, but never signing keys and key containers (*.key, *.pem, *.p12, *.pfx, *.jks), environment secret files (.env, .env.*, .envrc — except names ending in .example/.sample/.template/.dist), SSH private keys and the .ssh directory, cloud/registry/git credentials (.aws except .aws/config, .npmrc, .pypirc, .netrc, .git-credentials, .kube/config, .docker/config.json, gcloud, Azure, GnuPG, the GitHub CLI's hosts.yml, Claude Code's .credentials.json) or the hook's own .scopebond directory. Case-insensitive.\",\n },\n {\n id: \"observe-net-mcp\", type: \"action_allowlist\", mode: \"monitor\",\n action_types: [\"net.fetch\", \"mcp.tool.call\"],\n description: \"Observe network fetches and MCP tool calls (recorded, not blocked) — add bounds to enforce.\",\n },\n { id: \"keys\", type: \"key_policy\", active_keys: [agentKid], description: \"Only the enrolled machine key may sign.\" },\n ],\n };\n}\n\n/** Build the runtime. Throws on any setup failure (unparseable policy, missing\n * key, unavailable store) — the CLI turns that into a fail-closed deny. */\nexport function createHookRuntime(config: RuntimeConfig) {\n const policy = upgradeStarterPolicy(JSON.parse(readFileSync(config.policyPath, \"utf8\")));\n const agent = createSigner({ privateKeyPem: readFileSync(config.keyPath, \"utf8\") });\n const keys = new StaticPrincipalKeyRegistry([\n { kid: agent.kid, publicKeyPem: agent.publicKeyPem, purposes: [\"agent\"], status: \"active\" },\n ]);\n const { attester } = loadOrCreateAttester({ file: config.attesterPath });\n // When the project has a readable rule set, a denial names the command that edits it\n // rather than the generated file.\n const rulesRemedy = existsSync(join(dirname(config.policyPath), RULES_FILE))\n ? `run \\`${cliCommand(\"rules\")}\\` to see the limits in plain terms, or edit ${join(dirname(config.policyPath), RULES_FILE)}`\n : undefined;\n const { store: baseStore } = openReceiptStore({ db: config.dbPath });\n // When connected, mirror every stored receipt to the hosted portal through a\n // durable outbox. The wrapped store's decision is unchanged; export is best-effort.\n let store = baseStore;\n let exporter: CloudExporter | undefined;\n let outbox: { close(): void } | undefined;\n if (config.cloud) {\n const attached = attachExporter(config.dbPath + \".cloud-outbox.db\", config.cloud.connection, baseStore, config.cloud.fetch);\n store = attached.store;\n exporter = attached.exporter;\n outbox = attached.outbox;\n }\n const scopeRoots = loadRules(dirname(config.policyPath))?.allowed_roots ?? [];\n // The dispatch boundary is checked once per tool call, after every intent has been allowed, so the\n // gateway's own per-intent hook only reports what the runtime already decided for this call.\n const boundary = openDispatchGuard(dirname(config.policyPath));\n let boundaryVerdict: DispatchDecision | null = null;\n const gatewayGuard: DispatchGuard = { authorize: async () => boundaryVerdict ?? { allow: true, reason: \"ok\", consumed_approvals: [], budgets: [] } };\n const gateway = createGateway({ policy, authentication: { keys }, attester, store, mode: \"check_only\", dispatchGuard: gatewayGuard });\n\n return {\n gateway,\n agentKid: agent.kid,\n exporter,\n /** Deliver queued receipts to Cloud with a bounded timeout, then it is safe to\n * exit. Undelivered receipts persist in the durable outbox for the next run. */\n async flush(): Promise<void> {\n if (!exporter) return;\n const before = exporter.status().lastSuccessAt;\n await flushBounded(exporter, config.cloud?.flushTimeoutMs);\n // The process exits after this call; keep what the attempt saw for `status` and `doctor`.\n try { recordDeliveryAttempt(dirname(config.dbPath), exporter.status(), Date.now(), before); } catch { /* diagnostic only */ }\n },\n /** Release the SQLite handles. The hook is a per-tool-call process, and a writer that\n * exits without closing leaves its write-ahead log on disk for the next process to\n * extend — measured at ~11 KiB of WAL per receipt against ~1.7 KiB when closed, so a\n * busy session was writing tens of megabytes of pure overhead into the user's project.\n * Always safe to call, and every exit path should. */\n close(): void {\n try { baseStore.close?.(); } catch { /* the decision is already recorded */ }\n try { outbox?.close(); } catch { /* best effort */ }\n try { boundary?.close(); } catch { /* best effort */ }\n },\n /** Decide one mapped action, recording a receipt either way. */\n async evaluateOne(mapped: Mapped): Promise<Decision> {\n const signed = agent.sign(mapped.intent);\n if (!mapped.evaluated && !config.strict) {\n // Unknown tool or unparseable command, non-strict: observe without\n // evaluating — grants nothing (D30).\n const { receipt } = await gateway.observeAction({ intent: signed.intent, authorization: signed.authorization });\n return { decision: \"not_evaluated\", reason: `no policy applies to ${mapped.intent.action_type}`, receipt };\n }\n // Evaluated actions, and (in strict mode) unmapped tool.<name>/opaque commands,\n // go through policy — a closed allowlist denies an unlisted action.\n const result = await gateway.handleAction({ intent: signed.intent, authorization: signed.authorization });\n if (result.allowed) return { decision: \"allow\", reason: result.reason, receipt: result.receipt };\n // A deny is the one message the user and their agent actually read, so it is\n // composed from the deciding clause's own words rather than the engine's\n // internal reason (\"param ref fails pattern\").\n const clauseId = result.verdict?.clause_id ?? null;\n return {\n decision: \"deny\",\n reason: explainDeny({\n policy, clauseId, detail: result.reason,\n intent: signed.intent as ExplainIntent, policyPath: config.policyPath,\n postHoc: mapped.postHoc,\n // Point at the editable surface when there is one. `policy.json` is compiled\n // from `rules.json`, so telling someone to hand-edit it invites a change the\n // next `rules` run would overwrite.\n remedy: rulesRemedy,\n }),\n clauseId,\n receipt: result.receipt,\n };\n },\n /** Decide a whole tool call. A shell call decomposes into several simple\n * commands; every one is recorded, and a single deny denies the call. */\n async evaluate(mapped: Mapped | Mapped[], options: { groupKey?: string } = {}): Promise<Decision> {\n const raw = Array.isArray(mapped) ? mapped : [mapped];\n if (raw.length === 0) return { decision: \"not_evaluated\", reason: \"no action\", receipts: [] };\n // Every intent of one tool call carries one parent group id, so the receipts of a\n // decomposed call (a command, its file reads and writes, a rename destination)\n // link to each other and count once as an action. It is part of the signed\n // intent, so it is authenticated by the same signature as the rest.\n const scoped = scopeRoots.length ? applyRootScope(raw, { cwd: config.cwd ?? process.cwd(), roots: scopeRoots }) : raw;\n const list = withActionGroup(scoped, actionGroupId(options.groupKey));\n const receipts: unknown[] = [];\n const dispatched: NonNullable<Decision[\"dispatched\"]> = [];\n let allow: Decision | null = null;\n let notEvaluated: Decision | null = null;\n for (const m of list) {\n const d = await this.evaluateOne(m);\n if (d.receipt !== undefined) receipts.push(d.receipt);\n dispatched.push({ action: { action_type: m.intent.action_type, params: m.intent.params as Record<string, unknown> }, ...(d.receipt !== undefined ? { receipt: d.receipt } : {}) });\n if (d.decision === \"deny\") return { ...d, receipts, dispatched }; // any deny denies the call\n if (d.decision === \"allow\" && !allow) allow = d;\n if (d.decision === \"not_evaluated\" && !notEvaluated) notEvaluated = d;\n }\n // No deny: allow if any command was evaluated-and-allowed, else not_evaluated.\n const chosen = allow ?? notEvaluated!;\n if (boundary) {\n // Immediately before permitted dispatch: approvals are consumed and the budget slot is reserved\n // atomically, once for the whole parent action, or nothing is spent and the call is denied.\n const group = String(list[0]!.intent.params[ACTION_GROUP_PARAM]);\n const delegation = process.env[DELEGATION_ENV] ?? \"\";\n const verdict = await boundary.authorize({\n actor: agent.kid, action_group: group, policy_digest: gateway.policyHash,\n intents: list.map((m) => dispatchIntentOf(m.intent as never)),\n ...(delegation !== \"\" ? { delegation_id: delegation } : {}),\n });\n if (!verdict.allow) {\n // Record the refusal as its own denied receipt, then deny the call.\n boundaryVerdict = verdict;\n try {\n const last = list[list.length - 1]!;\n const signed = agent.sign(last.intent);\n const result = await gateway.handleAction({ intent: signed.intent, authorization: signed.authorization });\n if (result.receipt !== undefined) receipts.push(result.receipt);\n } finally { boundaryVerdict = null; }\n return { decision: \"deny\", reason: `Scopebond blocked this before it ran: ${verdict.reason}${verdict.detail ? ` (${verdict.detail})` : \"\"}`, receipts, dispatched, dispatch: verdict };\n }\n return { ...chosen, receipts, dispatched, dispatch: verdict };\n }\n return { ...chosen, receipts, dispatched };\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,6EAA6E;AAC7E,iFAAiF;AACjF,sDAAsD;AAEtD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,0BAA0B,EAAsB,MAAM,oBAAoB,CAAC;AACnG,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAuB,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAsB,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,gBAAgB,EAA6C,MAAM,oBAAoB,CAAC;AAmCjG,6EAA6E;AAC7E,gFAAgF;AAChF,iFAAiF;AACjF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,oFAAoF;AACpF,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;AACjH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,GAAW,EAAU,EAAE,CAAC,cAAc,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAI,mCAAmC;AACrH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,cAAc,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAS,yBAAyB;AAC5G,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAe,kDAAkD;AAErI,kFAAkF;AAClF,iEAAiE;AACjE,MAAM,oBAAoB,GAAG;IAC3B,KAAK,CAAC,cAAc,CAAC,EAAE,cAAc,EAAE,CAAC,oBAAoB,CAAC,GAAG,EAAE,KAAK,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,kBAAkB,CAAC;IACrH,cAAc,EAAE,CAAC,iBAAiB,CAAC,GAAG,EAAE,KAAK,CAAC,uBAAuB,CAAC,EAAE,KAAK,CAAC,wBAAwB,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC;IAC/H,KAAK,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC;CACjE,CAAC;AACF,MAAM,aAAa,GAAG;IACpB,KAAK,CAAC,qBAAqB,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAC,EAAE,KAAK,CAAC,oBAAoB,CAAC,EAAE,KAAK,CAAC,qBAAqB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACzI,KAAK,CAAC,uBAAuB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CACrD,CAAC;AACF,MAAM,eAAe,GAAG,CAAC,GAAG,oBAAoB,EAAE,KAAK,CAAC,sBAAsB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;AACvG,MAAM,QAAQ,GAAG;IACf,GAAG,aAAa;IAChB,KAAK,CAAC,wBAAwB,CAAC,EAAE,KAAK,CAAC,2BAA2B,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC;IACtH,KAAK,CAAC,mBAAmB,CAAC,EAAE,KAAK,CAAC,kBAAkB,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC;CAC7E,CAAC;AACF,mFAAmF;AACnF,oDAAoD;AACpD,MAAM,oBAAoB,GAAG,GAAG,GAAG,CAAC,GAAG,oBAAoB,EAAE,GAAG,aAAa,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;AAC/F,MAAM,eAAe,GAAG,GAAG,GAAG,CAAC,GAAG,eAAe,EAAE,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;AAChF;uFACuF;AACvF,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,GAAG,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;AAC7E,gIAAgI;AAChI,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,CAAC,cAAc,CAAC,GAAG,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACrF;yHACyH;AACzH,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,GAAG,KAAK,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC;AAEzE,MAAM,cAAc,GAAG,GAAG,GAAG;IAC3B,KAAK,CAAC,cAAc,CAAC;IACrB,QAAQ,EAAE,CAAC,qCAAqC,CAAC,IAAI;IACrD,sFAAsF;IACtF,cAAc,EAAE,CAAC,QAAQ,CAAC,yBAAyB,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,oBAAoB;IACpI,KAAK,CAAC,UAAU,CAAC;IACjB,mFAAmF;IACnF,yEAAyE;IACzE,cAAc,EAAE,CAAC,QAAQ,CAAC,cAAc,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,MAAM;IACvG,cAAc,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC,QAAQ,CAAC,MAAM;IACxD,KAAK,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,oBAAoB,CAAC;IACtG,GAAG,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC,yBAAyB,CAAC;IAC3F,KAAK,CAAC,kBAAkB,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC;IAC/D,GAAG,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,0BAA0B,CAAC,EAAE,KAAK,CAAC,iCAAiC,CAAC;CACjG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;AAElB,oFAAoF;AACpF,sFAAsF;AACtF,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK;IAC1H,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,EAAE,kBAAkB;CACjJ,CAAC;AACF,MAAM,UAAU,GAAG,UAAU,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,4BAA4B,CAAC,QAAQ,CAAC;AAE1G,8EAA8E;AAC9E,4EAA4E;AAC5E,qFAAqF;AACrF,sFAAsF;AACtF,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC;AAE1G,oFAAoF;AACpF,kFAAkF;AAClF,wCAAwC;AACxC,MAAM,uBAAuB,GAA2B;IACtD,CAAC,oBAAoB,CAAC,EAAE,eAAe;IACvC,qCAAqC,EAAE,QAAQ;IAC/C,8FAA8F,EAAE,UAAU;IAC1G,6CAA6C,EAAE,UAAU;IACzD,mVAAmV,EAAE,eAAe;IACpW,+RAA+R,EAAE,eAAe;IAChT,gHAAgH,EAAE,eAAe;IACjI,yGAAyG,EAAE,cAAc;IACzH,2CAA2C,EAAE,cAAc;CAC5D,CAAC;AAEF;;uFAEuF;AACvF,MAAM,UAAU,oBAAoB,CAAI,MAAS;IAC/C,MAAM,CAAC,GAAG,MAAsF,CAAC;IACjG,KAAK,MAAM,MAAM,IAAI,CAAC,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC;QACtC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,IAAI,EAAE,CAAC,EAAE,CAAC;YAC9D,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,IAAI,uBAAuB,CAAC,KAAK,CAAC,OAAO,CAAC;gBAAE,KAAK,CAAC,OAAO,GAAG,uBAAuB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACnJ,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;0BAG0B;AAC1B,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,OAAO;QACL,kBAAkB,EAAE,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC;QAChE,OAAO,EAAE;YACP;gBACE,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,UAAU,CAAC;gBAC7F,YAAY,EAAE,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;gBAC5C,WAAW,EAAE,sRAAsR;aACpS;YACD;gBACE,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,YAAY,CAAC;gBACzF,YAAY,EAAE,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE;gBAClD,WAAW,EAAE,4eAA4e;aAC1f;YACD;gBACE,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,YAAY,CAAC;gBAC5F,YAAY,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,EAAE;gBACpD,WAAW,EAAE,kbAAkb;aAChc;YACD;gBACE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,WAAW,CAAC;gBAC1F,YAAY,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,EAAE;gBACnD,WAAW,EAAE,+gBAA+gB;aAC7hB;YACD;gBACE,EAAE,EAAE,iBAAiB,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,SAAS;gBAChE,YAAY,EAAE,CAAC,WAAW,EAAE,eAAe,CAAC;gBAC5C,WAAW,EAAE,6FAA6F;aAC3G;YACD,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,EAAE,WAAW,EAAE,yCAAyC,EAAE;SACpH;KACF,CAAC;AACJ,CAAC;AAED;4EAC4E;AAC5E,MAAM,UAAU,iBAAiB,CAAC,MAAqB;IACrD,MAAM,MAAM,GAAG,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACzF,MAAM,KAAK,GAAG,YAAY,CAAC,EAAE,aAAa,EAAE,YAAY,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACpF,MAAM,IAAI,GAAG,IAAI,0BAA0B,CAAC;QAC1C,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE;KAC5F,CAAC,CAAC;IACH,MAAM,EAAE,QAAQ,EAAE,GAAG,oBAAoB,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;IACzE,qFAAqF;IACrF,kCAAkC;IAClC,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC,CAAC;QAC1E,CAAC,CAAC,SAAS,UAAU,CAAC,OAAO,CAAC,gDAAgD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC,EAAE;QAC5H,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,gBAAgB,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACrE,6EAA6E;IAC7E,oFAAoF;IACpF,IAAI,KAAK,GAAG,SAAS,CAAC;IACtB,IAAI,QAAmC,CAAC;IACxC,IAAI,MAAqC,CAAC;IAC1C,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,QAAQ,GAAG,cAAc,CAAC,MAAM,CAAC,MAAM,GAAG,kBAAkB,EAAE,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5H,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;QACvB,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC;QAC7B,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC3B,CAAC;IACD,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,aAAa,IAAI,EAAE,CAAC;IAC9E,mGAAmG;IACnG,6FAA6F;IAC7F,MAAM,QAAQ,GAAG,iBAAiB,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/D,IAAI,eAAe,GAA4B,IAAI,CAAC;IACpD,MAAM,YAAY,GAAkB,EAAE,SAAS,EAAE,KAAK,IAAI,EAAE,CAAC,eAAe,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,CAAC;IACrJ,MAAM,OAAO,GAAG,aAAa,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC,CAAC;IAEtI,OAAO;QACL,OAAO;QACP,QAAQ,EAAE,KAAK,CAAC,GAAG;QACnB,QAAQ;QACR;yFACiF;QACjF,KAAK,CAAC,KAAK;YACT,IAAI,CAAC,QAAQ;gBAAE,OAAO;YACtB,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,aAAa,CAAC;YAC/C,MAAM,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;YAC3D,0FAA0F;YAC1F,IAAI,CAAC;gBAAC,qBAAqB,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,CAAC;QAC/H,CAAC;QACD;;;;+DAIuD;QACvD,KAAK;YACH,IAAI,CAAC;gBAAC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,sCAAsC,CAAC,CAAC;YAC7E,IAAI,CAAC;gBAAC,MAAM,EAAE,KAAK,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;YACpD,IAAI,CAAC;gBAAC,QAAQ,EAAE,KAAK,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;QACxD,CAAC;QACD,gEAAgE;QAChE,KAAK,CAAC,WAAW,CAAC,MAAc;YAC9B,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACzC,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBACxC,mEAAmE;gBACnE,qCAAqC;gBACrC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;gBAChH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,wBAAwB,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,CAAC;YAC7G,CAAC;YACD,gFAAgF;YAChF,oEAAoE;YACpE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;YAC1G,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;YACjG,6EAA6E;YAC7E,yEAAyE;YACzE,+CAA+C;YAC/C,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,EAAE,SAAS,IAAI,IAAI,CAAC;YACnD,OAAO;gBACL,QAAQ,EAAE,MAAM;gBAChB,MAAM,EAAE,WAAW,CAAC;oBAClB,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM;oBACvC,MAAM,EAAE,MAAM,CAAC,MAAuB,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU;oBACrE,OAAO,EAAE,MAAM,CAAC,OAAO;oBACvB,6EAA6E;oBAC7E,6EAA6E;oBAC7E,oCAAoC;oBACpC,MAAM,EAAE,WAAW;iBACpB,CAAC;gBACF,QAAQ;gBACR,OAAO,EAAE,MAAM,CAAC,OAAO;aACxB,CAAC;QACJ,CAAC;QACD;kFAC0E;QAC1E,KAAK,CAAC,QAAQ,CAAC,MAAyB,EAAE,UAAiC,EAAE;YAC3E,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YACtD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;YAC9F,kFAAkF;YAClF,+EAA+E;YAC/E,2EAA2E;YAC3E,oEAAoE;YACpE,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YACtH,MAAM,IAAI,GAAG,eAAe,CAAC,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;YACtE,MAAM,QAAQ,GAAc,EAAE,CAAC;YAC/B,MAAM,UAAU,GAAwC,EAAE,CAAC;YAC3D,IAAI,KAAK,GAAoB,IAAI,CAAC;YAClC,IAAI,YAAY,GAAoB,IAAI,CAAC;YACzC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;gBACrB,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;gBACpC,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS;oBAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;gBACtD,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,MAAiC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;gBACnL,IAAI,CAAC,CAAC,QAAQ,KAAK,MAAM;oBAAE,OAAO,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAY,2BAA2B;gBACxG,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,KAAK;oBAAE,KAAK,GAAG,CAAC,CAAC;gBAChD,IAAI,CAAC,CAAC,QAAQ,KAAK,eAAe,IAAI,CAAC,YAAY;oBAAE,YAAY,GAAG,CAAC,CAAC;YACxE,CAAC;YACD,+EAA+E;YAC/E,MAAM,MAAM,GAAG,KAAK,IAAI,YAAa,CAAC;YACtC,IAAI,QAAQ,EAAE,CAAC;gBACb,gGAAgG;gBAChG,4FAA4F;gBAC5F,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC;gBACjE,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;gBACrD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC;oBACvC,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,CAAC,UAAU;oBACxE,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAe,CAAC,CAAC;oBAC7D,GAAG,CAAC,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC5D,CAAC,CAAC;gBACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;oBACnB,oEAAoE;oBACpE,eAAe,GAAG,OAAO,CAAC;oBAC1B,IAAI,CAAC;wBACH,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;wBACpC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;wBACvC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;wBAC1G,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS;4BAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;oBAClE,CAAC;4BAAS,CAAC;wBAAC,eAAe,GAAG,IAAI,CAAC;oBAAC,CAAC;oBACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,yCAAyC,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;gBACzL,CAAC;gBACD,OAAO,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;YAChE,CAAC;YACD,OAAO,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;QAC7C,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["// The hook runtime: a check-only (M0) gateway built once from the enrolled\n// machine key, policy and a durable local receipt log. Each mapped action is\n// signed by the machine key, decided against policy and countersigned — locally,\n// with no HTTP server and no Cloud dependency in 0.1.\n\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { createGateway, StaticPrincipalKeyRegistry, type CloudExporter } from \"@scopebond/gateway\";\nimport { loadOrCreateAttester, openReceiptStore } from \"@scopebond/gateway/node\";\nimport { createSigner } from \"@scopebond/sdk\";\nimport type { Mapped } from \"./map.js\";\nimport { attachExporter, flushBounded, type HookConnection } from \"./cloud.js\";\nimport { explainDeny, type ExplainIntent } from \"./explain.js\";\nimport { RULES_FILE, loadRules } from \"./rules.js\";\nimport { applyRootScope } from \"./paths.js\";\nimport { withActionGroup, actionGroupId, ACTION_GROUP_PARAM } from \"./group.js\";\nimport { cliCommand } from \"./version.js\";\nimport { recordDeliveryAttempt } from \"./delivery-state.js\";\nimport { openDispatchGuard, DELEGATION_ENV } from \"@scopebond/gateway/node\";\nimport { dispatchIntentOf, type DispatchDecision, type DispatchGuard } from \"@scopebond/gateway\";\n\nexport interface RuntimeConfig {\n policyPath: string;\n keyPath: string;\n attesterPath: string;\n dbPath: string;\n /** Strict mode: an unmapped tool is policy-checked (and denied by a closed\n * allowlist) instead of observed. Fail-closed for tools with no taxonomy\n * mapping; default false (observe, matching the connector conformance vector). */\n strict?: boolean;\n /** The working directory the harness reported, used to resolve workspace roots when\n * the rule set lists `allowed_roots`. Defaults to the process directory. */\n cwd?: string;\n /** When connected to a Cloud workspace, receipts are auto-exported to the portal.\n * Export is best-effort and never changes the local decision. */\n cloud?: { connection: HookConnection; fetch?: typeof fetch; flushTimeoutMs?: number };\n}\n\nexport interface Decision {\n decision: \"allow\" | \"deny\" | \"not_evaluated\";\n reason: string;\n /** The clause that decided a deny, when the verdict named one. */\n clauseId?: string | null;\n /** The deciding receipt (the denied one, or the first allow). */\n receipt?: unknown;\n /** Every receipt produced — one per simple command in a decomposed shell call. */\n receipts?: unknown[];\n /** Each action as actually evaluated (after root scoping and grouping), with its\n * receipt: what the observation emitters bind a request digest to. */\n /** What the dispatch boundary spent or refused for this call, when one is configured. */\n dispatch?: DispatchDecision;\n dispatched?: Array<{ action: { action_type: string; params: Record<string, unknown> }; receipt?: unknown }>;\n}\n\n// The hook's own config and keys must be off-limits to the agent it governs:\n// otherwise the agent could rewrite its policy or read the signing keys and the\n// receipts stop meaning anything. These patterns are \"allow if the path does NOT\n// match a protected location\"; they run against the cwd-relative path the mapper\n// produces (and equally against an absolute one).\n//\n// Matching is case-insensitive — Windows and macOS open `.ENV` and `.env` as the\n// same file — by expanding each letter to a two-case class (policy patterns are\n// plain regular expressions with no flags). `ci()` is applied to literal text only.\nexport const ci = (s: string): string => s.replace(/[A-Za-z]/g, (c) => `[${c.toLowerCase()}${c.toUpperCase()}]`);\nexport const under = (dir: string): string => `(?!(?:.*/)?${ci(dir)}(?:/|$))`; // dir itself or anything inside it\nexport const named = (file: string): string => `(?!(?:.*/)?${ci(file)}$)`; // exactly this file name\nexport const dir = (d: string): string => `(?!(?:.*/)?${ci(d)}/?$)`; // the directory itself (a recursive read or copy)\n\n// The protected-write set, in two typed groups the catalog classifies separately:\n// guardrail/hook configuration (C02) and CI configuration (H03).\nconst GUARDRAIL_WRITE_PREV = [\n under(\"\\\\.scopebond\"), `(?!(?:.*/)?${ci(\"\\\\.claude/settings\")})`, under(\"\\\\.claude/hooks\"), under(\"\\\\.claude/agents\"),\n `(?!(?:.*/)?${ci(\"\\\\.cursor/hooks\")})`, named(\"\\\\.codex/hooks\\\\.json\"), named(\"\\\\.codex/config\\\\.toml\"), named(\"\\\\.mcp\\\\.json\"),\n under(\"\\\\.git/hooks\"), named(\"\\\\.git/config\"), under(\"\\\\.husky\"),\n];\nconst CI_WRITE_PREV = [\n under(\"\\\\.github/workflows\"), under(\"\\\\.github/actions\"), named(\"\\\\.gitlab-ci\\\\.yml\"), named(\"\\\\.gitlab-ci\\\\.yaml\"), under(\"\\\\.circleci\"),\n named(\"azure-pipelines\\\\.yml\"), named(\"Jenkinsfile\"),\n];\nconst GUARDRAIL_WRITE = [...GUARDRAIL_WRITE_PREV, named(\"\\\\.cursor/mcp\\\\.json\"), under(\"\\\\.githooks\")];\nconst CI_WRITE = [\n ...CI_WRITE_PREV,\n named(\"azure-pipelines\\\\.yaml\"), named(\"bitbucket-pipelines\\\\.yml\"), named(\"\\\\.travis\\\\.yml\"), named(\"\\\\.drone\\\\.yml\"),\n named(\"cloudbuild\\\\.yaml\"), named(\"cloudbuild\\\\.yml\"), under(\"\\\\.buildkite\"),\n];\n// What 0.8 compiled, kept so an installed policy that still carries it verbatim is\n// upgraded in memory (see LEGACY_STARTER_PATTERNS).\nconst PROTECTED_WRITE_PREV = \"^\" + [...GUARDRAIL_WRITE_PREV, ...CI_WRITE_PREV].join(\"\") + \".+\";\nconst PROTECTED_WRITE = \"^\" + [...GUARDRAIL_WRITE, ...CI_WRITE].join(\"\") + \".+\";\n/** The write protection that is always on, whoever manages the rules: the hook's own settings and those of the agents it\n * guards. A workspace can relax CI-configuration writes; it can never relax these. */\nexport const GUARDRAIL_WRITE_PATTERN = \"^\" + GUARDRAIL_WRITE.join(\"\") + \".+\";\n/** The always-on floor as bare lookaheads, for a pattern that must also refuse these paths (the hook's own folder included). */\nexport const GUARDRAIL_LOOKAHEADS = under(\"\\\\.scopebond\") + GUARDRAIL_WRITE.join(\"\");\n/** The read protection that is always on, whoever manages the rules: the hook's own folder, which holds this computer's\n * signing key and connection. A workspace can record other protected reads instead of blocking them; never this one. */\nexport const GUARDRAIL_READ_PATTERN = \"^\" + under(\"\\\\.scopebond\") + \".+\";\n\nconst PROTECTED_READ = \"^\" + [\n under(\"\\\\.scopebond\"),\n `(?!.*${ci(\"\\\\.(?:key|pem|p12|pfx|jks|keystore)\")}$)`,\n // .env, .env.* — except templates whose name ends in .example/.sample/.template/.dist\n `(?!(?:.*/)?${ci(\"\\\\.env\")}(?!(?:\\\\.[^/]*)?\\\\.(?:${ci(\"example\")}|${ci(\"sample\")}|${ci(\"template\")}|${ci(\"dist\")})$)(?:\\\\.[^/]*)?$)`,\n named(\"\\\\.envrc\"),\n // Credentials outside the workspace that an agent can reach by absolute or ~ path,\n // and their directories as a whole (`cp -r ~/.ssh`, `grep -r x ~/.aws`).\n `(?!(?:.*/)?${ci(\"\\\\.ssh\")}(?:$|/(?!.*${ci(\"\\\\.pub\")}$)(?!${ci(\"known_hosts\")}$)(?!${ci(\"config\")}$)))`,\n `(?!(?:.*/)?${ci(\"\\\\.aws\")}(?:$|/(?!${ci(\"config\")}$)))`,\n named(\"\\\\.npmrc\"), named(\"\\\\.pypirc\"), named(\"\\\\.netrc\"), named(\"_netrc\"), named(\"\\\\.git-credentials\"),\n dir(\"\\\\.kube\"), named(\"\\\\.kube/config\"), dir(\"\\\\.docker\"), named(\"\\\\.docker/config\\\\.json\"),\n under(\"\\\\.config/gcloud\"), under(\"\\\\.azure\"), under(\"\\\\.gnupg\"),\n dir(\"\\\\.config/gh\"), named(\"\\\\.config/gh/hosts\\\\.yml\"), named(\"\\\\.claude/\\\\.credentials\\\\.json\"),\n].join(\"\") + \".+\";\n\n// Destructive programs, POSIX and Windows. The mapper records the program as typed,\n// so the pattern accepts any case and an executable suffix (`RM.exe`, `Remove-Item`).\nexport const DESTRUCTIVE = [\n \"rm\", \"sudo\", \"doas\", \"shutdown\", \"reboot\", \"halt\", \"poweroff\", \"mkfs\", \"dd\", \"shred\", \"truncate\", \"unlink\", \"wipe\", \"srm\",\n \"del\", \"rd\", \"rmdir\", \"erase\", \"deltree\", \"format\", \"diskpart\", \"remove-item\", \"ri\", \"clear-content\", \"clc\", \"stop-computer\", \"restart-computer\",\n];\nconst SAFE_SHELL = `^(?!(?:${DESTRUCTIVE.map(ci).join(\"|\")})(?:${ci(\"\\\\.(?:exe|cmd|bat|com|ps1)\")})?$).+`;\n\n// A push destination the starter policy refuses: main, master, release/*, the\n// \"every branch\" flags (`--all`, `--mirror`, `--branches`) and a push whose\n// destination the mapper could not read (`--unknown`: a git alias, a configured push\n// refspec, `send-pack`), in any case. `--tags` alone pushes only tags and is allowed.\nconst SAFE_REF = `^(?!(?:${ci(\"main\")}|${ci(\"master\")})$)(?!${ci(\"release\")}/)(?!-(?!-${ci(\"tags\")}$)).+`;\n\n// Patterns written by earlier starter policies (0.3–0.5), upgraded in memory when a\n// policy on disk still carries them verbatim. An operator's own edits never match\n// these strings and are left untouched.\nconst LEGACY_STARTER_PATTERNS: Record<string, string> = {\n [PROTECTED_WRITE_PREV]: PROTECTED_WRITE,\n \"^(?!(?:main|master)$)(?!release/).+\": SAFE_REF,\n \"^(?!(?:rm|sudo|shutdown|reboot|mkfs|dd|del|rd|rmdir|erase|deltree|format|Remove-Item|ri)$).+\": SAFE_SHELL,\n \"^(?!(?:rm|sudo|shutdown|reboot|mkfs|dd)$).+\": SAFE_SHELL,\n \"^(?!(?:.*/)?\\\\.scopebond/)(?!(?:.*/)?\\\\.claude/settings)(?!(?:.*/)?\\\\.cursor/hooks)(?!(?:.*/)?\\\\.codex/(?:hooks\\\\.json|config\\\\.toml)$)(?!(?:.*/)?\\\\.git/hooks/)(?!(?:.*/)?\\\\.github/workflows/)(?!(?:.*/)?\\\\.github/actions/)(?!(?:.*/)?\\\\.gitlab-ci\\\\.yml$)(?!(?:.*/)?\\\\.circleci/)(?!(?:.*/)?azure-pipelines\\\\.yml$)(?!(?:.*/)?Jenkinsfile$).+\": PROTECTED_WRITE,\n \"^(?!(?:.*/)?\\\\.scopebond/)(?!(?:.*/)?\\\\.claude/settings)(?!(?:.*/)?\\\\.cursor/hooks)(?!(?:.*/)?\\\\.git/hooks/)(?!(?:.*/)?\\\\.github/workflows/)(?!(?:.*/)?\\\\.github/actions/)(?!(?:.*/)?\\\\.gitlab-ci\\\\.yml$)(?!(?:.*/)?\\\\.circleci/)(?!(?:.*/)?azure-pipelines\\\\.yml$)(?!(?:.*/)?Jenkinsfile$).+\": PROTECTED_WRITE,\n \"^(?!(?:.*/)?\\\\.scopebond/)(?!(?:.*/)?\\\\.claude/settings)(?!(?:.*/)?\\\\.cursor/hooks)(?!(?:.*/)?\\\\.git/hooks/).+\": PROTECTED_WRITE,\n \"^(?!(?:.*/)?\\\\.scopebond/)(?!.*\\\\.key$)(?!(?:.*/)?\\\\.env(?:\\\\.(?!example$|sample$|template$)[^/]*)?$).+\": PROTECTED_READ,\n \"^(?!(?:.*/)?\\\\.scopebond/)(?!.*\\\\.key$).+\": PROTECTED_READ,\n};\n\n/** Upgrade the starter-policy patterns an older hook wrote, in memory, so a user who\n * installed an earlier version gets the current protections without re-running init.\n * Only exact legacy strings are replaced; any other pattern is the operator's own. */\nexport function upgradeStarterPolicy<T>(policy: T): T {\n const p = policy as { clauses?: Array<{ param_bounds?: Record<string, { pattern?: string }> }> };\n for (const clause of p?.clauses ?? []) {\n for (const bound of Object.values(clause?.param_bounds ?? {})) {\n if (bound && typeof bound.pattern === \"string\" && LEGACY_STARTER_PATTERNS[bound.pattern]) bound.pattern = LEGACY_STARTER_PATTERNS[bound.pattern];\n }\n }\n return policy;\n}\n\n/** The default starter policy for a coding agent: protect release branches, deny\n * destructive programs, allow workspace file access except the hook's own config\n * and keys, and trust only the enrolled machine key. Every threshold is the\n * operator's to edit. */\nexport function starterPolicy(agentKid: string): Record<string, unknown> {\n return {\n vocabulary_version: \"1.0\", policy_id: \"coding-agent\", version: 1,\n clauses: [\n {\n id: \"protect-branches\", type: \"action_allowlist\", mode: \"enforce\", action_types: [\"git.push\"],\n param_bounds: { ref: { pattern: SAFE_REF } },\n description: \"Deny pushes to main, master and release/* (any case, any refspec spelling), pushes of every branch at once (--all, --mirror) and pushes whose destination cannot be read from the command (a git alias, a configured push refspec, send-pack). A tags-only push (--tags) is allowed.\",\n },\n {\n id: \"safe-shell\", type: \"action_allowlist\", mode: \"enforce\", action_types: [\"shell.exec\"],\n param_bounds: { program: { pattern: SAFE_SHELL } },\n description: \"Deny destructive programs — POSIX (rm, sudo, doas, shutdown, reboot, mkfs, dd, shred, truncate, unlink, wipe) and Windows/PowerShell (del, rd, rmdir, erase, deltree, format, diskpart, Remove-Item, Clear-Content) — in any case and with or without .exe. An empty program (a command that could not be parsed, or whose program is only known at run time: $VAR, $(…), eval of a variable) is denied. Argument-shaped deletion (find -delete, git clean) is not a program name and is not covered here.\",\n },\n {\n id: \"protect-write\", type: \"action_allowlist\", mode: \"enforce\", action_types: [\"file.write\"],\n param_bounds: { path: { pattern: PROTECTED_WRITE } },\n description: \"Allow workspace writes, but never to the hook's policy/keys, Claude Code settings, hooks and agents, Cursor or Codex hook settings, .mcp.json and .cursor/mcp.json, git hooks (.git/hooks, .githooks) and git config, Husky hooks, or CI config (.github/workflows, .github/actions, .gitlab-ci.yml/.yaml, .circleci, azure-pipelines, bitbucket-pipelines.yml, .travis.yml, .drone.yml, cloudbuild, .buildkite, Jenkinsfile). Case-insensitive.\",\n },\n {\n id: \"protect-read\", type: \"action_allowlist\", mode: \"enforce\", action_types: [\"file.read\"],\n param_bounds: { path: { pattern: PROTECTED_READ } },\n description: \"Allow workspace reads, but never signing keys and key containers (*.key, *.pem, *.p12, *.pfx, *.jks), environment secret files (.env, .env.*, .envrc — except names ending in .example/.sample/.template/.dist), SSH private keys and the .ssh directory, cloud/registry/git credentials (.aws except .aws/config, .npmrc, .pypirc, .netrc, .git-credentials, .kube/config, .docker/config.json, gcloud, Azure, GnuPG, the GitHub CLI's hosts.yml, Claude Code's .credentials.json) or the hook's own .scopebond directory. Case-insensitive.\",\n },\n {\n id: \"observe-net-mcp\", type: \"action_allowlist\", mode: \"monitor\",\n action_types: [\"net.fetch\", \"mcp.tool.call\"],\n description: \"Observe network fetches and MCP tool calls (recorded, not blocked) — add bounds to enforce.\",\n },\n { id: \"keys\", type: \"key_policy\", active_keys: [agentKid], description: \"Only the enrolled machine key may sign.\" },\n ],\n };\n}\n\n/** Build the runtime. Throws on any setup failure (unparseable policy, missing\n * key, unavailable store) — the CLI turns that into a fail-closed deny. */\nexport function createHookRuntime(config: RuntimeConfig) {\n const policy = upgradeStarterPolicy(JSON.parse(readFileSync(config.policyPath, \"utf8\")));\n const agent = createSigner({ privateKeyPem: readFileSync(config.keyPath, \"utf8\") });\n const keys = new StaticPrincipalKeyRegistry([\n { kid: agent.kid, publicKeyPem: agent.publicKeyPem, purposes: [\"agent\"], status: \"active\" },\n ]);\n const { attester } = loadOrCreateAttester({ file: config.attesterPath });\n // When the project has a readable rule set, a denial names the command that edits it\n // rather than the generated file.\n const rulesRemedy = existsSync(join(dirname(config.policyPath), RULES_FILE))\n ? `run \\`${cliCommand(\"rules\")}\\` to see the limits in plain terms, or edit ${join(dirname(config.policyPath), RULES_FILE)}`\n : undefined;\n const { store: baseStore } = openReceiptStore({ db: config.dbPath });\n // When connected, mirror every stored receipt to the hosted portal through a\n // durable outbox. The wrapped store's decision is unchanged; export is best-effort.\n let store = baseStore;\n let exporter: CloudExporter | undefined;\n let outbox: { close(): void } | undefined;\n if (config.cloud) {\n const attached = attachExporter(config.dbPath + \".cloud-outbox.db\", config.cloud.connection, baseStore, config.cloud.fetch);\n store = attached.store;\n exporter = attached.exporter;\n outbox = attached.outbox;\n }\n const scopeRoots = loadRules(dirname(config.policyPath))?.allowed_roots ?? [];\n // The dispatch boundary is checked once per tool call, after every intent has been allowed, so the\n // gateway's own per-intent hook only reports what the runtime already decided for this call.\n const boundary = openDispatchGuard(dirname(config.policyPath));\n let boundaryVerdict: DispatchDecision | null = null;\n const gatewayGuard: DispatchGuard = { authorize: async () => boundaryVerdict ?? { allow: true, reason: \"ok\", consumed_approvals: [], budgets: [] } };\n const gateway = createGateway({ policy, authentication: { keys }, attester, store, mode: \"check_only\", dispatchGuard: gatewayGuard });\n\n return {\n gateway,\n agentKid: agent.kid,\n exporter,\n /** Deliver queued receipts to Cloud with a bounded timeout, then it is safe to\n * exit. Undelivered receipts persist in the durable outbox for the next run. */\n async flush(): Promise<void> {\n if (!exporter) return;\n const before = exporter.status().lastSuccessAt;\n await flushBounded(exporter, config.cloud?.flushTimeoutMs);\n // The process exits after this call; keep what the attempt saw for `status` and `doctor`.\n try { recordDeliveryAttempt(dirname(config.dbPath), exporter.status(), Date.now(), before); } catch { /* diagnostic only */ }\n },\n /** Release the SQLite handles. The hook is a per-tool-call process, and a writer that\n * exits without closing leaves its write-ahead log on disk for the next process to\n * extend — measured at ~11 KiB of WAL per receipt against ~1.7 KiB when closed, so a\n * busy session was writing tens of megabytes of pure overhead into the user's project.\n * Always safe to call, and every exit path should. */\n close(): void {\n try { baseStore.close?.(); } catch { /* the decision is already recorded */ }\n try { outbox?.close(); } catch { /* best effort */ }\n try { boundary?.close(); } catch { /* best effort */ }\n },\n /** Decide one mapped action, recording a receipt either way. */\n async evaluateOne(mapped: Mapped): Promise<Decision> {\n const signed = agent.sign(mapped.intent);\n if (!mapped.evaluated && !config.strict) {\n // Unknown tool or unparseable command, non-strict: observe without\n // evaluating — grants nothing (D30).\n const { receipt } = await gateway.observeAction({ intent: signed.intent, authorization: signed.authorization });\n return { decision: \"not_evaluated\", reason: `no policy applies to ${mapped.intent.action_type}`, receipt };\n }\n // Evaluated actions, and (in strict mode) unmapped tool.<name>/opaque commands,\n // go through policy — a closed allowlist denies an unlisted action.\n const result = await gateway.handleAction({ intent: signed.intent, authorization: signed.authorization });\n if (result.allowed) return { decision: \"allow\", reason: result.reason, receipt: result.receipt };\n // A deny is the one message the user and their agent actually read, so it is\n // composed from the deciding clause's own words rather than the engine's\n // internal reason (\"param ref fails pattern\").\n const clauseId = result.verdict?.clause_id ?? null;\n return {\n decision: \"deny\",\n reason: explainDeny({\n policy, clauseId, detail: result.reason,\n intent: signed.intent as ExplainIntent, policyPath: config.policyPath,\n postHoc: mapped.postHoc,\n // Point at the editable surface when there is one. `policy.json` is compiled\n // from `rules.json`, so telling someone to hand-edit it invites a change the\n // next `rules` run would overwrite.\n remedy: rulesRemedy,\n }),\n clauseId,\n receipt: result.receipt,\n };\n },\n /** Decide a whole tool call. A shell call decomposes into several simple\n * commands; every one is recorded, and a single deny denies the call. */\n async evaluate(mapped: Mapped | Mapped[], options: { groupKey?: string } = {}): Promise<Decision> {\n const raw = Array.isArray(mapped) ? mapped : [mapped];\n if (raw.length === 0) return { decision: \"not_evaluated\", reason: \"no action\", receipts: [] };\n // Every intent of one tool call carries one parent group id, so the receipts of a\n // decomposed call (a command, its file reads and writes, a rename destination)\n // link to each other and count once as an action. It is part of the signed\n // intent, so it is authenticated by the same signature as the rest.\n const scoped = scopeRoots.length ? applyRootScope(raw, { cwd: config.cwd ?? process.cwd(), roots: scopeRoots }) : raw;\n const list = withActionGroup(scoped, actionGroupId(options.groupKey));\n const receipts: unknown[] = [];\n const dispatched: NonNullable<Decision[\"dispatched\"]> = [];\n let allow: Decision | null = null;\n let notEvaluated: Decision | null = null;\n for (const m of list) {\n const d = await this.evaluateOne(m);\n if (d.receipt !== undefined) receipts.push(d.receipt);\n dispatched.push({ action: { action_type: m.intent.action_type, params: m.intent.params as Record<string, unknown> }, ...(d.receipt !== undefined ? { receipt: d.receipt } : {}) });\n if (d.decision === \"deny\") return { ...d, receipts, dispatched }; // any deny denies the call\n if (d.decision === \"allow\" && !allow) allow = d;\n if (d.decision === \"not_evaluated\" && !notEvaluated) notEvaluated = d;\n }\n // No deny: allow if any command was evaluated-and-allowed, else not_evaluated.\n const chosen = allow ?? notEvaluated!;\n if (boundary) {\n // Immediately before permitted dispatch: approvals are consumed and the budget slot is reserved\n // atomically, once for the whole parent action, or nothing is spent and the call is denied.\n const group = String(list[0]!.intent.params[ACTION_GROUP_PARAM]);\n const delegation = process.env[DELEGATION_ENV] ?? \"\";\n const verdict = await boundary.authorize({\n actor: agent.kid, action_group: group, policy_digest: gateway.policyHash,\n intents: list.map((m) => dispatchIntentOf(m.intent as never)),\n ...(delegation !== \"\" ? { delegation_id: delegation } : {}),\n });\n if (!verdict.allow) {\n // Record the refusal as its own denied receipt, then deny the call.\n boundaryVerdict = verdict;\n try {\n const last = list[list.length - 1]!;\n const signed = agent.sign(last.intent);\n const result = await gateway.handleAction({ intent: signed.intent, authorization: signed.authorization });\n if (result.receipt !== undefined) receipts.push(result.receipt);\n } finally { boundaryVerdict = null; }\n return { decision: \"deny\", reason: `Scopebond blocked this before it ran: ${verdict.reason}${verdict.detail ? ` (${verdict.detail})` : \"\"}`, receipts, dispatched, dispatch: verdict };\n }\n return { ...chosen, receipts, dispatched, dispatch: verdict };\n }\n return { ...chosen, receipts, dispatched };\n },\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scopebond/hook",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"description": "Scopebond hook for Claude Code, Cursor, and OpenAI Codex — checks coding-agent actions against policy before they run and records a signed receipt locally.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Avouro LLC",
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
"dist"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@scopebond/policy-schema": "^0.5.0",
|
|
46
45
|
"@scopebond/sdk": "^0.1.3",
|
|
47
|
-
"@scopebond/gateway": "^0.12.0"
|
|
46
|
+
"@scopebond/gateway": "^0.12.0",
|
|
47
|
+
"@scopebond/policy-schema": "^0.5.0"
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"access": "public"
|