@agent-beam/beam 0.2.3 → 0.2.5
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/README.md +6 -0
- package/Skills/README.md +64 -0
- package/Skills/agent-cost-abuse-review/SKILL.md +63 -0
- package/Skills/agent-incident-response/SKILL.md +109 -0
- package/Skills/agent-monitoring-review/SKILL.md +104 -0
- package/Skills/agent-network-segmentation/SKILL.md +70 -0
- package/Skills/agent-permissions-review/SKILL.md +107 -0
- package/Skills/agent-plugin-marketplace-review/SKILL.md +68 -0
- package/Skills/agent-transport-security/SKILL.md +108 -0
- package/Skills/agent-web-security/SKILL.md +103 -0
- package/Skills/agents/README.md +43 -0
- package/Skills/agents/claude/beam-ai-auditor.md +25 -0
- package/Skills/agents/claude/beam-appsec-auditor.md +25 -0
- package/Skills/agents/claude/beam-findings-reviewer.md +25 -0
- package/Skills/agents/claude/beam-incident-analyst.md +25 -0
- package/Skills/agents/claude/beam-infrastructure-auditor.md +25 -0
- package/Skills/agents/claude/beam-mcp-auditor.md +25 -0
- package/Skills/agents/claude/beam-skill-auditor.md +25 -0
- package/Skills/agents/codex/beam-ai-auditor.toml +23 -0
- package/Skills/agents/codex/beam-appsec-auditor.toml +23 -0
- package/Skills/agents/codex/beam-findings-reviewer.toml +23 -0
- package/Skills/agents/codex/beam-incident-analyst.toml +23 -0
- package/Skills/agents/codex/beam-infrastructure-auditor.toml +23 -0
- package/Skills/agents/codex/beam-mcp-auditor.toml +23 -0
- package/Skills/agents/codex/beam-skill-auditor.toml +23 -0
- package/Skills/ai-asset-scanner/SKILL.md +69 -0
- package/Skills/ai-security/SKILL.md +59 -0
- package/Skills/api-auth-security/SKILL.md +106 -0
- package/Skills/browser-agent-security/SKILL.md +77 -0
- package/Skills/cicd-agent-security/SKILL.md +108 -0
- package/Skills/cloud-agent-security/SKILL.md +101 -0
- package/Skills/container-sandbox-security/SKILL.md +101 -0
- package/Skills/data-retention-privacy-review/SKILL.md +69 -0
- package/Skills/dependency-supply-chain/SKILL.md +106 -0
- package/Skills/llm-output-handling/SKILL.md +66 -0
- package/Skills/manifest.json +147 -0
- package/Skills/mcp-scanner/SKILL.md +64 -0
- package/Skills/model-artifact-scanner/SKILL.md +108 -0
- package/Skills/multi-agent-trust-review/SKILL.md +71 -0
- package/Skills/prompt-injection-review/SKILL.md +102 -0
- package/Skills/rag-memory-security/SKILL.md +105 -0
- package/Skills/scripts/validate-skills.mjs +113 -0
- package/Skills/secrets-egress-review/SKILL.md +105 -0
- package/Skills/security-assessment/SKILL.md +73 -0
- package/Skills/security-reporting/SKILL.md +73 -0
- package/Skills/skill-scanner/SKILL.md +68 -0
- package/Skills/tool-schema-review/SKILL.md +66 -0
- package/Skills/training-data-security/SKILL.md +70 -0
- package/Skills/webhook-callback-security/SKILL.md +71 -0
- package/dist/agents.d.ts +2 -1
- package/dist/agents.js +24 -8
- package/dist/agents.js.map +1 -1
- package/dist/approvals.d.ts +65 -0
- package/dist/approvals.js +86 -0
- package/dist/approvals.js.map +1 -0
- package/dist/banner.js +7 -2
- package/dist/banner.js.map +1 -1
- package/dist/cli.js +71 -15
- package/dist/cli.js.map +1 -1
- package/dist/client.js +107 -3
- package/dist/client.js.map +1 -1
- package/dist/color.d.ts +5 -0
- package/dist/color.js +15 -0
- package/dist/color.js.map +1 -0
- package/dist/data-detectors.d.ts +18 -0
- package/dist/data-detectors.js +33 -0
- package/dist/data-detectors.js.map +1 -0
- package/dist/forward.js +7 -2
- package/dist/forward.js.map +1 -1
- package/dist/hook-adapters.js +13 -2
- package/dist/hook-adapters.js.map +1 -1
- package/dist/install.js +63 -8
- package/dist/install.js.map +1 -1
- package/dist/owl.d.ts +6 -0
- package/dist/owl.js +50 -0
- package/dist/owl.js.map +1 -0
- package/dist/policy.d.ts +66 -6
- package/dist/policy.js +132 -40
- package/dist/policy.js.map +1 -1
- package/dist/prompts.d.ts +1 -0
- package/dist/prompts.js +22 -6
- package/dist/prompts.js.map +1 -1
- package/dist/run.d.ts +1 -0
- package/dist/run.js +48 -0
- package/dist/run.js.map +1 -0
- package/dist/sandbox.d.ts +13 -0
- package/dist/sandbox.js +116 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/setup.js +45 -5
- package/dist/setup.js.map +1 -1
- package/dist/shims.d.ts +13 -0
- package/dist/shims.js +85 -0
- package/dist/shims.js.map +1 -0
- package/dist/store.d.ts +16 -0
- package/dist/store.js +19 -2
- package/dist/store.js.map +1 -1
- package/package.json +35 -6
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const detectors = [
|
|
2
|
+
{ kind: "PRIVATE_KEY", pattern: /-----BEGIN [^-]*PRIVATE KEY-----[\s\S]*?(?:-----END [^-]*PRIVATE KEY-----|$)/g, replacement: "[PRIVATE_KEY_REDACTED]" },
|
|
3
|
+
{ kind: "API_KEY", pattern: /\b(?:sk-(?:proj-|ant-)?[A-Za-z0-9_-]{12,}|gh[pousr]_[A-Za-z0-9_]{16,}|github_pat_[A-Za-z0-9_]{16,}|AKIA[A-Z0-9]{16}|xox[baprs]-[\w-]{10,})\b/g, replacement: "[API_KEY_REDACTED]" },
|
|
4
|
+
{ kind: "SECRET", pattern: /((?:api[_-]?key|access[_-]?token|token|secret|authorization)\s*[:=]\s*["']?)([^\s,"';&}\]]+)/gi, replacement: "$1[SECRET_REDACTED]" },
|
|
5
|
+
{ kind: "PASSWORD", pattern: /((?:password|passwd|pwd)\s*[:=]\s*["']?)([^\s,"';&}\]]+)/gi, replacement: "$1[PASSWORD_REDACTED]" },
|
|
6
|
+
{ kind: "EMAIL", pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, replacement: "[EMAIL_REDACTED]" },
|
|
7
|
+
{ kind: "PHONE", pattern: /(?<!\w)(?:\+\d{1,3}[\s.-]?)?(?:\d[\s.-]?){8,14}\d(?!\w)/g, replacement: "[PHONE_REDACTED]" },
|
|
8
|
+
];
|
|
9
|
+
export function detectSensitive(text, custom = []) {
|
|
10
|
+
const findings = [];
|
|
11
|
+
for (const detector of detectors) {
|
|
12
|
+
detector.pattern.lastIndex = 0;
|
|
13
|
+
for (const match of text.matchAll(detector.pattern))
|
|
14
|
+
findings.push({ kind: detector.kind, confidence: 0.9, start: match.index ?? 0, end: (match.index ?? 0) + match[0].length, replacement: detector.replacement });
|
|
15
|
+
}
|
|
16
|
+
for (const customDetector of custom) {
|
|
17
|
+
try {
|
|
18
|
+
const pattern = new RegExp(customDetector.pattern, "g");
|
|
19
|
+
for (const match of text.matchAll(pattern))
|
|
20
|
+
findings.push({ kind: "CUSTOM", confidence: 0.8, start: match.index ?? 0, end: (match.index ?? 0) + match[0].length, replacement: customDetector.replacement });
|
|
21
|
+
}
|
|
22
|
+
catch { /* policy validation reports malformed custom detectors */ }
|
|
23
|
+
}
|
|
24
|
+
return findings.sort((a, b) => b.start - a.start);
|
|
25
|
+
}
|
|
26
|
+
export function redactSensitive(text, custom = []) {
|
|
27
|
+
const findings = detectSensitive(text, custom);
|
|
28
|
+
let result = text;
|
|
29
|
+
for (const finding of findings)
|
|
30
|
+
result = result.slice(0, finding.start) + finding.replacement + result.slice(finding.end);
|
|
31
|
+
return { text: result, findings };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=data-detectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-detectors.js","sourceRoot":"","sources":["../src/data-detectors.ts"],"names":[],"mappings":"AAGA,MAAM,SAAS,GAAwE;IACrF,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,+EAA+E,EAAE,WAAW,EAAE,wBAAwB,EAAE;IACxJ,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,+IAA+I,EAAE,WAAW,EAAE,oBAAoB,EAAE;IAChN,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,gGAAgG,EAAE,WAAW,EAAE,qBAAqB,EAAE;IACjK,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,4DAA4D,EAAE,WAAW,EAAE,uBAAuB,EAAE;IACjI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,6CAA6C,EAAE,WAAW,EAAE,kBAAkB,EAAE;IAC1G,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,0DAA0D,EAAE,WAAW,EAAE,kBAAkB,EAAE;CACxH,CAAC;AAGF,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,SAA2B,EAAE;IACzE,MAAM,QAAQ,GAAkB,EAAE,CAAC;IACnC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,QAAQ,CAAC,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC;QAC/B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IACtN,CAAC;IACD,KAAK,MAAM,cAAc,IAAI,MAAM,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YACxD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,cAAc,CAAC,WAAW,EAAE,CAAC,CAAC;QAC9M,CAAC;QAAC,MAAM,CAAC,CAAC,0DAA0D,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,SAA2B,EAAE;IACzE,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,MAAM,GAAG,IAAI,CAAC;IAClE,KAAK,MAAM,OAAO,IAAI,QAAQ;QAAE,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1H,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AACpC,CAAC"}
|
package/dist/forward.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
1
|
+
import { mkdir, rename, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { getDataDirectory, getTelemetryUrl } from "./config.js";
|
|
4
|
+
import { lastGoodPolicyPath } from "./policy.js";
|
|
4
5
|
import { readIdentity } from "./enroll.js";
|
|
5
6
|
function policyPath() {
|
|
6
7
|
return join(getDataDirectory(), "policy.json");
|
|
@@ -134,7 +135,11 @@ export async function syncPolicy() {
|
|
|
134
135
|
const bundle = await response.json();
|
|
135
136
|
const path = policyPath();
|
|
136
137
|
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
137
|
-
|
|
138
|
+
const contents = `${JSON.stringify(bundle, null, 2)}\n`;
|
|
139
|
+
const temp = `${path}.tmp`;
|
|
140
|
+
await writeFile(temp, contents, { mode: 0o600 });
|
|
141
|
+
await rename(temp, path);
|
|
142
|
+
await writeFile(lastGoodPolicyPath(), contents, { mode: 0o600 });
|
|
138
143
|
return { status: "updated", version: bundle.version, path };
|
|
139
144
|
}
|
|
140
145
|
//# sourceMappingURL=forward.js.map
|
package/dist/forward.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"forward.js","sourceRoot":"","sources":["../src/forward.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"forward.js","sourceRoot":"","sources":["../src/forward.ts"],"names":[],"mappings":"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"}
|
package/dist/hook-adapters.js
CHANGED
|
@@ -23,11 +23,22 @@ function generic(raw) {
|
|
|
23
23
|
session_id: raw.session_id ?? raw.sessionId ?? raw.session,
|
|
24
24
|
cwd: raw.cwd ?? raw.project_path ?? raw.workingDirectory ?? raw.working_directory,
|
|
25
25
|
tool_name: raw.tool_name ?? raw.toolName ?? raw.tool,
|
|
26
|
-
tool_input: raw.tool_input ?? raw.toolArgs ?? raw.toolInput ?? raw.input,
|
|
26
|
+
tool_input: raw.tool_input ?? raw.toolArgs ?? raw.toolInput ?? raw.input ?? raw.args,
|
|
27
27
|
hook_event_name: raw.hook_event_name ?? raw.hookEventName ?? raw.event,
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
// Gemini's documented pre_tool_execution contract nests the call under tool_call.
|
|
31
|
+
function gemini(raw) {
|
|
32
|
+
const call = raw.tool_call && typeof raw.tool_call === "object" ? raw.tool_call : {};
|
|
33
|
+
return {
|
|
34
|
+
...raw,
|
|
35
|
+
session_id: raw.session_id ?? raw.sessionId ?? raw.environment_id,
|
|
36
|
+
tool_name: raw.tool_name ?? raw.toolName ?? call.name,
|
|
37
|
+
tool_input: raw.tool_input ?? raw.toolArgs ?? call.args,
|
|
38
|
+
hook_event_name: raw.hook_event_name ?? "pre_tool_execution",
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const adapters = { passthrough, "copilot-camel": copilotCamel, generic, gemini };
|
|
31
42
|
export function adaptHookPayload(sourceAgent, raw) {
|
|
32
43
|
const agent = findAgent(sourceAgent);
|
|
33
44
|
const adapter = adapters[agent?.adapter ?? "generic"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-adapters.js","sourceRoot":"","sources":["../src/hook-adapters.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAY,MAAM,aAAa,CAAC;AAElD,8FAA8F;AAC9F,+FAA+F;AAC/F,SAAS,WAAW,CAAC,GAAQ;IAC3B,OAAO,GAAG,CAAC;AACb,CAAC;AAED,0FAA0F;AAC1F,0EAA0E;AAC1E,SAAS,YAAY,CAAC,GAAQ;IAC5B,OAAO;QACL,GAAG,GAAG;QACN,UAAU,EAAE,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,UAAU;QAC3C,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,SAAS,EAAE,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,SAAS;QACxC,UAAU,EAAE,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,UAAU;QAC1C,eAAe,EAAE,GAAG,CAAC,eAAe,IAAI,YAAY;KACrD,CAAC;AACJ,CAAC;AAED,gGAAgG;AAChG,SAAS,OAAO,CAAC,GAAQ;IACvB,OAAO;QACL,GAAG,GAAG;QACN,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,OAAO;QAC1D,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,gBAAgB,IAAI,GAAG,CAAC,iBAAiB;QACjF,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI;QACpD,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,KAAK;
|
|
1
|
+
{"version":3,"file":"hook-adapters.js","sourceRoot":"","sources":["../src/hook-adapters.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAY,MAAM,aAAa,CAAC;AAElD,8FAA8F;AAC9F,+FAA+F;AAC/F,SAAS,WAAW,CAAC,GAAQ;IAC3B,OAAO,GAAG,CAAC;AACb,CAAC;AAED,0FAA0F;AAC1F,0EAA0E;AAC1E,SAAS,YAAY,CAAC,GAAQ;IAC5B,OAAO;QACL,GAAG,GAAG;QACN,UAAU,EAAE,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,UAAU;QAC3C,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,SAAS,EAAE,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,SAAS;QACxC,UAAU,EAAE,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,UAAU;QAC1C,eAAe,EAAE,GAAG,CAAC,eAAe,IAAI,YAAY;KACrD,CAAC;AACJ,CAAC;AAED,gGAAgG;AAChG,SAAS,OAAO,CAAC,GAAQ;IACvB,OAAO;QACL,GAAG,GAAG;QACN,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,OAAO;QAC1D,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,gBAAgB,IAAI,GAAG,CAAC,iBAAiB;QACjF,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI;QACpD,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI;QACpF,eAAe,EAAE,GAAG,CAAC,eAAe,IAAI,GAAG,CAAC,aAAa,IAAI,GAAG,CAAC,KAAK;KACvE,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,SAAS,MAAM,CAAC,GAAQ;IACtB,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,SAAgB,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5F,OAAO;QACL,GAAG,GAAG;QACN,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,cAAc;QACjE,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI;QACrD,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI;QACvD,eAAe,EAAE,GAAG,CAAC,eAAe,IAAI,oBAAoB;KAC7D,CAAC;AACJ,CAAC;AAED,MAAM,QAAQ,GAAG,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AAEjF,MAAM,UAAU,gBAAgB,CAAC,WAAmB,EAAE,GAAQ;IAC5D,MAAM,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,EAAE,OAAO,IAAI,SAAS,CAAC,CAAC;IACtD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;AACtB,CAAC"}
|
package/dist/install.js
CHANGED
|
@@ -1,15 +1,56 @@
|
|
|
1
1
|
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
4
5
|
import { AGENTS, findAgent, hookConfigFullPath } from "./agents.js";
|
|
6
|
+
const OPEN_CODE_PLUGIN = `import { spawnSync } from "node:child_process";
|
|
7
|
+
|
|
8
|
+
export const BeamPlugin = async () => ({
|
|
9
|
+
"tool.execute.before": async (input: any, output: any) => {
|
|
10
|
+
const payload = { tool_name: input?.tool, tool_input: output?.args ?? {}, session_id: input?.sessionID, cwd: input?.directory ?? input?.worktree, source_agent: "opencode", source_type: "hook" };
|
|
11
|
+
const result = spawnSync("beam", ["hook", "opencode"], { input: JSON.stringify(payload), encoding: "utf8", timeout: 5000 });
|
|
12
|
+
let response: any = {};
|
|
13
|
+
try { response = JSON.parse(result.stdout || "{}"); } catch { /* fail closed below */ }
|
|
14
|
+
if (result.error || result.status !== 0 || response.decision === "deny") throw new Error(response.reason || "Beam denied this tool execution.");
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
`;
|
|
18
|
+
// A bare "beam" command relies on beam's global bin symlink being resolvable on PATH -- but the
|
|
19
|
+
// shell that runs an agent's hooks (e.g. Claude Code's own PreToolUse child process) doesn't
|
|
20
|
+
// always source the same profile a user's interactive shell does, so "beam" can 404 silently
|
|
21
|
+
// there even though it works fine when the user types it themselves. Embedding the absolute path
|
|
22
|
+
// to this running install (both the node binary and dist/cli.js, resolved via import.meta.url
|
|
23
|
+
// rather than argv[1] so it's correct however beam itself was invoked) sidesteps PATH entirely.
|
|
24
|
+
function resolveHookCommand(agentId) {
|
|
25
|
+
const cliPath = fileURLToPath(new URL("./cli.js", import.meta.url));
|
|
26
|
+
return `${quoteForShell(process.execPath)} ${quoteForShell(cliPath)} hook ${agentId}`;
|
|
27
|
+
}
|
|
28
|
+
function quoteForShell(value) {
|
|
29
|
+
return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
30
|
+
}
|
|
31
|
+
// Pre-fix installs wrote a bare "beam hook <agent>" command that depends on PATH; recognizing it
|
|
32
|
+
// here lets installHook migrate an existing entry to the absolute-path form instead of leaving a
|
|
33
|
+
// dead duplicate behind, and lets uninstallHook still find and remove it.
|
|
34
|
+
function legacyHookCommand(agentId) {
|
|
35
|
+
return `beam hook ${agentId}`;
|
|
36
|
+
}
|
|
5
37
|
export async function installHook(agentId, home = homedir()) {
|
|
6
38
|
const agent = findAgent(agentId);
|
|
7
39
|
if (!agent)
|
|
8
40
|
throw new Error(`Unknown agent '${agentId}'. Run 'beam agent list' to see supported agents.`);
|
|
9
|
-
if (
|
|
41
|
+
if (agent.id === "opencode" && agent.pluginPath) {
|
|
42
|
+
const path = join(home, agent.pluginPath);
|
|
43
|
+
const existing = await readFile(path, "utf8").catch(() => "");
|
|
44
|
+
if (!existing) {
|
|
45
|
+
await mkdir(dirname(path), { recursive: true });
|
|
46
|
+
await writeFile(path, OPEN_CODE_PLUGIN, { mode: 0o600 });
|
|
47
|
+
}
|
|
48
|
+
return { agent: agent.id, path, alreadyInstalled: Boolean(existing) };
|
|
49
|
+
}
|
|
50
|
+
if (!agent.hookConfigPath || !agent.mergeHookConfig || !agent.unmergeHookConfig)
|
|
10
51
|
throw new Error(`${agent.name} has no supported hook install path yet; see 'beam agent list' for its status.`);
|
|
11
52
|
const path = hookConfigFullPath(agent, home);
|
|
12
|
-
const command =
|
|
53
|
+
const command = resolveHookCommand(agent.id);
|
|
13
54
|
let existing = {};
|
|
14
55
|
let raw = "";
|
|
15
56
|
try {
|
|
@@ -23,7 +64,9 @@ export async function installHook(agentId, home = homedir()) {
|
|
|
23
64
|
throw e;
|
|
24
65
|
}
|
|
25
66
|
}
|
|
26
|
-
|
|
67
|
+
// Strip any pre-fix legacy entry first so re-installing migrates it rather than duplicating.
|
|
68
|
+
const base = agent.unmergeHookConfig(existing, legacyHookCommand(agent.id));
|
|
69
|
+
const merged = agent.mergeHookConfig(base, command);
|
|
27
70
|
const alreadyInstalled = JSON.stringify(merged) === JSON.stringify(existing) && raw.trim() !== "";
|
|
28
71
|
if (!alreadyInstalled) {
|
|
29
72
|
await mkdir(dirname(path), { recursive: true });
|
|
@@ -39,10 +82,19 @@ export async function uninstallHook(agentId, home = homedir()) {
|
|
|
39
82
|
const agent = findAgent(agentId);
|
|
40
83
|
if (!agent)
|
|
41
84
|
throw new Error(`Unknown agent '${agentId}'. Run 'beam agent list' to see supported agents.`);
|
|
85
|
+
if (agent.id === "opencode" && agent.pluginPath) {
|
|
86
|
+
const path = join(home, agent.pluginPath);
|
|
87
|
+
const existing = await readFile(path, "utf8").catch(() => "");
|
|
88
|
+
if (existing === OPEN_CODE_PLUGIN) {
|
|
89
|
+
const { rm } = await import("node:fs/promises");
|
|
90
|
+
await rm(path, { force: true });
|
|
91
|
+
return { agent: agent.id, path, removed: true };
|
|
92
|
+
}
|
|
93
|
+
return { agent: agent.id, path, removed: false };
|
|
94
|
+
}
|
|
42
95
|
if (!agent.hookConfigPath || !agent.unmergeHookConfig)
|
|
43
|
-
throw new Error(`${agent.name} has no supported hook
|
|
96
|
+
throw new Error(`${agent.name} has no supported hook path yet; see 'beam agent list' for its status.`);
|
|
44
97
|
const path = hookConfigFullPath(agent, home);
|
|
45
|
-
const command = `beam hook ${agent.id}`;
|
|
46
98
|
let existing = {};
|
|
47
99
|
let raw = "";
|
|
48
100
|
try {
|
|
@@ -56,7 +108,10 @@ export async function uninstallHook(agentId, home = homedir()) {
|
|
|
56
108
|
throw new Error(`${path} contains invalid JSON; fix or remove it manually.`);
|
|
57
109
|
throw e;
|
|
58
110
|
}
|
|
59
|
-
|
|
111
|
+
// Remove both the current absolute-path command and any pre-fix legacy "beam hook <agent>"
|
|
112
|
+
// entry, so uninstall cleans up a machine regardless of which form was installed.
|
|
113
|
+
const withoutCurrent = agent.unmergeHookConfig(existing, resolveHookCommand(agent.id));
|
|
114
|
+
const updated = agent.unmergeHookConfig(withoutCurrent, legacyHookCommand(agent.id));
|
|
60
115
|
const removed = JSON.stringify(updated) !== JSON.stringify(existing);
|
|
61
116
|
if (removed)
|
|
62
117
|
await writeFile(path, `${JSON.stringify(updated, null, 2)}\n`);
|
|
@@ -69,7 +124,7 @@ export async function uninstallHook(agentId, home = homedir()) {
|
|
|
69
124
|
export async function uninstallAllHooks(home = homedir()) {
|
|
70
125
|
const results = [];
|
|
71
126
|
for (const agent of AGENTS) {
|
|
72
|
-
if (!agent.hookConfigPath || !agent.unmergeHookConfig)
|
|
127
|
+
if (agent.id !== "opencode" && (!agent.hookConfigPath || !agent.unmergeHookConfig))
|
|
73
128
|
continue;
|
|
74
129
|
results.push(await uninstallHook(agent.id, home));
|
|
75
130
|
}
|
|
@@ -105,7 +160,7 @@ export async function installAllDetectedHooks(home = homedir()) {
|
|
|
105
160
|
results.push({ agent: agent.id, name: agent.name, status: "not-detected" });
|
|
106
161
|
continue;
|
|
107
162
|
}
|
|
108
|
-
if (!agent.hookConfigPath || !agent.mergeHookConfig) {
|
|
163
|
+
if (agent.id !== "opencode" && (!agent.hookConfigPath || !agent.mergeHookConfig)) {
|
|
109
164
|
results.push({ agent: agent.id, name: agent.name, status: "not-supported" });
|
|
110
165
|
continue;
|
|
111
166
|
}
|
package/dist/install.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,kBAAkB,EAAkC,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"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"}
|
package/dist/owl.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Print the owl and a short hello the first time `beam setup` runs on a
|
|
3
|
+
* machine, then record that it happened. Any later setup runs stay quiet.
|
|
4
|
+
* Never throws — a greeting is never worth failing onboarding over.
|
|
5
|
+
*/
|
|
6
|
+
export declare function showFirstRunWelcome(): Promise<void>;
|
package/dist/owl.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { mkdir, writeFile, access } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { getBeamHome } from "./config.js";
|
|
4
|
+
// Beam the owl, in 2-bit block art — the same little mascot that watches over
|
|
5
|
+
// your agents on agentbeam.com, drawn for a monospace terminal.
|
|
6
|
+
const OWL = String.raw `
|
|
7
|
+
▟▀▙ ▟▀▙
|
|
8
|
+
█◉█ █◉█
|
|
9
|
+
▜▄▛▲▜▄▛
|
|
10
|
+
▔`;
|
|
11
|
+
const WELCOME = [
|
|
12
|
+
"",
|
|
13
|
+
" Hi, I'm Beam. I'll keep an eye on what your AI agents do —",
|
|
14
|
+
" locally, on this machine. No agent activity leaves without you.",
|
|
15
|
+
"",
|
|
16
|
+
];
|
|
17
|
+
// A once-ever marker in ~/.beam. Its presence means this machine has already
|
|
18
|
+
// been greeted, so the welcome only ever shows on the very first setup.
|
|
19
|
+
function welcomeMarkerPath() {
|
|
20
|
+
return path.join(getBeamHome(), ".welcomed");
|
|
21
|
+
}
|
|
22
|
+
async function hasBeenWelcomed() {
|
|
23
|
+
try {
|
|
24
|
+
await access(welcomeMarkerPath());
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Print the owl and a short hello the first time `beam setup` runs on a
|
|
33
|
+
* machine, then record that it happened. Any later setup runs stay quiet.
|
|
34
|
+
* Never throws — a greeting is never worth failing onboarding over.
|
|
35
|
+
*/
|
|
36
|
+
export async function showFirstRunWelcome() {
|
|
37
|
+
try {
|
|
38
|
+
if (await hasBeenWelcomed())
|
|
39
|
+
return;
|
|
40
|
+
console.log(OWL);
|
|
41
|
+
for (const line of WELCOME)
|
|
42
|
+
console.log(line);
|
|
43
|
+
await mkdir(getBeamHome(), { recursive: true });
|
|
44
|
+
await writeFile(welcomeMarkerPath(), `${new Date().toISOString()}\n`, "utf8");
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Ignore — if we can't write the marker, worst case the owl greets twice.
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=owl.js.map
|
package/dist/owl.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owl.js","sourceRoot":"","sources":["../src/owl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,8EAA8E;AAC9E,gEAAgE;AAChE,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAA;;;;SAIb,CAAC;AAEV,MAAM,OAAO,GAAG;IACd,EAAE;IACF,8DAA8D;IAC9D,mEAAmE;IACnE,EAAE;CACH,CAAC;AAEF,6EAA6E;AAC7E,wEAAwE;AACxE,SAAS,iBAAiB;IACxB,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,WAAW,CAAC,CAAC;AAC/C,CAAC;AAED,KAAK,UAAU,eAAe;IAC5B,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,iBAAiB,EAAE,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,IAAI,CAAC;QACH,IAAI,MAAM,eAAe,EAAE;YAAE,OAAO;QACpC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjB,KAAK,MAAM,IAAI,IAAI,OAAO;YAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,KAAK,CAAC,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,MAAM,SAAS,CAAC,iBAAiB,EAAE,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAChF,CAAC;IAAC,MAAM,CAAC;QACP,0EAA0E;IAC5E,CAAC;AACH,CAAC"}
|
package/dist/policy.d.ts
CHANGED
|
@@ -1,27 +1,87 @@
|
|
|
1
|
+
import type { CustomDetector } from "./data-detectors.js";
|
|
2
|
+
import type { ApprovalPolicy } from "./approvals.js";
|
|
1
3
|
export type PolicyMode = "observe" | "advisory" | "enforce";
|
|
4
|
+
export type PolicyAction = "ALLOW" | "ASK" | "BLOCK" | "REDACT" | "AUDIT";
|
|
5
|
+
export interface PolicyRule {
|
|
6
|
+
id: string;
|
|
7
|
+
title?: string;
|
|
8
|
+
action: PolicyAction;
|
|
9
|
+
risk?: number;
|
|
10
|
+
tool?: string;
|
|
11
|
+
command?: string;
|
|
12
|
+
args?: string[];
|
|
13
|
+
path?: string;
|
|
14
|
+
url?: string;
|
|
15
|
+
role?: string;
|
|
16
|
+
user?: string;
|
|
17
|
+
agent?: string;
|
|
18
|
+
cwd?: string;
|
|
19
|
+
repository?: string;
|
|
20
|
+
branch?: string;
|
|
21
|
+
environment?: string;
|
|
22
|
+
time?: {
|
|
23
|
+
after?: string;
|
|
24
|
+
before?: string;
|
|
25
|
+
};
|
|
26
|
+
reason?: string;
|
|
27
|
+
approval?: ApprovalPolicy;
|
|
28
|
+
}
|
|
2
29
|
export interface PolicyRules {
|
|
3
30
|
mode: PolicyMode;
|
|
4
31
|
blockedTools: string[];
|
|
5
32
|
blockedCommandPatterns: string[];
|
|
6
33
|
disabledAgents: string[];
|
|
34
|
+
rules?: PolicyRule[];
|
|
35
|
+
risk?: {
|
|
36
|
+
askAt?: number;
|
|
37
|
+
blockAt?: number;
|
|
38
|
+
};
|
|
39
|
+
customDetectors?: CustomDetector[];
|
|
7
40
|
}
|
|
8
41
|
export interface PolicyBundle {
|
|
9
42
|
version: number;
|
|
10
43
|
rules: PolicyRules;
|
|
11
44
|
notAfter: string | null;
|
|
45
|
+
errors?: string[];
|
|
12
46
|
}
|
|
13
47
|
export declare function policyPath(): string;
|
|
48
|
+
export declare function lastGoodPolicyPath(): string;
|
|
14
49
|
export declare function readPolicy(): Promise<PolicyBundle | null>;
|
|
15
|
-
export type DecisionAction = "allow" | "warn" | "deny";
|
|
50
|
+
export type DecisionAction = "allow" | "ask" | "block" | "redact" | "audit" | "warn" | "deny";
|
|
51
|
+
export interface ActionContext {
|
|
52
|
+
agent: string;
|
|
53
|
+
tool: string;
|
|
54
|
+
command: string;
|
|
55
|
+
args?: string[];
|
|
56
|
+
path?: string;
|
|
57
|
+
url?: string;
|
|
58
|
+
role?: string;
|
|
59
|
+
user?: string;
|
|
60
|
+
cwd?: string;
|
|
61
|
+
repository?: string;
|
|
62
|
+
branch?: string;
|
|
63
|
+
environment?: string;
|
|
64
|
+
time?: string;
|
|
65
|
+
}
|
|
16
66
|
export interface Decision {
|
|
17
67
|
action: DecisionAction;
|
|
18
68
|
reason?: string;
|
|
19
69
|
mode: PolicyMode;
|
|
20
70
|
policyVersion: number;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
71
|
+
policy?: string;
|
|
72
|
+
rule?: string;
|
|
73
|
+
approval?: ApprovalPolicy;
|
|
74
|
+
actor?: string;
|
|
75
|
+
role?: string;
|
|
76
|
+
agent?: string;
|
|
77
|
+
tool?: string;
|
|
78
|
+
command?: string;
|
|
79
|
+
args?: string[];
|
|
80
|
+
resource?: string;
|
|
81
|
+
riskScore: number;
|
|
82
|
+
riskLevel: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
|
|
83
|
+
riskFactors: string[];
|
|
84
|
+
customDetectors?: CustomDetector[];
|
|
85
|
+
errors?: string[];
|
|
26
86
|
}
|
|
27
87
|
export declare function evaluate(bundle: PolicyBundle | null, ctx: ActionContext): Decision;
|
package/dist/policy.js
CHANGED
|
@@ -1,59 +1,151 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { getDataDirectory } from "./config.js";
|
|
4
|
-
const EMPTY = { mode: "observe", blockedTools: [], blockedCommandPatterns: [], disabledAgents: [] };
|
|
5
|
-
export function policyPath() {
|
|
6
|
-
|
|
4
|
+
const EMPTY = { mode: "observe", blockedTools: [], blockedCommandPatterns: [], disabledAgents: [], rules: [], customDetectors: [] };
|
|
5
|
+
export function policyPath() { return join(getDataDirectory(), "policy.json"); }
|
|
6
|
+
export function lastGoodPolicyPath() { return join(getDataDirectory(), "policy.last-good.json"); }
|
|
7
|
+
function validAction(v) { return ["ALLOW", "ASK", "BLOCK", "REDACT", "AUDIT"].includes(String(v).toUpperCase()); }
|
|
8
|
+
function validRule(v, index, errors) {
|
|
9
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) {
|
|
10
|
+
errors.push(`rules[${index}] must be an object`);
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const r = v;
|
|
14
|
+
if (typeof r.id !== "string" || !r.id.trim()) {
|
|
15
|
+
errors.push(`rules[${index}].id is required`);
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
if (!validAction(r.action)) {
|
|
19
|
+
errors.push(`rules[${index}] (${r.id}) has an invalid action`);
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
if (r.args !== undefined && (!Array.isArray(r.args) || r.args.some(a => typeof a !== "string"))) {
|
|
23
|
+
errors.push(`rules[${index}] (${r.id}).args must be strings`);
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
if (r.risk !== undefined && (typeof r.risk !== "number" || r.risk < 0 || r.risk > 100)) {
|
|
27
|
+
errors.push(`rules[${index}] (${r.id}).risk must be 0-100`);
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
return { ...r, action: String(r.action).toUpperCase() };
|
|
7
31
|
}
|
|
8
32
|
export async function readPolicy() {
|
|
33
|
+
const read = async (path) => {
|
|
34
|
+
const parsed = JSON.parse(await readFile(path, "utf8"));
|
|
35
|
+
if (!parsed.rules || typeof parsed.rules !== "object")
|
|
36
|
+
throw new Error("missing rules");
|
|
37
|
+
const errors = [];
|
|
38
|
+
const source = parsed.rules;
|
|
39
|
+
if (source.customDetectors !== undefined && (!Array.isArray(source.customDetectors) || source.customDetectors.some(d => !d || typeof d.name !== "string" || typeof d.pattern !== "string" || typeof d.replacement !== "string")))
|
|
40
|
+
errors.push("customDetectors must contain name, pattern, and replacement strings");
|
|
41
|
+
for (const detector of Array.isArray(source.customDetectors) ? source.customDetectors : []) {
|
|
42
|
+
try {
|
|
43
|
+
new RegExp(detector.pattern);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
errors.push(`Invalid custom detector pattern: ${detector.name}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const rules = Array.isArray(source.rules) ? source.rules.map((r, i) => validRule(r, i, errors)).filter((r) => Boolean(r)) : [];
|
|
50
|
+
if (source.rules !== undefined && !Array.isArray(source.rules))
|
|
51
|
+
errors.push("rules must be an array");
|
|
52
|
+
return { version: typeof parsed.version === "number" ? parsed.version : 0, notAfter: typeof parsed.notAfter === "string" ? parsed.notAfter : null, rules: { ...EMPTY, ...source, rules }, errors };
|
|
53
|
+
};
|
|
9
54
|
try {
|
|
10
|
-
|
|
11
|
-
|
|
55
|
+
return await read(policyPath());
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (error.code === "ENOENT")
|
|
12
59
|
return null;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
60
|
+
try {
|
|
61
|
+
return await read(lastGoodPolicyPath());
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// A present but malformed policy with no last-known-good copy fails closed.
|
|
65
|
+
return { version: 0, notAfter: null, errors: ["Policy file is malformed or unreadable; no last-known-good policy is available."], rules: { ...EMPTY, mode: "enforce" } };
|
|
66
|
+
}
|
|
18
67
|
}
|
|
19
|
-
|
|
20
|
-
|
|
68
|
+
}
|
|
69
|
+
const safety = { ALLOW: 0, AUDIT: 1, REDACT: 2, ASK: 3, BLOCK: 4 };
|
|
70
|
+
function glob(pattern, value) { const e = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "§§").replace(/\*/g, "[^/]*").replace(/§§/g, ".*").replace(/\?/g, "."); return new RegExp(`^${e}$`, "i").test(value); }
|
|
71
|
+
function same(a, b) { return !a || (b ?? "").toLowerCase() === a.toLowerCase(); }
|
|
72
|
+
function riskInfo(c) { const s = `${c.command} ${c.path ?? ""} ${c.url ?? ""}`; const factors = []; let n = 0; if (/\brm\s+.*-rf\s+\//i.test(s)) {
|
|
73
|
+
n += 98;
|
|
74
|
+
factors.push("destructive operation");
|
|
75
|
+
}
|
|
76
|
+
else if (/\brm\b/i.test(s)) {
|
|
77
|
+
n += 45;
|
|
78
|
+
factors.push("filesystem operation");
|
|
79
|
+
} if (/\bsudo\b/i.test(s)) {
|
|
80
|
+
n += 35;
|
|
81
|
+
factors.push("privileged operation");
|
|
82
|
+
} if (/\b(curl|wget)\b|https?:\/\//i.test(s)) {
|
|
83
|
+
n += 25;
|
|
84
|
+
factors.push("external destination");
|
|
85
|
+
} if (/\.env|\.ssh|\.aws|private.?key|api.?key|token|secret/i.test(s)) {
|
|
86
|
+
n += 35;
|
|
87
|
+
factors.push("sensitive data or file");
|
|
88
|
+
} const score = Math.min(100, n); return { score, level: score >= 90 ? "CRITICAL" : score >= 70 ? "HIGH" : score >= 30 ? "MEDIUM" : "LOW", factors }; }
|
|
89
|
+
function matches(r, c) {
|
|
90
|
+
if (!same(r.tool, c.tool) || !same(r.role, c.role) || !same(r.user, c.user) || !same(r.agent, c.agent) || !same(r.repository, c.repository) || !same(r.branch, c.branch) || !same(r.environment, c.environment))
|
|
91
|
+
return false;
|
|
92
|
+
if (r.cwd && !glob(r.cwd, c.cwd ?? ""))
|
|
93
|
+
return false;
|
|
94
|
+
if (r.command && !glob(r.command, c.command.split(/\s+/)[0] ?? ""))
|
|
95
|
+
return false;
|
|
96
|
+
if (r.path && !glob(r.path, c.path ?? ""))
|
|
97
|
+
return false;
|
|
98
|
+
if (r.url && !glob(r.url, c.url ?? ""))
|
|
99
|
+
return false;
|
|
100
|
+
if (r.args && r.args.some((a, i) => !glob(a, c.args?.[i] ?? "")))
|
|
101
|
+
return false;
|
|
102
|
+
if (r.risk !== undefined && c.riskScore < r.risk)
|
|
103
|
+
return false;
|
|
104
|
+
if (r.time && c.time) {
|
|
105
|
+
const t = c.time.slice(11, 16);
|
|
106
|
+
if (r.time.after && t < r.time.after || r.time.before && t > r.time.before)
|
|
107
|
+
return false;
|
|
21
108
|
}
|
|
109
|
+
return true;
|
|
22
110
|
}
|
|
23
|
-
|
|
111
|
+
function specificity(r) { return [r.tool, r.command, r.args?.length, r.path, r.url, r.role, r.user, r.agent, r.cwd, r.repository, r.branch, r.environment, r.time].filter(Boolean).length; }
|
|
24
112
|
export function evaluate(bundle, ctx) {
|
|
113
|
+
const info = riskInfo(ctx);
|
|
114
|
+
const score = info.score;
|
|
115
|
+
const base = { mode: bundle?.rules.mode ?? "observe", policyVersion: bundle?.version ?? 0, riskScore: score, riskLevel: info.level, riskFactors: info.factors, customDetectors: bundle?.rules.customDetectors ?? [], actor: ctx.user, role: ctx.role, agent: ctx.agent, tool: ctx.tool, command: ctx.command, args: ctx.args, resource: ctx.path ?? ctx.url, errors: bundle?.errors };
|
|
25
116
|
if (!bundle)
|
|
26
|
-
return { action: "allow", mode: "observe", policyVersion: 0 };
|
|
27
|
-
const { rules, version } = bundle;
|
|
28
|
-
const base = { mode: rules.mode, policyVersion: version };
|
|
29
|
-
if (rules.mode === "observe")
|
|
30
117
|
return { action: "allow", ...base };
|
|
31
|
-
|
|
32
|
-
|
|
118
|
+
if (bundle.errors?.length)
|
|
119
|
+
return { action: "deny", ...base, reason: `Security policy rejected: ${bundle.errors.join("; ")}`, rule: "policy.invalid" };
|
|
120
|
+
if (bundle.rules.mode === "observe" || (bundle.notAfter && Date.parse(bundle.notAfter) < Date.now()))
|
|
33
121
|
return { action: "allow", ...base };
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
for (const pattern of rules.blockedCommandPatterns) {
|
|
46
|
-
if (!ctx.command)
|
|
47
|
-
break;
|
|
122
|
+
const candidates = [];
|
|
123
|
+
for (const r of bundle.rules.rules ?? [])
|
|
124
|
+
if (matches(r, { ...ctx, riskScore: score }))
|
|
125
|
+
candidates.push({ r, rank: specificity(r) });
|
|
126
|
+
for (const tool of bundle.rules.blockedTools) {
|
|
127
|
+
const r = { id: `legacy.tool.${tool}`, tool, action: "BLOCK", reason: `The ${tool} tool is blocked by workspace policy.` };
|
|
128
|
+
if (matches(r, { ...ctx, riskScore: score }))
|
|
129
|
+
candidates.push({ r, rank: 1 });
|
|
130
|
+
}
|
|
131
|
+
if (bundle.rules.disabledAgents.some(a => a.toLowerCase() === ctx.agent.toLowerCase()))
|
|
132
|
+
candidates.push({ r: { id: "legacy.disabled-agent", agent: ctx.agent, action: "BLOCK", reason: `${ctx.agent} is disabled by workspace policy.` }, rank: 1 });
|
|
133
|
+
for (const pattern of bundle.rules.blockedCommandPatterns) {
|
|
48
134
|
try {
|
|
49
|
-
if (new RegExp(pattern, "i").test(ctx.command))
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
/* ignore an unparseable pattern */
|
|
135
|
+
if (new RegExp(pattern, "i").test(ctx.command))
|
|
136
|
+
candidates.push({ r: { id: `legacy.command.${pattern}`, action: "BLOCK", reason: `Command matches blocked pattern: /${pattern}/` }, rank: 1 });
|
|
55
137
|
}
|
|
138
|
+
catch { /* preserve legacy fail-open behavior; structured rules are validated and reported */ }
|
|
56
139
|
}
|
|
57
|
-
|
|
140
|
+
const selected = candidates.sort((a, b) => b.rank - a.rank || safety[b.r.action] - safety[a.r.action])[0]?.r;
|
|
141
|
+
const thresholds = bundle.rules.risk ?? { askAt: 30, blockAt: 70 };
|
|
142
|
+
let action = selected?.action ?? "ALLOW";
|
|
143
|
+
if (!selected && score >= (thresholds.blockAt ?? 70))
|
|
144
|
+
action = "BLOCK";
|
|
145
|
+
else if (!selected && score >= (thresholds.askAt ?? 30))
|
|
146
|
+
action = "ASK";
|
|
147
|
+
const reason = selected?.reason ?? (action === "BLOCK" ? `Risk score ${score} exceeds the block threshold.` : action === "ASK" ? `Risk score ${score} requires approval.` : action === "REDACT" ? "Sensitive data must be transformed before this action." : undefined);
|
|
148
|
+
const mapped = action === "BLOCK" ? (bundle.rules.mode === "enforce" ? "deny" : "warn") : action === "ASK" && bundle.rules.mode === "advisory" ? "warn" : action.toLowerCase();
|
|
149
|
+
return { ...base, action: mapped, reason, policy: "local policy", rule: selected?.id, approval: selected?.approval };
|
|
58
150
|
}
|
|
59
151
|
//# sourceMappingURL=policy.js.map
|