@agent-beam/beam 0.2.21 → 0.2.22

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.
Files changed (47) hide show
  1. package/dist/cli.js +179 -2
  2. package/dist/cli.js.map +1 -1
  3. package/dist/client.js +23 -6
  4. package/dist/client.js.map +1 -1
  5. package/dist/forward.d.ts +2 -0
  6. package/dist/forward.js +19 -0
  7. package/dist/forward.js.map +1 -1
  8. package/dist/hook-adapters.js +14 -1
  9. package/dist/hook-adapters.js.map +1 -1
  10. package/dist/serve.d.ts +2 -0
  11. package/dist/serve.js +23 -1
  12. package/dist/serve.js.map +1 -1
  13. package/dist/setup.js +32 -1
  14. package/dist/setup.js.map +1 -1
  15. package/dist/skill-context.d.ts +11 -0
  16. package/dist/skill-context.js +78 -0
  17. package/dist/skill-context.js.map +1 -0
  18. package/dist/skill-gate.d.ts +21 -0
  19. package/dist/skill-gate.js +180 -0
  20. package/dist/skill-gate.js.map +1 -0
  21. package/dist/skill-inventory.d.ts +81 -0
  22. package/dist/skill-inventory.js +167 -0
  23. package/dist/skill-inventory.js.map +1 -0
  24. package/dist/skill-risk-rules.d.ts +17 -0
  25. package/dist/skill-risk-rules.js +61 -0
  26. package/dist/skill-risk-rules.js.map +1 -0
  27. package/dist/skill-scan.d.ts +27 -0
  28. package/dist/skill-scan.js +157 -0
  29. package/dist/skill-scan.js.map +1 -0
  30. package/dist/skill-setup.d.ts +11 -0
  31. package/dist/skill-setup.js +26 -0
  32. package/dist/skill-setup.js.map +1 -0
  33. package/dist/skill-sync.d.ts +40 -0
  34. package/dist/skill-sync.js +79 -0
  35. package/dist/skill-sync.js.map +1 -0
  36. package/dist/skill-watch.d.ts +16 -0
  37. package/dist/skill-watch.js +41 -0
  38. package/dist/skill-watch.js.map +1 -0
  39. package/dist/skills.d.ts +33 -0
  40. package/dist/skills.js +139 -0
  41. package/dist/skills.js.map +1 -0
  42. package/dist/trusted-skills.d.ts +9 -0
  43. package/dist/trusted-skills.js +31 -0
  44. package/dist/trusted-skills.js.map +1 -0
  45. package/dist/tty-prompt.js +3 -1
  46. package/dist/tty-prompt.js.map +1 -1
  47. package/package.json +1 -1
@@ -0,0 +1,81 @@
1
+ import { type SkillScan } from "./skill-scan.js";
2
+ export type SkillFindingSummary = {
3
+ id: string;
4
+ title: string;
5
+ severity: string;
6
+ file: string;
7
+ line?: number;
8
+ explanation?: string;
9
+ evidence?: string;
10
+ fp?: string;
11
+ };
12
+ /** Every finding, minimal: enough for the dashboard to re-score a skill after some are reviewed as safe. */
13
+ export type SkillFindingRef = {
14
+ id: string;
15
+ severity: string;
16
+ fp: string;
17
+ };
18
+ export type InventorySkill = {
19
+ /** Stable per-machine id (hash of the folder), so a report can name a skill without its path. Absent in files written before this field existed. */
20
+ id?: string;
21
+ topFindings?: SkillFindingSummary[];
22
+ allFindings?: SkillFindingRef[];
23
+ name: string;
24
+ dir: string;
25
+ agent: string;
26
+ scope: "global" | "project" | "other";
27
+ repo: string | null;
28
+ hash: string;
29
+ score: number;
30
+ level: SkillScan["level"];
31
+ findings: number;
32
+ injection: boolean;
33
+ files: number;
34
+ firstSeen: string;
35
+ lastSeen: string;
36
+ modifiedAt: string | null;
37
+ };
38
+ export type Inventory = {
39
+ version: 1;
40
+ generatedAt: string;
41
+ roots: string[];
42
+ truncated: boolean;
43
+ skills: InventorySkill[];
44
+ /** When a whole-home search last ran (`beam skills index`). Null/absent: only agent skill folders have been checked. */
45
+ lastFullScanAt?: string | null;
46
+ };
47
+ export type IndexChanges = {
48
+ added: string[];
49
+ modified: string[];
50
+ removed: string[];
51
+ };
52
+ export declare const inventoryFile: () => string;
53
+ export declare function agentSkillRoots(home?: string): Promise<string[]>;
54
+ export type DiscoverOptions = {
55
+ roots?: string[];
56
+ maxDepth?: number;
57
+ maxDirs?: number;
58
+ budgetMs?: number;
59
+ };
60
+ export declare function discoverAllSkills(options?: DiscoverOptions): Promise<{
61
+ found: Array<{
62
+ dir: string;
63
+ repo: string | null;
64
+ }>;
65
+ roots: string[];
66
+ truncated: boolean;
67
+ }>;
68
+ export declare function readInventory(): Promise<Inventory | null>;
69
+ export declare function indexSkills(options?: DiscoverOptions, home?: string): Promise<{
70
+ inventory: Inventory;
71
+ changes: IndexChanges;
72
+ }>;
73
+ export declare function trustedHashes(): Promise<Set<string>>;
74
+ export declare function deviceSkillsScore(skills: Array<{
75
+ score: number;
76
+ level: string;
77
+ hash: string;
78
+ }>, trusted: Set<string>): {
79
+ score: number;
80
+ highCount: number;
81
+ };
@@ -0,0 +1,167 @@
1
+ import { createHash } from "node:crypto";
2
+ import { access, mkdir, readFile, readdir, rename, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { basename, join, sep } from "node:path";
5
+ import { getDataDirectory } from "./config.js";
6
+ import { redact } from "./core.js";
7
+ import { findingFingerprint, scanSkillDir } from "./skill-scan.js";
8
+ const MAX_ALL_FINDINGS = 200;
9
+ import { readTrustedSkills } from "./trusted-skills.js";
10
+ export const inventoryFile = () => join(getDataDirectory(), "skills-inventory.json");
11
+ const SKIP = new Set(["node_modules", ".git", "Library", ".Trash", "Applications", "venv", ".venv", "__pycache__", "site-packages", "target", "Pods", ".gradle", ".m2", ".cargo", ".rustup", ".npm", ".pnpm-store", ".cache", ".next", ".turbo", "DerivedData"]);
12
+ // Dot-directories are skipped wholesale (caches, tool state) except the ones agents keep skills in.
13
+ const DOT_ALLOW = new Set([".claude", ".agents", ".codex", ".cursor", ".gemini", ".github", ".windsurf", ".roo", ".cline", ".continue", ".opencode", ".copilot"]);
14
+ function classify(dir, home) {
15
+ const p = dir.split(sep);
16
+ const agent = p.includes(".claude") ? "claude-code" : p.includes(".codex") ? "codex" : p.includes(".cursor") ? "cursor"
17
+ : p.includes(".gemini") ? "gemini" : p.includes(".copilot") ? "copilot-cli" : p.includes(".opencode") ? "opencode" : p.includes(".agents") ? "universal" : p.includes(".windsurf") ? "windsurf" : "unknown";
18
+ const rel = dir.startsWith(home + sep) ? dir.slice(home.length + 1).split(sep) : [];
19
+ const globalRoot = rel.length === 3 && rel[0].startsWith(".") && rel[1] === "skills";
20
+ return { agent, scope: globalRoot ? "global" : agent === "unknown" ? "other" : "project" };
21
+ }
22
+ // The folders agents keep their own skills and plugins in. These are dot-directories directly under
23
+ // home, which macOS does not protect -- unlike Desktop, Documents, Downloads, iCloud and external
24
+ // volumes, which raise a "beam would like to access files in..." prompt the moment something walks
25
+ // them. Anything that runs unattended (the background service) must stay inside these; walking the
26
+ // rest of home is reserved for an explicit `beam skills index` / `beam skills list`.
27
+ const AGENT_HOME_DIRS = [".claude", ".agents", ".codex", ".cursor", ".gemini", ".copilot", ".windsurf", ".opencode", ".continue", ".config/opencode"];
28
+ export async function agentSkillRoots(home = homedir()) {
29
+ const found = [];
30
+ for (const rel of AGENT_HOME_DIRS) {
31
+ const dir = join(home, rel);
32
+ try {
33
+ await access(dir);
34
+ found.push(dir);
35
+ }
36
+ catch { /* not installed */ }
37
+ }
38
+ return found;
39
+ }
40
+ // Finds every folder that contains a SKILL.md. Does not descend into a skill it found (bundled
41
+ // fixtures/examples aren't separate skills), never follows symlinks, and stops at the dir/time
42
+ // budget rather than hanging on a huge disk -- `truncated` reports when that happened.
43
+ export async function discoverAllSkills(options = {}) {
44
+ // An explicit empty list means "search nothing", never "fall back to the whole home directory".
45
+ const roots = options.roots ?? [homedir()];
46
+ const maxDepth = options.maxDepth ?? 10, maxDirs = options.maxDirs ?? 400_000;
47
+ const deadline = Date.now() + (options.budgetMs ?? 60_000);
48
+ const found = [];
49
+ const seen = new Set();
50
+ let visited = 0, truncated = false;
51
+ const stack = roots.map(r => ({ dir: r, depth: 0, repo: null }));
52
+ while (stack.length) {
53
+ if (visited++ > maxDirs || Date.now() > deadline) {
54
+ truncated = true;
55
+ break;
56
+ }
57
+ const { dir, depth, repo } = stack.pop();
58
+ let entries;
59
+ try {
60
+ entries = await readdir(dir, { withFileTypes: true });
61
+ }
62
+ catch {
63
+ continue;
64
+ }
65
+ if (entries.some(e => e.isFile() && e.name === "SKILL.md")) {
66
+ if (!seen.has(dir)) {
67
+ seen.add(dir);
68
+ found.push({ dir, repo });
69
+ }
70
+ continue;
71
+ }
72
+ const here = entries.some(e => e.name === ".git") ? dir : repo;
73
+ if (depth >= maxDepth)
74
+ continue;
75
+ for (const e of entries) {
76
+ if (!e.isDirectory() || e.isSymbolicLink() || SKIP.has(e.name))
77
+ continue;
78
+ if (e.name.startsWith(".") && !DOT_ALLOW.has(e.name))
79
+ continue;
80
+ stack.push({ dir: join(dir, e.name), depth: depth + 1, repo: here });
81
+ }
82
+ }
83
+ return { found, roots, truncated };
84
+ }
85
+ export async function readInventory() {
86
+ try {
87
+ const inv = JSON.parse(await readFile(inventoryFile(), "utf8"));
88
+ return inv?.version === 1 && Array.isArray(inv.skills) ? inv : null;
89
+ }
90
+ catch {
91
+ return null;
92
+ }
93
+ }
94
+ async function write(inv) {
95
+ await mkdir(getDataDirectory(), { recursive: true, mode: 0o700 });
96
+ const file = inventoryFile();
97
+ const temp = `${file}.tmp`;
98
+ await writeFile(temp, JSON.stringify(inv, null, 2) + "\n", { mode: 0o600 });
99
+ await rename(temp, file);
100
+ }
101
+ // Full re-index: discover, scan each skill, diff against the previous file, write it back.
102
+ // Skills that vanished are dropped -- but only when the walk finished; a truncated walk can't
103
+ // prove absence, so previously known skills under the same roots are kept in that case.
104
+ export async function indexSkills(options = {}, home = homedir()) {
105
+ const previous = await readInventory();
106
+ const prev = new Map((previous?.skills ?? []).map(s => [s.dir, s]));
107
+ const full = options.roots === undefined;
108
+ const { found, roots, truncated } = await discoverAllSkills(full ? { ...options, roots: [home] } : options);
109
+ const now = new Date().toISOString();
110
+ const skills = [];
111
+ const changes = { added: [], modified: [], removed: [] };
112
+ for (const { dir, repo } of found) {
113
+ let scan;
114
+ try {
115
+ scan = await scanSkillDir(dir, basename(dir));
116
+ }
117
+ catch {
118
+ continue;
119
+ }
120
+ const old = prev.get(dir);
121
+ if (!old)
122
+ changes.added.push(dir);
123
+ else if (old.hash !== scan.hash)
124
+ changes.modified.push(dir);
125
+ skills.push({
126
+ id: createHash("sha256").update(dir).digest("hex").slice(0, 16),
127
+ topFindings: scan.findings.slice(0, 5).map(f => ({
128
+ id: f.id, title: f.title, severity: f.severity, file: f.file, ...(f.line ? { line: f.line } : {}),
129
+ explanation: f.explanation.slice(0, 300), ...(f.evidence ? { evidence: redact(f.evidence).slice(0, 300) } : {}),
130
+ fp: findingFingerprint(f, f.file),
131
+ })),
132
+ allFindings: scan.findings.slice(0, MAX_ALL_FINDINGS).map(f => ({ id: f.id, severity: f.severity, fp: findingFingerprint(f, f.file) })),
133
+ name: scan.name, dir, ...classify(dir, home), repo, hash: scan.hash, score: scan.score, level: scan.level,
134
+ findings: scan.findings.length, injection: scan.findings.some(f => f.id.startsWith("injection.")), files: scan.filesScanned,
135
+ firstSeen: old?.firstSeen ?? now, lastSeen: now, modifiedAt: !old ? null : old.hash !== scan.hash ? now : old.modifiedAt,
136
+ });
137
+ }
138
+ const scanned = new Set(skills.map(s => s.dir));
139
+ for (const old of prev.values()) {
140
+ if (scanned.has(old.dir))
141
+ continue;
142
+ const inScope = roots.some(r => old.dir === r || old.dir.startsWith(r + sep));
143
+ if (truncated && inScope)
144
+ skills.push(old); // can't prove it's gone
145
+ else if (inScope)
146
+ changes.removed.push(old.dir);
147
+ else
148
+ skills.push(old); // outside this run's roots (e.g. an ad-hoc --root): leave untouched
149
+ }
150
+ skills.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name));
151
+ const inventory = { version: 1, generatedAt: now, roots: [...new Set([...(previous?.roots ?? []), ...roots])], truncated, skills, lastFullScanAt: full ? now : previous?.lastFullScanAt ?? null };
152
+ await write(inventory);
153
+ return { inventory, changes };
154
+ }
155
+ export async function trustedHashes() { return new Set((await readTrustedSkills()).map(t => t.hash)); }
156
+ // One 0-100 skills risk score for this machine (higher = riskier). Starts from the worst skill and
157
+ // adds 5 for every further HIGH/CRITICAL one (capped at +20). Skills you have trusted are left
158
+ // out. The workspace collector computes the same number for each device
159
+ // (beam/apps/collector/src/skill-score.ts) -- keep the two in step.
160
+ export function deviceSkillsScore(skills, trusted) {
161
+ const counted = skills.filter(s => !trusted.has(s.hash));
162
+ if (!counted.length)
163
+ return { score: 0, highCount: 0 };
164
+ const highCount = counted.filter(s => s.level === "HIGH" || s.level === "CRITICAL").length;
165
+ return { score: Math.min(100, Math.max(...counted.map(s => s.score)) + Math.min(20, 5 * Math.max(0, highCount - 1))), highCount };
166
+ }
167
+ //# sourceMappingURL=skill-inventory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-inventory.js","sourceRoot":"","sources":["../src/skill-inventory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAkB,MAAM,iBAAiB,CAAC;AAEnF,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAwBxD,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,uBAAuB,CAAC,CAAC;AAErF,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC;AACjQ,oGAAoG;AACpG,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC;AAElK,SAAS,QAAQ,CAAC,GAAW,EAAE,IAAY;IACzC,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,MAAM,KAAK,GAAG,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ;QACrH,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9M,MAAM,GAAG,GAAG,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACpF,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC;IACrF,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;AAC7F,CAAC;AAED,oGAAoG;AACpG,kGAAkG;AAClG,mGAAmG;AACnG,mGAAmG;AACnG,qFAAqF;AACrF,MAAM,eAAe,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,kBAAkB,CAAC,CAAC;AACtJ,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAI,GAAG,OAAO,EAAE;IACpD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QAAC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAAC,IAAI,CAAC;YAAC,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC;YAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,CAAC;IAAC,CAAC;IAC7I,OAAO,KAAK,CAAC;AACf,CAAC;AAID,+FAA+F;AAC/F,+FAA+F;AAC/F,uFAAuF;AACvF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,UAA2B,EAAE;IACnE,gGAAgG;IAChG,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC;IAC9E,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC,CAAC;IAC3D,MAAM,KAAK,GAAgD,EAAE,CAAC;IAC9D,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,IAAI,OAAO,GAAG,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC;IACnC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,IAAqB,EAAE,CAAC,CAAC,CAAC;IAClF,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC;QACpB,IAAI,OAAO,EAAE,GAAG,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;YAAC,SAAS,GAAG,IAAI,CAAC;YAAC,MAAM;QAAC,CAAC;QAC9E,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,GAAG,EAAG,CAAC;QAC1C,IAAI,OAAO,CAAC;QAAC,IAAI,CAAC;YAAC,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,SAAS;QAAC,CAAC;QAC/F,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,EAAE,CAAC;YAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAAC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAAC,CAAC;YAAC,SAAS;QAAC,CAAC;QAC3I,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/D,IAAI,KAAK,IAAI,QAAQ;YAAE,SAAS;QAChC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,cAAc,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;gBAAE,SAAS;YACzE,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;gBAAE,SAAS;YAC/D,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AACrC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,IAAI,CAAC;QAAC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,aAAa,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;QAAC,OAAO,GAAG,EAAE,OAAO,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAAC,CAAC;IAC7I,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;AACxB,CAAC;AAED,KAAK,UAAU,KAAK,CAAC,GAAc;IACjC,MAAM,KAAK,CAAC,gBAAgB,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAClE,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC;IAAC,MAAM,IAAI,GAAG,GAAG,IAAI,MAAM,CAAC;IACzD,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5E,MAAM,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED,2FAA2F;AAC3F,8FAA8F;AAC9F,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,UAA2B,EAAE,EAAE,IAAI,GAAG,OAAO,EAAE;IAC/E,MAAM,QAAQ,GAAG,MAAM,aAAa,EAAE,CAAC;IACvC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC;IACzC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC5G,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,MAAM,GAAqB,EAAE,CAAC;IAAC,MAAM,OAAO,GAAiB,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC5G,KAAK,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,KAAK,EAAE,CAAC;QAClC,IAAI,IAAe,CAAC;QAAC,IAAI,CAAC;YAAC,IAAI,GAAG,MAAM,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,SAAS;QAAC,CAAC;QAC/F,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,CAAC,GAAG;YAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;aAAM,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI;YAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/F,MAAM,CAAC,IAAI,CAAC;YACV,EAAE,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;YAC/D,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC/C,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjG,WAAW,EAAE,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/G,EAAE,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC;aAClC,CAAC,CAAC;YACH,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,EAAE,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACvI,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK;YACzG,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY;YAC3H,SAAS,EAAE,GAAG,EAAE,SAAS,IAAI,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU;SACzH,CAAC,CAAC;IACL,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QAChC,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QACnC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;QAC9E,IAAI,SAAS,IAAI,OAAO;YAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,wBAAwB;aAC/D,IAAI,OAAO;YAAE,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;;YAC3C,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,oEAAoE;IAC7F,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACzE,MAAM,SAAS,GAAc,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,cAAc,IAAI,IAAI,EAAE,CAAC;IAC7M,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC;IACvB,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,KAA2B,OAAO,IAAI,GAAG,CAAC,CAAC,MAAM,iBAAiB,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAE7H,mGAAmG;AACnG,+FAA+F;AAC/F,wEAAwE;AACxE,oEAAoE;AACpE,MAAM,UAAU,iBAAiB,CAAC,MAA6D,EAAE,OAAoB;IACnH,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACzD,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IACvD,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,IAAI,CAAC,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,MAAM,CAAC;IAC3F,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC;AACpI,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { type Finding, type Severity } from "./core.js";
2
+ type RiskRule = {
3
+ id: string;
4
+ title: string;
5
+ severity: Severity;
6
+ explanation: string;
7
+ re: RegExp;
8
+ };
9
+ export declare const riskRules: RiskRule[];
10
+ export declare function scanRisky(content: string): Finding[];
11
+ export declare function comboFindings(perFile: Array<{
12
+ file: string;
13
+ ids: Set<string>;
14
+ }>): Array<Finding & {
15
+ file: string;
16
+ }>;
17
+ export {};
@@ -0,0 +1,61 @@
1
+ import { redact } from "./core.js";
2
+ import { firstInstruction } from "./skill-context.js";
3
+ export const riskRules = [
4
+ { id: "risk.privilege_escalation", title: "Privilege escalation (sudo / su / setuid)", severity: "high", explanation: "The skill runs commands with elevated privileges. Confirm it needs root and prefer least-privilege alternatives.",
5
+ re: /\b(?:sudo|doas)\b|(?:^|[\s;&|`(])su\s+(?:-\s*\w*|root)\b|\bchown\s+(?:-R\s+)?root\b|\b(?:setuid|setgid)\b|\bchmod\s+(?:[ugoa]*\+s\b|[2467][0-7]{3}\b)/i },
6
+ { id: "risk.sudoers", title: "Sudoers or admin-group modification", severity: "critical", explanation: "The skill edits sudo configuration or grants admin rights, which permanently widens what any process on the machine can do.",
7
+ re: /\/etc\/sudoers|\bvisudo\b|\bNOPASSWD\b|\busermod\s+-aG\s+(?:sudo|wheel|admin)\b/i },
8
+ { id: "risk.persistence", title: "Persistence mechanism", severity: "high", explanation: "The skill schedules itself or edits startup files, so its effects survive after the agent session ends.",
9
+ re: /\bcrontab\s+(?:-[a-z]+\s+)?\S|\/etc\/cron\.\w+|\blaunchctl\s+(?:load|bootstrap|enable)\b|Launch(?:Agents|Daemons)\b|\bsystemctl\s+(?:--user\s+)?enable\b|\bschtasks(?:\.exe)?\s+\/create\b|Register-ScheduledTask|\bsc(?:\.exe)?\s+create\b|(?:>>|\btee\s+-a)\s*~?\/?(?:\S*\/)?\.(?:bashrc|zshrc|profile|bash_profile|zprofile)\b|\/etc\/(?:rc\.local|profile\.d)\b/i },
10
+ { id: "risk.sensitive_files", title: "Credential or identity file access", severity: "high", explanation: "The skill touches SSH keys, cloud credentials, browser stores or password files. Confirm the agent needs their contents.",
11
+ re: /(?:~|\$HOME)?\/?\.ssh\/|\bid_(?:rsa|ed25519|ecdsa)\b|\/etc\/(?:shadow|passwd)\b|(?:~|\$HOME)\/\.aws\/|\.config\/gcloud\b|\.kube\/config\b|\.docker\/config\.json\b|\.gnupg\b|\.netrc\b|\.git-credentials\b|\bLogin Data\b|\bcookies\.sqlite\b|\bwallet\.dat\b|Library\/Keychains/i },
12
+ { id: "risk.exfil_tools", title: "Data exfiltration tooling", severity: "high", explanation: "The skill pipes data to a network tool, uploads files, or sends to a public collection endpoint.",
13
+ re: /\|\s*(?:curl|wget|nc|ncat|netcat|socat)\b|\b(?:scp|rsync)\s+[^\n]*\S+@\S+:|\b(?:sendmail|swaks)\b|\bmailx?\s+-s\b|\bcurl\b[^\n]*(?:\s-d\s+[@<]|--data(?:-binary|-raw)?\s+[@<]|\s-F\s|\s-T\s|--upload-file)|\b(?:transfer\.sh|pastebin\.com|webhook\.site|requestbin\.\w+|interact\.sh|ngrok\.io|burpcollaborator\.net|oast\.\w+)\b/i },
14
+ { id: "risk.obfuscation", title: "Obfuscated or encoded content", severity: "medium", explanation: "Encoded strings, hex escapes or URL shorteners hide what will actually run or where it goes.",
15
+ re: /\bbase64\s+(?:-d|--decode|-D)\b|\batob\s*\(|\bFromBase64String\b|(?:\\x[0-9a-f]{2}){4,}|\b(?:bit\.ly|tinyurl\.com|t\.co|is\.gd|goo\.gl|cutt\.ly)\/\w|\bchr\s*\(\s*\d+\s*\)\s*\+\s*chr/i },
16
+ { id: "risk.decode_exec", title: "Decode then execute", severity: "high", explanation: "Decompressed or decoded data is piped straight into a shell or interpreter, so its contents are never reviewed.",
17
+ re: /\b(?:gunzip|zcat|gzip\s+-d|xxd\s+-r|openssl\s+enc\s+-d)\b[^\n]*\|\s*(?:sudo\s+)?(?:ba|z|da)?sh\b|\bbase64\s+(?:-d|--decode)\b[^\n]*\|\s*(?:sudo\s+)?(?:python3?|node|ruby|perl)\b/i },
18
+ { id: "risk.code_exec", title: "Dynamic code execution", severity: "medium", explanation: "Strings are evaluated or shells are spawned from code. Check what feeds them.",
19
+ re: /\beval\s*\(|\bnew\s+Function\s*\(|\b__import__\s*\(|\bimportlib\.import_module\b|\bos\.system\s*\(|\bsubprocess\.\w+\([^\n]*shell\s*=\s*True|\bRuntime\.getRuntime\(\)\.exec|\bchild_process\b[^\n]*\bexec(?:Sync)?\s*\(/i },
20
+ { id: "risk.download_exec", title: "Download and run", severity: "high", explanation: "The skill fetches a file or script and executes it in the same step.",
21
+ re: /\b(?:curl|wget)\b[^\n]*(?:\s-o\s|\s-O\b|--output\b)[^\n]*(?:&&|;)\s*(?:chmod\s+\+x|(?:ba|z)?sh\b|python3?\b|node\b|\.\/)|\bcurl\b[^\n]*\|\s*(?:sudo\s+)?(?:python3?|node|ruby|perl)\b|\b(?:iwr|Invoke-WebRequest)\b[^\n]*\|\s*(?:iex|Invoke-Expression)\b|\bDownloadString\s*\(|\b(?:iex|Invoke-Expression)\b[^\n]*\b(?:iwr|Invoke-WebRequest|DownloadString)\b/i },
22
+ { id: "risk.supply_chain", title: "Unpinned or non-registry install", severity: "medium", explanation: "Packages come from a git URL, a custom index, or a mutable tag instead of a reviewed registry version.",
23
+ re: /\bpip3?\s+install\s+[^\n]*git\+|\bnpm\s+(?:i|install)\s+(?:-g\s+)?(?:github:|git\+|https?:\/\/)|\bnpx\s+(?:-y\s+)?(?:github:|https?:\/\/)|(?:--index-url|--extra-index-url|--trusted-host|--allow-unverified)\b|\bgo\s+install\s+\S+@latest\b|\bcargo\s+install\s+--git\b|\bnpm\s+config\s+set\s+registry\b|\bnpm\s+(?:i|install)\s+[^\n]*--registry\b/i },
24
+ { id: "risk.destructive_broad", title: "Broad destructive command", severity: "high", explanation: "The command can wipe a disk, the home directory, or shared git history.",
25
+ re: /\brm\s+(?:-[a-z]*[rf][a-z]*\s+)+(?:\/(?:\s|$|\*)|~\/?(?:\s|$|\*)|\$HOME\/?(?:\s|$|\*))|\bmkfs\.\w+|\bdd\s+if=[^\n]*\bof=\/dev\/|:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;\s*:|>\s*\/dev\/sd[a-z]|\bchmod\s+-R\s+777\s+\/(?:\s|$)|\bgit\s+push\s+[^\n]*(?:--force|\s-f\b)[^\n]*\b(?:main|master)\b/i },
26
+ ];
27
+ export function scanRisky(content) {
28
+ const findings = [];
29
+ const lines = content.split(/\r?\n/);
30
+ for (const rule of riskRules) {
31
+ for (let i = 0; i < lines.length; i++) {
32
+ if (lines[i].length > 2000 || !firstInstruction(lines[i], rule.re, "command", { prev: lines[i - 1], next: lines[i + 1] }))
33
+ continue;
34
+ findings.push({ id: rule.id, title: rule.title, severity: rule.severity, explanation: rule.explanation, evidence: redact(lines[i].trim()).slice(0, 200), line: i + 1 });
35
+ break;
36
+ }
37
+ }
38
+ return findings;
39
+ }
40
+ // Pairs that are worse together than either alone (explainx escalates the same way: privilege
41
+ // escalation plus destruction is critical). "skill" combos look across the whole skill, since an
42
+ // instruction file and the script it runs are separate files. The credential/exfil pair is
43
+ // "file"-scoped: both halves are common in unrelated reference docs (an API example with curl,
44
+ // a path-traversal note mentioning /etc/passwd), so co-occurring anywhere in a big skill means
45
+ // nothing -- they only count together in one file.
46
+ const COMBOS = [
47
+ { scope: "skill", all: ["risk.privilege_escalation", "risk.destructive_broad"], id: "risk.combo.privileged_destructive", title: "Elevated privileges combined with destructive commands", explanation: "Root access plus a broad delete or overwrite can destroy the whole machine." },
48
+ { scope: "skill", all: ["risk.privilege_escalation", "risk.download_exec"], id: "risk.combo.privileged_download_exec", title: "Downloads and runs code with elevated privileges", explanation: "Unreviewed remote code would run as root." },
49
+ { scope: "file", all: ["risk.sensitive_files", "risk.exfil_tools"], id: "risk.combo.credential_exfil", title: "Credential files together with outbound transfer tooling", explanation: "Reading key or credential files and having a way to send data out is the shape of credential theft." },
50
+ ];
51
+ export function comboFindings(perFile) {
52
+ const all = new Set(perFile.flatMap(f => [...f.ids]));
53
+ const out = [];
54
+ for (const c of COMBOS) {
55
+ const hits = c.scope === "skill" ? (c.all.every(id => all.has(id)) ? ["(whole skill)"] : []) : perFile.filter(f => c.all.every(id => f.ids.has(id))).map(f => f.file);
56
+ for (const file of hits)
57
+ out.push({ id: c.id, title: c.title, severity: "critical", explanation: c.explanation, evidence: `Together: ${c.all.join(" + ")}`, file });
58
+ }
59
+ return out;
60
+ }
61
+ //# sourceMappingURL=skill-risk-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-risk-rules.js","sourceRoot":"","sources":["../src/skill-risk-rules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { type Finding, type Severity } from "./core.js";
2
+ export declare function skillRiskScore(findings: Finding[]): {
3
+ score: number;
4
+ level: SkillScan["level"];
5
+ };
6
+ export interface SkillFileScan {
7
+ file: string;
8
+ findings: Finding[];
9
+ }
10
+ export interface SkillScan {
11
+ name: string;
12
+ dir: string;
13
+ hash: string;
14
+ score: number;
15
+ level: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
16
+ risk: Severity;
17
+ files: SkillFileScan[];
18
+ findings: Array<Finding & {
19
+ file: string;
20
+ }>;
21
+ filesScanned: number;
22
+ skipped: string[];
23
+ }
24
+ export declare function findingFingerprint(f: Pick<Finding, "id" | "evidence">, file: string): string;
25
+ export declare function scanInjection(content: string): Finding[];
26
+ export declare function scanSkillDir(dir: string, name: string): Promise<SkillScan>;
27
+ export declare function scanSkillFile(file: string): Promise<SkillScan>;
@@ -0,0 +1,157 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readdir, readFile, stat } from "node:fs/promises";
3
+ import { basename, join, relative } from "node:path";
4
+ import { comboFindings, scanRisky } from "./skill-risk-rules.js";
5
+ import { firstInstruction } from "./skill-context.js";
6
+ import { detect, redact, risk, scanText, severityRank } from "./core.js";
7
+ const ruleMatches = (rule, text) => rule.re ? new RegExp(rule.re.source, rule.re.flags.replace("g", "")).test(text) : Boolean(rule.match?.(text));
8
+ const injectionRules = [
9
+ { id: "injection.role_override", title: "Role or system-prompt override", severity: "critical", explanation: "The skill tells the agent to drop its existing instructions or adopt a new identity.", re: /\b(?:ignore|disregard)\s+(?:all\s+|any\s+)?(?:of\s+)?(?:the\s+|your\s+)?(?:previous|prior|above|earlier|preceding)\s+(?:system\s+)?(?:instructions|prompts?|rules|messages)\b|\b(?:disregard|forget|override|bypass)\s+(?:all\s+|any\s+)?(?:your|the|previous|prior|above)\s+(?:system\s+)?(?:prompt|instructions|rules|guidelines|safeguards)\b|\bforget\s+everything\s+(?:above|before)\b|\byou\s+are\s+now\s+(?:in\s+)?(?:DAN|developer\s+mode|an?\s+unrestricted)\b/i },
10
+ { id: "injection.fake_delimiter", title: "Forged conversation or system delimiter", severity: "critical", explanation: "Chat-template or 'end of skill' markers are used to make the following text look like a higher-trust message.", re: /<\|(?:im_start|im_end|system|endoftext)\|>|<\/?system(?:-reminder)?>|\[\/?INST\]|---\s*END\s+(?:OF\s+)?SKILL\s*---|\bBEGIN\s+SYSTEM\s+PROMPT\b/i },
11
+ { id: "injection.concealment", title: "Instruction to hide activity from the user", severity: "high", explanation: "The skill asks the agent to act without telling the user or to hide what it did.", re: /(?:without|don'?t|do\s+not|never)\s+(?:telling|tell|mention(?:ing)?|inform(?:ing)?|reveal(?:ing)?|show(?:ing)?)\s+(?:the\s+)?(?:user|human|operator)\b(?!\s+to\b)|\b(?:silently|secretly|covertly)\s+(?:run|execute|send|upload|post|fetch|install|delete|modify)\b|hide\s+(?:this|these|the)\s+(?:step|action|output|command)s?\s+from/i },
12
+ { id: "injection.priority_hijack", title: "Attempt to outrank the user or system", severity: "high", explanation: "The skill claims its instructions take precedence over the user's or the platform's.", re: /\b(?:these|this)\s+instructions?\s+(?:take|has|have)\s+(?:priority|precedence)\s+over|\bbefore\s+(?:doing\s+)?anything\s+else,?\s+(?:you\s+must\s+)?(?:(?:fetch|send|curl|wget|download|upload)\b|(?:run|execute)\s+[`"']?(?:curl|wget|nc|ncat|bash|sh|eval|powershell)\b)/i },
13
+ { id: "injection.hidden_text", context: false, title: "Hidden or invisible text", severity: "high", explanation: "Zero-width, bidirectional-control, or Unicode tag characters can smuggle instructions a human reviewer never sees.", re: /[​-‏‪-‮⁠-⁤]|[\u{E0000}-\u{E007F}]/u },
14
+ { id: "injection.hidden_comment", title: "Instructions inside an HTML comment", severity: "high", explanation: "HTML comments are invisible when a skill is rendered but are still read by the agent.", re: /(?:ignore\s+(?:all\s+|the\s+)?(?:previous|prior|above)|\byou\s+(?:must|should|will)\b|(?:new|hidden|secret)\s+instructions?|system\s+prompt|do\s+not\s+(?:tell|mention|reveal)|\b(?:curl|wget)\s+\S+|\bsend\s+(?:it|this|the\s+\w+)\s+to\b|exfiltrat\w+|\bassistant\s*:)/i },
15
+ { id: "injection.encoded_payload", title: "Encoded payload with a follow-instruction", severity: "high", explanation: "A base64 or hex blob is paired with a request to decode and act on it.", match: s => /(?:[A-Za-z0-9+/]{80,}={0,2})/.test(s) && /\b(?:decode|decrypt|deobfuscate)\b[^\n]{0,60}\b(?:and\s+)?(?:follow|run|execute|obey|apply)\b/i.test(s) },
16
+ { id: "injection.exfil_instruction", title: "Instruction to send data to an external address", severity: "critical", explanation: "The skill directs the agent to transmit conversation, files, or environment to a remote URL.", re: /\b(?:send|post|upload|forward|include|append)\b[^\n]{0,80}\b(?:conversation|chat\s+history|system\s+prompt|context|transcript|environment|env\s+vars?|credentials?|api\s+keys?|source\s+code)\b[^\n]{0,80}https?:\/\//i },
17
+ ];
18
+ // 0-100. Prompt injection is weighted up because a skill is text the agent will obey, and one
19
+ // critical finding alone is already enough to land in HIGH.
20
+ const WEIGHT = { critical: 40, high: 20, medium: 8, info: 2 };
21
+ export function skillRiskScore(findings) {
22
+ // Each rule counts once per skill (a big skill repeating "production" across 30 files is one
23
+ // signal, not thirty). Ordinary rules that merely *mention* env vars, sudo or curl are common in
24
+ // legitimate skills, so their non-critical total is capped below the HIGH band; reaching HIGH or
25
+ // CRITICAL takes prompt injection or a genuinely critical pattern.
26
+ const unique = [...new Map(findings.map(f => [f.id, f])).values()];
27
+ const injection = unique.filter(f => f.id.startsWith("injection.")).reduce((s, f) => s + WEIGHT[f.severity] * 1.5, 0);
28
+ const critical = unique.filter(f => !f.id.startsWith("injection.") && f.severity === "critical").length * WEIGHT.critical;
29
+ const ordinary = Math.min(35, unique.filter(f => !f.id.startsWith("injection.") && f.severity !== "critical").reduce((s, f) => s + WEIGHT[f.severity] / (f.id.startsWith("risk.") ? 1 : 2), 0));
30
+ const score = Math.min(100, Math.round(injection + critical + ordinary));
31
+ return { score, level: score >= 70 ? "CRITICAL" : score >= 40 ? "HIGH" : score >= 15 ? "MEDIUM" : "LOW" };
32
+ }
33
+ // Core rules that don't fit skill prose. `instructions.override` is the older, context-blind version
34
+ // of injection.role_override / injection.concealment, which already cover it with quoting and
35
+ // negation handled. `mcp.write` matches tool identifiers such as mcp__memory__memory_write, which
36
+ // in live events means "the agent is calling a write tool", but in a skill is a documentation
37
+ // mention of a tool by name.
38
+ const SKIP_CORE_FOR_SKILLS = new Set(["instructions.override", "mcp.write"]);
39
+ const TEXT_EXT = /\.(?:md|mdx|txt|json|ya?ml|toml|sh|bash|zsh|py|js|mjs|cjs|ts|rb|ps1)$/i;
40
+ const SKIP_DIR = new Set([".git", "node_modules", ".venv", "__pycache__"]);
41
+ const MAX_FILES = 200;
42
+ const MAX_FILE_BYTES = 500_000;
43
+ async function listFiles(root, dir = root, out = []) {
44
+ for (const entry of await readdir(dir, { withFileTypes: true })) {
45
+ if (out.length >= MAX_FILES)
46
+ break;
47
+ if (entry.isSymbolicLink() || SKIP_DIR.has(entry.name))
48
+ continue;
49
+ const full = join(dir, entry.name);
50
+ if (entry.isDirectory())
51
+ await listFiles(root, full, out);
52
+ else if (entry.isFile())
53
+ out.push(full);
54
+ }
55
+ return out;
56
+ }
57
+ // A finding's identity for review purposes: the rule, the file inside the skill, and the text of
58
+ // the line -- but not the line number, so a "safe" verdict survives the line moving when the skill
59
+ // is edited elsewhere, and stops applying the moment the flagged text itself changes. Computed on
60
+ // the device (where the full text is) and never reversible to the line.
61
+ export function findingFingerprint(f, file) {
62
+ const text = redact(f.evidence ?? "").replace(/\s+/g, " ").trim().toLowerCase().slice(0, 300);
63
+ return createHash("sha256").update(`${f.id}|${file}|${text}`).digest("hex").slice(0, 16);
64
+ }
65
+ const lineOf = (content, index) => content.slice(0, index).split("\n").length;
66
+ const snippet = (text) => redact(text.trim().replace(/\s+/g, " ")).slice(0, 200);
67
+ export function scanInjection(content) {
68
+ const findings = [];
69
+ const lines = content.split(/\r?\n/);
70
+ const add = (rule, evidence, line) => findings.push({ id: rule.id, title: rule.title, severity: rule.severity, explanation: rule.explanation, evidence, line });
71
+ for (const rule of injectionRules) {
72
+ // These two span lines, so they are matched against the whole file -- but each still points at
73
+ // the line and text that triggered it, since that is what a reviewer needs to see.
74
+ if (rule.id === "injection.hidden_comment") {
75
+ for (const block of content.matchAll(/<!--[\s\S]*?-->/g)) {
76
+ if (ruleMatches(rule, block[0])) {
77
+ add(rule, snippet(block[0]), lineOf(content, block.index ?? 0));
78
+ break;
79
+ }
80
+ }
81
+ continue;
82
+ }
83
+ if (rule.id === "injection.encoded_payload") {
84
+ if (!ruleMatches(rule, content))
85
+ continue;
86
+ const at = lines.findIndex(l => /\b(?:decode|decrypt|deobfuscate)\b[^\n]{0,60}\b(?:and\s+)?(?:follow|run|execute|obey|apply)\b/i.test(l));
87
+ add(rule, at >= 0 ? snippet(lines[at]) : "Long encoded string with a decode-and-run instruction", at >= 0 ? at + 1 : 1);
88
+ continue;
89
+ }
90
+ for (let i = 0; i < lines.length; i++) {
91
+ // A phrase that is only being quoted, refused, or given as an example is not an instruction.
92
+ const hit = rule.re && rule.context !== false ? firstInstruction(lines[i], rule.re, "phrase", { prev: lines[i - 1], next: lines[i + 1] }) : ruleMatches(rule, lines[i]) ? { index: 0 } : null;
93
+ if (hit) {
94
+ add(rule, snippet(lines[i]), i + 1);
95
+ break;
96
+ }
97
+ }
98
+ }
99
+ return findings;
100
+ }
101
+ // scanText looks at three-line windows (so a credential on one line and a "send it to" on the next
102
+ // are seen together) and reports the window's first line. For a reviewer that is misleading: the
103
+ // risky text can be two lines further down. When the rule matches a single line on its own, point
104
+ // at that line and show only it; when it needs the lines together, keep the window.
105
+ function narrowToLine(finding, content) {
106
+ if (!finding.line)
107
+ return finding;
108
+ const lines = content.split(/\r?\n/);
109
+ for (let k = 0; k < 3; k++) {
110
+ const text = lines[finding.line - 1 + k];
111
+ if (text !== undefined && detect(text).some(f => f.id === finding.id))
112
+ return { ...finding, line: finding.line + k, evidence: snippet(text) };
113
+ }
114
+ return { ...finding, evidence: snippet(finding.evidence) };
115
+ }
116
+ // Scans every text file in a skill folder -- not just SKILL.md, since bundled scripts and
117
+ // reference docs are loaded or executed by the agent too. Never executes anything.
118
+ export async function scanSkillDir(dir, name) {
119
+ return scanPaths(dir, name, await listFiles(dir));
120
+ }
121
+ // One markdown file on its own (a stray skills.md), without sweeping its whole parent folder.
122
+ export async function scanSkillFile(file) {
123
+ return scanPaths(join(file, ".."), basename(file), [file]);
124
+ }
125
+ async function scanPaths(dir, name, paths) {
126
+ const skipped = [];
127
+ const files = [];
128
+ const hash = createHash("sha256");
129
+ for (const path of paths) {
130
+ const rel = relative(dir, path);
131
+ if (!TEXT_EXT.test(path))
132
+ continue;
133
+ if ((await stat(path)).size > MAX_FILE_BYTES) {
134
+ skipped.push(`${rel} (over 500 KB)`);
135
+ continue;
136
+ }
137
+ const content = await readFile(path, "utf8");
138
+ if (!content.trim())
139
+ continue;
140
+ hash.update(`${rel}\0${content}\0`);
141
+ const findings = [...scanInjection(content), ...scanRisky(content)];
142
+ try {
143
+ for (const f of scanText(rel, content, "skill").findings)
144
+ if (!SKIP_CORE_FOR_SKILLS.has(f.id) && !findings.some(x => x.id === f.id))
145
+ findings.push(narrowToLine(f, content));
146
+ }
147
+ catch { /* over the scanText size cap; injection rules above still ran */ }
148
+ files.push({ file: rel, findings });
149
+ }
150
+ const fileFindings = files.flatMap(f => f.findings.map(x => ({ ...x, file: f.file })));
151
+ // Combinations (e.g. sudo in one file, a broad rm in another) are judged over the whole skill or per file, see skill-risk-rules.ts.
152
+ const combos = comboFindings(files.map(f => ({ file: f.file, ids: new Set(f.findings.map(x => x.id)) })));
153
+ const findings = [...fileFindings, ...combos]
154
+ .sort((a, b) => severityRank[b.severity] - severityRank[a.severity]);
155
+ return { name, dir, hash: hash.digest("hex"), ...skillRiskScore(findings), risk: risk(findings), files, findings, filesScanned: files.length, skipped };
156
+ }
157
+ //# sourceMappingURL=skill-scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-scan.js","sourceRoot":"","sources":["../src/skill-scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { type SkillSyncResult } from "./skill-sync.js";
2
+ export type SkillSetupResult = {
3
+ total: number;
4
+ highRisk: number;
5
+ searchedHome: boolean;
6
+ sync: SkillSyncResult;
7
+ };
8
+ export declare function indexAndReportSkills(options: {
9
+ searchProjects: boolean;
10
+ home?: string;
11
+ }): Promise<SkillSetupResult>;
@@ -0,0 +1,26 @@
1
+ import { agentSkillRoots, indexSkills, readInventory } from "./skill-inventory.js";
2
+ import { syncSkillInventory } from "./skill-sync.js";
3
+ // What `beam setup` does about skills: index what is installed and, if this device is connected
4
+ // to a workspace, report the names and risk scores right away instead of waiting for the daily
5
+ // check. The agents' own skill folders are always covered (no macOS folder prompt). Searching the
6
+ // rest of the home folder for skills inside project repos is what raises the "beam would like to
7
+ // access Desktop / Documents / Downloads" prompt, so it only happens when the caller asked for it
8
+ // (setup asks; a non-interactive run does not). Doing it once also switches on the daily
9
+ // whole-home rescan in skill-watch.ts.
10
+ export async function indexAndReportSkills(options) {
11
+ const roots = await agentSkillRoots(options.home);
12
+ if (options.searchProjects)
13
+ await indexSkills({}, options.home);
14
+ else if (roots.length)
15
+ await indexSkills({ roots }, options.home);
16
+ const inventory = await readInventory();
17
+ const skills = inventory?.skills ?? [];
18
+ const sync = await syncSkillInventory({ home: options.home }).catch(() => ({ status: "failed", reason: "unexpected error" }));
19
+ return {
20
+ total: skills.length,
21
+ highRisk: skills.filter(s => s.level === "HIGH" || s.level === "CRITICAL").length,
22
+ searchedHome: options.searchProjects,
23
+ sync,
24
+ };
25
+ }
26
+ //# sourceMappingURL=skill-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-setup.js","sourceRoot":"","sources":["../src/skill-setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAwB,MAAM,iBAAiB,CAAC;AAI3E,gGAAgG;AAChG,+FAA+F;AAC/F,kGAAkG;AAClG,iGAAiG;AACjG,kGAAkG;AAClG,yFAAyF;AACzF,uCAAuC;AACvC,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAAmD;IAC5F,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,IAAI,OAAO,CAAC,cAAc;QAAE,MAAM,WAAW,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;SAC3D,IAAI,KAAK,CAAC,MAAM;QAAE,MAAM,WAAW,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,MAAM,SAAS,GAAG,MAAM,aAAa,EAAE,CAAC;IACxC,MAAM,MAAM,GAAG,SAAS,EAAE,MAAM,IAAI,EAAE,CAAC;IACvC,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAoB,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC;IAC/I,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,MAAM;QACpB,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,IAAI,CAAC,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,MAAM;QACjF,YAAY,EAAE,OAAO,CAAC,cAAc;QACpC,IAAI;KACL,CAAC;AACJ,CAAC"}