sentinelflow 0.2.0 → 0.2.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.
Files changed (2) hide show
  1. package/dist/bundle.js +4784 -0
  2. package/package.json +10 -8
package/dist/bundle.js ADDED
@@ -0,0 +1,4784 @@
1
+ #!/usr/bin/env node
2
+ #!/usr/bin/env node
3
+ "use strict";
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __commonJS = (cb, mod) => function __require() {
6
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
7
+ };
8
+
9
+ // packages/core/dist/schema/agent.js
10
+ var require_agent = __commonJS({
11
+ "packages/core/dist/schema/agent.js"(exports2) {
12
+ "use strict";
13
+ Object.defineProperty(exports2, "__esModule", { value: true });
14
+ exports2.createAgent = createAgent;
15
+ function createAgent(params) {
16
+ const now = (/* @__PURE__ */ new Date()).toISOString();
17
+ return {
18
+ id: params.id ?? generateId(),
19
+ name: params.name,
20
+ description: params.description ?? "",
21
+ framework: params.framework,
22
+ framework_id: params.framework_id,
23
+ owner: params.owner,
24
+ team: params.team,
25
+ created_at: params.created_at ?? now,
26
+ updated_at: params.updated_at ?? now,
27
+ source_file: params.source_file,
28
+ repository: params.repository,
29
+ tools: params.tools ?? [],
30
+ allowed_tools: params.allowed_tools,
31
+ blocked_tools: params.blocked_tools,
32
+ model: params.model,
33
+ model_routing: params.model_routing,
34
+ mcp_servers: params.mcp_servers,
35
+ data_sources: params.data_sources ?? [],
36
+ data_classification: params.data_classification,
37
+ file_system_access: params.file_system_access,
38
+ network_access: params.network_access,
39
+ delegates_to: params.delegates_to,
40
+ delegated_from: params.delegated_from,
41
+ swarm_role: params.swarm_role ?? "standalone",
42
+ topology: params.topology ?? "standalone",
43
+ governance: params.governance ?? {
44
+ status: "discovered"
45
+ },
46
+ runtime: params.runtime
47
+ };
48
+ }
49
+ function generateId() {
50
+ return "sf-" + crypto.randomUUID();
51
+ }
52
+ }
53
+ });
54
+
55
+ // packages/core/dist/schema/finding.js
56
+ var require_finding = __commonJS({
57
+ "packages/core/dist/schema/finding.js"(exports2) {
58
+ "use strict";
59
+ Object.defineProperty(exports2, "__esModule", { value: true });
60
+ exports2.createScanReport = createScanReport;
61
+ function createScanReport(rootDir, findings, frameworksDetected, agentsDiscovered, durationMs) {
62
+ const summary = {
63
+ critical: findings.filter((f) => f.severity === "critical").length,
64
+ high: findings.filter((f) => f.severity === "high").length,
65
+ medium: findings.filter((f) => f.severity === "medium").length,
66
+ low: findings.filter((f) => f.severity === "low").length,
67
+ info: findings.filter((f) => f.severity === "info").length,
68
+ total: findings.length
69
+ };
70
+ return {
71
+ id: `scan-${Date.now()}`,
72
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
73
+ root_dir: rootDir,
74
+ duration_ms: durationMs,
75
+ frameworks_detected: frameworksDetected,
76
+ agents_discovered: agentsDiscovered,
77
+ findings,
78
+ summary
79
+ };
80
+ }
81
+ }
82
+ });
83
+
84
+ // packages/core/dist/schema/event.js
85
+ var require_event = __commonJS({
86
+ "packages/core/dist/schema/event.js"(exports2) {
87
+ "use strict";
88
+ Object.defineProperty(exports2, "__esModule", { value: true });
89
+ }
90
+ });
91
+
92
+ // packages/core/dist/registry/local.js
93
+ var require_local = __commonJS({
94
+ "packages/core/dist/registry/local.js"(exports2) {
95
+ "use strict";
96
+ var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
97
+ if (k2 === void 0) k2 = k;
98
+ var desc = Object.getOwnPropertyDescriptor(m, k);
99
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
100
+ desc = { enumerable: true, get: function() {
101
+ return m[k];
102
+ } };
103
+ }
104
+ Object.defineProperty(o, k2, desc);
105
+ }) : (function(o, m, k, k2) {
106
+ if (k2 === void 0) k2 = k;
107
+ o[k2] = m[k];
108
+ }));
109
+ var __setModuleDefault2 = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
110
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
111
+ }) : function(o, v) {
112
+ o["default"] = v;
113
+ });
114
+ var __importStar2 = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
115
+ var ownKeys = function(o) {
116
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
117
+ var ar = [];
118
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
119
+ return ar;
120
+ };
121
+ return ownKeys(o);
122
+ };
123
+ return function(mod) {
124
+ if (mod && mod.__esModule) return mod;
125
+ var result = {};
126
+ if (mod != null) {
127
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding2(result, mod, k[i]);
128
+ }
129
+ __setModuleDefault2(result, mod);
130
+ return result;
131
+ };
132
+ })();
133
+ Object.defineProperty(exports2, "__esModule", { value: true });
134
+ exports2.LocalRegistry = void 0;
135
+ var fs = __importStar2(require("fs"));
136
+ var path = __importStar2(require("path"));
137
+ var SF_DIR = ".sentinelflow";
138
+ var AGENTS_FILE = "agents.json";
139
+ var REPORTS_FILE = "reports.json";
140
+ var EVENTS_FILE = "events.json";
141
+ var MAX_REPORTS = 50;
142
+ var MAX_EVENTS = 1e4;
143
+ function atomicWriteSync(filePath, data) {
144
+ const tmpPath = filePath + ".tmp." + process.pid;
145
+ try {
146
+ fs.writeFileSync(tmpPath, data, "utf-8");
147
+ fs.renameSync(tmpPath, filePath);
148
+ } catch (error) {
149
+ try {
150
+ if (fs.existsSync(tmpPath)) {
151
+ fs.unlinkSync(tmpPath);
152
+ }
153
+ } catch {
154
+ }
155
+ const message = error instanceof Error ? error.message : String(error);
156
+ throw new Error(`Failed to write ${filePath}: ${message}`);
157
+ }
158
+ }
159
+ function safeReadJSON(filePath, fallback) {
160
+ try {
161
+ if (!fs.existsSync(filePath)) {
162
+ return fallback;
163
+ }
164
+ const raw = fs.readFileSync(filePath, "utf-8");
165
+ if (raw.trim() === "") {
166
+ return fallback;
167
+ }
168
+ return JSON.parse(raw);
169
+ } catch (error) {
170
+ const message = error instanceof Error ? error.message : String(error);
171
+ console.warn(`Warning: Could not read ${filePath}: ${message}. Using defaults.`);
172
+ return fallback;
173
+ }
174
+ }
175
+ var LocalRegistry = class {
176
+ basePath;
177
+ agents = /* @__PURE__ */ new Map();
178
+ reports = [];
179
+ events = [];
180
+ initialized = false;
181
+ constructor(projectRoot) {
182
+ this.basePath = path.join(projectRoot, SF_DIR);
183
+ }
184
+ // ─── Lifecycle ────────────────────────────────────────────
185
+ async initialize() {
186
+ if (this.initialized)
187
+ return;
188
+ if (!fs.existsSync(this.basePath)) {
189
+ fs.mkdirSync(this.basePath, { recursive: true });
190
+ }
191
+ const agentsData = safeReadJSON(path.join(this.basePath, AGENTS_FILE), []);
192
+ for (const agent of agentsData) {
193
+ this.agents.set(agent.id, agent);
194
+ }
195
+ this.reports = safeReadJSON(path.join(this.basePath, REPORTS_FILE), []);
196
+ this.events = safeReadJSON(path.join(this.basePath, EVENTS_FILE), []);
197
+ this.initialized = true;
198
+ }
199
+ async close() {
200
+ if (!this.initialized)
201
+ return;
202
+ this.persistAll();
203
+ this.initialized = false;
204
+ }
205
+ // ─── Persistence ──────────────────────────────────────────
206
+ persistAgents() {
207
+ atomicWriteSync(path.join(this.basePath, AGENTS_FILE), JSON.stringify([...this.agents.values()], null, 2));
208
+ }
209
+ persistReports() {
210
+ atomicWriteSync(path.join(this.basePath, REPORTS_FILE), JSON.stringify(this.reports, null, 2));
211
+ }
212
+ persistAll() {
213
+ this.persistAgents();
214
+ this.persistReports();
215
+ }
216
+ ensureInitialized() {
217
+ if (!this.initialized) {
218
+ throw new Error("Registry not initialized. Call initialize() before using the registry.");
219
+ }
220
+ }
221
+ // ─── Agent CRUD ───────────────────────────────────────────
222
+ async upsertAgent(agent) {
223
+ this.ensureInitialized();
224
+ agent.updated_at = (/* @__PURE__ */ new Date()).toISOString();
225
+ this.agents.set(agent.id, agent);
226
+ this.persistAgents();
227
+ }
228
+ async getAgent(id) {
229
+ this.ensureInitialized();
230
+ return this.agents.get(id) ?? null;
231
+ }
232
+ async getAgentByName(name, framework) {
233
+ this.ensureInitialized();
234
+ for (const agent of this.agents.values()) {
235
+ if (agent.name === name && agent.framework === framework) {
236
+ return agent;
237
+ }
238
+ }
239
+ return null;
240
+ }
241
+ async listAgents(options) {
242
+ this.ensureInitialized();
243
+ let agents = [...this.agents.values()];
244
+ if (options?.framework) {
245
+ agents = agents.filter((a) => a.framework === options.framework);
246
+ }
247
+ if (options?.status) {
248
+ agents = agents.filter((a) => a.governance.status === options.status);
249
+ }
250
+ if (options?.risk_level) {
251
+ agents = agents.filter((a) => a.governance.risk_level === options.risk_level);
252
+ }
253
+ if (options?.owner) {
254
+ agents = agents.filter((a) => a.owner === options.owner);
255
+ }
256
+ if (options?.team) {
257
+ agents = agents.filter((a) => a.team === options.team);
258
+ }
259
+ const offset = options?.offset ?? 0;
260
+ const limit = options?.limit ?? 100;
261
+ return agents.slice(offset, offset + limit);
262
+ }
263
+ async deleteAgent(id) {
264
+ this.ensureInitialized();
265
+ if (!this.agents.has(id)) {
266
+ throw new Error(`Agent not found: ${id}`);
267
+ }
268
+ this.agents.delete(id);
269
+ this.persistAgents();
270
+ }
271
+ async countAgents() {
272
+ this.ensureInitialized();
273
+ return this.agents.size;
274
+ }
275
+ // ─── Findings ─────────────────────────────────────────────
276
+ async storeScanReport(report) {
277
+ this.ensureInitialized();
278
+ this.reports.push(report);
279
+ if (this.reports.length > MAX_REPORTS) {
280
+ this.reports = this.reports.slice(-MAX_REPORTS);
281
+ }
282
+ this.persistReports();
283
+ }
284
+ async getLatestScanReport() {
285
+ this.ensureInitialized();
286
+ if (this.reports.length === 0)
287
+ return null;
288
+ return this.reports[this.reports.length - 1] ?? null;
289
+ }
290
+ async listFindings(agentId) {
291
+ this.ensureInitialized();
292
+ const latest = await this.getLatestScanReport();
293
+ if (!latest)
294
+ return [];
295
+ if (agentId) {
296
+ return latest.findings.filter((f) => f.agent_id === agentId);
297
+ }
298
+ return latest.findings;
299
+ }
300
+ // ─── Events (Phase 2) ────────────────────────────────────
301
+ async ingestEvents(events) {
302
+ this.ensureInitialized();
303
+ this.events.push(...events);
304
+ if (this.events.length > MAX_EVENTS) {
305
+ this.events = this.events.slice(-MAX_EVENTS);
306
+ }
307
+ }
308
+ async queryEvents(agentId, limit = 100) {
309
+ this.ensureInitialized();
310
+ return this.events.filter((e) => e.agent_id === agentId).slice(-limit);
311
+ }
312
+ };
313
+ exports2.LocalRegistry = LocalRegistry;
314
+ }
315
+ });
316
+
317
+ // packages/core/dist/index.js
318
+ var require_dist = __commonJS({
319
+ "packages/core/dist/index.js"(exports2) {
320
+ "use strict";
321
+ var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
322
+ if (k2 === void 0) k2 = k;
323
+ var desc = Object.getOwnPropertyDescriptor(m, k);
324
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
325
+ desc = { enumerable: true, get: function() {
326
+ return m[k];
327
+ } };
328
+ }
329
+ Object.defineProperty(o, k2, desc);
330
+ }) : (function(o, m, k, k2) {
331
+ if (k2 === void 0) k2 = k;
332
+ o[k2] = m[k];
333
+ }));
334
+ var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
335
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding2(exports3, m, p);
336
+ };
337
+ Object.defineProperty(exports2, "__esModule", { value: true });
338
+ exports2.LocalRegistry = void 0;
339
+ __exportStar(require_agent(), exports2);
340
+ __exportStar(require_finding(), exports2);
341
+ __exportStar(require_event(), exports2);
342
+ var local_1 = require_local();
343
+ Object.defineProperty(exports2, "LocalRegistry", { enumerable: true, get: function() {
344
+ return local_1.LocalRegistry;
345
+ } });
346
+ }
347
+ });
348
+
349
+ // packages/parsers/dist/claude-code.js
350
+ var require_claude_code = __commonJS({
351
+ "packages/parsers/dist/claude-code.js"(exports2) {
352
+ "use strict";
353
+ var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
354
+ if (k2 === void 0) k2 = k;
355
+ var desc = Object.getOwnPropertyDescriptor(m, k);
356
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
357
+ desc = { enumerable: true, get: function() {
358
+ return m[k];
359
+ } };
360
+ }
361
+ Object.defineProperty(o, k2, desc);
362
+ }) : (function(o, m, k, k2) {
363
+ if (k2 === void 0) k2 = k;
364
+ o[k2] = m[k];
365
+ }));
366
+ var __setModuleDefault2 = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
367
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
368
+ }) : function(o, v) {
369
+ o["default"] = v;
370
+ });
371
+ var __importStar2 = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
372
+ var ownKeys = function(o) {
373
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
374
+ var ar = [];
375
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
376
+ return ar;
377
+ };
378
+ return ownKeys(o);
379
+ };
380
+ return function(mod) {
381
+ if (mod && mod.__esModule) return mod;
382
+ var result = {};
383
+ if (mod != null) {
384
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding2(result, mod, k[i]);
385
+ }
386
+ __setModuleDefault2(result, mod);
387
+ return result;
388
+ };
389
+ })();
390
+ var __importDefault = exports2 && exports2.__importDefault || function(mod) {
391
+ return mod && mod.__esModule ? mod : { "default": mod };
392
+ };
393
+ Object.defineProperty(exports2, "__esModule", { value: true });
394
+ exports2.ClaudeCodeParser = void 0;
395
+ var fs = __importStar2(require("fs"));
396
+ var path = __importStar2(require("path"));
397
+ var gray_matter_1 = __importDefault(require("gray-matter"));
398
+ var core_1 = require_dist();
399
+ function safeReadFile(filePath) {
400
+ try {
401
+ if (!fs.existsSync(filePath))
402
+ return null;
403
+ return fs.readFileSync(filePath, "utf-8");
404
+ } catch (error) {
405
+ return null;
406
+ }
407
+ }
408
+ function safeParseJSON(content, filePath) {
409
+ try {
410
+ return JSON.parse(content);
411
+ } catch (error) {
412
+ return null;
413
+ }
414
+ }
415
+ function safeListDir(dirPath, extensions) {
416
+ try {
417
+ if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory()) {
418
+ return [];
419
+ }
420
+ return fs.readdirSync(dirPath).filter((f) => extensions.some((ext) => f.endsWith(ext)));
421
+ } catch {
422
+ return [];
423
+ }
424
+ }
425
+ var ClaudeCodeParser = class {
426
+ framework = "claude-code";
427
+ displayName = "Claude Code";
428
+ markers = [".claude", "CLAUDE.md", "AGENTS.md"];
429
+ async detect(rootDir) {
430
+ return fs.existsSync(path.join(rootDir, ".claude")) || fs.existsSync(path.join(rootDir, "CLAUDE.md")) || fs.existsSync(path.join(rootDir, "AGENTS.md"));
431
+ }
432
+ async parse(rootDir) {
433
+ const agents = [];
434
+ const configFiles = [];
435
+ const warnings = [];
436
+ this.parseSettingsFile(rootDir, agents, configFiles, warnings);
437
+ this.parseAgentsDirectory(rootDir, agents, configFiles, warnings);
438
+ this.collectConfigFile(path.join(rootDir, "CLAUDE.md"), configFiles);
439
+ this.collectConfigFile(path.join(rootDir, "AGENTS.md"), configFiles);
440
+ this.collectConfigFile(path.join(rootDir, "hooks", "hooks.json"), configFiles);
441
+ const commandsDir = path.join(rootDir, ".claude", "commands");
442
+ for (const file of safeListDir(commandsDir, [".md"])) {
443
+ this.collectConfigFile(path.join(commandsDir, file), configFiles);
444
+ }
445
+ const claudeAgentsDir = path.join(rootDir, ".claude", "agents");
446
+ for (const file of safeListDir(claudeAgentsDir, [".md"])) {
447
+ const filePath = path.join(claudeAgentsDir, file);
448
+ const content = safeReadFile(filePath);
449
+ if (content !== null) {
450
+ configFiles.push({ path: filePath, content, framework: "claude-code" });
451
+ const agent = this.parseAgentMarkdown(content, filePath, warnings);
452
+ if (agent)
453
+ agents.push(agent);
454
+ }
455
+ }
456
+ if (agents.length === 0 && configFiles.length > 0) {
457
+ agents.push((0, core_1.createAgent)({
458
+ name: "claude-code-default",
459
+ framework: "claude-code",
460
+ description: "Default Claude Code agent for this project",
461
+ source_file: configFiles[0]?.path,
462
+ swarm_role: "standalone"
463
+ }));
464
+ }
465
+ return { agents, config_files: configFiles, warnings };
466
+ }
467
+ // ─── Private: Parse .claude/settings.json ───────────────────
468
+ parseSettingsFile(rootDir, agents, configFiles, warnings) {
469
+ const settingsPath = path.join(rootDir, ".claude", "settings.json");
470
+ const content = safeReadFile(settingsPath);
471
+ if (content === null)
472
+ return;
473
+ configFiles.push({ path: settingsPath, content, framework: "claude-code" });
474
+ const settings = safeParseJSON(content, settingsPath);
475
+ if (settings === null) {
476
+ warnings.push(`Invalid JSON in ${settingsPath}`);
477
+ return;
478
+ }
479
+ const allowedTools = Array.isArray(settings.allowedTools) ? settings.allowedTools : [];
480
+ const blockedTools = Array.isArray(settings.blockedTools) ? settings.blockedTools : [];
481
+ const mcpServers = [];
482
+ if (settings.mcpServers && typeof settings.mcpServers === "object") {
483
+ for (const [name, config] of Object.entries(settings.mcpServers)) {
484
+ mcpServers.push({
485
+ name,
486
+ url: typeof config.url === "string" ? config.url : void 0,
487
+ tools_exposed: Array.isArray(config.tools) ? config.tools : void 0
488
+ });
489
+ }
490
+ }
491
+ const tools = allowedTools.map((t) => this.classifyTool(t));
492
+ agents.push((0, core_1.createAgent)({
493
+ name: "claude-code-project",
494
+ framework: "claude-code",
495
+ description: "Project-level Claude Code configuration from settings.json",
496
+ source_file: settingsPath,
497
+ tools,
498
+ allowed_tools: allowedTools.length > 0 ? allowedTools : void 0,
499
+ blocked_tools: blockedTools.length > 0 ? blockedTools : void 0,
500
+ mcp_servers: mcpServers.length > 0 ? mcpServers : void 0,
501
+ swarm_role: "standalone"
502
+ }));
503
+ const localSettingsPath = path.join(rootDir, ".claude", "settings.local.json");
504
+ const localContent = safeReadFile(localSettingsPath);
505
+ if (localContent !== null) {
506
+ configFiles.push({
507
+ path: localSettingsPath,
508
+ content: localContent,
509
+ framework: "claude-code"
510
+ });
511
+ }
512
+ }
513
+ // ─── Private: Parse agents/ directory ───────────────────────
514
+ parseAgentsDirectory(rootDir, agents, configFiles, warnings) {
515
+ const agentsDir = path.join(rootDir, "agents");
516
+ const files = safeListDir(agentsDir, [".md", ".yaml", ".yml"]);
517
+ for (const file of files) {
518
+ const filePath = path.join(agentsDir, file);
519
+ const content = safeReadFile(filePath);
520
+ if (content === null)
521
+ continue;
522
+ configFiles.push({ path: filePath, content, framework: "claude-code" });
523
+ const agent = this.parseAgentMarkdown(content, filePath, warnings);
524
+ if (agent)
525
+ agents.push(agent);
526
+ }
527
+ }
528
+ // ─── Private: Parse a single agent Markdown file ────────────
529
+ parseAgentMarkdown(content, filePath, warnings) {
530
+ if (content.trim().length === 0) {
531
+ warnings.push(`Empty file: ${filePath}`);
532
+ return null;
533
+ }
534
+ let frontmatterData = {};
535
+ let bodyContent = content;
536
+ try {
537
+ const parsed = (0, gray_matter_1.default)(content);
538
+ frontmatterData = parsed.data;
539
+ bodyContent = parsed.content;
540
+ } catch (error) {
541
+ const name2 = path.basename(filePath, path.extname(filePath));
542
+ warnings.push(`Could not parse frontmatter in ${filePath}: ${error instanceof Error ? error.message : String(error)}. Using filename as agent name.`);
543
+ return (0, core_1.createAgent)({
544
+ name: name2,
545
+ framework: "claude-code",
546
+ description: bodyContent.slice(0, 200).trim(),
547
+ source_file: filePath
548
+ });
549
+ }
550
+ const name = typeof frontmatterData.name === "string" ? frontmatterData.name : path.basename(filePath, path.extname(filePath));
551
+ const description = typeof frontmatterData.description === "string" ? frontmatterData.description : bodyContent.trim().slice(0, 200);
552
+ const model = typeof frontmatterData.model === "string" ? frontmatterData.model : void 0;
553
+ let toolNames = [];
554
+ if (Array.isArray(frontmatterData.tools)) {
555
+ toolNames = frontmatterData.tools.map(String);
556
+ } else if (typeof frontmatterData.tools === "string") {
557
+ toolNames = frontmatterData.tools.split(",").map((t) => t.trim());
558
+ }
559
+ if (typeof frontmatterData["allowed-tools"] === "string") {
560
+ const additional = frontmatterData["allowed-tools"].split(",").map((t) => t.trim());
561
+ toolNames = [...toolNames, ...additional];
562
+ }
563
+ const tools = toolNames.filter((t) => t.length > 0).map((t) => this.classifyTool(t));
564
+ const swarmRole = this.inferSwarmRole(name, description);
565
+ return (0, core_1.createAgent)({
566
+ name,
567
+ framework: "claude-code",
568
+ description,
569
+ source_file: filePath,
570
+ model,
571
+ tools,
572
+ swarm_role: swarmRole
573
+ });
574
+ }
575
+ // ─── Private: Collect a config file without parsing agents ──
576
+ collectConfigFile(filePath, configFiles) {
577
+ const content = safeReadFile(filePath);
578
+ if (content !== null) {
579
+ configFiles.push({ path: filePath, content, framework: "claude-code" });
580
+ }
581
+ }
582
+ // ─── Private: Classify a tool name by type and risk ─────────
583
+ classifyTool(toolName) {
584
+ const lower = toolName.toLowerCase().trim();
585
+ if (lower.includes("bash") || lower.includes("shell") || lower.includes("exec") || lower === "command") {
586
+ return { name: toolName, type: "bash", risk_level: "high" };
587
+ }
588
+ if (lower.includes("write") || lower.includes("create_file") || lower.includes("str_replace") || lower === "edit") {
589
+ return { name: toolName, type: "file_write", risk_level: "medium" };
590
+ }
591
+ if (lower.includes("read") || lower.includes("view") || lower.includes("cat") || lower.includes("glob") || lower.includes("grep")) {
592
+ return { name: toolName, type: "file_read", risk_level: "low" };
593
+ }
594
+ if (lower.includes("search") || lower.includes("web_search")) {
595
+ return { name: toolName, type: "web_search", risk_level: "low" };
596
+ }
597
+ if (lower.includes("fetch") || lower.includes("http") || lower.includes("curl") || lower.includes("web_fetch")) {
598
+ return { name: toolName, type: "web_fetch", risk_level: "medium" };
599
+ }
600
+ if (lower.includes("mcp")) {
601
+ return { name: toolName, type: "mcp", risk_level: "medium" };
602
+ }
603
+ return { name: toolName, type: "custom", risk_level: "low" };
604
+ }
605
+ // ─── Private: Infer swarm role from name/description ────────
606
+ inferSwarmRole(name, description) {
607
+ const text = `${name} ${description}`.toLowerCase();
608
+ if (text.includes("planner") || text.includes("orchestrat") || text.includes("coordinat") || text.includes("dispatcher")) {
609
+ return "orchestrator";
610
+ }
611
+ if (text.includes("review") || text.includes("audit") || text.includes("check") || text.includes("verify")) {
612
+ return "reviewer";
613
+ }
614
+ if (text.includes("specialist") || text.includes("expert") || text.includes("specific") || text.includes("resolver")) {
615
+ return "specialist";
616
+ }
617
+ if (text.includes("worker") || text.includes("execut") || text.includes("build") || text.includes("runner")) {
618
+ return "worker";
619
+ }
620
+ return "standalone";
621
+ }
622
+ };
623
+ exports2.ClaudeCodeParser = ClaudeCodeParser;
624
+ }
625
+ });
626
+
627
+ // packages/parsers/dist/cursor.js
628
+ var require_cursor = __commonJS({
629
+ "packages/parsers/dist/cursor.js"(exports2) {
630
+ "use strict";
631
+ var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
632
+ if (k2 === void 0) k2 = k;
633
+ var desc = Object.getOwnPropertyDescriptor(m, k);
634
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
635
+ desc = { enumerable: true, get: function() {
636
+ return m[k];
637
+ } };
638
+ }
639
+ Object.defineProperty(o, k2, desc);
640
+ }) : (function(o, m, k, k2) {
641
+ if (k2 === void 0) k2 = k;
642
+ o[k2] = m[k];
643
+ }));
644
+ var __setModuleDefault2 = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
645
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
646
+ }) : function(o, v) {
647
+ o["default"] = v;
648
+ });
649
+ var __importStar2 = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
650
+ var ownKeys = function(o) {
651
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
652
+ var ar = [];
653
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
654
+ return ar;
655
+ };
656
+ return ownKeys(o);
657
+ };
658
+ return function(mod) {
659
+ if (mod && mod.__esModule) return mod;
660
+ var result = {};
661
+ if (mod != null) {
662
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding2(result, mod, k[i]);
663
+ }
664
+ __setModuleDefault2(result, mod);
665
+ return result;
666
+ };
667
+ })();
668
+ var __importDefault = exports2 && exports2.__importDefault || function(mod) {
669
+ return mod && mod.__esModule ? mod : { "default": mod };
670
+ };
671
+ Object.defineProperty(exports2, "__esModule", { value: true });
672
+ exports2.CursorParser = void 0;
673
+ var fs = __importStar2(require("fs"));
674
+ var path = __importStar2(require("path"));
675
+ var gray_matter_1 = __importDefault(require("gray-matter"));
676
+ var core_1 = require_dist();
677
+ function safeReadFile(filePath) {
678
+ try {
679
+ if (!fs.existsSync(filePath))
680
+ return null;
681
+ return fs.readFileSync(filePath, "utf-8");
682
+ } catch {
683
+ return null;
684
+ }
685
+ }
686
+ function safeListDir(dirPath, extensions) {
687
+ try {
688
+ if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory())
689
+ return [];
690
+ return fs.readdirSync(dirPath).filter((f) => extensions.some((ext) => f.endsWith(ext)));
691
+ } catch {
692
+ return [];
693
+ }
694
+ }
695
+ var CursorParser = class {
696
+ framework = "cursor";
697
+ displayName = "Cursor";
698
+ markers = [".cursor", ".cursorrules"];
699
+ async detect(rootDir) {
700
+ return fs.existsSync(path.join(rootDir, ".cursor")) || fs.existsSync(path.join(rootDir, ".cursorrules"));
701
+ }
702
+ async parse(rootDir) {
703
+ const agents = [];
704
+ const configFiles = [];
705
+ const warnings = [];
706
+ const rulesDir = path.join(rootDir, ".cursor", "rules");
707
+ for (const file of safeListDir(rulesDir, [".mdc", ".md"])) {
708
+ const filePath = path.join(rulesDir, file);
709
+ const content = safeReadFile(filePath);
710
+ if (!content)
711
+ continue;
712
+ configFiles.push({ path: filePath, content, framework: "cursor" });
713
+ try {
714
+ const parsed = (0, gray_matter_1.default)(content);
715
+ const data = parsed.data;
716
+ const name = typeof data.description === "string" ? data.description.substring(0, 60).replace(/[^a-zA-Z0-9-_ ]/g, "").trim().toLowerCase().replace(/\s+/g, "-") : path.basename(file, path.extname(file));
717
+ agents.push((0, core_1.createAgent)({
718
+ name: `cursor-rule-${name}`,
719
+ framework: "cursor",
720
+ description: typeof data.description === "string" ? data.description : parsed.content.slice(0, 200).trim(),
721
+ source_file: filePath,
722
+ model: typeof data.model === "string" ? data.model : void 0
723
+ }));
724
+ } catch (error) {
725
+ warnings.push(`Could not parse ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
726
+ }
727
+ }
728
+ const legacyRulesPath = path.join(rootDir, ".cursorrules");
729
+ const legacyContent = safeReadFile(legacyRulesPath);
730
+ if (legacyContent) {
731
+ configFiles.push({ path: legacyRulesPath, content: legacyContent, framework: "cursor" });
732
+ agents.push((0, core_1.createAgent)({
733
+ name: "cursor-legacy-rules",
734
+ framework: "cursor",
735
+ description: legacyContent.slice(0, 200).trim(),
736
+ source_file: legacyRulesPath
737
+ }));
738
+ }
739
+ const mcpPath = path.join(rootDir, ".cursor", "mcp.json");
740
+ const mcpContent = safeReadFile(mcpPath);
741
+ if (mcpContent) {
742
+ configFiles.push({ path: mcpPath, content: mcpContent, framework: "cursor" });
743
+ try {
744
+ const mcpConfig = JSON.parse(mcpContent);
745
+ const servers = mcpConfig.mcpServers;
746
+ if (servers) {
747
+ const mcpServers = Object.entries(servers).map(([name, config]) => ({
748
+ name,
749
+ url: typeof config.url === "string" ? config.url : void 0,
750
+ tools_exposed: Array.isArray(config.tools) ? config.tools : void 0
751
+ }));
752
+ if (agents.length > 0 && agents[0]) {
753
+ agents[0].mcp_servers = mcpServers;
754
+ } else {
755
+ agents.push((0, core_1.createAgent)({
756
+ name: "cursor-mcp-config",
757
+ framework: "cursor",
758
+ description: "Cursor MCP server configuration",
759
+ source_file: mcpPath,
760
+ mcp_servers: mcpServers
761
+ }));
762
+ }
763
+ }
764
+ } catch {
765
+ warnings.push(`Invalid JSON in ${mcpPath}`);
766
+ }
767
+ }
768
+ const ignorePath = path.join(rootDir, ".cursorignore");
769
+ const ignoreContent = safeReadFile(ignorePath);
770
+ if (ignoreContent) {
771
+ configFiles.push({ path: ignorePath, content: ignoreContent, framework: "cursor" });
772
+ }
773
+ if (agents.length === 0 && configFiles.length > 0) {
774
+ agents.push((0, core_1.createAgent)({
775
+ name: "cursor-default",
776
+ framework: "cursor",
777
+ description: "Default Cursor configuration for this project",
778
+ source_file: configFiles[0]?.path
779
+ }));
780
+ }
781
+ return { agents, config_files: configFiles, warnings };
782
+ }
783
+ };
784
+ exports2.CursorParser = CursorParser;
785
+ }
786
+ });
787
+
788
+ // packages/parsers/dist/codex.js
789
+ var require_codex = __commonJS({
790
+ "packages/parsers/dist/codex.js"(exports2) {
791
+ "use strict";
792
+ var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
793
+ if (k2 === void 0) k2 = k;
794
+ var desc = Object.getOwnPropertyDescriptor(m, k);
795
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
796
+ desc = { enumerable: true, get: function() {
797
+ return m[k];
798
+ } };
799
+ }
800
+ Object.defineProperty(o, k2, desc);
801
+ }) : (function(o, m, k, k2) {
802
+ if (k2 === void 0) k2 = k;
803
+ o[k2] = m[k];
804
+ }));
805
+ var __setModuleDefault2 = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
806
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
807
+ }) : function(o, v) {
808
+ o["default"] = v;
809
+ });
810
+ var __importStar2 = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
811
+ var ownKeys = function(o) {
812
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
813
+ var ar = [];
814
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
815
+ return ar;
816
+ };
817
+ return ownKeys(o);
818
+ };
819
+ return function(mod) {
820
+ if (mod && mod.__esModule) return mod;
821
+ var result = {};
822
+ if (mod != null) {
823
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding2(result, mod, k[i]);
824
+ }
825
+ __setModuleDefault2(result, mod);
826
+ return result;
827
+ };
828
+ })();
829
+ var __importDefault = exports2 && exports2.__importDefault || function(mod) {
830
+ return mod && mod.__esModule ? mod : { "default": mod };
831
+ };
832
+ Object.defineProperty(exports2, "__esModule", { value: true });
833
+ exports2.CodexParser = void 0;
834
+ var fs = __importStar2(require("fs"));
835
+ var path = __importStar2(require("path"));
836
+ var gray_matter_1 = __importDefault(require("gray-matter"));
837
+ var core_1 = require_dist();
838
+ function safeReadFile(filePath) {
839
+ try {
840
+ if (!fs.existsSync(filePath))
841
+ return null;
842
+ return fs.readFileSync(filePath, "utf-8");
843
+ } catch {
844
+ return null;
845
+ }
846
+ }
847
+ function safeListDir(dirPath, extensions) {
848
+ try {
849
+ if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory())
850
+ return [];
851
+ return fs.readdirSync(dirPath).filter((f) => extensions.some((ext) => f.endsWith(ext)));
852
+ } catch {
853
+ return [];
854
+ }
855
+ }
856
+ var CodexParser = class {
857
+ framework = "codex";
858
+ displayName = "Codex / OpenCode";
859
+ markers = [".codex", "codex.md", ".opencode", ".agents"];
860
+ async detect(rootDir) {
861
+ return fs.existsSync(path.join(rootDir, ".codex")) || fs.existsSync(path.join(rootDir, "codex.md")) || fs.existsSync(path.join(rootDir, ".opencode")) || fs.existsSync(path.join(rootDir, ".agents"));
862
+ }
863
+ async parse(rootDir) {
864
+ const agents = [];
865
+ const configFiles = [];
866
+ const warnings = [];
867
+ const codexConfigPath = path.join(rootDir, ".codex", "config.toml");
868
+ const codexConfig = safeReadFile(codexConfigPath);
869
+ if (codexConfig) {
870
+ configFiles.push({ path: codexConfigPath, content: codexConfig, framework: "codex" });
871
+ const modelMatch = codexConfig.match(/model\s*=\s*["']([^"']+)["']/);
872
+ const approvalMatch = codexConfig.match(/approval_mode\s*=\s*["']([^"']+)["']/);
873
+ const model = modelMatch?.[1];
874
+ const approvalMode = approvalMatch?.[1];
875
+ const tools = [];
876
+ if (approvalMode === "full-auto") {
877
+ tools.push({ name: "full-auto-execution", type: "bash", risk_level: "high" });
878
+ tools.push({ name: "full-auto-write", type: "file_write", risk_level: "high" });
879
+ } else if (approvalMode === "auto-edit") {
880
+ tools.push({ name: "auto-edit", type: "file_write", risk_level: "medium" });
881
+ }
882
+ agents.push((0, core_1.createAgent)({
883
+ name: "codex-project",
884
+ framework: "codex",
885
+ description: `Codex CLI configuration (approval_mode: ${approvalMode ?? "suggest"})`,
886
+ source_file: codexConfigPath,
887
+ model,
888
+ tools
889
+ }));
890
+ }
891
+ const codexMdPath = path.join(rootDir, "codex.md");
892
+ const codexMd = safeReadFile(codexMdPath);
893
+ if (codexMd) {
894
+ configFiles.push({ path: codexMdPath, content: codexMd, framework: "codex" });
895
+ }
896
+ const agentsDir = path.join(rootDir, ".agents");
897
+ for (const file of safeListDir(agentsDir, [".md", ".yaml", ".yml"])) {
898
+ const filePath = path.join(agentsDir, file);
899
+ const content = safeReadFile(filePath);
900
+ if (!content || content.trim().length === 0)
901
+ continue;
902
+ configFiles.push({ path: filePath, content, framework: "codex" });
903
+ try {
904
+ const parsed = (0, gray_matter_1.default)(content);
905
+ const data = parsed.data;
906
+ const name = typeof data.name === "string" ? data.name : path.basename(file, path.extname(file));
907
+ agents.push((0, core_1.createAgent)({
908
+ name,
909
+ framework: "codex",
910
+ description: typeof data.description === "string" ? data.description : parsed.content.trim().slice(0, 200),
911
+ source_file: filePath,
912
+ model: typeof data.model === "string" ? data.model : void 0
913
+ }));
914
+ } catch (error) {
915
+ warnings.push(`Could not parse ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
916
+ }
917
+ }
918
+ const opencodePath = path.join(rootDir, ".opencode", "config.json");
919
+ const opencodeConfig = safeReadFile(opencodePath);
920
+ if (opencodeConfig) {
921
+ configFiles.push({ path: opencodePath, content: opencodeConfig, framework: "codex" });
922
+ try {
923
+ const config = JSON.parse(opencodeConfig);
924
+ if (!agents.some((a) => a.name === "codex-project")) {
925
+ agents.push((0, core_1.createAgent)({
926
+ name: "opencode-project",
927
+ framework: "codex",
928
+ description: "OpenCode project configuration",
929
+ source_file: opencodePath,
930
+ model: typeof config.model === "string" ? config.model : void 0
931
+ }));
932
+ }
933
+ } catch {
934
+ warnings.push(`Invalid JSON in ${opencodePath}`);
935
+ }
936
+ }
937
+ for (const subdir of ["instructions", "agents", "commands", "prompts"]) {
938
+ const dir = path.join(rootDir, ".opencode", subdir);
939
+ for (const file of safeListDir(dir, [".md", ".txt"])) {
940
+ const filePath = path.join(dir, file);
941
+ const content = safeReadFile(filePath);
942
+ if (content) {
943
+ configFiles.push({ path: filePath, content, framework: "codex" });
944
+ }
945
+ }
946
+ }
947
+ if (agents.length === 0 && configFiles.length > 0) {
948
+ agents.push((0, core_1.createAgent)({
949
+ name: "codex-default",
950
+ framework: "codex",
951
+ description: "Default Codex/OpenCode configuration",
952
+ source_file: configFiles[0]?.path
953
+ }));
954
+ }
955
+ return { agents, config_files: configFiles, warnings };
956
+ }
957
+ };
958
+ exports2.CodexParser = CodexParser;
959
+ }
960
+ });
961
+
962
+ // packages/parsers/dist/langchain.js
963
+ var require_langchain = __commonJS({
964
+ "packages/parsers/dist/langchain.js"(exports2) {
965
+ "use strict";
966
+ var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
967
+ if (k2 === void 0) k2 = k;
968
+ var desc = Object.getOwnPropertyDescriptor(m, k);
969
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
970
+ desc = { enumerable: true, get: function() {
971
+ return m[k];
972
+ } };
973
+ }
974
+ Object.defineProperty(o, k2, desc);
975
+ }) : (function(o, m, k, k2) {
976
+ if (k2 === void 0) k2 = k;
977
+ o[k2] = m[k];
978
+ }));
979
+ var __setModuleDefault2 = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
980
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
981
+ }) : function(o, v) {
982
+ o["default"] = v;
983
+ });
984
+ var __importStar2 = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
985
+ var ownKeys = function(o) {
986
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
987
+ var ar = [];
988
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
989
+ return ar;
990
+ };
991
+ return ownKeys(o);
992
+ };
993
+ return function(mod) {
994
+ if (mod && mod.__esModule) return mod;
995
+ var result = {};
996
+ if (mod != null) {
997
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding2(result, mod, k[i]);
998
+ }
999
+ __setModuleDefault2(result, mod);
1000
+ return result;
1001
+ };
1002
+ })();
1003
+ Object.defineProperty(exports2, "__esModule", { value: true });
1004
+ exports2.LangChainParser = void 0;
1005
+ var fs = __importStar2(require("fs"));
1006
+ var path = __importStar2(require("path"));
1007
+ var core_1 = require_dist();
1008
+ function safeReadFile(filePath) {
1009
+ try {
1010
+ if (!fs.existsSync(filePath))
1011
+ return null;
1012
+ return fs.readFileSync(filePath, "utf-8");
1013
+ } catch {
1014
+ return null;
1015
+ }
1016
+ }
1017
+ function findPythonFiles(dir, maxDepth = 3) {
1018
+ const results = [];
1019
+ function walk(currentDir, depth) {
1020
+ if (depth > maxDepth)
1021
+ return;
1022
+ try {
1023
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
1024
+ for (const entry of entries) {
1025
+ const fullPath = path.join(currentDir, entry.name);
1026
+ if (entry.isDirectory() && !["node_modules", ".git", "__pycache__", ".venv", "venv", ".tox", "dist", "build"].includes(entry.name)) {
1027
+ walk(fullPath, depth + 1);
1028
+ } else if (entry.isFile() && entry.name.endsWith(".py")) {
1029
+ results.push(fullPath);
1030
+ }
1031
+ }
1032
+ } catch {
1033
+ }
1034
+ }
1035
+ walk(dir, 0);
1036
+ return results;
1037
+ }
1038
+ var AGENT_PATTERNS = [
1039
+ { pattern: /(?:create_react_agent|create_openai_functions_agent|create_tool_calling_agent|create_structured_chat_agent)\s*\(/g, type: "agent_factory" },
1040
+ { pattern: /AgentExecutor\s*(?:\(|\.from_agent_and_tools)/g, type: "agent_executor" },
1041
+ { pattern: /class\s+(\w+).*(?:BaseTool|StructuredTool|Tool)/g, type: "custom_tool" },
1042
+ { pattern: /StateGraph\s*\(/g, type: "langgraph" },
1043
+ { pattern: /CompiledGraph|MessageGraph/g, type: "langgraph" }
1044
+ ];
1045
+ var TOOL_PATTERNS = [
1046
+ { pattern: /(?:PythonREPLTool|ShellTool|BashTool)\s*\(/g, name: "code_execution", type: "bash", risk: "high" },
1047
+ { pattern: /(?:SQLDatabaseToolkit|QuerySQLDataBaseTool)\s*\(/g, name: "database", type: "database", risk: "high" },
1048
+ { pattern: /(?:RequestsGetTool|RequestsPostTool|RequestsPutTool)\s*\(/g, name: "http_requests", type: "web_fetch", risk: "medium" },
1049
+ { pattern: /(?:FileSearchTool|DirectoryReadTool|ReadFileTool)\s*\(/g, name: "file_read", type: "file_read", risk: "low" },
1050
+ { pattern: /(?:WriteFileTool|FileManagementToolkit)\s*\(/g, name: "file_write", type: "file_write", risk: "medium" },
1051
+ { pattern: /(?:DuckDuckGoSearchRun|GoogleSearchAPIWrapper|TavilySearchResults|BraveSearchRun)\s*\(/g, name: "web_search", type: "web_search", risk: "low" },
1052
+ { pattern: /(?:GmailToolkit|SlackToolkit|JiraToolkit)\s*\(/g, name: "saas_integration", type: "api_call", risk: "medium" }
1053
+ ];
1054
+ var MODEL_PATTERNS = [
1055
+ { pattern: /ChatOpenAI\s*\([^)]*model\s*=\s*["']([^"']+)["']/g, provider: "openai" },
1056
+ { pattern: /ChatAnthropic\s*\([^)]*model\s*=\s*["']([^"']+)["']/g, provider: "anthropic" },
1057
+ { pattern: /ChatGoogleGenerativeAI\s*\([^)]*model\s*=\s*["']([^"']+)["']/g, provider: "google" },
1058
+ { pattern: /AzureChatOpenAI\s*\(/g, provider: "azure_openai" },
1059
+ { pattern: /ChatBedrock\s*\(/g, provider: "aws_bedrock" }
1060
+ ];
1061
+ var LangChainParser = class {
1062
+ framework = "langchain";
1063
+ displayName = "LangChain / LangGraph";
1064
+ markers = ["langgraph.json"];
1065
+ async detect(rootDir) {
1066
+ const pyprojectPath = path.join(rootDir, "pyproject.toml");
1067
+ const requirementsPath = path.join(rootDir, "requirements.txt");
1068
+ const langgraphPath = path.join(rootDir, "langgraph.json");
1069
+ if (fs.existsSync(langgraphPath))
1070
+ return true;
1071
+ for (const depFile of [pyprojectPath, requirementsPath]) {
1072
+ const content = safeReadFile(depFile);
1073
+ if (content && (content.includes("langchain") || content.includes("langgraph"))) {
1074
+ return true;
1075
+ }
1076
+ }
1077
+ const poetryLock = safeReadFile(path.join(rootDir, "poetry.lock"));
1078
+ if (poetryLock && poetryLock.includes("langchain"))
1079
+ return true;
1080
+ return false;
1081
+ }
1082
+ async parse(rootDir) {
1083
+ const agents = [];
1084
+ const configFiles = [];
1085
+ const warnings = [];
1086
+ for (const depFile of ["pyproject.toml", "requirements.txt", "setup.py", "poetry.lock"]) {
1087
+ const filePath = path.join(rootDir, depFile);
1088
+ const content = safeReadFile(filePath);
1089
+ if (content && (content.includes("langchain") || content.includes("langgraph"))) {
1090
+ configFiles.push({ path: filePath, content, framework: "langchain" });
1091
+ }
1092
+ }
1093
+ const lgPath = path.join(rootDir, "langgraph.json");
1094
+ const lgContent = safeReadFile(lgPath);
1095
+ if (lgContent) {
1096
+ configFiles.push({ path: lgPath, content: lgContent, framework: "langchain" });
1097
+ }
1098
+ const pyFiles = findPythonFiles(rootDir);
1099
+ let agentCount = 0;
1100
+ for (const pyFile of pyFiles) {
1101
+ const content = safeReadFile(pyFile);
1102
+ if (!content)
1103
+ continue;
1104
+ if (!content.includes("langchain") && !content.includes("langgraph"))
1105
+ continue;
1106
+ configFiles.push({ path: pyFile, content, framework: "langchain" });
1107
+ let hasAgent = false;
1108
+ for (const { pattern, type } of AGENT_PATTERNS) {
1109
+ pattern.lastIndex = 0;
1110
+ if (pattern.test(content)) {
1111
+ hasAgent = true;
1112
+ break;
1113
+ }
1114
+ }
1115
+ if (!hasAgent)
1116
+ continue;
1117
+ const tools = [];
1118
+ for (const { pattern, name, type, risk } of TOOL_PATTERNS) {
1119
+ pattern.lastIndex = 0;
1120
+ if (pattern.test(content)) {
1121
+ tools.push({ name, type, risk_level: risk });
1122
+ }
1123
+ }
1124
+ let model;
1125
+ for (const { pattern, provider } of MODEL_PATTERNS) {
1126
+ pattern.lastIndex = 0;
1127
+ const match = pattern.exec(content);
1128
+ if (match) {
1129
+ model = match[1] ? `${provider}/${match[1]}` : provider;
1130
+ break;
1131
+ }
1132
+ }
1133
+ const classMatch = content.match(/class\s+(\w+Agent)\b/);
1134
+ const agentName = classMatch?.[1] ?? path.basename(pyFile, ".py").replace(/_/g, "-");
1135
+ const isLangGraph = content.includes("StateGraph") || content.includes("langgraph");
1136
+ agents.push((0, core_1.createAgent)({
1137
+ name: agentName,
1138
+ framework: "langchain",
1139
+ description: `${isLangGraph ? "LangGraph" : "LangChain"} agent defined in ${path.relative(rootDir, pyFile)}`,
1140
+ source_file: pyFile,
1141
+ model,
1142
+ tools,
1143
+ swarm_role: isLangGraph ? "worker" : "standalone"
1144
+ }));
1145
+ agentCount++;
1146
+ }
1147
+ const envPath = path.join(rootDir, ".env");
1148
+ const envContent = safeReadFile(envPath);
1149
+ if (envContent) {
1150
+ configFiles.push({ path: envPath, content: envContent, framework: "langchain" });
1151
+ }
1152
+ if (agents.length === 0 && configFiles.length > 0) {
1153
+ agents.push((0, core_1.createAgent)({
1154
+ name: "langchain-project",
1155
+ framework: "langchain",
1156
+ description: "LangChain/LangGraph project detected from dependencies",
1157
+ source_file: configFiles[0]?.path
1158
+ }));
1159
+ }
1160
+ return { agents, config_files: configFiles, warnings };
1161
+ }
1162
+ };
1163
+ exports2.LangChainParser = LangChainParser;
1164
+ }
1165
+ });
1166
+
1167
+ // packages/parsers/dist/crewai.js
1168
+ var require_crewai = __commonJS({
1169
+ "packages/parsers/dist/crewai.js"(exports2) {
1170
+ "use strict";
1171
+ var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
1172
+ if (k2 === void 0) k2 = k;
1173
+ var desc = Object.getOwnPropertyDescriptor(m, k);
1174
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
1175
+ desc = { enumerable: true, get: function() {
1176
+ return m[k];
1177
+ } };
1178
+ }
1179
+ Object.defineProperty(o, k2, desc);
1180
+ }) : (function(o, m, k, k2) {
1181
+ if (k2 === void 0) k2 = k;
1182
+ o[k2] = m[k];
1183
+ }));
1184
+ var __setModuleDefault2 = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
1185
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
1186
+ }) : function(o, v) {
1187
+ o["default"] = v;
1188
+ });
1189
+ var __importStar2 = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
1190
+ var ownKeys = function(o) {
1191
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
1192
+ var ar = [];
1193
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
1194
+ return ar;
1195
+ };
1196
+ return ownKeys(o);
1197
+ };
1198
+ return function(mod) {
1199
+ if (mod && mod.__esModule) return mod;
1200
+ var result = {};
1201
+ if (mod != null) {
1202
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding2(result, mod, k[i]);
1203
+ }
1204
+ __setModuleDefault2(result, mod);
1205
+ return result;
1206
+ };
1207
+ })();
1208
+ Object.defineProperty(exports2, "__esModule", { value: true });
1209
+ exports2.CrewAIParser = void 0;
1210
+ var fs = __importStar2(require("fs"));
1211
+ var path = __importStar2(require("path"));
1212
+ var core_1 = require_dist();
1213
+ function safeReadFile(filePath) {
1214
+ try {
1215
+ if (!fs.existsSync(filePath))
1216
+ return null;
1217
+ return fs.readFileSync(filePath, "utf-8");
1218
+ } catch {
1219
+ return null;
1220
+ }
1221
+ }
1222
+ function findPythonFiles(dir, maxDepth = 3) {
1223
+ const results = [];
1224
+ function walk(currentDir, depth) {
1225
+ if (depth > maxDepth)
1226
+ return;
1227
+ try {
1228
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
1229
+ for (const entry of entries) {
1230
+ const fullPath = path.join(currentDir, entry.name);
1231
+ if (entry.isDirectory() && !["node_modules", ".git", "__pycache__", ".venv", "venv", "dist", "build"].includes(entry.name)) {
1232
+ walk(fullPath, depth + 1);
1233
+ } else if (entry.isFile() && entry.name.endsWith(".py")) {
1234
+ results.push(fullPath);
1235
+ }
1236
+ }
1237
+ } catch {
1238
+ }
1239
+ }
1240
+ walk(dir, 0);
1241
+ return results;
1242
+ }
1243
+ function parseCrewYAML(content) {
1244
+ const agents = [];
1245
+ const blocks = content.split(/\n(?=\w)/);
1246
+ for (const block of blocks) {
1247
+ const lines = block.split("\n");
1248
+ const headerLine = lines[0];
1249
+ if (!headerLine || !headerLine.trim().endsWith(":"))
1250
+ continue;
1251
+ const agentKey = headerLine.trim().replace(":", "");
1252
+ const fields = { _key: agentKey };
1253
+ for (let i = 1; i < lines.length; i++) {
1254
+ const line = lines[i];
1255
+ if (!line)
1256
+ continue;
1257
+ const match = line.match(/^\s+(\w[\w_]*):\s*(.*)/);
1258
+ if (match && match[1] && match[2] !== void 0) {
1259
+ const key = match[1];
1260
+ let value = match[2].trim();
1261
+ if (value === "true" || value === "True") {
1262
+ fields[key] = true;
1263
+ } else if (value === "false" || value === "False") {
1264
+ fields[key] = false;
1265
+ } else if (value.startsWith("[") && value.endsWith("]")) {
1266
+ fields[key] = value.slice(1, -1).split(",").map((s) => s.trim().replace(/["']/g, ""));
1267
+ } else {
1268
+ fields[key] = value.replace(/^["']|["']$/g, "");
1269
+ }
1270
+ }
1271
+ }
1272
+ if (Object.keys(fields).length > 1) {
1273
+ agents.push(fields);
1274
+ }
1275
+ }
1276
+ return agents;
1277
+ }
1278
+ var CREWAI_TOOL_MAP = {
1279
+ "SerperDevTool": { type: "web_search", risk: "low" },
1280
+ "ScrapeWebsiteTool": { type: "web_fetch", risk: "medium" },
1281
+ "WebsiteSearchTool": { type: "web_search", risk: "low" },
1282
+ "FileReadTool": { type: "file_read", risk: "low" },
1283
+ "FileWriterTool": { type: "file_write", risk: "medium" },
1284
+ "DirectoryReadTool": { type: "file_read", risk: "low" },
1285
+ "DirectorySearchTool": { type: "file_read", risk: "low" },
1286
+ "CodeInterpreterTool": { type: "code_execution", risk: "high" },
1287
+ "CodeDocsSearchTool": { type: "web_search", risk: "low" },
1288
+ "GithubSearchTool": { type: "api_call", risk: "low" },
1289
+ "PGSearchTool": { type: "database", risk: "medium" },
1290
+ "MySQLSearchTool": { type: "database", risk: "medium" }
1291
+ };
1292
+ var CrewAIParser = class {
1293
+ framework = "crewai";
1294
+ displayName = "CrewAI";
1295
+ markers = ["crew.yaml", "agents.yaml"];
1296
+ async detect(rootDir) {
1297
+ const yamlFiles = ["crew.yaml", "crew.yml", "agents.yaml", "agents.yml"];
1298
+ for (const f of yamlFiles) {
1299
+ if (fs.existsSync(path.join(rootDir, f)))
1300
+ return true;
1301
+ if (fs.existsSync(path.join(rootDir, "config", f)))
1302
+ return true;
1303
+ }
1304
+ for (const depFile of ["pyproject.toml", "requirements.txt"]) {
1305
+ const content = safeReadFile(path.join(rootDir, depFile));
1306
+ if (content && content.includes("crewai"))
1307
+ return true;
1308
+ }
1309
+ return false;
1310
+ }
1311
+ async parse(rootDir) {
1312
+ const agents = [];
1313
+ const configFiles = [];
1314
+ const warnings = [];
1315
+ const yamlLocations = [
1316
+ path.join(rootDir, "crew.yaml"),
1317
+ path.join(rootDir, "crew.yml"),
1318
+ path.join(rootDir, "agents.yaml"),
1319
+ path.join(rootDir, "agents.yml"),
1320
+ path.join(rootDir, "config", "agents.yaml"),
1321
+ path.join(rootDir, "config", "agents.yml")
1322
+ ];
1323
+ for (const yamlPath of yamlLocations) {
1324
+ const content = safeReadFile(yamlPath);
1325
+ if (!content)
1326
+ continue;
1327
+ configFiles.push({ path: yamlPath, content, framework: "crewai" });
1328
+ const parsedAgents = parseCrewYAML(content);
1329
+ for (const agentDef of parsedAgents) {
1330
+ const name = String(agentDef._key ?? "unnamed");
1331
+ const role = typeof agentDef.role === "string" ? agentDef.role : "";
1332
+ const goal = typeof agentDef.goal === "string" ? agentDef.goal : "";
1333
+ const backstory = typeof agentDef.backstory === "string" ? agentDef.backstory : "";
1334
+ const toolNames = Array.isArray(agentDef.tools) ? agentDef.tools : typeof agentDef.tools === "string" ? [agentDef.tools] : [];
1335
+ const tools = toolNames.map((t) => {
1336
+ const mapped = CREWAI_TOOL_MAP[t];
1337
+ return mapped ? { name: t, type: mapped.type, risk_level: mapped.risk } : { name: t, type: "custom", risk_level: "low" };
1338
+ });
1339
+ const allowDelegation = agentDef.allow_delegation;
1340
+ const delegateTo = allowDelegation === true || allowDelegation === void 0 ? parsedAgents.filter((a) => a._key !== name).map((a) => String(a._key)) : void 0;
1341
+ const swarmRole = role.toLowerCase().includes("manager") || role.toLowerCase().includes("lead") || role.toLowerCase().includes("coordinator") ? "orchestrator" : "worker";
1342
+ agents.push((0, core_1.createAgent)({
1343
+ name,
1344
+ framework: "crewai",
1345
+ description: `${role}${goal ? ` \u2014 ${goal}` : ""}`,
1346
+ source_file: yamlPath,
1347
+ tools,
1348
+ swarm_role: swarmRole,
1349
+ delegates_to: delegateTo && delegateTo.length > 0 ? delegateTo : void 0
1350
+ }));
1351
+ }
1352
+ }
1353
+ for (const tasksFile of ["tasks.yaml", "tasks.yml", "config/tasks.yaml"]) {
1354
+ const content = safeReadFile(path.join(rootDir, tasksFile));
1355
+ if (content) {
1356
+ configFiles.push({ path: path.join(rootDir, tasksFile), content, framework: "crewai" });
1357
+ }
1358
+ }
1359
+ const pyFiles = findPythonFiles(rootDir);
1360
+ for (const pyFile of pyFiles) {
1361
+ const content = safeReadFile(pyFile);
1362
+ if (!content || !content.includes("crewai"))
1363
+ continue;
1364
+ configFiles.push({ path: pyFile, content, framework: "crewai" });
1365
+ const agentDecorators = content.matchAll(/@agent\s*\n\s*def\s+(\w+)/g);
1366
+ for (const match of agentDecorators) {
1367
+ const name = match[1];
1368
+ if (name && !agents.some((a) => a.name === name)) {
1369
+ agents.push((0, core_1.createAgent)({
1370
+ name,
1371
+ framework: "crewai",
1372
+ description: `CrewAI agent defined in ${path.relative(rootDir, pyFile)}`,
1373
+ source_file: pyFile,
1374
+ swarm_role: "worker"
1375
+ }));
1376
+ }
1377
+ }
1378
+ }
1379
+ for (const depFile of ["pyproject.toml", "requirements.txt"]) {
1380
+ const filePath = path.join(rootDir, depFile);
1381
+ const content = safeReadFile(filePath);
1382
+ if (content && content.includes("crewai")) {
1383
+ configFiles.push({ path: filePath, content, framework: "crewai" });
1384
+ }
1385
+ }
1386
+ if (agents.length === 0 && configFiles.length > 0) {
1387
+ agents.push((0, core_1.createAgent)({
1388
+ name: "crewai-project",
1389
+ framework: "crewai",
1390
+ description: "CrewAI project detected from configuration",
1391
+ source_file: configFiles[0]?.path
1392
+ }));
1393
+ }
1394
+ return { agents, config_files: configFiles, warnings };
1395
+ }
1396
+ };
1397
+ exports2.CrewAIParser = CrewAIParser;
1398
+ }
1399
+ });
1400
+
1401
+ // packages/parsers/dist/kiro.js
1402
+ var require_kiro = __commonJS({
1403
+ "packages/parsers/dist/kiro.js"(exports2) {
1404
+ "use strict";
1405
+ var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
1406
+ if (k2 === void 0) k2 = k;
1407
+ var desc = Object.getOwnPropertyDescriptor(m, k);
1408
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
1409
+ desc = { enumerable: true, get: function() {
1410
+ return m[k];
1411
+ } };
1412
+ }
1413
+ Object.defineProperty(o, k2, desc);
1414
+ }) : (function(o, m, k, k2) {
1415
+ if (k2 === void 0) k2 = k;
1416
+ o[k2] = m[k];
1417
+ }));
1418
+ var __setModuleDefault2 = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
1419
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
1420
+ }) : function(o, v) {
1421
+ o["default"] = v;
1422
+ });
1423
+ var __importStar2 = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
1424
+ var ownKeys = function(o) {
1425
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
1426
+ var ar = [];
1427
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
1428
+ return ar;
1429
+ };
1430
+ return ownKeys(o);
1431
+ };
1432
+ return function(mod) {
1433
+ if (mod && mod.__esModule) return mod;
1434
+ var result = {};
1435
+ if (mod != null) {
1436
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding2(result, mod, k[i]);
1437
+ }
1438
+ __setModuleDefault2(result, mod);
1439
+ return result;
1440
+ };
1441
+ })();
1442
+ var __importDefault = exports2 && exports2.__importDefault || function(mod) {
1443
+ return mod && mod.__esModule ? mod : { "default": mod };
1444
+ };
1445
+ Object.defineProperty(exports2, "__esModule", { value: true });
1446
+ exports2.KiroParser = void 0;
1447
+ var fs = __importStar2(require("fs"));
1448
+ var path = __importStar2(require("path"));
1449
+ var gray_matter_1 = __importDefault(require("gray-matter"));
1450
+ var core_1 = require_dist();
1451
+ function safeReadFile(filePath) {
1452
+ try {
1453
+ if (!fs.existsSync(filePath))
1454
+ return null;
1455
+ return fs.readFileSync(filePath, "utf-8");
1456
+ } catch {
1457
+ return null;
1458
+ }
1459
+ }
1460
+ function safeListDir(dirPath, extensions) {
1461
+ try {
1462
+ if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory())
1463
+ return [];
1464
+ return fs.readdirSync(dirPath).filter((f) => extensions.some((ext) => f.endsWith(ext)));
1465
+ } catch {
1466
+ return [];
1467
+ }
1468
+ }
1469
+ var KiroParser = class {
1470
+ framework = "custom";
1471
+ // Kiro maps to "custom" in our framework enum
1472
+ displayName = "Kiro";
1473
+ markers = [".kiro", "kiro.md"];
1474
+ async detect(rootDir) {
1475
+ return fs.existsSync(path.join(rootDir, ".kiro")) || fs.existsSync(path.join(rootDir, "kiro.md"));
1476
+ }
1477
+ async parse(rootDir) {
1478
+ const agents = [];
1479
+ const configFiles = [];
1480
+ const warnings = [];
1481
+ const steeringDir = path.join(rootDir, ".kiro", "steering");
1482
+ for (const file of safeListDir(steeringDir, [".md"])) {
1483
+ const filePath = path.join(steeringDir, file);
1484
+ const content = safeReadFile(filePath);
1485
+ if (!content)
1486
+ continue;
1487
+ configFiles.push({ path: filePath, content, framework: "custom" });
1488
+ try {
1489
+ const parsed = (0, gray_matter_1.default)(content);
1490
+ const data = parsed.data;
1491
+ const name = typeof data.name === "string" ? data.name : path.basename(file, ".md");
1492
+ agents.push((0, core_1.createAgent)({
1493
+ name: `kiro-steering-${name}`,
1494
+ framework: "custom",
1495
+ description: typeof data.description === "string" ? data.description : parsed.content.trim().slice(0, 200),
1496
+ source_file: filePath
1497
+ }));
1498
+ } catch (error) {
1499
+ warnings.push(`Could not parse ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
1500
+ }
1501
+ }
1502
+ const specsDir = path.join(rootDir, ".kiro", "specs");
1503
+ for (const file of safeListDir(specsDir, [".md"])) {
1504
+ const filePath = path.join(specsDir, file);
1505
+ const content = safeReadFile(filePath);
1506
+ if (content) {
1507
+ configFiles.push({ path: filePath, content, framework: "custom" });
1508
+ }
1509
+ }
1510
+ const kiroMdPath = path.join(rootDir, "kiro.md");
1511
+ const kiroMd = safeReadFile(kiroMdPath);
1512
+ if (kiroMd) {
1513
+ configFiles.push({ path: kiroMdPath, content: kiroMd, framework: "custom" });
1514
+ }
1515
+ if (agents.length === 0 && configFiles.length > 0) {
1516
+ agents.push((0, core_1.createAgent)({
1517
+ name: "kiro-default",
1518
+ framework: "custom",
1519
+ description: "Kiro IDE project configuration",
1520
+ source_file: configFiles[0]?.path
1521
+ }));
1522
+ }
1523
+ return { agents, config_files: configFiles, warnings };
1524
+ }
1525
+ };
1526
+ exports2.KiroParser = KiroParser;
1527
+ }
1528
+ });
1529
+
1530
+ // packages/parsers/dist/auto-detect.js
1531
+ var require_auto_detect = __commonJS({
1532
+ "packages/parsers/dist/auto-detect.js"(exports2) {
1533
+ "use strict";
1534
+ Object.defineProperty(exports2, "__esModule", { value: true });
1535
+ exports2.detectFrameworks = detectFrameworks;
1536
+ exports2.parseAll = parseAll;
1537
+ var claude_code_1 = require_claude_code();
1538
+ var cursor_1 = require_cursor();
1539
+ var codex_1 = require_codex();
1540
+ var langchain_1 = require_langchain();
1541
+ var crewai_1 = require_crewai();
1542
+ var kiro_1 = require_kiro();
1543
+ function getAllParsers() {
1544
+ return [
1545
+ new claude_code_1.ClaudeCodeParser(),
1546
+ new cursor_1.CursorParser(),
1547
+ new codex_1.CodexParser(),
1548
+ new langchain_1.LangChainParser(),
1549
+ new crewai_1.CrewAIParser(),
1550
+ new kiro_1.KiroParser()
1551
+ ];
1552
+ }
1553
+ async function detectFrameworks(rootDir) {
1554
+ const allParsers = getAllParsers();
1555
+ const detected = [];
1556
+ for (const parser of allParsers) {
1557
+ if (await parser.detect(rootDir)) {
1558
+ detected.push(parser);
1559
+ }
1560
+ }
1561
+ return { detected, all_parsers: allParsers };
1562
+ }
1563
+ async function parseAll(rootDir) {
1564
+ const { detected } = await detectFrameworks(rootDir);
1565
+ const allAgents = [];
1566
+ const allConfigFiles = [];
1567
+ const allWarnings = [];
1568
+ const frameworks = [];
1569
+ for (const parser of detected) {
1570
+ const result = await parser.parse(rootDir);
1571
+ allAgents.push(...result.agents);
1572
+ allConfigFiles.push(...result.config_files);
1573
+ allWarnings.push(...result.warnings);
1574
+ frameworks.push(parser.displayName);
1575
+ }
1576
+ return {
1577
+ agents: allAgents,
1578
+ config_files: allConfigFiles,
1579
+ frameworks,
1580
+ warnings: allWarnings
1581
+ };
1582
+ }
1583
+ }
1584
+ });
1585
+
1586
+ // packages/parsers/dist/index.js
1587
+ var require_dist2 = __commonJS({
1588
+ "packages/parsers/dist/index.js"(exports2) {
1589
+ "use strict";
1590
+ Object.defineProperty(exports2, "__esModule", { value: true });
1591
+ exports2.parseAll = exports2.detectFrameworks = exports2.KiroParser = exports2.CrewAIParser = exports2.LangChainParser = exports2.CodexParser = exports2.CursorParser = exports2.ClaudeCodeParser = void 0;
1592
+ var claude_code_1 = require_claude_code();
1593
+ Object.defineProperty(exports2, "ClaudeCodeParser", { enumerable: true, get: function() {
1594
+ return claude_code_1.ClaudeCodeParser;
1595
+ } });
1596
+ var cursor_1 = require_cursor();
1597
+ Object.defineProperty(exports2, "CursorParser", { enumerable: true, get: function() {
1598
+ return cursor_1.CursorParser;
1599
+ } });
1600
+ var codex_1 = require_codex();
1601
+ Object.defineProperty(exports2, "CodexParser", { enumerable: true, get: function() {
1602
+ return codex_1.CodexParser;
1603
+ } });
1604
+ var langchain_1 = require_langchain();
1605
+ Object.defineProperty(exports2, "LangChainParser", { enumerable: true, get: function() {
1606
+ return langchain_1.LangChainParser;
1607
+ } });
1608
+ var crewai_1 = require_crewai();
1609
+ Object.defineProperty(exports2, "CrewAIParser", { enumerable: true, get: function() {
1610
+ return crewai_1.CrewAIParser;
1611
+ } });
1612
+ var kiro_1 = require_kiro();
1613
+ Object.defineProperty(exports2, "KiroParser", { enumerable: true, get: function() {
1614
+ return kiro_1.KiroParser;
1615
+ } });
1616
+ var auto_detect_1 = require_auto_detect();
1617
+ Object.defineProperty(exports2, "detectFrameworks", { enumerable: true, get: function() {
1618
+ return auto_detect_1.detectFrameworks;
1619
+ } });
1620
+ Object.defineProperty(exports2, "parseAll", { enumerable: true, get: function() {
1621
+ return auto_detect_1.parseAll;
1622
+ } });
1623
+ }
1624
+ });
1625
+
1626
+ // packages/scanner/dist/rules/interface.js
1627
+ var require_interface = __commonJS({
1628
+ "packages/scanner/dist/rules/interface.js"(exports2) {
1629
+ "use strict";
1630
+ Object.defineProperty(exports2, "__esModule", { value: true });
1631
+ exports2.createEnterpriseFinding = createEnterpriseFinding;
1632
+ exports2.checkSuppression = checkSuppression;
1633
+ exports2.parseInlineSuppressions = parseInlineSuppressions;
1634
+ exports2.getRuleDocsUrl = getRuleDocsUrl;
1635
+ function createEnterpriseFinding(rule, params) {
1636
+ return {
1637
+ id: params.id,
1638
+ rule_id: rule.id,
1639
+ rule_name: rule.name,
1640
+ severity: rule.severity,
1641
+ category: rule.category,
1642
+ title: params.title,
1643
+ description: params.description,
1644
+ recommendation: params.recommendation,
1645
+ agent_id: params.agent_id,
1646
+ agent_name: params.agent_name,
1647
+ framework: params.framework,
1648
+ location: params.location,
1649
+ compliance: rule.compliance,
1650
+ cwe: params.cwe,
1651
+ cve: params.cve,
1652
+ mitre_atlas: params.mitre_atlas,
1653
+ remediation_effort: params.remediation_effort,
1654
+ auto_fix: params.auto_fix ?? rule.auto_fix,
1655
+ metadata: params.metadata,
1656
+ first_detected: (/* @__PURE__ */ new Date()).toISOString(),
1657
+ status: "open"
1658
+ };
1659
+ }
1660
+ function checkSuppression(ruleId, filePath, suppressions, now = /* @__PURE__ */ new Date()) {
1661
+ for (const entry of suppressions) {
1662
+ if (entry.rule_id !== ruleId)
1663
+ continue;
1664
+ if (entry.path && filePath) {
1665
+ const pattern = entry.path.replace(/\*/g, ".*");
1666
+ if (!new RegExp(`^${pattern}$`).test(filePath))
1667
+ continue;
1668
+ }
1669
+ if (entry.expires) {
1670
+ const expiresDate = new Date(entry.expires);
1671
+ if (now > expiresDate) {
1672
+ return { suppressed: false, entry, expired: true };
1673
+ }
1674
+ }
1675
+ return { suppressed: true, entry };
1676
+ }
1677
+ return { suppressed: false };
1678
+ }
1679
+ var INLINE_SUPPRESS_PATTERN = /[#\/\/]\s*sentinelflow-ignore:\s*(SF-[A-Z]+-\d+)\s*(?:--\s*(.+))?$/;
1680
+ function parseInlineSuppressions(filePath, content) {
1681
+ const entries = [];
1682
+ const lines = content.split("\n");
1683
+ for (const line of lines) {
1684
+ const match = INLINE_SUPPRESS_PATTERN.exec(line);
1685
+ if (match && match[1]) {
1686
+ entries.push({
1687
+ rule_id: match[1],
1688
+ path: filePath,
1689
+ reason: match[2]?.trim() ?? "No justification provided",
1690
+ source: "inline"
1691
+ });
1692
+ }
1693
+ }
1694
+ return entries;
1695
+ }
1696
+ function getRuleDocsUrl(rule) {
1697
+ return rule.docs_url ?? `https://sentinelflow.dev/rules/${rule.id}`;
1698
+ }
1699
+ }
1700
+ });
1701
+
1702
+ // packages/scanner/dist/rules/prompt-injection.js
1703
+ var require_prompt_injection = __commonJS({
1704
+ "packages/scanner/dist/rules/prompt-injection.js"(exports2) {
1705
+ "use strict";
1706
+ Object.defineProperty(exports2, "__esModule", { value: true });
1707
+ exports2.PROMPT_INJECTION_RULES = exports2.longContextWithoutSummarization = exports2.noOutputValidation = exports2.sensitiveDataInPrompts = exports2.noSystemPrompt = void 0;
1708
+ var interface_1 = require_interface();
1709
+ var COMPLIANCE_PI = [
1710
+ { framework: "OWASP_LLM_2025", reference: "LLM01", description: "Prompt Injection" },
1711
+ { framework: "MITRE_ATLAS", reference: "AML.T0051", description: "LLM Prompt Injection" },
1712
+ { framework: "NIST_AI_RMF", reference: "MEASURE 2.4", description: "Input validation measures" }
1713
+ ];
1714
+ exports2.noSystemPrompt = {
1715
+ id: "SF-PI-001",
1716
+ name: "No System Prompt Defined",
1717
+ description: "Agent operates without a system prompt, lacking any instruction boundary against prompt injection.",
1718
+ category: "prompt_injection",
1719
+ severity: "high",
1720
+ frameworks: "all",
1721
+ compliance: COMPLIANCE_PI,
1722
+ phase: "static",
1723
+ evaluate(ctx) {
1724
+ return ctx.agents.filter((a) => !a.description || a.description.trim().length < 10).filter((a) => {
1725
+ const hasClaudeMd = ctx.config_files.some((f) => f.path.endsWith("CLAUDE.md") && f.content.trim().length > 50);
1726
+ return !hasClaudeMd;
1727
+ }).map((agent) => (0, interface_1.createEnterpriseFinding)(this, {
1728
+ id: `${this.id}-${agent.id}`,
1729
+ title: `Agent "${agent.name}" has no system prompt or project guidance`,
1730
+ description: `No CLAUDE.md, AGENTS.md, or substantial agent description found for "${agent.name}". Without a system prompt, the agent has no instruction boundary, making it fully susceptible to prompt injection from any input source.`,
1731
+ recommendation: "Create a CLAUDE.md file with clear behavioral boundaries, or add a detailed description to the agent definition. Include explicit instructions about what the agent should and should NOT do.",
1732
+ agent_id: agent.id,
1733
+ agent_name: agent.name,
1734
+ framework: agent.framework,
1735
+ mitre_atlas: "AML.T0051",
1736
+ remediation_effort: "low"
1737
+ }));
1738
+ }
1739
+ };
1740
+ var SENSITIVE_PATTERNS_IN_PROMPTS = [
1741
+ { pattern: /(?:internal|private|confidential)\s*(?:url|endpoint|api|server)[:=\s]+\S+/gi, name: "internal URL" },
1742
+ { pattern: /(?:database|db)\s*(?:host|server|connection)[:=\s]+\S+/gi, name: "database endpoint" },
1743
+ { pattern: /(?:admin|root|superuser)\s*(?:password|credential|secret)[:=\s]+\S+/gi, name: "admin credential" },
1744
+ { pattern: /(?:vpn|ssh|rdp)\s*[:=\s]+\S+\.(?:com|net|org|io)/gi, name: "infrastructure endpoint" }
1745
+ ];
1746
+ exports2.sensitiveDataInPrompts = {
1747
+ id: "SF-PI-002",
1748
+ name: "System Prompt Contains Sensitive Data",
1749
+ description: "System prompts containing internal URLs, credentials, or infrastructure details are extractable via prompt injection techniques (OWASP LLM07).",
1750
+ category: "prompt_injection",
1751
+ severity: "high",
1752
+ frameworks: "all",
1753
+ compliance: [
1754
+ ...COMPLIANCE_PI,
1755
+ { framework: "OWASP_LLM_2025", reference: "LLM07", description: "System Prompt Leakage" }
1756
+ ],
1757
+ phase: "static",
1758
+ evaluate(ctx) {
1759
+ const findings = [];
1760
+ let counter = 0;
1761
+ const promptFiles = ctx.config_files.filter((f) => f.path.endsWith("CLAUDE.md") || f.path.endsWith("AGENTS.md") || f.path.includes("/agents/") || f.path.includes("/.claude/agents/"));
1762
+ for (const file of promptFiles) {
1763
+ for (const { pattern, name } of SENSITIVE_PATTERNS_IN_PROMPTS) {
1764
+ pattern.lastIndex = 0;
1765
+ let match;
1766
+ while ((match = pattern.exec(file.content)) !== null) {
1767
+ const line = file.content.substring(0, match.index).split("\n").length;
1768
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
1769
+ id: `${this.id}-${counter++}`,
1770
+ title: `${name} found in prompt file ${file.path}`,
1771
+ description: `A potential ${name} was found at line ${line} in a system prompt file. System prompts can be extracted via prompt injection, exposing this data.`,
1772
+ recommendation: "Remove sensitive infrastructure details from system prompts. Use environment variables or secrets managers for endpoints and credentials. Refer to resources by abstract names rather than actual URLs.",
1773
+ location: { file: file.path, line, snippet: match[0].substring(0, 60) },
1774
+ cwe: "CWE-200",
1775
+ remediation_effort: "low"
1776
+ }));
1777
+ }
1778
+ }
1779
+ }
1780
+ return findings;
1781
+ }
1782
+ };
1783
+ exports2.noOutputValidation = {
1784
+ id: "SF-PI-005",
1785
+ name: "No Output Validation Before Tool Execution",
1786
+ description: "Agent passes LLM outputs directly to tools without schema validation, enabling injection chains.",
1787
+ category: "prompt_injection",
1788
+ severity: "high",
1789
+ frameworks: ["langchain", "crewai", "autogen", "custom"],
1790
+ compliance: [
1791
+ ...COMPLIANCE_PI,
1792
+ { framework: "OWASP_LLM_2025", reference: "LLM05", description: "Improper Output Handling" }
1793
+ ],
1794
+ phase: "static",
1795
+ evaluate(ctx) {
1796
+ const findings = [];
1797
+ for (const file of ctx.config_files) {
1798
+ const dangerousPatterns = [
1799
+ { pattern: /eval\s*\(/g, name: "eval()" },
1800
+ { pattern: /exec\s*\(/g, name: "exec()" },
1801
+ { pattern: /subprocess\.(?:run|call|Popen)\s*\(/g, name: "subprocess execution" },
1802
+ { pattern: /os\.system\s*\(/g, name: "os.system()" },
1803
+ { pattern: /PythonREPLTool|ShellTool/g, name: "unrestricted code execution tool" }
1804
+ ];
1805
+ for (const { pattern, name } of dangerousPatterns) {
1806
+ pattern.lastIndex = 0;
1807
+ let match;
1808
+ while ((match = pattern.exec(file.content)) !== null) {
1809
+ const line = file.content.substring(0, match.index).split("\n").length;
1810
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
1811
+ id: `${this.id}-${findings.length}`,
1812
+ title: `Dangerous ${name} pattern in ${file.path}`,
1813
+ description: `${name} found at line ${line}. LLM outputs passed to code execution functions without validation enable prompt-to-RCE chains. LangChain CVE-2023-29374, CVE-2023-36258, and CVE-2024-21511 all exploited this pattern.`,
1814
+ recommendation: "Add schema validation between LLM output and tool execution. Use structured output parsing (Pydantic models, JSON Schema validation) rather than passing raw LLM text to execution functions. Sandbox all code execution.",
1815
+ location: { file: file.path, line, snippet: match[0] },
1816
+ cwe: "CWE-94",
1817
+ cve: ["CVE-2023-29374", "CVE-2023-36258", "CVE-2024-21511"],
1818
+ remediation_effort: "medium"
1819
+ }));
1820
+ }
1821
+ }
1822
+ }
1823
+ return findings;
1824
+ }
1825
+ };
1826
+ exports2.longContextWithoutSummarization = {
1827
+ id: "SF-PI-008",
1828
+ name: "Large Context Window Without Summarization Strategy",
1829
+ description: "Agents using maximum context windows are vulnerable to many-shot jailbreaking (Anthropic, April 2024).",
1830
+ category: "prompt_injection",
1831
+ severity: "medium",
1832
+ frameworks: "all",
1833
+ compliance: COMPLIANCE_PI,
1834
+ phase: "static",
1835
+ evaluate(ctx) {
1836
+ const findings = [];
1837
+ const hasCompaction = ctx.config_files.some((f) => f.content.includes("compact") || f.content.includes("summariz") || f.content.includes("context_window") || f.content.includes("max_tokens"));
1838
+ if (!hasCompaction && ctx.agents.length > 0) {
1839
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
1840
+ id: `${this.id}-global`,
1841
+ title: "No context window management or compaction strategy detected",
1842
+ description: "No evidence of context window management (compaction, summarization, or token limits) found in the project configuration. Long context sessions are vulnerable to many-shot jailbreaking and increase prompt injection surface area.",
1843
+ recommendation: "Implement a context compaction strategy. Use Claude Code's /compact command, set max_tokens limits, or implement conversation summarization. ECC's suggest-compact hook provides a reference implementation.",
1844
+ remediation_effort: "medium"
1845
+ }));
1846
+ }
1847
+ return findings;
1848
+ }
1849
+ };
1850
+ exports2.PROMPT_INJECTION_RULES = [
1851
+ exports2.noSystemPrompt,
1852
+ exports2.sensitiveDataInPrompts,
1853
+ exports2.noOutputValidation,
1854
+ exports2.longContextWithoutSummarization
1855
+ ];
1856
+ }
1857
+ });
1858
+
1859
+ // packages/scanner/dist/rules/access-control.js
1860
+ var require_access_control = __commonJS({
1861
+ "packages/scanner/dist/rules/access-control.js"(exports2) {
1862
+ "use strict";
1863
+ Object.defineProperty(exports2, "__esModule", { value: true });
1864
+ exports2.ACCESS_CONTROL_RULES = exports2.noOwner = exports2.privilegeEscalation = exports2.noHumanInTheLoop = exports2.excessivePermissions = exports2.hardcodedCredentials = void 0;
1865
+ var interface_1 = require_interface();
1866
+ var COMPLIANCE_AC = [
1867
+ { framework: "OWASP_LLM_2025", reference: "LLM06", description: "Excessive Agency" },
1868
+ { framework: "NIST_AI_RMF", reference: "GOVERN 2", description: "Identity and access management" },
1869
+ { framework: "SOC2", reference: "CC6.1", description: "Logical access security" }
1870
+ ];
1871
+ var SECRET_PATTERNS = [
1872
+ { pattern: /(?:sk-|pk_live_|pk_test_)[a-zA-Z0-9]{20,}/g, name: "API key" },
1873
+ { pattern: /sk-ant-[a-zA-Z0-9-]{80,}/g, name: "Anthropic API key" },
1874
+ { pattern: /(?:ghp_|gho_|ghu_|ghs_|ghr_)[a-zA-Z0-9]{36,}/g, name: "GitHub token" },
1875
+ { pattern: /AKIA[0-9A-Z]{16}/g, name: "AWS Access Key ID" },
1876
+ { pattern: /(?:aws_secret_access_key|AWS_SECRET)\s*[:=]\s*["']?[A-Za-z0-9/+=]{40}["']?/gi, name: "AWS Secret Key" },
1877
+ { pattern: /AIza[0-9A-Za-z_-]{35}/g, name: "Google API key" },
1878
+ { pattern: /glpat-[a-zA-Z0-9_-]{20,}/g, name: "GitLab token" },
1879
+ { pattern: /xox[baprs]-[a-zA-Z0-9-]{10,}/g, name: "Slack token" },
1880
+ { pattern: /(?:hooks\.slack\.com\/services\/)[A-Z0-9/]+/g, name: "Slack Webhook" },
1881
+ { pattern: /(?:mongodb(?:\+srv)?:\/\/)[^\s"']+/g, name: "MongoDB connection string" },
1882
+ { pattern: /(?:postgres(?:ql)?:\/\/)[^\s"']+/g, name: "PostgreSQL connection string" },
1883
+ { pattern: /(?:mysql:\/\/)[^\s"']+/g, name: "MySQL connection string" },
1884
+ { pattern: /(?:redis:\/\/)[^\s"']+/g, name: "Redis connection string" },
1885
+ { pattern: /(?:password|passwd|pwd)\s*[:=]\s*["'][^"']{8,}["']/gi, name: "Password" },
1886
+ { pattern: /(?:secret|token|api_key|apikey|auth_token)\s*[:=]\s*["'][^"']{8,}["']/gi, name: "Secret/Token" },
1887
+ { pattern: /Bearer\s+[a-zA-Z0-9._~+/=-]{20,}/g, name: "Bearer token" }
1888
+ ];
1889
+ var IGNORE_FILE_PATTERNS = [/\.example$/, /\.template$/, /\.sample$/, /SKILL\.md$/, /node_modules/, /\.git\//];
1890
+ function maskSecret(s) {
1891
+ if (s.length <= 8)
1892
+ return "****";
1893
+ return s.substring(0, 4) + "****" + s.substring(s.length - 4);
1894
+ }
1895
+ exports2.hardcodedCredentials = {
1896
+ id: "SF-AC-001",
1897
+ name: "Hardcoded Credentials in Agent Configuration",
1898
+ description: "Agents using hardcoded API keys, tokens, or passwords instead of managed identities or secrets managers.",
1899
+ category: "access_control",
1900
+ severity: "critical",
1901
+ frameworks: "all",
1902
+ compliance: [
1903
+ ...COMPLIANCE_AC,
1904
+ { framework: "HIPAA", reference: "\xA7164.312(d)", description: "Person or entity authentication" }
1905
+ ],
1906
+ phase: "static",
1907
+ evaluate(ctx) {
1908
+ const findings = [];
1909
+ let counter = 0;
1910
+ for (const file of ctx.config_files) {
1911
+ if (IGNORE_FILE_PATTERNS.some((p) => p.test(file.path)))
1912
+ continue;
1913
+ for (const { pattern, name } of SECRET_PATTERNS) {
1914
+ pattern.lastIndex = 0;
1915
+ let match;
1916
+ while ((match = pattern.exec(file.content)) !== null) {
1917
+ const line = file.content.substring(0, match.index).split("\n").length;
1918
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
1919
+ id: `${this.id}-${counter++}`,
1920
+ title: `${name} found in ${file.path}`,
1921
+ description: `A potential ${name} was detected at line ${line}. Hardcoded credentials in agent configs can be exfiltrated via prompt injection, leaked through version control, or exposed in agent traces and logs.`,
1922
+ recommendation: "Use managed identities (Azure Managed Identity, GCP Workload Identity) or a secrets manager (Infisical, Vault, AWS Secrets Manager). Reference secrets via environment variables: $ENV_VAR_NAME.",
1923
+ location: { file: file.path, line, snippet: maskSecret(match[0]) },
1924
+ cwe: "CWE-798",
1925
+ remediation_effort: "low"
1926
+ }));
1927
+ }
1928
+ }
1929
+ }
1930
+ return findings;
1931
+ }
1932
+ };
1933
+ exports2.excessivePermissions = {
1934
+ id: "SF-AC-002",
1935
+ name: "Agent Permissions Exceed Least Privilege",
1936
+ description: "Agent has broad tool access without explicit restrictions, violating the principle of least privilege.",
1937
+ category: "access_control",
1938
+ severity: "high",
1939
+ frameworks: "all",
1940
+ compliance: [
1941
+ ...COMPLIANCE_AC,
1942
+ { framework: "EU_AI_ACT", reference: "Article 15", description: "Accuracy, robustness, cybersecurity" }
1943
+ ],
1944
+ phase: "static",
1945
+ evaluate(ctx) {
1946
+ return ctx.agents.filter((a) => a.tools.length > 0 && (!a.allowed_tools || a.allowed_tools.length === 0) && (!a.blocked_tools || a.blocked_tools.length === 0)).map((agent) => (0, interface_1.createEnterpriseFinding)(this, {
1947
+ id: `${this.id}-${agent.id}`,
1948
+ title: `Agent "${agent.name}" has no explicit tool allowlist`,
1949
+ description: `Agent "${agent.name}" has ${agent.tools.length} tools available with no explicit allowlist or blocklist. This grants implicit access to all tools, including potentially dangerous ones. Microsoft's Copilot oversharing incident showed how excessive permissions amplify data exposure across the entire tenant.`,
1950
+ recommendation: "Define an explicit allowedTools list with only the tools this agent needs. For Claude Code, use granular Bash permissions: 'Bash(npm test)' instead of 'Bash(*)'.",
1951
+ agent_id: agent.id,
1952
+ agent_name: agent.name,
1953
+ framework: agent.framework,
1954
+ location: agent.source_file ? { file: agent.source_file } : void 0,
1955
+ remediation_effort: "low"
1956
+ }));
1957
+ }
1958
+ };
1959
+ exports2.noHumanInTheLoop = {
1960
+ id: "SF-AC-005",
1961
+ name: "No Human-in-the-Loop for High-Impact Actions",
1962
+ description: "Agent can perform irreversible actions (file writes, bash execution, network calls) without human approval gates.",
1963
+ category: "access_control",
1964
+ severity: "high",
1965
+ frameworks: "all",
1966
+ compliance: [
1967
+ ...COMPLIANCE_AC,
1968
+ { framework: "EU_AI_ACT", reference: "Article 14", description: "Human oversight" },
1969
+ { framework: "NIST_AI_RMF", reference: "MANAGE 3", description: "Human oversight of AI" }
1970
+ ],
1971
+ phase: "static",
1972
+ evaluate(ctx) {
1973
+ const findings = [];
1974
+ for (const agent of ctx.agents) {
1975
+ const highRiskTools = agent.tools.filter((t) => t.type === "bash" || t.type === "file_write" || t.type === "api_call" || t.type === "database");
1976
+ if (highRiskTools.length === 0)
1977
+ continue;
1978
+ const hasDangerousSkip = ctx.config_files.some((f) => f.content.includes("dangerously-skip-permissions") || f.content.includes("dangerouslySkipPermissions"));
1979
+ const hasApprovalHook = ctx.config_files.some((f) => f.content.includes("PreToolUse") && (f.content.includes("block") || f.content.includes("approve")));
1980
+ if (hasDangerousSkip || !hasApprovalHook) {
1981
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
1982
+ id: `${this.id}-${agent.id}`,
1983
+ title: `Agent "${agent.name}" can execute high-impact actions without approval`,
1984
+ description: `Agent "${agent.name}" has access to ${highRiskTools.map((t) => t.name).join(", ")} (high-impact tools) with ${hasDangerousSkip ? "permissions explicitly skipped" : "no PreToolUse approval hooks detected"}. EU AI Act Article 14 requires human oversight for high-risk AI systems.`,
1985
+ recommendation: "Add PreToolUse hooks that gate high-impact actions. For Claude Code, remove --dangerously-skip-permissions and configure approval hooks in settings.json.",
1986
+ agent_id: agent.id,
1987
+ agent_name: agent.name,
1988
+ framework: agent.framework,
1989
+ remediation_effort: "medium"
1990
+ }));
1991
+ }
1992
+ }
1993
+ return findings;
1994
+ }
1995
+ };
1996
+ exports2.privilegeEscalation = {
1997
+ id: "SF-AC-007",
1998
+ name: "Agent Can Escalate Its Own Privileges",
1999
+ description: "Agent's tool set includes capabilities that could modify its own permissions or create new credentials.",
2000
+ category: "access_control",
2001
+ severity: "critical",
2002
+ frameworks: "all",
2003
+ compliance: COMPLIANCE_AC,
2004
+ phase: "static",
2005
+ evaluate(ctx) {
2006
+ const findings = [];
2007
+ for (const agent of ctx.agents) {
2008
+ const hasBash = agent.tools.some((t) => t.type === "bash");
2009
+ const hasFileWrite = agent.tools.some((t) => t.type === "file_write");
2010
+ if (hasBash && hasFileWrite) {
2011
+ const canModifyConfig = !agent.file_system_access?.blocked_paths?.some((p) => p.includes(".claude") || p.includes("settings"));
2012
+ if (canModifyConfig) {
2013
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2014
+ id: `${this.id}-${agent.id}`,
2015
+ title: `Agent "${agent.name}" can modify its own configuration`,
2016
+ description: `Agent has both bash execution and file write access without blocked paths for configuration directories. It could modify .claude/settings.json, install new MCP servers, change its own allowedTools, or create credentials.`,
2017
+ recommendation: "Block write access to .claude/, .sentinelflow/, hooks/, and any configuration directories. Add these paths to file_system_access.blocked_paths.",
2018
+ agent_id: agent.id,
2019
+ agent_name: agent.name,
2020
+ framework: agent.framework,
2021
+ cwe: "CWE-269",
2022
+ remediation_effort: "low"
2023
+ }));
2024
+ }
2025
+ }
2026
+ }
2027
+ return findings;
2028
+ }
2029
+ };
2030
+ exports2.noOwner = {
2031
+ id: "SF-AC-008",
2032
+ name: "Agent Has No Declared Owner",
2033
+ description: "Unowned agents are shadow AI \u2014 no one is accountable for their behavior, security, or compliance.",
2034
+ category: "access_control",
2035
+ severity: "medium",
2036
+ frameworks: "all",
2037
+ compliance: [
2038
+ ...COMPLIANCE_AC,
2039
+ { framework: "ISO_42001", reference: "Clause 5.3", description: "Roles, responsibilities, authorities" }
2040
+ ],
2041
+ phase: "static",
2042
+ evaluate(ctx) {
2043
+ return ctx.agents.filter((a) => !a.owner && !a.team).map((agent) => (0, interface_1.createEnterpriseFinding)(this, {
2044
+ id: `${this.id}-${agent.id}`,
2045
+ title: `Agent "${agent.name}" has no declared owner`,
2046
+ description: `No owner or team assigned. Microsoft's Cyber Pulse report found that organizations often don't know how many agents exist or who owns them \u2014 this is the governance gap.`,
2047
+ recommendation: "Add an owner field to the agent definition or register it with: sentinelflow registry update <id> --owner <name>",
2048
+ agent_id: agent.id,
2049
+ agent_name: agent.name,
2050
+ framework: agent.framework,
2051
+ location: agent.source_file ? { file: agent.source_file } : void 0,
2052
+ remediation_effort: "low"
2053
+ }));
2054
+ }
2055
+ };
2056
+ exports2.ACCESS_CONTROL_RULES = [
2057
+ exports2.hardcodedCredentials,
2058
+ exports2.excessivePermissions,
2059
+ exports2.noHumanInTheLoop,
2060
+ exports2.privilegeEscalation,
2061
+ exports2.noOwner
2062
+ ];
2063
+ }
2064
+ });
2065
+
2066
+ // packages/scanner/dist/rules/supply-chain.js
2067
+ var require_supply_chain = __commonJS({
2068
+ "packages/scanner/dist/rules/supply-chain.js"(exports2) {
2069
+ "use strict";
2070
+ Object.defineProperty(exports2, "__esModule", { value: true });
2071
+ exports2.SUPPLY_CHAIN_RULES = exports2.langchainPassthrough = exports2.configInPublicRepo = exports2.frameworkCVE = exports2.mcpNoAuth = exports2.toolDescriptionPoisoning = exports2.mcpNoIntegrity = void 0;
2072
+ var interface_1 = require_interface();
2073
+ var COMPLIANCE_SC = [
2074
+ { framework: "OWASP_LLM_2025", reference: "LLM03", description: "Supply Chain Vulnerabilities" },
2075
+ { framework: "MITRE_ATLAS", reference: "AML.T0010", description: "ML Supply Chain Compromise" },
2076
+ { framework: "NIST_AI_RMF", reference: "GOVERN 5", description: "AI supply chain management" }
2077
+ ];
2078
+ var INJECTION_PATTERNS_IN_TOOL_DESC = [
2079
+ /\bignore\b.*\bprevious\b.*\binstructions?\b/gi,
2080
+ /\byou\s+(?:are|must|should|will)\b/gi,
2081
+ /\bdo\s+not\b.*\btell\b.*\buser\b/gi,
2082
+ /<!--[\s\S]*?-->/g,
2083
+ /\u200B|\u200C|\u200D|\uFEFF/g
2084
+ // Zero-width characters
2085
+ ];
2086
+ exports2.mcpNoIntegrity = {
2087
+ id: "SF-SC-001",
2088
+ name: "MCP Server Connected Without Integrity Verification",
2089
+ description: "No tool definition hashing, signing, or pinning exists for connected MCP servers. Servers can mutate tool definitions between sessions.",
2090
+ category: "supply_chain",
2091
+ severity: "high",
2092
+ frameworks: "all",
2093
+ compliance: COMPLIANCE_SC,
2094
+ phase: "static",
2095
+ evaluate(ctx) {
2096
+ const findings = [];
2097
+ for (const agent of ctx.agents) {
2098
+ if (agent.mcp_servers && agent.mcp_servers.length > 0) {
2099
+ for (const server of agent.mcp_servers) {
2100
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2101
+ id: `${this.id}-${agent.id}-${server.name}`,
2102
+ title: `MCP server "${server.name}" connected without integrity verification`,
2103
+ description: `MCP server "${server.name}" on agent "${agent.name}" has no tool definition hashing or pinning configured. The base MCP specification provides no integrity verification \u2014 a server can silently change tool definitions between sessions (rug-pull attack documented by Invariant Labs, 2025).`,
2104
+ recommendation: "Hash all tool definitions at approval time and verify on each connection. SentinelFlow's Phase 2 interceptors will provide automatic tool definition monitoring.",
2105
+ agent_id: agent.id,
2106
+ agent_name: agent.name,
2107
+ framework: agent.framework,
2108
+ mitre_atlas: "AML.T0010",
2109
+ remediation_effort: "medium"
2110
+ }));
2111
+ }
2112
+ }
2113
+ }
2114
+ return findings;
2115
+ }
2116
+ };
2117
+ exports2.toolDescriptionPoisoning = {
2118
+ id: "SF-SC-003",
2119
+ name: "Tool Description Contains Prompt Injection Patterns",
2120
+ description: "MCP tool descriptions, SKILL.md files, or command definitions contain hidden instructions, Unicode manipulation, or references to credentials.",
2121
+ category: "supply_chain",
2122
+ severity: "critical",
2123
+ frameworks: "all",
2124
+ compliance: COMPLIANCE_SC,
2125
+ phase: "static",
2126
+ evaluate(ctx) {
2127
+ const findings = [];
2128
+ let counter = 0;
2129
+ const skillFiles = ctx.config_files.filter((f) => f.path.includes("SKILL.md") || f.path.includes("/skills/") || f.path.includes("/commands/") || f.path.includes("mcp"));
2130
+ for (const file of skillFiles) {
2131
+ for (const pattern of INJECTION_PATTERNS_IN_TOOL_DESC) {
2132
+ pattern.lastIndex = 0;
2133
+ let match;
2134
+ while ((match = pattern.exec(file.content)) !== null) {
2135
+ const line = file.content.substring(0, match.index).split("\n").length;
2136
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2137
+ id: `${this.id}-${counter++}`,
2138
+ title: `Potential prompt injection in tool definition: ${file.path}`,
2139
+ description: `Suspicious pattern found at line ${line}: "${match[0].substring(0, 50)}". Tool descriptions are injected into the agent's context and can override system instructions. This is the tool poisoning attack documented by Invariant Labs.`,
2140
+ recommendation: "Review the flagged content manually. Remove any instructional language from tool descriptions. Tool descriptions should be purely descriptive, not prescriptive.",
2141
+ location: { file: file.path, line, snippet: match[0].substring(0, 80) },
2142
+ mitre_atlas: "AML.T0010.001",
2143
+ remediation_effort: "low"
2144
+ }));
2145
+ }
2146
+ }
2147
+ }
2148
+ return findings;
2149
+ }
2150
+ };
2151
+ exports2.mcpNoAuth = {
2152
+ id: "SF-SC-007",
2153
+ name: "MCP Server Lacks Authentication",
2154
+ description: "HTTP-transport MCP servers should require OAuth 2.0 or bearer token authentication.",
2155
+ category: "supply_chain",
2156
+ severity: "high",
2157
+ frameworks: "all",
2158
+ compliance: COMPLIANCE_SC,
2159
+ phase: "static",
2160
+ evaluate(ctx) {
2161
+ const findings = [];
2162
+ for (const agent of ctx.agents) {
2163
+ if (!agent.mcp_servers)
2164
+ continue;
2165
+ for (const server of agent.mcp_servers) {
2166
+ if (server.url && (server.url.startsWith("http://") || server.url.startsWith("https://"))) {
2167
+ const mcpConfigs = ctx.config_files.filter((f) => f.path.includes("mcp") || f.path.includes("settings"));
2168
+ const hasAuth = mcpConfigs.some((f) => f.content.includes("auth") || f.content.includes("token") || f.content.includes("bearer") || f.content.includes("oauth"));
2169
+ if (!hasAuth) {
2170
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2171
+ id: `${this.id}-${agent.id}-${server.name}`,
2172
+ title: `MCP server "${server.name}" has no authentication configured`,
2173
+ description: `HTTP-transport MCP server "${server.name}" (${server.url}) has no authentication. Unauthenticated MCP servers can be accessed by any process on the network.`,
2174
+ recommendation: "Configure OAuth 2.0 or bearer token authentication for all HTTP-transport MCP servers.",
2175
+ agent_id: agent.id,
2176
+ agent_name: agent.name,
2177
+ cwe: "CWE-306",
2178
+ remediation_effort: "medium"
2179
+ }));
2180
+ }
2181
+ }
2182
+ }
2183
+ }
2184
+ return findings;
2185
+ }
2186
+ };
2187
+ exports2.frameworkCVE = {
2188
+ id: "SF-SC-009",
2189
+ name: "Agent Framework Has Known Critical CVEs",
2190
+ description: "Check for usage of agent frameworks with documented critical vulnerabilities.",
2191
+ category: "supply_chain",
2192
+ severity: "critical",
2193
+ frameworks: ["langchain", "crewai", "autogen"],
2194
+ compliance: COMPLIANCE_SC,
2195
+ phase: "static",
2196
+ evaluate(ctx) {
2197
+ const findings = [];
2198
+ const KNOWN_VULNERABLE_PATTERNS = [
2199
+ { pattern: /langchain[<>=]*0\.0\./g, name: "LangChain 0.0.x", cves: ["CVE-2023-29374", "CVE-2023-36258", "CVE-2023-36188"] },
2200
+ { pattern: /allow_dangerous_deserialization\s*=\s*True/g, name: "LangChain unsafe deserialization", cves: ["CVE-2024-21511"] },
2201
+ { pattern: /load_chain|load_agent/g, name: "LangChain unsafe chain loading", cves: ["CVE-2023-44467"] }
2202
+ ];
2203
+ for (const file of ctx.config_files) {
2204
+ for (const { pattern, name, cves } of KNOWN_VULNERABLE_PATTERNS) {
2205
+ pattern.lastIndex = 0;
2206
+ if (pattern.test(file.content)) {
2207
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2208
+ id: `${this.id}-${findings.length}`,
2209
+ title: `${name} pattern detected in ${file.path}`,
2210
+ description: `Pattern associated with known critical vulnerabilities found. Related CVEs: ${cves.join(", ")}. These vulnerabilities enable arbitrary code execution via prompt injection chains.`,
2211
+ recommendation: "Update to the latest framework version. Replace deprecated APIs with safe alternatives.",
2212
+ location: { file: file.path },
2213
+ cve: cves,
2214
+ remediation_effort: "medium"
2215
+ }));
2216
+ }
2217
+ }
2218
+ }
2219
+ return findings;
2220
+ }
2221
+ };
2222
+ exports2.configInPublicRepo = {
2223
+ id: "SF-SC-008",
2224
+ name: "Agent Configuration Committed to Version Control",
2225
+ description: "Settings files containing MCP configs, hooks, or credentials committed to repository.",
2226
+ category: "supply_chain",
2227
+ severity: "medium",
2228
+ frameworks: "all",
2229
+ compliance: COMPLIANCE_SC,
2230
+ phase: "static",
2231
+ evaluate(ctx) {
2232
+ const findings = [];
2233
+ const fs = require("fs");
2234
+ const path = require("path");
2235
+ const gitignorePath = path.join(ctx.root_dir, ".gitignore");
2236
+ if (!fs.existsSync(gitignorePath))
2237
+ return findings;
2238
+ const gitignore = fs.readFileSync(gitignorePath, "utf-8");
2239
+ const sensitiveFiles = [
2240
+ ".claude/settings.local.json",
2241
+ ".mcp.json",
2242
+ ".env"
2243
+ ];
2244
+ for (const file of sensitiveFiles) {
2245
+ if (!gitignore.includes(file) && !gitignore.includes(file.replace("/", ""))) {
2246
+ const fullPath = path.join(ctx.root_dir, file);
2247
+ if (fs.existsSync(fullPath)) {
2248
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2249
+ id: `${this.id}-${findings.length}`,
2250
+ title: `Sensitive file "${file}" may be committed to version control`,
2251
+ description: `"${file}" exists but is not in .gitignore. Agent configuration files containing MCP server configs, local settings, or environment variables should not be committed to version control.`,
2252
+ recommendation: `Add "${file}" to .gitignore. If already committed, rotate any exposed credentials and use git filter-branch or BFG to remove from history.`,
2253
+ location: { file: ".gitignore" },
2254
+ cwe: "CWE-540",
2255
+ remediation_effort: "low"
2256
+ }));
2257
+ }
2258
+ }
2259
+ }
2260
+ return findings;
2261
+ }
2262
+ };
2263
+ exports2.langchainPassthrough = {
2264
+ id: "SF-SC-010",
2265
+ name: "LangChain: Unsanitized RunnablePassthrough to Tools",
2266
+ description: "LangChain chains using RunnablePassthrough forward user input directly to downstream tools without intermediate validation, amplifying prompt injection risk.",
2267
+ category: "supply_chain",
2268
+ severity: "high",
2269
+ frameworks: ["langchain"],
2270
+ compliance: [
2271
+ { framework: "OWASP_LLM_2025", reference: "LLM01", description: "Prompt Injection via passthrough" },
2272
+ { framework: "OWASP_LLM_2025", reference: "LLM05", description: "Improper Output Handling in chain" },
2273
+ { framework: "MITRE_ATLAS", reference: "AML.T0051", description: "LLM Prompt Injection" }
2274
+ ],
2275
+ phase: "static",
2276
+ lifecycle: "experimental",
2277
+ since: "0.2.0",
2278
+ auto_fix: {
2279
+ description: "Replace RunnablePassthrough() with a validation step that sanitizes user input before forwarding to tools.",
2280
+ suggested_config: "# Replace:\n# RunnablePassthrough()\n# With:\n# RunnableLambda(validate_and_sanitize)"
2281
+ },
2282
+ known_false_positives: [
2283
+ {
2284
+ condition: "RunnablePassthrough used for metadata forwarding in internal pipelines with no user-facing input",
2285
+ recommended_action: "Suppress with: # sentinelflow-ignore: SF-SC-010 -- Internal pipeline, no user input"
2286
+ }
2287
+ ],
2288
+ framework_compat: [{ framework: "langchain", min_version: "0.2.0" }],
2289
+ evaluate(ctx) {
2290
+ const findings = [];
2291
+ for (const file of ctx.config_files) {
2292
+ if (!file.path.endsWith(".py"))
2293
+ continue;
2294
+ if (!file.content.includes("langchain") && !file.content.includes("langgraph"))
2295
+ continue;
2296
+ const passthroughPattern = /RunnablePassthrough\s*\(\s*\)/g;
2297
+ let match;
2298
+ while ((match = passthroughPattern.exec(file.content)) !== null) {
2299
+ const surroundingLines = file.content.substring(Math.max(0, (match.index ?? 0) - 500), Math.min(file.content.length, (match.index ?? 0) + 500));
2300
+ const hasToolBinding = /\.bind_tools\(|Tool\(|@tool|BaseTool|StructuredTool/.test(surroundingLines);
2301
+ if (hasToolBinding) {
2302
+ const line = file.content.substring(0, match.index ?? 0).split("\n").length;
2303
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2304
+ id: `${this.id}-${findings.length}`,
2305
+ title: `Unsanitized passthrough to tool-bound chain in ${file.path}`,
2306
+ description: "RunnablePassthrough() forwards input directly to a chain that binds tools. User-controlled input reaches tool invocations without intermediate validation. This amplifies prompt injection risk \u2014 a malicious prompt can invoke tools with attacker-controlled arguments.",
2307
+ recommendation: "Replace RunnablePassthrough() with a RunnableLambda that validates and sanitizes input before forwarding. At minimum, strip special characters and enforce schema validation on tool arguments. See https://sentinelflow.dev/rules/SF-SC-010",
2308
+ location: { file: file.path, line, snippet: "RunnablePassthrough()" },
2309
+ cwe: "CWE-1427",
2310
+ cve: ["CVE-2025-68664"],
2311
+ remediation_effort: "medium",
2312
+ auto_fix: this.auto_fix
2313
+ }));
2314
+ }
2315
+ }
2316
+ }
2317
+ return findings;
2318
+ }
2319
+ };
2320
+ exports2.SUPPLY_CHAIN_RULES = [
2321
+ exports2.mcpNoIntegrity,
2322
+ exports2.toolDescriptionPoisoning,
2323
+ exports2.mcpNoAuth,
2324
+ exports2.frameworkCVE,
2325
+ exports2.configInPublicRepo,
2326
+ exports2.langchainPassthrough
2327
+ ];
2328
+ }
2329
+ });
2330
+
2331
+ // packages/scanner/dist/rules/data-governance.js
2332
+ var require_data_governance = __commonJS({
2333
+ "packages/scanner/dist/rules/data-governance.js"(exports2) {
2334
+ "use strict";
2335
+ Object.defineProperty(exports2, "__esModule", { value: true });
2336
+ exports2.DATA_GOVERNANCE_RULES = exports2.unrestrrictedFileWrite = exports2.crossEnvironmentAccess = exports2.noDataClassification = void 0;
2337
+ var interface_1 = require_interface();
2338
+ var COMPLIANCE_DG = [
2339
+ { framework: "GDPR", reference: "Article 25", description: "Data protection by design" },
2340
+ { framework: "EU_AI_ACT", reference: "Article 10", description: "Data and data governance" },
2341
+ { framework: "NIST_AI_RMF", reference: "MAP 3", description: "AI data governance" }
2342
+ ];
2343
+ exports2.noDataClassification = {
2344
+ id: "SF-DG-002",
2345
+ name: "No Data Classification on Agent Data Sources",
2346
+ description: "Agent accesses data sources without classification metadata, making it impossible to enforce data flow policies.",
2347
+ category: "data_governance",
2348
+ severity: "medium",
2349
+ frameworks: "all",
2350
+ compliance: COMPLIANCE_DG,
2351
+ phase: "static",
2352
+ evaluate(ctx) {
2353
+ return ctx.agents.filter((a) => a.data_sources.length > 0).filter((a) => a.data_sources.some((ds) => !ds.classification || ds.classification.length === 0)).map((agent) => (0, interface_1.createEnterpriseFinding)(this, {
2354
+ id: `${this.id}-${agent.id}`,
2355
+ title: `Agent "${agent.name}" accesses unclassified data sources`,
2356
+ description: `Agent "${agent.name}" accesses ${agent.data_sources.filter((ds) => !ds.classification || ds.classification.length === 0).length} data source(s) without classification metadata. Without classification, data flow policies cannot be enforced \u2014 PII could flow from a confidential source to a public channel.`,
2357
+ recommendation: "Add data classification metadata (pii, phi, financial, confidential, internal, public) to each data source in the agent configuration.",
2358
+ agent_id: agent.id,
2359
+ agent_name: agent.name,
2360
+ framework: agent.framework,
2361
+ remediation_effort: "medium"
2362
+ }));
2363
+ }
2364
+ };
2365
+ exports2.crossEnvironmentAccess = {
2366
+ id: "SF-DG-006",
2367
+ name: "Agent Bridges Production and Non-Production Environments",
2368
+ description: "Agents with tool access spanning production and development environments can copy sensitive data across boundaries.",
2369
+ category: "data_governance",
2370
+ severity: "high",
2371
+ frameworks: "all",
2372
+ compliance: [
2373
+ ...COMPLIANCE_DG,
2374
+ { framework: "SOC2", reference: "CC6.1", description: "Logical access security" }
2375
+ ],
2376
+ phase: "static",
2377
+ evaluate(ctx) {
2378
+ const findings = [];
2379
+ const prodIndicators = /\b(prod|production|live|main)\b/i;
2380
+ const devIndicators = /\b(dev|development|staging|test|sandbox|local)\b/i;
2381
+ for (const agent of ctx.agents) {
2382
+ if (!agent.mcp_servers || agent.mcp_servers.length < 2)
2383
+ continue;
2384
+ const hasProd = agent.mcp_servers.some((s) => prodIndicators.test(s.name) || s.url && prodIndicators.test(s.url));
2385
+ const hasDev = agent.mcp_servers.some((s) => devIndicators.test(s.name) || s.url && devIndicators.test(s.url));
2386
+ if (hasProd && hasDev) {
2387
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2388
+ id: `${this.id}-${agent.id}`,
2389
+ title: `Agent "${agent.name}" bridges production and non-production environments`,
2390
+ description: `Agent has MCP servers connecting to both production and development/staging environments. This creates a data path that bypasses environment isolation controls.`,
2391
+ recommendation: "Separate agents by environment. Use distinct agent configurations for production and non-production, with no cross-environment tool access.",
2392
+ agent_id: agent.id,
2393
+ agent_name: agent.name,
2394
+ framework: agent.framework,
2395
+ remediation_effort: "medium"
2396
+ }));
2397
+ }
2398
+ }
2399
+ return findings;
2400
+ }
2401
+ };
2402
+ exports2.unrestrrictedFileWrite = {
2403
+ id: "SF-DG-003",
2404
+ name: "Unrestricted File System Write Access",
2405
+ description: "Agent can write to the file system without path restrictions, creating uncontrolled data copies.",
2406
+ category: "data_governance",
2407
+ severity: "critical",
2408
+ frameworks: "all",
2409
+ compliance: [
2410
+ ...COMPLIANCE_DG,
2411
+ { framework: "HIPAA", reference: "\xA7164.312(a)(1)", description: "Access control" }
2412
+ ],
2413
+ phase: "static",
2414
+ evaluate(ctx) {
2415
+ return ctx.agents.filter((a) => a.tools.some((t) => t.type === "file_write")).filter((a) => !a.file_system_access?.write_paths || a.file_system_access.write_paths.length === 0).map((agent) => (0, interface_1.createEnterpriseFinding)(this, {
2416
+ id: `${this.id}-${agent.id}`,
2417
+ title: `Agent "${agent.name}" has unrestricted file system write access`,
2418
+ description: `Agent can write to any path with no restrictions. This enables overwriting configuration files, injecting code, creating unaudited data copies, or exfiltrating data to accessible filesystem locations.`,
2419
+ recommendation: "Define explicit write_paths restricting writes to the project directory. Block .env, .git/, .claude/, and system configuration paths.",
2420
+ agent_id: agent.id,
2421
+ agent_name: agent.name,
2422
+ framework: agent.framework,
2423
+ location: agent.source_file ? { file: agent.source_file } : void 0,
2424
+ cwe: "CWE-732",
2425
+ remediation_effort: "low"
2426
+ }));
2427
+ }
2428
+ };
2429
+ exports2.DATA_GOVERNANCE_RULES = [
2430
+ exports2.noDataClassification,
2431
+ exports2.crossEnvironmentAccess,
2432
+ exports2.unrestrrictedFileWrite
2433
+ ];
2434
+ }
2435
+ });
2436
+
2437
+ // packages/scanner/dist/rules/cost-governance.js
2438
+ var require_cost_governance = __commonJS({
2439
+ "packages/scanner/dist/rules/cost-governance.js"(exports2) {
2440
+ "use strict";
2441
+ Object.defineProperty(exports2, "__esModule", { value: true });
2442
+ exports2.COST_GOVERNANCE_RULES = exports2.noCostAttribution = exports2.expensiveModelForSimpleTasks = exports2.noTimeout = exports2.noIterationLimit = exports2.noTokenBudget = void 0;
2443
+ var interface_1 = require_interface();
2444
+ var COMPLIANCE_CG = [
2445
+ { framework: "OWASP_LLM_2025", reference: "LLM10", description: "Unbounded Consumption" },
2446
+ { framework: "NIST_AI_RMF", reference: "MANAGE 2", description: "Resource management" }
2447
+ ];
2448
+ exports2.noTokenBudget = {
2449
+ id: "SF-CG-001",
2450
+ name: "No Token Budget Configured",
2451
+ description: "Agent has no token or cost budget, allowing unlimited API consumption.",
2452
+ category: "cost_governance",
2453
+ severity: "medium",
2454
+ frameworks: "all",
2455
+ compliance: COMPLIANCE_CG,
2456
+ phase: "static",
2457
+ evaluate(ctx) {
2458
+ return ctx.agents.filter((a) => !a.governance.token_budget).map((agent) => (0, interface_1.createEnterpriseFinding)(this, {
2459
+ id: `${this.id}-${agent.id}`,
2460
+ title: `Agent "${agent.name}" has no cost budget`,
2461
+ description: `No token or cost budget defined. Runaway agent loops and unexpected usage spikes can result in thousands of dollars in API costs. Multiple documented incidents of AutoGPT instances accumulating $1,000+ in hours.`,
2462
+ recommendation: "Define token_budget with monthly_limit and daily_limit in the agent's governance configuration.",
2463
+ agent_id: agent.id,
2464
+ agent_name: agent.name,
2465
+ framework: agent.framework,
2466
+ remediation_effort: "low"
2467
+ }));
2468
+ }
2469
+ };
2470
+ exports2.noIterationLimit = {
2471
+ id: "SF-CG-002",
2472
+ name: "No Maximum Iteration Limit on Agent Loops",
2473
+ description: "Agent framework has no max_iterations or equivalent configured.",
2474
+ category: "cost_governance",
2475
+ severity: "high",
2476
+ frameworks: ["langchain", "crewai", "autogen"],
2477
+ compliance: COMPLIANCE_CG,
2478
+ phase: "static",
2479
+ evaluate(ctx) {
2480
+ const findings = [];
2481
+ const hasIterLimit = ctx.config_files.some((f) => f.content.includes("max_iterations") || f.content.includes("max_iter") || f.content.includes("maxIterations") || f.content.includes("max_turns") || f.content.includes("iteration_limit"));
2482
+ if (!hasIterLimit && ctx.agents.some((a) => ["langchain", "crewai", "autogen"].includes(a.framework))) {
2483
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2484
+ id: `${this.id}-global`,
2485
+ title: "No iteration limit detected for agent execution loops",
2486
+ description: "No max_iterations, max_turns, or equivalent configuration found. Without iteration limits, an agent in a reasoning loop can make unlimited API calls.",
2487
+ recommendation: "Set max_iterations in your agent framework configuration. Recommended: 10-25 for simple tasks, 50-100 for complex multi-step tasks.",
2488
+ remediation_effort: "low"
2489
+ }));
2490
+ }
2491
+ return findings;
2492
+ }
2493
+ };
2494
+ exports2.noTimeout = {
2495
+ id: "SF-CG-003",
2496
+ name: "No Circuit Breaker or Timeout Configured",
2497
+ description: "Agent tasks have no wall-clock time limits or deadman's switch.",
2498
+ category: "cost_governance",
2499
+ severity: "medium",
2500
+ frameworks: "all",
2501
+ compliance: COMPLIANCE_CG,
2502
+ phase: "static",
2503
+ evaluate(ctx) {
2504
+ const findings = [];
2505
+ const hasTimeout = ctx.config_files.some((f) => f.content.includes("timeout") || f.content.includes("max_time") || f.content.includes("deadline") || f.content.includes("circuit_breaker"));
2506
+ if (!hasTimeout && ctx.agents.length > 0) {
2507
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2508
+ id: `${this.id}-global`,
2509
+ title: "No timeout or circuit breaker configured for agent execution",
2510
+ description: "No timeout, deadline, or circuit breaker configuration detected. Agent tasks could run indefinitely consuming resources.",
2511
+ recommendation: "Configure timeouts for all agent tasks. Add hook-based deadman switches that require human confirmation after N minutes of continuous execution.",
2512
+ remediation_effort: "low"
2513
+ }));
2514
+ }
2515
+ return findings;
2516
+ }
2517
+ };
2518
+ exports2.expensiveModelForSimpleTasks = {
2519
+ id: "SF-CG-004",
2520
+ name: "Expensive Model Used Without Routing Strategy",
2521
+ description: "All agent tasks route to a single expensive model without tiered model routing.",
2522
+ category: "cost_governance",
2523
+ severity: "low",
2524
+ frameworks: "all",
2525
+ compliance: COMPLIANCE_CG,
2526
+ phase: "static",
2527
+ evaluate(ctx) {
2528
+ const findings = [];
2529
+ const expensiveModels = ["opus", "gpt-4", "gpt-4o", "claude-3-opus"];
2530
+ for (const agent of ctx.agents) {
2531
+ if (agent.model && expensiveModels.some((m) => agent.model.toLowerCase().includes(m))) {
2532
+ if (!agent.model_routing || agent.model_routing.length === 0) {
2533
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2534
+ id: `${this.id}-${agent.id}`,
2535
+ title: `Agent "${agent.name}" uses expensive model "${agent.model}" with no routing`,
2536
+ description: `All tasks route to ${agent.model} without tiered model routing. The cost difference between Opus/GPT-4 and Haiku/GPT-3.5 is 30-100x for simple tasks.`,
2537
+ recommendation: "Implement tiered model routing: simple tasks \u2192 Haiku/Flash, moderate tasks \u2192 Sonnet, complex tasks \u2192 Opus.",
2538
+ agent_id: agent.id,
2539
+ agent_name: agent.name,
2540
+ framework: agent.framework,
2541
+ remediation_effort: "medium"
2542
+ }));
2543
+ }
2544
+ }
2545
+ }
2546
+ return findings;
2547
+ }
2548
+ };
2549
+ exports2.noCostAttribution = {
2550
+ id: "SF-CG-007",
2551
+ name: "No Cost Attribution or Monitoring",
2552
+ description: "Agent API costs are not tagged by team, project, or task type.",
2553
+ category: "cost_governance",
2554
+ severity: "low",
2555
+ frameworks: "all",
2556
+ compliance: COMPLIANCE_CG,
2557
+ phase: "static",
2558
+ evaluate(ctx) {
2559
+ const findings = [];
2560
+ const hasCostTracking = ctx.config_files.some((f) => f.content.includes("cost") || f.content.includes("budget") || f.content.includes("billing") || f.content.includes("usage_tracking"));
2561
+ if (!hasCostTracking && ctx.agents.length > 0) {
2562
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2563
+ id: `${this.id}-global`,
2564
+ title: "No cost attribution or monitoring infrastructure detected",
2565
+ description: "No evidence of cost tracking, budget monitoring, or usage attribution found. Without attribution, cost overruns cannot be traced to specific agents or teams.",
2566
+ recommendation: "Configure SentinelFlow's cost governance module or integrate with your cloud provider's cost management tools. Tag API calls by agent, team, and project.",
2567
+ remediation_effort: "medium"
2568
+ }));
2569
+ }
2570
+ return findings;
2571
+ }
2572
+ };
2573
+ exports2.COST_GOVERNANCE_RULES = [
2574
+ exports2.noTokenBudget,
2575
+ exports2.noIterationLimit,
2576
+ exports2.noTimeout,
2577
+ exports2.expensiveModelForSimpleTasks,
2578
+ exports2.noCostAttribution
2579
+ ];
2580
+ }
2581
+ });
2582
+
2583
+ // packages/scanner/dist/rules/framework-config.js
2584
+ var require_framework_config = __commonJS({
2585
+ "packages/scanner/dist/rules/framework-config.js"(exports2) {
2586
+ "use strict";
2587
+ Object.defineProperty(exports2, "__esModule", { value: true });
2588
+ exports2.FRAMEWORK_CONFIG_RULES = exports2.cursorAlwaysApplyBroadGlob = exports2.codexFullAutoMode = exports2.noDescription = exports2.unrestrictedDelegation = exports2.unsafeDeserialization = exports2.gitHookBypass = exports2.wildcardBashPermissions = exports2.dangerouslySkipPermissions = void 0;
2589
+ var interface_1 = require_interface();
2590
+ var COMPLIANCE_FC = [
2591
+ { framework: "OWASP_LLM_2025", reference: "LLM06", description: "Excessive Agency" },
2592
+ { framework: "NIST_AI_RMF", reference: "GOVERN 1", description: "Governance policies" }
2593
+ ];
2594
+ exports2.dangerouslySkipPermissions = {
2595
+ id: "SF-FC-001",
2596
+ name: "Claude Code: Permission Checks Disabled",
2597
+ description: "The --dangerously-skip-permissions flag disables all interactive permission checks, allowing unrestricted system access.",
2598
+ category: "framework_config",
2599
+ severity: "critical",
2600
+ frameworks: ["claude-code"],
2601
+ compliance: COMPLIANCE_FC,
2602
+ phase: "static",
2603
+ // ─── Lifecycle (Phase 1.5) ────────────────────────────────
2604
+ lifecycle: "stable",
2605
+ since: "0.1.0",
2606
+ auto_fix: {
2607
+ description: "Remove dangerouslySkipPermissions from settings.json and use granular tool allowlists instead.",
2608
+ file_pattern: ".claude/settings.json",
2609
+ find: '"dangerouslySkipPermissions": true',
2610
+ replace: "",
2611
+ suggested_config: '{\n "allowedTools": [\n "Read",\n "Bash(npm test)",\n "Bash(npm run build)",\n "Bash(git diff)"\n ]\n}'
2612
+ },
2613
+ known_false_positives: [
2614
+ {
2615
+ condition: "CI/CD sandboxed environment with no production access, no secrets, and ephemeral compute",
2616
+ recommended_action: "Suppress with: # sentinelflow-ignore: SF-FC-001 -- CI sandbox per SEC-XXXX"
2617
+ },
2618
+ {
2619
+ condition: "Documentation or example files that reference the flag without actually enabling it",
2620
+ recommended_action: "Suppress with policy file: exclude docs/ and examples/ directories"
2621
+ }
2622
+ ],
2623
+ framework_compat: [
2624
+ { framework: "claude-code", min_version: "1.0.0" }
2625
+ ],
2626
+ evaluate(ctx) {
2627
+ const findings = [];
2628
+ for (const file of ctx.config_files) {
2629
+ if (file.content.includes("dangerously-skip-permissions") || file.content.includes("dangerouslySkipPermissions")) {
2630
+ const line = file.content.substring(0, file.content.indexOf("dangerously")).split("\n").length;
2631
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2632
+ id: `${this.id}-${findings.length}`,
2633
+ title: "--dangerously-skip-permissions detected",
2634
+ description: "The --dangerously-skip-permissions flag removes all interactive approval gates. The agent can execute arbitrary bash commands, modify any file, and access any network resource without user confirmation. Only acceptable in fully sandboxed CI/CD environments with no access to production systems.",
2635
+ recommendation: "Remove --dangerously-skip-permissions. Configure granular tool permissions in .claude/settings.json instead. Use PreToolUse hooks for approval workflows.",
2636
+ location: { file: file.path, line },
2637
+ cwe: "CWE-862",
2638
+ remediation_effort: "low"
2639
+ }));
2640
+ }
2641
+ }
2642
+ return findings;
2643
+ }
2644
+ };
2645
+ exports2.wildcardBashPermissions = {
2646
+ id: "SF-FC-002",
2647
+ name: "Claude Code: Wildcard Bash Permissions",
2648
+ description: 'Configurations with "Bash(*)" remove all command-specific approval gates.',
2649
+ category: "framework_config",
2650
+ severity: "high",
2651
+ frameworks: ["claude-code"],
2652
+ compliance: COMPLIANCE_FC,
2653
+ phase: "static",
2654
+ evaluate(ctx) {
2655
+ const findings = [];
2656
+ for (const file of ctx.config_files) {
2657
+ const patterns = [/Bash\(\*\)/g, /Bash\(true\)/gi, /"Bash"/g];
2658
+ for (const pattern of patterns) {
2659
+ pattern.lastIndex = 0;
2660
+ let match;
2661
+ while ((match = pattern.exec(file.content)) !== null) {
2662
+ if (match[0] === '"Bash"' && file.content.includes("allowedTools")) {
2663
+ const line = file.content.substring(0, match.index).split("\n").length;
2664
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2665
+ id: `${this.id}-${findings.length}`,
2666
+ title: `Unrestricted Bash access in ${file.path}`,
2667
+ description: 'Bash listed in allowedTools without command restrictions. This grants the agent ability to execute any shell command. Use granular permissions: "Bash(npm test)", "Bash(git diff)" instead of "Bash" or "Bash(*)".',
2668
+ recommendation: 'Replace unrestricted Bash access with specific command allowlists: "Bash(npm test)", "Bash(npm run build)", "Bash(git diff)".',
2669
+ location: { file: file.path, line, snippet: match[0] },
2670
+ remediation_effort: "low"
2671
+ }));
2672
+ }
2673
+ }
2674
+ }
2675
+ }
2676
+ return findings;
2677
+ }
2678
+ };
2679
+ exports2.gitHookBypass = {
2680
+ id: "SF-FC-003",
2681
+ name: "Git Safety Bypass Patterns",
2682
+ description: "Detects --no-verify, force push, and hook skip patterns that bypass git safety controls.",
2683
+ category: "framework_config",
2684
+ severity: "high",
2685
+ frameworks: "all",
2686
+ compliance: [
2687
+ ...COMPLIANCE_FC,
2688
+ { framework: "SOC2", reference: "CC8.1", description: "Change management" }
2689
+ ],
2690
+ phase: "static",
2691
+ evaluate(ctx) {
2692
+ const findings = [];
2693
+ const GIT_BYPASS = [
2694
+ { pattern: /--no-verify/g, name: "--no-verify flag", sev: "high" },
2695
+ { pattern: /--force-push|git\s+push\s+.*(?:--force|-f)\b/g, name: "force push", sev: "high" },
2696
+ { pattern: /GIT_HOOKS_SKIP|HUSKY\s*=\s*0/g, name: "hook skip env var", sev: "high" },
2697
+ { pattern: /pre-commit\s+.*(?:disable|skip|uninstall)/g, name: "pre-commit disable", sev: "medium" }
2698
+ ];
2699
+ for (const file of ctx.config_files) {
2700
+ for (const { pattern, name } of GIT_BYPASS) {
2701
+ pattern.lastIndex = 0;
2702
+ let match;
2703
+ while ((match = pattern.exec(file.content)) !== null) {
2704
+ const line = file.content.substring(0, match.index).split("\n").length;
2705
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2706
+ id: `${this.id}-${findings.length}`,
2707
+ title: `Git ${name} detected in ${file.path}`,
2708
+ description: `${name} at line ${line} allows bypassing git hooks that prevent committing secrets, enforce formatting, and run tests.`,
2709
+ recommendation: "Remove git hook bypass patterns. If agents need to commit, ensure they go through standard git hooks.",
2710
+ location: { file: file.path, line, snippet: match[0] },
2711
+ cwe: "CWE-693",
2712
+ remediation_effort: "low"
2713
+ }));
2714
+ }
2715
+ }
2716
+ }
2717
+ return findings;
2718
+ }
2719
+ };
2720
+ exports2.unsafeDeserialization = {
2721
+ id: "SF-FC-005",
2722
+ name: "Unsafe Deserialization Enabled",
2723
+ description: "Loading serialized agents/chains from untrusted sources with pickle format enables remote code execution.",
2724
+ category: "framework_config",
2725
+ severity: "critical",
2726
+ frameworks: ["langchain", "custom"],
2727
+ compliance: COMPLIANCE_FC,
2728
+ phase: "static",
2729
+ evaluate(ctx) {
2730
+ const findings = [];
2731
+ for (const file of ctx.config_files) {
2732
+ const patterns = [
2733
+ { pattern: /allow_dangerous_deserialization\s*=\s*True/g, name: "dangerous deserialization flag" },
2734
+ { pattern: /pickle\.load|torch\.load|joblib\.load/g, name: "unsafe deserialization call" },
2735
+ { pattern: /\.pkl\b|\.pickle\b|\.pt\b|\.pth\b/g, name: "pickle file reference" }
2736
+ ];
2737
+ for (const { pattern, name } of patterns) {
2738
+ pattern.lastIndex = 0;
2739
+ let match;
2740
+ while ((match = pattern.exec(file.content)) !== null) {
2741
+ const line = file.content.substring(0, match.index).split("\n").length;
2742
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2743
+ id: `${this.id}-${findings.length}`,
2744
+ title: `${name} in ${file.path}`,
2745
+ description: `Unsafe deserialization at line ${line}. Pickle-format files enable arbitrary code execution during loading. JFrog found ~100 malicious models on Hugging Face using this exact vector.`,
2746
+ recommendation: "Use safe serialization formats (safetensors, JSON, GGUF). Never load pickle files from untrusted sources. Set allow_dangerous_deserialization=False.",
2747
+ location: { file: file.path, line, snippet: match[0] },
2748
+ cwe: "CWE-502",
2749
+ cve: ["CVE-2024-21511"],
2750
+ remediation_effort: "medium"
2751
+ }));
2752
+ }
2753
+ }
2754
+ }
2755
+ return findings;
2756
+ }
2757
+ };
2758
+ exports2.unrestrictedDelegation = {
2759
+ id: "SF-FC-006",
2760
+ name: "Unrestricted Agent Delegation",
2761
+ description: "Agent can delegate tasks to any other agent without restrictions (CrewAI allow_delegation=True default).",
2762
+ category: "framework_config",
2763
+ severity: "medium",
2764
+ frameworks: ["crewai", "autogen", "custom"],
2765
+ compliance: COMPLIANCE_FC,
2766
+ phase: "static",
2767
+ evaluate(ctx) {
2768
+ const findings = [];
2769
+ for (const file of ctx.config_files) {
2770
+ if (file.content.includes("allow_delegation") && !file.content.includes("allow_delegation=False") && !file.content.includes("allow_delegation: false")) {
2771
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2772
+ id: `${this.id}-${findings.length}`,
2773
+ title: `Unrestricted delegation enabled in ${file.path}`,
2774
+ description: "Agent delegation is enabled without restrictions. Any agent can delegate to any other agent, enabling privilege escalation and prompt injection propagation.",
2775
+ recommendation: "Set allow_delegation=False by default. Enable selectively with explicit delegation targets and scope restrictions.",
2776
+ location: { file: file.path },
2777
+ remediation_effort: "low"
2778
+ }));
2779
+ }
2780
+ }
2781
+ return findings;
2782
+ }
2783
+ };
2784
+ exports2.noDescription = {
2785
+ id: "SF-FC-007",
2786
+ name: "Agent Has No Description or Purpose",
2787
+ description: "Agent lacks a description, making governance review and compliance documentation impossible.",
2788
+ category: "framework_config",
2789
+ severity: "low",
2790
+ frameworks: "all",
2791
+ compliance: [
2792
+ { framework: "ISO_42001", reference: "A.6.2", description: "AI system documentation" },
2793
+ { framework: "EU_AI_ACT", reference: "Article 11", description: "Technical documentation" }
2794
+ ],
2795
+ phase: "static",
2796
+ evaluate(ctx) {
2797
+ return ctx.agents.filter((a) => !a.description || a.description.trim() === "").map((agent) => (0, interface_1.createEnterpriseFinding)(this, {
2798
+ id: `${this.id}-${agent.id}`,
2799
+ title: `Agent "${agent.name}" has no description`,
2800
+ description: "Agent lacks a purpose statement. For governance and EU AI Act compliance, every agent needs clear documentation of what it does.",
2801
+ recommendation: "Add a description field to the agent definition.",
2802
+ agent_id: agent.id,
2803
+ agent_name: agent.name,
2804
+ framework: agent.framework,
2805
+ location: agent.source_file ? { file: agent.source_file } : void 0,
2806
+ remediation_effort: "low"
2807
+ }));
2808
+ }
2809
+ };
2810
+ exports2.codexFullAutoMode = {
2811
+ id: "SF-FC-008",
2812
+ name: "Codex CLI: Full-Auto Mode Enabled",
2813
+ description: "Codex CLI approval_mode is set to 'full-auto', granting the agent unrestricted code execution and file modification without human approval.",
2814
+ category: "framework_config",
2815
+ severity: "critical",
2816
+ frameworks: ["codex"],
2817
+ compliance: [
2818
+ { framework: "OWASP_LLM_2025", reference: "LLM06", description: "Excessive Agency \u2014 no approval gates" },
2819
+ { framework: "EU_AI_ACT", reference: "Article 14", description: "Human oversight requirement" },
2820
+ { framework: "NIST_AI_RMF", reference: "MANAGE 2.4", description: "Override mechanisms absent" }
2821
+ ],
2822
+ phase: "static",
2823
+ lifecycle: "stable",
2824
+ since: "0.2.0",
2825
+ auto_fix: {
2826
+ description: "Change approval_mode from 'full-auto' to 'suggest' or 'auto-edit' in .codex/config.toml.",
2827
+ find: 'approval_mode = "full-auto"',
2828
+ replace: 'approval_mode = "suggest"',
2829
+ suggested_config: 'model = "o4-mini"\napproval_mode = "suggest"'
2830
+ },
2831
+ known_false_positives: [
2832
+ {
2833
+ condition: "Sandboxed CI/CD environment with ephemeral compute and no production access",
2834
+ recommended_action: "Suppress with: # sentinelflow-ignore: SF-FC-008 -- CI sandbox per SEC-XXXX"
2835
+ }
2836
+ ],
2837
+ framework_compat: [{ framework: "codex", min_version: "0.1.0" }],
2838
+ evaluate(ctx) {
2839
+ const findings = [];
2840
+ for (const file of ctx.config_files) {
2841
+ if (!file.path.includes(".codex") && !file.path.endsWith("config.toml"))
2842
+ continue;
2843
+ const autoMatch = file.content.match(/approval_mode\s*=\s*["']full-auto["']/);
2844
+ if (autoMatch) {
2845
+ const line = file.content.substring(0, autoMatch.index ?? 0).split("\n").length;
2846
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2847
+ id: `${this.id}-${findings.length}`,
2848
+ title: "Codex CLI running in full-auto mode",
2849
+ description: "The approval_mode is set to 'full-auto' in .codex/config.toml. This grants the Codex agent unrestricted ability to execute code, modify files, and run shell commands without any human confirmation. This is equivalent to Claude Code's --dangerously-skip-permissions flag.",
2850
+ recommendation: 'Change approval_mode to "suggest" (safest) or "auto-edit" (allows file edits but requires approval for shell commands) in .codex/config.toml. See https://sentinelflow.dev/rules/SF-FC-008',
2851
+ location: { file: file.path, line, snippet: 'approval_mode = "full-auto"' },
2852
+ cwe: "CWE-862",
2853
+ remediation_effort: "low",
2854
+ auto_fix: this.auto_fix
2855
+ }));
2856
+ }
2857
+ }
2858
+ return findings;
2859
+ }
2860
+ };
2861
+ exports2.cursorAlwaysApplyBroadGlob = {
2862
+ id: "SF-FC-009",
2863
+ name: "Cursor: AlwaysApply Rule with Broad Glob Pattern",
2864
+ description: "A Cursor .mdc rule file uses alwaysApply: true with a broad glob pattern (e.g., '**/*'), silently injecting instructions into every agent interaction without user review.",
2865
+ category: "framework_config",
2866
+ severity: "high",
2867
+ frameworks: ["cursor"],
2868
+ compliance: [
2869
+ { framework: "OWASP_LLM_2025", reference: "LLM01", description: "Prompt Injection via auto-applied rules" },
2870
+ { framework: "OWASP_LLM_2025", reference: "LLM06", description: "Excessive Agency \u2014 unreviewed rule injection" },
2871
+ { framework: "EU_AI_ACT", reference: "Article 14", description: "Human oversight bypassed by auto-apply" }
2872
+ ],
2873
+ phase: "static",
2874
+ lifecycle: "stable",
2875
+ since: "0.2.0",
2876
+ auto_fix: {
2877
+ description: "Change alwaysApply to false and use specific glob patterns instead of **/*.",
2878
+ find: "alwaysApply: true",
2879
+ replace: "alwaysApply: false"
2880
+ },
2881
+ known_false_positives: [
2882
+ {
2883
+ condition: "Project-wide coding standards (formatting, language preferences) that are intentionally global",
2884
+ recommended_action: "Suppress with: # sentinelflow-ignore: SF-FC-009 -- Global coding standard, reviewed by team"
2885
+ }
2886
+ ],
2887
+ framework_compat: [{ framework: "cursor" }],
2888
+ evaluate(ctx) {
2889
+ const findings = [];
2890
+ for (const file of ctx.config_files) {
2891
+ if (!file.path.endsWith(".mdc"))
2892
+ continue;
2893
+ const hasAlwaysApply = /alwaysApply:\s*true/i.test(file.content);
2894
+ const hasBroadGlob = /globs:\s*["']?\*\*\/?\*["']?/i.test(file.content) || /globs:\s*["']?\*["']?\s*$/m.test(file.content);
2895
+ if (hasAlwaysApply && hasBroadGlob) {
2896
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2897
+ id: `${this.id}-${findings.length}`,
2898
+ title: `Cursor rule auto-applies to all files: ${file.path}`,
2899
+ description: "This .mdc rule uses alwaysApply: true with a glob pattern matching all files. The rule's instructions are silently injected into every Cursor interaction without user review. A malicious or overly broad rule here effectively becomes a persistent prompt injection vector \u2014 especially dangerous in shared repos.",
2900
+ recommendation: "Set alwaysApply: false and use specific glob patterns (e.g., '**/*.ts' instead of '**/*'). Review the rule content for any tool access or behavioral instructions that should require explicit user activation. See https://sentinelflow.dev/rules/SF-FC-009",
2901
+ location: { file: file.path, snippet: "alwaysApply: true" },
2902
+ remediation_effort: "low",
2903
+ auto_fix: this.auto_fix
2904
+ }));
2905
+ }
2906
+ }
2907
+ return findings;
2908
+ }
2909
+ };
2910
+ exports2.FRAMEWORK_CONFIG_RULES = [
2911
+ exports2.dangerouslySkipPermissions,
2912
+ exports2.wildcardBashPermissions,
2913
+ exports2.gitHookBypass,
2914
+ exports2.unsafeDeserialization,
2915
+ exports2.unrestrictedDelegation,
2916
+ exports2.noDescription,
2917
+ exports2.codexFullAutoMode,
2918
+ exports2.cursorAlwaysApplyBroadGlob
2919
+ ];
2920
+ }
2921
+ });
2922
+
2923
+ // packages/scanner/dist/rules/multi-agent.js
2924
+ var require_multi_agent = __commonJS({
2925
+ "packages/scanner/dist/rules/multi-agent.js"(exports2) {
2926
+ "use strict";
2927
+ Object.defineProperty(exports2, "__esModule", { value: true });
2928
+ exports2.MULTI_AGENT_RULES = exports2.multiFrameworkConfigDrift = exports2.crewaiHierarchicalNoLimits = exports2.noOutputValidationBetweenAgents = exports2.privilegeEscalationViaDelegate = exports2.noDelegationDepthLimit = void 0;
2929
+ var interface_1 = require_interface();
2930
+ var COMPLIANCE_MA = [
2931
+ { framework: "OWASP_LLM_2025", reference: "LLM06", description: "Excessive Agency" },
2932
+ { framework: "MITRE_ATLAS", reference: "AML.T0051.002", description: "Indirect prompt injection" },
2933
+ { framework: "NIST_AI_RMF", reference: "MANAGE 1", description: "AI risk management" }
2934
+ ];
2935
+ exports2.noDelegationDepthLimit = {
2936
+ id: "SF-MA-001",
2937
+ name: "Delegation Chain Has No Depth Limit",
2938
+ description: "Multi-agent delegation with no maximum hop count enables infinite loops and authorization erosion.",
2939
+ category: "multi_agent",
2940
+ severity: "high",
2941
+ frameworks: "all",
2942
+ compliance: COMPLIANCE_MA,
2943
+ phase: "static",
2944
+ evaluate(ctx) {
2945
+ const findings = [];
2946
+ const hasMultiAgent = ctx.agents.some((a) => a.delegates_to && a.delegates_to.length > 0) || ctx.agents.filter((a) => a.swarm_role !== "standalone").length > 1;
2947
+ if (hasMultiAgent) {
2948
+ const hasDepthLimit = ctx.config_files.some((f) => f.content.includes("max_depth") || f.content.includes("max_hops") || f.content.includes("delegation_limit") || f.content.includes("ttl"));
2949
+ if (!hasDepthLimit) {
2950
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2951
+ id: `${this.id}-global`,
2952
+ title: "Multi-agent delegation chain has no depth limit",
2953
+ description: "Multiple agents with delegation relationships detected but no maximum delegation depth configured. Each hop may lose authorization context, and unbounded chains enable infinite loops.",
2954
+ recommendation: "Configure a maximum delegation depth (recommended: 3-5 hops). Implement TTL counters that decrement with each delegation step.",
2955
+ remediation_effort: "medium"
2956
+ }));
2957
+ }
2958
+ }
2959
+ return findings;
2960
+ }
2961
+ };
2962
+ exports2.privilegeEscalationViaDelegate = {
2963
+ id: "SF-MA-003",
2964
+ name: "Delegation to Higher-Privilege Agent",
2965
+ description: "An agent delegates to another agent with broader tool access or data permissions.",
2966
+ category: "multi_agent",
2967
+ severity: "high",
2968
+ frameworks: "all",
2969
+ compliance: COMPLIANCE_MA,
2970
+ phase: "static",
2971
+ evaluate(ctx) {
2972
+ const findings = [];
2973
+ const agentMap = new Map(ctx.agents.map((a) => [a.id, a]));
2974
+ for (const agent of ctx.agents) {
2975
+ if (!agent.delegates_to)
2976
+ continue;
2977
+ for (const delegateId of agent.delegates_to) {
2978
+ const delegate = agentMap.get(delegateId);
2979
+ if (!delegate)
2980
+ continue;
2981
+ if (delegate.tools.length > agent.tools.length) {
2982
+ const extraTools = delegate.tools.filter((dt) => !agent.tools.some((at) => at.name === dt.name));
2983
+ if (extraTools.some((t) => t.risk_level === "high" || t.risk_level === "critical")) {
2984
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
2985
+ id: `${this.id}-${agent.id}-${delegateId}`,
2986
+ title: `"${agent.name}" delegates to higher-privilege "${delegate.name}"`,
2987
+ description: `Agent "${agent.name}" can delegate to "${delegate.name}" which has access to high-risk tools not available to the delegating agent: ${extraTools.map((t) => t.name).join(", ")}. This is a confused deputy vulnerability.`,
2988
+ recommendation: "Ensure delegated agents have equal or lesser permissions than the delegator. Implement authorization checks that propagate the original user's scope.",
2989
+ agent_id: agent.id,
2990
+ agent_name: agent.name,
2991
+ cwe: "CWE-441",
2992
+ remediation_effort: "medium"
2993
+ }));
2994
+ }
2995
+ }
2996
+ }
2997
+ }
2998
+ return findings;
2999
+ }
3000
+ };
3001
+ exports2.noOutputValidationBetweenAgents = {
3002
+ id: "SF-MA-006",
3003
+ name: "No Output Validation Between Agent Steps",
3004
+ description: "In multi-agent workflows, one agent's output becomes the next agent's input without validation, enabling prompt injection cascades.",
3005
+ category: "multi_agent",
3006
+ severity: "medium",
3007
+ frameworks: "all",
3008
+ compliance: COMPLIANCE_MA,
3009
+ phase: "static",
3010
+ evaluate(ctx) {
3011
+ const findings = [];
3012
+ const multiAgentCount = ctx.agents.filter((a) => a.swarm_role !== "standalone").length;
3013
+ if (multiAgentCount >= 2) {
3014
+ const hasValidation = ctx.config_files.some((f) => f.content.includes("output_validator") || f.content.includes("output_parser") || f.content.includes("validate_output") || f.content.includes("schema_validation"));
3015
+ if (!hasValidation) {
3016
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
3017
+ id: `${this.id}-global`,
3018
+ title: `${multiAgentCount} agents in pipeline with no inter-agent output validation`,
3019
+ description: "Multi-agent workflow detected but no output validation between agent steps. A prompt injection in one agent's output will propagate to downstream agents \u2014 the 'prompt infection' vector demonstrated in 2024 research.",
3020
+ recommendation: "Add schema validation or output sanitization between each agent step. Use structured output formats (JSON Schema, Pydantic) rather than passing raw text.",
3021
+ remediation_effort: "high"
3022
+ }));
3023
+ }
3024
+ }
3025
+ return findings;
3026
+ }
3027
+ };
3028
+ exports2.crewaiHierarchicalNoLimits = {
3029
+ id: "SF-MA-007",
3030
+ name: "CrewAI: Hierarchical Process Without Delegation Limits",
3031
+ description: "CrewAI crew uses hierarchical process with a manager agent that can delegate tasks to workers, who may further delegate \u2014 creating recursive chains without depth limits.",
3032
+ category: "multi_agent",
3033
+ severity: "high",
3034
+ frameworks: ["crewai"],
3035
+ compliance: [
3036
+ { framework: "OWASP_LLM_2025", reference: "LLM06", description: "Excessive Agency via hierarchical delegation" },
3037
+ { framework: "OWASP_LLM_2025", reference: "LLM10", description: "Unbounded Consumption from recursive delegation" },
3038
+ { framework: "EU_AI_ACT", reference: "Article 9", description: "Risk management for multi-agent systems" }
3039
+ ],
3040
+ phase: "static",
3041
+ lifecycle: "stable",
3042
+ since: "0.2.0",
3043
+ auto_fix: {
3044
+ description: "Add max_delegation_depth or set allow_delegation: false on worker agents in crew.yaml.",
3045
+ suggested_config: "researcher:\n role: Senior Researcher\n allow_delegation: false # Prevent recursive delegation"
3046
+ },
3047
+ known_false_positives: [
3048
+ {
3049
+ condition: "Small crews (2-3 agents) where delegation is intentionally flat",
3050
+ recommended_action: "Suppress with: # sentinelflow-ignore: SF-MA-007 -- Flat crew, 2 agents only"
3051
+ }
3052
+ ],
3053
+ framework_compat: [{ framework: "crewai", min_version: "0.30.0" }],
3054
+ evaluate(ctx) {
3055
+ const findings = [];
3056
+ for (const file of ctx.config_files) {
3057
+ if (!file.path.match(/crew\.ya?ml$|agents\.ya?ml$/))
3058
+ continue;
3059
+ const hasHierarchical = /process:\s*["']?hierarchical["']?/i.test(file.content);
3060
+ if (!hasHierarchical)
3061
+ continue;
3062
+ const delegatingAgents = ctx.agents.filter((a) => a.framework === "crewai" && a.delegates_to && a.delegates_to.length > 0);
3063
+ if (delegatingAgents.length > 0) {
3064
+ const line = file.content.substring(0, file.content.indexOf("hierarchical")).split("\n").length;
3065
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
3066
+ id: `${this.id}-${findings.length}`,
3067
+ title: "CrewAI hierarchical process with unrestricted delegation",
3068
+ description: `Crew uses hierarchical process with ${delegatingAgents.length} agent(s) that can delegate (${delegatingAgents.map((a) => a.name).join(", ")}). In hierarchical mode, the manager agent delegates to workers who may further delegate, creating recursive chains that consume tokens unboundedly and can escalate privileges through delegation.`,
3069
+ recommendation: "Set allow_delegation: false on worker agents that should not sub-delegate. Only the manager agent should have delegation privileges in a hierarchical crew. See https://sentinelflow.dev/rules/SF-MA-007",
3070
+ location: { file: file.path, line, snippet: "process: hierarchical" },
3071
+ remediation_effort: "low",
3072
+ auto_fix: this.auto_fix
3073
+ }));
3074
+ }
3075
+ }
3076
+ return findings;
3077
+ }
3078
+ };
3079
+ exports2.multiFrameworkConfigDrift = {
3080
+ id: "SF-MA-008",
3081
+ name: "Multi-Framework: Permission Scope Divergence",
3082
+ description: "Multiple agent frameworks configured in the same repository have divergent permission scopes \u2014 the least restrictive configuration determines actual risk.",
3083
+ category: "multi_agent",
3084
+ severity: "medium",
3085
+ frameworks: "all",
3086
+ compliance: [
3087
+ { framework: "OWASP_LLM_2025", reference: "LLM06", description: "Excessive Agency via inconsistent permissions" },
3088
+ { framework: "EU_AI_ACT", reference: "Article 15", description: "Consistent cybersecurity measures" },
3089
+ { framework: "NIST_AI_RMF", reference: "GOVERN 1.1", description: "Uniform governance policies" }
3090
+ ],
3091
+ phase: "static",
3092
+ lifecycle: "experimental",
3093
+ since: "0.2.0",
3094
+ known_false_positives: [
3095
+ {
3096
+ condition: "Intentionally different permission scopes for different frameworks (e.g., Cursor for review-only, Claude Code for development)",
3097
+ recommended_action: "Suppress with: # sentinelflow-ignore: SF-MA-008 -- Intentional scope difference documented in SECURITY.md"
3098
+ }
3099
+ ],
3100
+ evaluate(ctx) {
3101
+ const findings = [];
3102
+ const frameworkSet = new Set(ctx.agents.map((a) => a.framework));
3103
+ if (frameworkSet.size < 2)
3104
+ return findings;
3105
+ const frameworkToolRisks = /* @__PURE__ */ new Map();
3106
+ for (const agent of ctx.agents) {
3107
+ if (!frameworkToolRisks.has(agent.framework)) {
3108
+ frameworkToolRisks.set(agent.framework, /* @__PURE__ */ new Set());
3109
+ }
3110
+ const riskSet = frameworkToolRisks.get(agent.framework);
3111
+ for (const tool of agent.tools) {
3112
+ if (tool.risk_level === "high")
3113
+ riskSet.add(tool.name);
3114
+ }
3115
+ }
3116
+ const withHighRisk = [];
3117
+ const withoutHighRisk = [];
3118
+ for (const [fw, risks] of frameworkToolRisks) {
3119
+ if (risks.size > 0)
3120
+ withHighRisk.push(fw);
3121
+ else
3122
+ withoutHighRisk.push(fw);
3123
+ }
3124
+ if (withHighRisk.length > 0 && withoutHighRisk.length > 0) {
3125
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
3126
+ id: `${this.id}-0`,
3127
+ title: "Permission scope divergence across frameworks",
3128
+ description: `This project configures ${frameworkSet.size} agent frameworks with different permission levels. ${withHighRisk.join(", ")} grant(s) high-risk tool access (bash, shell, code execution) while ${withoutHighRisk.join(", ")} do(es) not. The least restrictive configuration determines the project's actual risk surface.`,
3129
+ recommendation: "Align permission scopes across all configured frameworks to the principle of least privilege. If different scopes are intentional, document the rationale in SECURITY.md and suppress this finding. See https://sentinelflow.dev/rules/SF-MA-008",
3130
+ remediation_effort: "medium"
3131
+ }));
3132
+ }
3133
+ return findings;
3134
+ }
3135
+ };
3136
+ exports2.MULTI_AGENT_RULES = [
3137
+ exports2.noDelegationDepthLimit,
3138
+ exports2.privilegeEscalationViaDelegate,
3139
+ exports2.noOutputValidationBetweenAgents,
3140
+ exports2.crewaiHierarchicalNoLimits,
3141
+ exports2.multiFrameworkConfigDrift
3142
+ ];
3143
+ }
3144
+ });
3145
+
3146
+ // packages/scanner/dist/rules/audit-logging.js
3147
+ var require_audit_logging = __commonJS({
3148
+ "packages/scanner/dist/rules/audit-logging.js"(exports2) {
3149
+ "use strict";
3150
+ Object.defineProperty(exports2, "__esModule", { value: true });
3151
+ exports2.AUDIT_LOGGING_RULES = exports2.sensitiveDataInLogs = exports2.noSIEMIntegration = exports2.noAuditLogging = void 0;
3152
+ var interface_1 = require_interface();
3153
+ var COMPLIANCE_AL = [
3154
+ { framework: "EU_AI_ACT", reference: "Article 12", description: "Record-keeping / automatic logging" },
3155
+ { framework: "SOC2", reference: "CC7.1", description: "Detection of configuration changes" },
3156
+ { framework: "HIPAA", reference: "\xA7164.312(b)", description: "Audit controls" }
3157
+ ];
3158
+ exports2.noAuditLogging = {
3159
+ id: "SF-AL-001",
3160
+ name: "Agent Actions Not Logged",
3161
+ description: "No evidence of audit logging for agent tool invocations, decisions, or data access.",
3162
+ category: "audit_logging",
3163
+ severity: "high",
3164
+ frameworks: "all",
3165
+ compliance: COMPLIANCE_AL,
3166
+ phase: "static",
3167
+ evaluate(ctx) {
3168
+ const findings = [];
3169
+ const hasLogging = ctx.config_files.some((f) => f.content.includes("log") || f.content.includes("audit") || f.content.includes("observe") || f.content.includes("trace") || f.content.includes("PostToolUse") || f.content.includes("sentinelflow") || f.content.includes("langfuse") || f.content.includes("langsmith") || f.content.includes("arize") || f.content.includes("helicone"));
3170
+ if (!hasLogging && ctx.agents.length > 0) {
3171
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
3172
+ id: `${this.id}-global`,
3173
+ title: "No audit logging detected for agent actions",
3174
+ description: "No evidence of audit logging, observability tools, or PostToolUse hooks found. EU AI Act Article 12 requires automatic recording of events throughout an AI system's lifetime with minimum 6-month retention. Without logging, incident response and compliance auditing are impossible.",
3175
+ recommendation: "Integrate an observability platform (SentinelFlow interceptors, Langfuse, LangSmith) or add PostToolUse hooks that log every tool invocation with agent identity, action type, input/output summary, and timestamp.",
3176
+ remediation_effort: "medium"
3177
+ }));
3178
+ }
3179
+ return findings;
3180
+ }
3181
+ };
3182
+ exports2.noSIEMIntegration = {
3183
+ id: "SF-AL-004",
3184
+ name: "Agent Logs Not Integrated with SIEM",
3185
+ description: "Agent audit logs should stream to centralized security monitoring for correlation with other security events.",
3186
+ category: "audit_logging",
3187
+ severity: "medium",
3188
+ frameworks: "all",
3189
+ compliance: [
3190
+ ...COMPLIANCE_AL,
3191
+ { framework: "SOC2", reference: "CC7.2", description: "Activity monitoring" }
3192
+ ],
3193
+ phase: "static",
3194
+ evaluate(ctx) {
3195
+ const findings = [];
3196
+ const hasSIEM = ctx.config_files.some((f) => f.content.includes("sentinel") || f.content.includes("splunk") || f.content.includes("datadog") || f.content.includes("siem") || f.content.includes("elastic") || f.content.includes("chronicle"));
3197
+ if (!hasSIEM && ctx.agents.length > 0) {
3198
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
3199
+ id: `${this.id}-global`,
3200
+ title: "Agent logs not connected to SIEM/SOAR platform",
3201
+ description: "No evidence of SIEM integration (Microsoft Sentinel, Splunk, Datadog, etc.). Isolated agent logs miss attack patterns spanning multiple systems.",
3202
+ recommendation: "Stream agent telemetry to your centralized SIEM. SentinelFlow's interceptors support webhook output to Splunk, Datadog, and Microsoft Sentinel.",
3203
+ remediation_effort: "medium"
3204
+ }));
3205
+ }
3206
+ return findings;
3207
+ }
3208
+ };
3209
+ exports2.sensitiveDataInLogs = {
3210
+ id: "SF-AL-006",
3211
+ name: "Sensitive Data May Appear in Agent Logs",
3212
+ description: "Agent traces and conversation logs may contain PII, credentials, or classified data without redaction.",
3213
+ category: "audit_logging",
3214
+ severity: "medium",
3215
+ frameworks: "all",
3216
+ compliance: [
3217
+ ...COMPLIANCE_AL,
3218
+ { framework: "GDPR", reference: "Article 5(1)(c)", description: "Data minimisation" }
3219
+ ],
3220
+ phase: "static",
3221
+ evaluate(ctx) {
3222
+ const findings = [];
3223
+ const hasLogRedaction = ctx.config_files.some((f) => f.content.includes("redact") || f.content.includes("sanitize") || f.content.includes("mask") || f.content.includes("scrub") || f.content.includes("pii_filter"));
3224
+ const handlesData = ctx.agents.some((a) => a.data_classification?.some((dc) => ["pii", "phi", "financial"].includes(dc)) || a.data_sources.some((ds) => ds.classification?.some((c) => ["pii", "phi", "financial"].includes(c))));
3225
+ if (!hasLogRedaction && handlesData) {
3226
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
3227
+ id: `${this.id}-global`,
3228
+ title: "Agents handling sensitive data with no log redaction configured",
3229
+ description: "Agents access PII/PHI/financial data but no log sanitization or PII redaction is configured. Agent traces may contain the sensitive data itself, creating uncontrolled data copies in log storage.",
3230
+ recommendation: "Configure PII scrubbing in your logging pipeline. SentinelFlow interceptors include automatic PII detection and redaction in telemetry events.",
3231
+ remediation_effort: "medium"
3232
+ }));
3233
+ }
3234
+ return findings;
3235
+ }
3236
+ };
3237
+ exports2.AUDIT_LOGGING_RULES = [
3238
+ exports2.noAuditLogging,
3239
+ exports2.noSIEMIntegration,
3240
+ exports2.sensitiveDataInLogs
3241
+ ];
3242
+ }
3243
+ });
3244
+
3245
+ // packages/scanner/dist/rules/compliance-docs.js
3246
+ var require_compliance_docs = __commonJS({
3247
+ "packages/scanner/dist/rules/compliance-docs.js"(exports2) {
3248
+ "use strict";
3249
+ var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
3250
+ if (k2 === void 0) k2 = k;
3251
+ var desc = Object.getOwnPropertyDescriptor(m, k);
3252
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
3253
+ desc = { enumerable: true, get: function() {
3254
+ return m[k];
3255
+ } };
3256
+ }
3257
+ Object.defineProperty(o, k2, desc);
3258
+ }) : (function(o, m, k, k2) {
3259
+ if (k2 === void 0) k2 = k;
3260
+ o[k2] = m[k];
3261
+ }));
3262
+ var __setModuleDefault2 = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
3263
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
3264
+ }) : function(o, v) {
3265
+ o["default"] = v;
3266
+ });
3267
+ var __importStar2 = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
3268
+ var ownKeys = function(o) {
3269
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
3270
+ var ar = [];
3271
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
3272
+ return ar;
3273
+ };
3274
+ return ownKeys(o);
3275
+ };
3276
+ return function(mod) {
3277
+ if (mod && mod.__esModule) return mod;
3278
+ var result = {};
3279
+ if (mod != null) {
3280
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding2(result, mod, k[i]);
3281
+ }
3282
+ __setModuleDefault2(result, mod);
3283
+ return result;
3284
+ };
3285
+ })();
3286
+ Object.defineProperty(exports2, "__esModule", { value: true });
3287
+ exports2.COMPLIANCE_DOCS_RULES = exports2.noIncidentResponsePlan = exports2.noHumanOversightDocs = exports2.noTechnicalDocumentation = exports2.noRiskAssessment = void 0;
3288
+ var fs = __importStar2(require("fs"));
3289
+ var path = __importStar2(require("path"));
3290
+ var interface_1 = require_interface();
3291
+ var COMPLIANCE_CD = [
3292
+ { framework: "EU_AI_ACT", reference: "Articles 9-15", description: "High-risk AI system requirements" },
3293
+ { framework: "ISO_42001", reference: "Clause 6-10", description: "AI management system requirements" },
3294
+ { framework: "NIST_AI_RMF", reference: "GOVERN", description: "AI governance documentation" }
3295
+ ];
3296
+ exports2.noRiskAssessment = {
3297
+ id: "SF-CD-001",
3298
+ name: "No AI Risk Assessment Documented",
3299
+ description: "EU AI Act Article 9 requires continuous risk assessment for high-risk AI systems.",
3300
+ category: "compliance_docs",
3301
+ severity: "medium",
3302
+ frameworks: "all",
3303
+ compliance: COMPLIANCE_CD,
3304
+ phase: "static",
3305
+ evaluate(ctx) {
3306
+ const findings = [];
3307
+ const riskDocPatterns = [
3308
+ "risk_assessment",
3309
+ "risk-assessment",
3310
+ "ai_risk",
3311
+ "risk_register",
3312
+ "threat_model",
3313
+ "threat-model",
3314
+ "risk_analysis"
3315
+ ];
3316
+ const hasRiskDoc = ctx.config_files.some((f) => riskDocPatterns.some((p) => f.content.toLowerCase().includes(p)));
3317
+ const riskFiles = ["RISK_ASSESSMENT.md", "risk-assessment.md", "AI_RISK.md", "threat-model.md"];
3318
+ const hasRiskFile = riskFiles.some((f) => fs.existsSync(path.join(ctx.root_dir, f)));
3319
+ if (!hasRiskDoc && !hasRiskFile && ctx.agents.length > 0) {
3320
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
3321
+ id: `${this.id}-global`,
3322
+ title: "No AI risk assessment documentation found",
3323
+ description: "No risk assessment, threat model, or risk register found for the AI agents in this project. EU AI Act Article 9 requires continuous risk assessment and management for high-risk AI systems. ISO 42001 Clause 6.1.2 requires AI-specific risk assessment. Enforcement deadline: August 2, 2026.",
3324
+ recommendation: "Create a risk assessment document covering: agent capabilities, data access, potential failure modes, and mitigation measures. Use NIST AI RMF's MAP-MEASURE-MANAGE lifecycle as a framework.",
3325
+ remediation_effort: "high"
3326
+ }));
3327
+ }
3328
+ return findings;
3329
+ }
3330
+ };
3331
+ exports2.noTechnicalDocumentation = {
3332
+ id: "SF-CD-002",
3333
+ name: "No Technical Documentation for AI System",
3334
+ description: "EU AI Act Article 11 requires comprehensive technical documentation before deployment.",
3335
+ category: "compliance_docs",
3336
+ severity: "medium",
3337
+ frameworks: "all",
3338
+ compliance: COMPLIANCE_CD,
3339
+ phase: "static",
3340
+ evaluate(ctx) {
3341
+ const findings = [];
3342
+ const hasAgentsMd = fs.existsSync(path.join(ctx.root_dir, "AGENTS.md"));
3343
+ const hasClaudeMd = fs.existsSync(path.join(ctx.root_dir, "CLAUDE.md"));
3344
+ const hasArchDoc = ["ARCHITECTURE.md", "docs/architecture.md", "docs/ARCHITECTURE.md", "SYSTEM_DESIGN.md"].some((f) => fs.existsSync(path.join(ctx.root_dir, f)));
3345
+ if (!hasAgentsMd && !hasArchDoc && ctx.agents.length > 0) {
3346
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
3347
+ id: `${this.id}-global`,
3348
+ title: "No technical documentation found for the AI agent system",
3349
+ description: "No AGENTS.md, ARCHITECTURE.md, or system design documentation found. EU AI Act Article 11 and Annex IV require comprehensive technical documentation including system architecture, design specifications, and cybersecurity measures BEFORE deployment to the market.",
3350
+ recommendation: "Create AGENTS.md describing: system architecture, agent roles, tool access, data flows, security controls, and human oversight measures. SentinelFlow's /governance-scan command can generate a starting template.",
3351
+ remediation_effort: "medium"
3352
+ }));
3353
+ }
3354
+ return findings;
3355
+ }
3356
+ };
3357
+ exports2.noHumanOversightDocs = {
3358
+ id: "SF-CD-004",
3359
+ name: "No Human Oversight Documentation",
3360
+ description: "EU AI Act Article 14 requires documented human oversight measures including ability to intervene and override.",
3361
+ category: "compliance_docs",
3362
+ severity: "medium",
3363
+ frameworks: "all",
3364
+ compliance: [
3365
+ ...COMPLIANCE_CD,
3366
+ { framework: "EU_AI_ACT", reference: "Article 14", description: "Human oversight" }
3367
+ ],
3368
+ phase: "static",
3369
+ evaluate(ctx) {
3370
+ const findings = [];
3371
+ const oversightTerms = [
3372
+ "human_oversight",
3373
+ "human-oversight",
3374
+ "human_review",
3375
+ "human-in-the-loop",
3376
+ "hitl",
3377
+ "approval_workflow",
3378
+ "approval-workflow",
3379
+ "kill_switch",
3380
+ "emergency_stop"
3381
+ ];
3382
+ const hasOversight = ctx.config_files.some((f) => oversightTerms.some((t) => f.content.toLowerCase().includes(t)));
3383
+ const hasHooks = ctx.config_files.some((f) => f.path.includes("hooks") && f.content.includes("PreToolUse"));
3384
+ if (!hasOversight && !hasHooks && ctx.agents.length > 0) {
3385
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
3386
+ id: `${this.id}-global`,
3387
+ title: "No human oversight measures documented or configured",
3388
+ description: "No human oversight mechanisms (approval workflows, PreToolUse hooks, kill switches) or documentation found. EU AI Act Article 14 requires that high-risk AI systems include measures enabling human oversight, including the ability to understand, interpret, correctly use, and override the AI system.",
3389
+ recommendation: "Configure PreToolUse hooks for high-impact actions. Document oversight procedures including: how operators can intervene, what actions require approval, and how to shut down agent operations in an emergency.",
3390
+ remediation_effort: "medium"
3391
+ }));
3392
+ }
3393
+ return findings;
3394
+ }
3395
+ };
3396
+ exports2.noIncidentResponsePlan = {
3397
+ id: "SF-CD-005",
3398
+ name: "No AI Incident Response Plan",
3399
+ description: "No dedicated playbook for AI-specific incidents (prompt injection breaches, agent manipulation, model compromise).",
3400
+ category: "compliance_docs",
3401
+ severity: "low",
3402
+ frameworks: "all",
3403
+ compliance: [
3404
+ ...COMPLIANCE_CD,
3405
+ { framework: "NIST_AI_RMF", reference: "MANAGE 4.1", description: "Incident response" },
3406
+ { framework: "SOC2", reference: "CC7.3", description: "Incident response procedures" }
3407
+ ],
3408
+ phase: "static",
3409
+ evaluate(ctx) {
3410
+ const findings = [];
3411
+ const irFiles = [
3412
+ "INCIDENT_RESPONSE.md",
3413
+ "incident-response.md",
3414
+ "IR_PLAN.md",
3415
+ "docs/incident-response.md",
3416
+ "SECURITY.md"
3417
+ ];
3418
+ const hasIR = irFiles.some((f) => fs.existsSync(path.join(ctx.root_dir, f)));
3419
+ const hasIRContent = ctx.config_files.some((f) => f.content.includes("incident_response") || f.content.includes("incident-response") || f.content.includes("security incident"));
3420
+ if (!hasIR && !hasIRContent && ctx.agents.length > 0) {
3421
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
3422
+ id: `${this.id}-global`,
3423
+ title: "No AI-specific incident response plan found",
3424
+ description: "No incident response documentation for AI-specific threats found. Prompt injection breaches, agent manipulation, and model compromise require dedicated response playbooks beyond traditional IT incident response.",
3425
+ recommendation: "Create an AI incident response plan covering: prompt injection detection and response, agent credential rotation, model rollback procedures, and communication templates for stakeholders.",
3426
+ remediation_effort: "medium"
3427
+ }));
3428
+ }
3429
+ return findings;
3430
+ }
3431
+ };
3432
+ exports2.COMPLIANCE_DOCS_RULES = [
3433
+ exports2.noRiskAssessment,
3434
+ exports2.noTechnicalDocumentation,
3435
+ exports2.noHumanOversightDocs,
3436
+ exports2.noIncidentResponsePlan
3437
+ ];
3438
+ }
3439
+ });
3440
+
3441
+ // packages/scanner/dist/rules/network-security.js
3442
+ var require_network_security = __commonJS({
3443
+ "packages/scanner/dist/rules/network-security.js"(exports2) {
3444
+ "use strict";
3445
+ Object.defineProperty(exports2, "__esModule", { value: true });
3446
+ exports2.NETWORK_SECURITY_RULES = exports2.unencryptedMCPTransport = exports2.noSandboxing = exports2.unrestrictedNetworkAccess = void 0;
3447
+ var interface_1 = require_interface();
3448
+ var COMPLIANCE_NS = [
3449
+ { framework: "SOC2", reference: "CC6.6", description: "System boundaries and network security" },
3450
+ { framework: "NIST_AI_RMF", reference: "GOVERN 6", description: "Infrastructure security" }
3451
+ ];
3452
+ exports2.unrestrictedNetworkAccess = {
3453
+ id: "SF-NS-003",
3454
+ name: "No Network Egress Filtering for Agent Environment",
3455
+ description: "Agents that can make network requests have no domain restrictions, enabling data exfiltration.",
3456
+ category: "network_security",
3457
+ severity: "medium",
3458
+ frameworks: "all",
3459
+ compliance: [
3460
+ ...COMPLIANCE_NS,
3461
+ { framework: "HIPAA", reference: "\xA7164.312(e)(1)", description: "Transmission security" }
3462
+ ],
3463
+ phase: "static",
3464
+ evaluate(ctx) {
3465
+ return ctx.agents.filter((a) => a.tools.some((t) => t.type === "web_search" || t.type === "web_fetch" || t.type === "api_call")).filter((a) => !a.network_access || !a.network_access.allowed_domains && !a.network_access.blocked_domains).map((agent) => (0, interface_1.createEnterpriseFinding)(this, {
3466
+ id: `${this.id}-${agent.id}`,
3467
+ title: `Agent "${agent.name}" has unrestricted network egress`,
3468
+ description: `Agent has network tools (${agent.tools.filter((t) => ["web_search", "web_fetch", "api_call"].includes(t.type)).map((t) => t.name).join(", ")}) with no domain allowlist or blocklist. An attacker exploiting prompt injection could exfiltrate data to any external endpoint.`,
3469
+ recommendation: "Define network_access.allowed_domains restricting outbound connections to approved API endpoints. Block known data exfiltration services.",
3470
+ agent_id: agent.id,
3471
+ agent_name: agent.name,
3472
+ framework: agent.framework,
3473
+ location: agent.source_file ? { file: agent.source_file } : void 0,
3474
+ cwe: "CWE-441",
3475
+ remediation_effort: "medium"
3476
+ }));
3477
+ }
3478
+ };
3479
+ exports2.noSandboxing = {
3480
+ id: "SF-NS-004",
3481
+ name: "Code Execution Not Sandboxed",
3482
+ description: "Agents executing code in the host process without containerization or VM-based sandboxing.",
3483
+ category: "network_security",
3484
+ severity: "high",
3485
+ frameworks: "all",
3486
+ compliance: COMPLIANCE_NS,
3487
+ phase: "static",
3488
+ evaluate(ctx) {
3489
+ const findings = [];
3490
+ const hasSandbox = ctx.config_files.some((f) => f.content.includes("docker") || f.content.includes("container") || f.content.includes("sandbox") || f.content.includes("isolat") || f.content.includes("vm") || f.content.includes("firecracker"));
3491
+ const hasCodeExec = ctx.agents.some((a) => a.tools.some((t) => t.type === "bash" || t.type === "code_execution"));
3492
+ if (hasCodeExec && !hasSandbox) {
3493
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
3494
+ id: `${this.id}-global`,
3495
+ title: "Agent code execution with no sandboxing detected",
3496
+ description: "Agents can execute code (bash/code_execution tools) but no containerization or sandboxing configuration was found. Code execution in the host process means a prompt injection \u2192 code execution chain has full system access.",
3497
+ recommendation: "Run agent code execution in sandboxed environments: Docker containers, Azure Container Apps dynamic sessions, or VM-based sandboxes. At minimum, use Linux namespaces or chroot to restrict file system access.",
3498
+ remediation_effort: "high"
3499
+ }));
3500
+ }
3501
+ return findings;
3502
+ }
3503
+ };
3504
+ exports2.unencryptedMCPTransport = {
3505
+ id: "SF-NS-001",
3506
+ name: "MCP Server Using Unencrypted Transport",
3507
+ description: "MCP servers connected via HTTP (not HTTPS) or unencrypted WebSocket.",
3508
+ category: "network_security",
3509
+ severity: "high",
3510
+ frameworks: "all",
3511
+ compliance: [
3512
+ ...COMPLIANCE_NS,
3513
+ { framework: "HIPAA", reference: "\xA7164.312(e)(1)", description: "Transmission security" },
3514
+ { framework: "SOC2", reference: "CC6.7", description: "Encryption in transit" }
3515
+ ],
3516
+ phase: "static",
3517
+ evaluate(ctx) {
3518
+ const findings = [];
3519
+ for (const agent of ctx.agents) {
3520
+ if (!agent.mcp_servers)
3521
+ continue;
3522
+ for (const server of agent.mcp_servers) {
3523
+ if (server.url && server.url.startsWith("http://") && !server.url.includes("localhost") && !server.url.includes("127.0.0.1")) {
3524
+ findings.push((0, interface_1.createEnterpriseFinding)(this, {
3525
+ id: `${this.id}-${agent.id}-${server.name}`,
3526
+ title: `MCP server "${server.name}" uses unencrypted HTTP`,
3527
+ description: `MCP server "${server.name}" connects via ${server.url} (HTTP, not HTTPS). Tool definitions, agent data, and potentially credentials are transmitted in plaintext.`,
3528
+ recommendation: "Switch to HTTPS for all non-local MCP server connections. Configure TLS 1.2+ with valid certificates.",
3529
+ agent_id: agent.id,
3530
+ agent_name: agent.name,
3531
+ cwe: "CWE-319",
3532
+ remediation_effort: "low"
3533
+ }));
3534
+ }
3535
+ }
3536
+ }
3537
+ return findings;
3538
+ }
3539
+ };
3540
+ exports2.NETWORK_SECURITY_RULES = [
3541
+ exports2.unrestrictedNetworkAccess,
3542
+ exports2.noSandboxing,
3543
+ exports2.unencryptedMCPTransport
3544
+ ];
3545
+ }
3546
+ });
3547
+
3548
+ // packages/scanner/dist/rules/index.js
3549
+ var require_rules = __commonJS({
3550
+ "packages/scanner/dist/rules/index.js"(exports2) {
3551
+ "use strict";
3552
+ Object.defineProperty(exports2, "__esModule", { value: true });
3553
+ exports2.createEnterpriseFinding = exports2.BUILT_IN_RULES = void 0;
3554
+ exports2.getRuleById = getRuleById;
3555
+ exports2.getRulesByCategory = getRulesByCategory;
3556
+ exports2.getRulesBySeverity = getRulesBySeverity;
3557
+ exports2.getRulesByFramework = getRulesByFramework;
3558
+ exports2.getRulesByCompliance = getRulesByCompliance;
3559
+ exports2.getRuleSummary = getRuleSummary;
3560
+ var prompt_injection_1 = require_prompt_injection();
3561
+ var access_control_1 = require_access_control();
3562
+ var supply_chain_1 = require_supply_chain();
3563
+ var data_governance_1 = require_data_governance();
3564
+ var cost_governance_1 = require_cost_governance();
3565
+ var framework_config_1 = require_framework_config();
3566
+ var multi_agent_1 = require_multi_agent();
3567
+ var audit_logging_1 = require_audit_logging();
3568
+ var compliance_docs_1 = require_compliance_docs();
3569
+ var network_security_1 = require_network_security();
3570
+ exports2.BUILT_IN_RULES = [
3571
+ ...prompt_injection_1.PROMPT_INJECTION_RULES,
3572
+ // 4 rules
3573
+ ...access_control_1.ACCESS_CONTROL_RULES,
3574
+ // 5 rules
3575
+ ...supply_chain_1.SUPPLY_CHAIN_RULES,
3576
+ // 6 rules (+1: SC-010 LangChain passthrough)
3577
+ ...data_governance_1.DATA_GOVERNANCE_RULES,
3578
+ // 3 rules
3579
+ ...cost_governance_1.COST_GOVERNANCE_RULES,
3580
+ // 5 rules
3581
+ ...framework_config_1.FRAMEWORK_CONFIG_RULES,
3582
+ // 8 rules (+2: FC-008 Codex full-auto, FC-009 Cursor alwaysApply)
3583
+ ...multi_agent_1.MULTI_AGENT_RULES,
3584
+ // 5 rules (+2: MA-007 CrewAI hierarchical, MA-008 config drift)
3585
+ ...audit_logging_1.AUDIT_LOGGING_RULES,
3586
+ // 3 rules
3587
+ ...compliance_docs_1.COMPLIANCE_DOCS_RULES,
3588
+ // 4 rules
3589
+ ...network_security_1.NETWORK_SECURITY_RULES
3590
+ // 3 rules
3591
+ ];
3592
+ function getRuleById(id) {
3593
+ return exports2.BUILT_IN_RULES.find((r) => r.id === id);
3594
+ }
3595
+ function getRulesByCategory(category) {
3596
+ return exports2.BUILT_IN_RULES.filter((r) => r.category === category);
3597
+ }
3598
+ function getRulesBySeverity(severity) {
3599
+ return exports2.BUILT_IN_RULES.filter((r) => r.severity === severity);
3600
+ }
3601
+ function getRulesByFramework(framework) {
3602
+ return exports2.BUILT_IN_RULES.filter((r) => r.frameworks === "all" || r.frameworks.includes(framework));
3603
+ }
3604
+ function getRulesByCompliance(framework) {
3605
+ return exports2.BUILT_IN_RULES.filter((r) => r.compliance.some((c) => c.framework === framework));
3606
+ }
3607
+ function getRuleSummary() {
3608
+ const by_category = {};
3609
+ const by_severity = {};
3610
+ const by_compliance = {};
3611
+ for (const rule of exports2.BUILT_IN_RULES) {
3612
+ by_category[rule.category] = (by_category[rule.category] ?? 0) + 1;
3613
+ by_severity[rule.severity] = (by_severity[rule.severity] ?? 0) + 1;
3614
+ for (const mapping of rule.compliance) {
3615
+ by_compliance[mapping.framework] = (by_compliance[mapping.framework] ?? 0) + 1;
3616
+ }
3617
+ }
3618
+ return {
3619
+ total: exports2.BUILT_IN_RULES.length,
3620
+ by_category,
3621
+ by_severity,
3622
+ by_compliance
3623
+ };
3624
+ }
3625
+ var interface_1 = require_interface();
3626
+ Object.defineProperty(exports2, "createEnterpriseFinding", { enumerable: true, get: function() {
3627
+ return interface_1.createEnterpriseFinding;
3628
+ } });
3629
+ }
3630
+ });
3631
+
3632
+ // packages/scanner/dist/engine.js
3633
+ var require_engine = __commonJS({
3634
+ "packages/scanner/dist/engine.js"(exports2) {
3635
+ "use strict";
3636
+ var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
3637
+ if (k2 === void 0) k2 = k;
3638
+ var desc = Object.getOwnPropertyDescriptor(m, k);
3639
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
3640
+ desc = { enumerable: true, get: function() {
3641
+ return m[k];
3642
+ } };
3643
+ }
3644
+ Object.defineProperty(o, k2, desc);
3645
+ }) : (function(o, m, k, k2) {
3646
+ if (k2 === void 0) k2 = k;
3647
+ o[k2] = m[k];
3648
+ }));
3649
+ var __setModuleDefault2 = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
3650
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
3651
+ }) : function(o, v) {
3652
+ o["default"] = v;
3653
+ });
3654
+ var __importStar2 = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
3655
+ var ownKeys = function(o) {
3656
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
3657
+ var ar = [];
3658
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
3659
+ return ar;
3660
+ };
3661
+ return ownKeys(o);
3662
+ };
3663
+ return function(mod) {
3664
+ if (mod && mod.__esModule) return mod;
3665
+ var result = {};
3666
+ if (mod != null) {
3667
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding2(result, mod, k[i]);
3668
+ }
3669
+ __setModuleDefault2(result, mod);
3670
+ return result;
3671
+ };
3672
+ })();
3673
+ Object.defineProperty(exports2, "__esModule", { value: true });
3674
+ exports2.scan = scan;
3675
+ var path = __importStar2(require("path"));
3676
+ var core_1 = require_dist();
3677
+ var parsers_1 = require_dist2();
3678
+ var index_1 = require_rules();
3679
+ var SEVERITY_ORDER = [
3680
+ "critical",
3681
+ "high",
3682
+ "medium",
3683
+ "low",
3684
+ "info"
3685
+ ];
3686
+ async function scan(options) {
3687
+ const startTime = Date.now();
3688
+ const rootDir = path.resolve(options.rootDir);
3689
+ const fs = await Promise.resolve().then(() => __importStar2(require("fs")));
3690
+ if (!fs.existsSync(rootDir)) {
3691
+ throw new Error(`Directory not found: ${rootDir}`);
3692
+ }
3693
+ if (!fs.statSync(rootDir).isDirectory()) {
3694
+ throw new Error(`Not a directory: ${rootDir}`);
3695
+ }
3696
+ const parseResult = await (0, parsers_1.parseAll)(rootDir);
3697
+ const detectedFrameworkSet = new Set(parseResult.agents.map((a) => a.framework));
3698
+ const context = {
3699
+ agents: parseResult.agents,
3700
+ config_files: parseResult.config_files,
3701
+ root_dir: rootDir
3702
+ };
3703
+ let rules = [...index_1.BUILT_IN_RULES];
3704
+ if (options.rules && options.rules.length > 0) {
3705
+ const requestedIds = new Set(options.rules);
3706
+ rules = rules.filter((r) => requestedIds.has(r.id));
3707
+ if (rules.length === 0) {
3708
+ parseResult.warnings.push(`No matching rules found for IDs: ${options.rules.join(", ")}. Available rule IDs: ${index_1.BUILT_IN_RULES.map((r) => r.id).join(", ")}`);
3709
+ }
3710
+ }
3711
+ if (options.minSeverity) {
3712
+ const minIdx = SEVERITY_ORDER.indexOf(options.minSeverity);
3713
+ if (minIdx === -1) {
3714
+ parseResult.warnings.push(`Invalid severity: "${options.minSeverity}". Valid values: ${SEVERITY_ORDER.join(", ")}`);
3715
+ } else {
3716
+ rules = rules.filter((r) => {
3717
+ const ruleIdx = SEVERITY_ORDER.indexOf(r.severity);
3718
+ return ruleIdx <= minIdx;
3719
+ });
3720
+ }
3721
+ }
3722
+ const allFindings = [];
3723
+ for (const rule of rules) {
3724
+ if (rule.frameworks !== "all") {
3725
+ const ruleFrameworks = new Set(rule.frameworks);
3726
+ const hasApplicableFramework = [...detectedFrameworkSet].some((f) => ruleFrameworks.has(f));
3727
+ if (!hasApplicableFramework) {
3728
+ continue;
3729
+ }
3730
+ }
3731
+ try {
3732
+ const findings = rule.evaluate(context);
3733
+ allFindings.push(...findings);
3734
+ } catch (error) {
3735
+ const message = error instanceof Error ? error.message : String(error);
3736
+ parseResult.warnings.push(`Rule ${rule.id} (${rule.name}) failed: ${message}`);
3737
+ }
3738
+ }
3739
+ allFindings.sort((a, b) => {
3740
+ return SEVERITY_ORDER.indexOf(a.severity) - SEVERITY_ORDER.indexOf(b.severity);
3741
+ });
3742
+ const durationMs = Date.now() - startTime;
3743
+ const report = (0, core_1.createScanReport)(rootDir, allFindings, parseResult.frameworks, parseResult.agents.length, durationMs);
3744
+ if (options.updateRegistry !== false) {
3745
+ try {
3746
+ const registry2 = new core_1.LocalRegistry(rootDir);
3747
+ await registry2.initialize();
3748
+ for (const agent of parseResult.agents) {
3749
+ const agentFindings = allFindings.filter((f) => f.agent_id === agent.id);
3750
+ agent.governance.last_scan = (/* @__PURE__ */ new Date()).toISOString();
3751
+ agent.governance.findings_count = {
3752
+ critical: agentFindings.filter((f) => f.severity === "critical").length,
3753
+ high: agentFindings.filter((f) => f.severity === "high").length,
3754
+ medium: agentFindings.filter((f) => f.severity === "medium").length,
3755
+ low: agentFindings.filter((f) => f.severity === "low").length,
3756
+ info: agentFindings.filter((f) => f.severity === "info").length
3757
+ };
3758
+ if (agentFindings.some((f) => f.severity === "critical")) {
3759
+ agent.governance.risk_level = "critical";
3760
+ } else if (agentFindings.some((f) => f.severity === "high")) {
3761
+ agent.governance.risk_level = "high";
3762
+ } else if (agentFindings.some((f) => f.severity === "medium")) {
3763
+ agent.governance.risk_level = "medium";
3764
+ } else {
3765
+ agent.governance.risk_level = "low";
3766
+ }
3767
+ await registry2.upsertAgent(agent);
3768
+ }
3769
+ await registry2.storeScanReport(report);
3770
+ await registry2.close();
3771
+ } catch (error) {
3772
+ const message = error instanceof Error ? error.message : String(error);
3773
+ parseResult.warnings.push(`Failed to update registry: ${message}`);
3774
+ }
3775
+ }
3776
+ return {
3777
+ report,
3778
+ agents: parseResult.agents,
3779
+ frameworks: parseResult.frameworks,
3780
+ warnings: parseResult.warnings
3781
+ };
3782
+ }
3783
+ }
3784
+ });
3785
+
3786
+ // packages/scanner/dist/reporter.js
3787
+ var require_reporter = __commonJS({
3788
+ "packages/scanner/dist/reporter.js"(exports2) {
3789
+ "use strict";
3790
+ Object.defineProperty(exports2, "__esModule", { value: true });
3791
+ exports2.formatTerminal = formatTerminal;
3792
+ exports2.formatJSON = formatJSON;
3793
+ exports2.formatSARIF = formatSARIF;
3794
+ exports2.formatMarkdown = formatMarkdown;
3795
+ var RESET = "\x1B[0m";
3796
+ var BOLD = "\x1B[1m";
3797
+ var DIM = "\x1B[2m";
3798
+ var RED = "\x1B[31m";
3799
+ var YELLOW = "\x1B[33m";
3800
+ var GREEN = "\x1B[32m";
3801
+ var CYAN = "\x1B[36m";
3802
+ var WHITE = "\x1B[37m";
3803
+ var BG_RED = "\x1B[41m";
3804
+ var SEVERITY_COLORS = {
3805
+ critical: `${BG_RED}${WHITE}${BOLD}`,
3806
+ high: `${RED}${BOLD}`,
3807
+ medium: `${YELLOW}`,
3808
+ low: `${DIM}`,
3809
+ info: `${DIM}`
3810
+ };
3811
+ var SEVERITY_LABELS = {
3812
+ critical: "CRITICAL",
3813
+ high: "HIGH",
3814
+ medium: "MEDIUM",
3815
+ low: "LOW",
3816
+ info: "INFO"
3817
+ };
3818
+ function formatTerminal(result) {
3819
+ const lines = [];
3820
+ const { report, agents, frameworks } = result;
3821
+ lines.push("");
3822
+ lines.push(` ${BOLD}${CYAN}SentinelFlow v0.2.0${RESET} ${DIM}\u2014 Agent Governance Scanner${RESET}`);
3823
+ lines.push("");
3824
+ lines.push(` Scanning ${BOLD}${report.root_dir}${RESET}...`);
3825
+ lines.push("");
3826
+ lines.push(` ${BOLD}Frameworks detected:${RESET}`);
3827
+ if (frameworks.length === 0) {
3828
+ lines.push(` ${DIM}No agent frameworks found${RESET}`);
3829
+ } else {
3830
+ for (const fw of frameworks) {
3831
+ lines.push(` ${GREEN}\u2713${RESET} ${fw}`);
3832
+ }
3833
+ }
3834
+ lines.push("");
3835
+ lines.push(` ${BOLD}Agents discovered:${RESET} ${agents.length}`);
3836
+ for (let i = 0; i < agents.length; i++) {
3837
+ const agent = agents[i];
3838
+ if (!agent)
3839
+ continue;
3840
+ const isLast = i === agents.length - 1;
3841
+ const prefix = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
3842
+ const role = agent.swarm_role !== "standalone" ? `, ${agent.swarm_role}` : "";
3843
+ lines.push(` ${DIM}${prefix}${RESET} ${BOLD}${agent.name}${RESET} ${DIM}(${agent.framework}${role})${RESET}`);
3844
+ }
3845
+ lines.push("");
3846
+ const { summary } = report;
3847
+ if (summary.total === 0) {
3848
+ lines.push(` ${GREEN}${BOLD}\u2713 No findings \u2014 all clear!${RESET}`);
3849
+ } else {
3850
+ const parts = [];
3851
+ if (summary.critical > 0)
3852
+ parts.push(`${RED}${BOLD}${summary.critical} critical${RESET}`);
3853
+ if (summary.high > 0)
3854
+ parts.push(`${RED}${summary.high} high${RESET}`);
3855
+ if (summary.medium > 0)
3856
+ parts.push(`${YELLOW}${summary.medium} medium${RESET}`);
3857
+ if (summary.low > 0)
3858
+ parts.push(`${DIM}${summary.low} low${RESET}`);
3859
+ if (summary.info > 0)
3860
+ parts.push(`${DIM}${summary.info} info${RESET}`);
3861
+ lines.push(` ${BOLD}Findings:${RESET} ${parts.join(", ")}`);
3862
+ lines.push("");
3863
+ for (const severity of ["critical", "high", "medium", "low"]) {
3864
+ const findings = report.findings.filter((f) => f.severity === severity);
3865
+ if (findings.length === 0)
3866
+ continue;
3867
+ const color = SEVERITY_COLORS[severity];
3868
+ lines.push(` ${color}${SEVERITY_LABELS[severity]}${RESET}`);
3869
+ lines.push(` ${DIM}\u250C${"\u2500".repeat(60)}\u2510${RESET}`);
3870
+ for (const finding of findings) {
3871
+ const location = finding.location ? `${finding.location.file}${finding.location.line ? `:${finding.location.line}` : ""}` : "";
3872
+ lines.push(` ${DIM}\u2502${RESET} ${BOLD}${finding.rule_id}${RESET} ${finding.title}`);
3873
+ if (location) {
3874
+ lines.push(` ${DIM}\u2502${RESET} ${DIM}${location}${RESET}`);
3875
+ }
3876
+ }
3877
+ lines.push(` ${DIM}\u2514${"\u2500".repeat(60)}\u2518${RESET}`);
3878
+ lines.push("");
3879
+ }
3880
+ }
3881
+ lines.push(` ${DIM}Scan completed in ${report.duration_ms}ms | 46 rules | ${agents.length} agents${RESET}`);
3882
+ lines.push("");
3883
+ return lines.join("\n");
3884
+ }
3885
+ function formatJSON(result) {
3886
+ return JSON.stringify(result.report, null, 2);
3887
+ }
3888
+ function formatSARIF(result) {
3889
+ const { report } = result;
3890
+ const sarifReport = {
3891
+ $schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json",
3892
+ version: "2.1.0",
3893
+ runs: [
3894
+ {
3895
+ tool: {
3896
+ driver: {
3897
+ name: "SentinelFlow",
3898
+ version: "0.2.0",
3899
+ informationUri: "https://github.com/Omsatyaswaroop29/sentinelflow",
3900
+ rules: index_1.BUILT_IN_RULES.map((rule) => ({
3901
+ id: rule.id,
3902
+ name: rule.name,
3903
+ shortDescription: { text: rule.name },
3904
+ fullDescription: { text: rule.description },
3905
+ defaultConfiguration: {
3906
+ level: mapSeverityToSARIF(rule.severity)
3907
+ },
3908
+ properties: {
3909
+ tags: [rule.category]
3910
+ }
3911
+ }))
3912
+ }
3913
+ },
3914
+ results: report.findings.map((finding) => ({
3915
+ ruleId: finding.rule_id,
3916
+ level: mapSeverityToSARIF(finding.severity),
3917
+ message: {
3918
+ text: `${finding.title}
3919
+
3920
+ ${finding.description}
3921
+
3922
+ Recommendation: ${finding.recommendation}`
3923
+ },
3924
+ locations: finding.location ? [
3925
+ {
3926
+ physicalLocation: {
3927
+ artifactLocation: {
3928
+ uri: finding.location.file
3929
+ },
3930
+ region: finding.location.line ? {
3931
+ startLine: finding.location.line
3932
+ } : void 0
3933
+ }
3934
+ }
3935
+ ] : []
3936
+ }))
3937
+ }
3938
+ ]
3939
+ };
3940
+ return JSON.stringify(sarifReport, null, 2);
3941
+ }
3942
+ function mapSeverityToSARIF(severity) {
3943
+ switch (severity) {
3944
+ case "critical":
3945
+ case "high":
3946
+ return "error";
3947
+ case "medium":
3948
+ return "warning";
3949
+ case "low":
3950
+ return "note";
3951
+ default:
3952
+ return "none";
3953
+ }
3954
+ }
3955
+ var index_1 = require_rules();
3956
+ function formatMarkdown(result) {
3957
+ const { report, agents, frameworks } = result;
3958
+ const lines = [];
3959
+ lines.push("# SentinelFlow Governance Report");
3960
+ lines.push("");
3961
+ lines.push(`**Scanned:** ${report.root_dir}`);
3962
+ lines.push(`**Date:** ${report.timestamp}`);
3963
+ lines.push(`**Duration:** ${report.duration_ms}ms`);
3964
+ lines.push(`**Frameworks:** ${frameworks.join(", ") || "None detected"}`);
3965
+ lines.push(`**Agents discovered:** ${report.agents_discovered}`);
3966
+ lines.push("");
3967
+ lines.push("## Summary");
3968
+ lines.push("");
3969
+ lines.push(`| Severity | Count |`);
3970
+ lines.push(`|----------|-------|`);
3971
+ lines.push(`| Critical | ${report.summary.critical} |`);
3972
+ lines.push(`| High | ${report.summary.high} |`);
3973
+ lines.push(`| Medium | ${report.summary.medium} |`);
3974
+ lines.push(`| Low | ${report.summary.low} |`);
3975
+ lines.push(`| **Total** | **${report.summary.total}** |`);
3976
+ lines.push("");
3977
+ lines.push("## Agents");
3978
+ lines.push("");
3979
+ for (const agent of agents) {
3980
+ lines.push(`- **${agent.name}** (${agent.framework}) \u2014 ${agent.swarm_role} \u2014 Risk: ${agent.governance.risk_level ?? "unassessed"}`);
3981
+ }
3982
+ lines.push("");
3983
+ if (report.findings.length > 0) {
3984
+ lines.push("## Findings");
3985
+ lines.push("");
3986
+ for (const severity of ["critical", "high", "medium", "low"]) {
3987
+ const findings = report.findings.filter((f) => f.severity === severity);
3988
+ if (findings.length === 0)
3989
+ continue;
3990
+ lines.push(`### ${severity.toUpperCase()}`);
3991
+ lines.push("");
3992
+ for (const f of findings) {
3993
+ lines.push(`#### ${f.rule_id}: ${f.title}`);
3994
+ lines.push("");
3995
+ lines.push(f.description);
3996
+ lines.push("");
3997
+ lines.push(`**Recommendation:** ${f.recommendation}`);
3998
+ if (f.location) {
3999
+ lines.push(`**Location:** \`${f.location.file}${f.location.line ? `:${f.location.line}` : ""}\``);
4000
+ }
4001
+ lines.push("");
4002
+ }
4003
+ }
4004
+ }
4005
+ lines.push("## Coverage");
4006
+ lines.push("");
4007
+ lines.push(`Static analysis of ${agents.length} agent definition(s). The following require runtime context and are **not covered** by this scan:`);
4008
+ lines.push("");
4009
+ lines.push("- IAM roles and cloud permissions");
4010
+ lines.push("- Secrets injected via Vault or environment variables");
4011
+ lines.push("- Network policies and service mesh configuration");
4012
+ lines.push("- Feature flags gating agent capabilities");
4013
+ lines.push("");
4014
+ lines.push("See [Framework Support Matrix](https://sentinelflow.dev/docs/framework-support) for full coverage details.");
4015
+ lines.push("");
4016
+ return lines.join("\n");
4017
+ }
4018
+ }
4019
+ });
4020
+
4021
+ // packages/scanner/dist/suppression.js
4022
+ var require_suppression = __commonJS({
4023
+ "packages/scanner/dist/suppression.js"(exports2) {
4024
+ "use strict";
4025
+ var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
4026
+ if (k2 === void 0) k2 = k;
4027
+ var desc = Object.getOwnPropertyDescriptor(m, k);
4028
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
4029
+ desc = { enumerable: true, get: function() {
4030
+ return m[k];
4031
+ } };
4032
+ }
4033
+ Object.defineProperty(o, k2, desc);
4034
+ }) : (function(o, m, k, k2) {
4035
+ if (k2 === void 0) k2 = k;
4036
+ o[k2] = m[k];
4037
+ }));
4038
+ var __setModuleDefault2 = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
4039
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
4040
+ }) : function(o, v) {
4041
+ o["default"] = v;
4042
+ });
4043
+ var __importStar2 = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
4044
+ var ownKeys = function(o) {
4045
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
4046
+ var ar = [];
4047
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
4048
+ return ar;
4049
+ };
4050
+ return ownKeys(o);
4051
+ };
4052
+ return function(mod) {
4053
+ if (mod && mod.__esModule) return mod;
4054
+ var result = {};
4055
+ if (mod != null) {
4056
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding2(result, mod, k[i]);
4057
+ }
4058
+ __setModuleDefault2(result, mod);
4059
+ return result;
4060
+ };
4061
+ })();
4062
+ Object.defineProperty(exports2, "__esModule", { value: true });
4063
+ exports2.PRESETS = void 0;
4064
+ exports2.parseInlineSuppressions = parseInlineSuppressions;
4065
+ exports2.loadPolicyFile = loadPolicyFile;
4066
+ exports2.applySuppressions = applySuppressions;
4067
+ var fs = __importStar2(require("fs"));
4068
+ var path = __importStar2(require("path"));
4069
+ function parseInlineSuppressions(filePath, content) {
4070
+ const suppressions = /* @__PURE__ */ new Map();
4071
+ const pattern = /(?:#|\/\/)\s*sentinelflow-ignore:\s*(SF-[A-Z]+-\d+)\s*(?:--\s*(.+))?$/gm;
4072
+ let match;
4073
+ while ((match = pattern.exec(content)) !== null) {
4074
+ const lineNum = content.substring(0, match.index).split("\n").length;
4075
+ const ruleId = match[1];
4076
+ const reason = match[2]?.trim() ?? "";
4077
+ const key = `${filePath}:${lineNum}`;
4078
+ suppressions.set(key, {
4079
+ rule_id: ruleId,
4080
+ reason,
4081
+ source: "inline",
4082
+ path_pattern: filePath
4083
+ });
4084
+ }
4085
+ return suppressions;
4086
+ }
4087
+ var POLICY_FILENAMES = [
4088
+ ".sentinelflow-policy.yaml",
4089
+ ".sentinelflow-policy.yml",
4090
+ ".sentinelflow.yaml",
4091
+ ".sentinelflow.yml"
4092
+ ];
4093
+ function loadPolicyFile(rootDir) {
4094
+ const warnings = [];
4095
+ for (const filename of POLICY_FILENAMES) {
4096
+ const filePath = path.join(rootDir, filename);
4097
+ if (!fs.existsSync(filePath))
4098
+ continue;
4099
+ try {
4100
+ const content = fs.readFileSync(filePath, "utf-8");
4101
+ const policy = parseSimpleYAML(content);
4102
+ return { policy, warnings };
4103
+ } catch (error) {
4104
+ warnings.push(`Failed to parse ${filename}: ${error instanceof Error ? error.message : String(error)}`);
4105
+ }
4106
+ }
4107
+ return { policy: null, warnings };
4108
+ }
4109
+ function parseSimpleYAML(content) {
4110
+ const policy = { version: "v1" };
4111
+ const versionMatch = content.match(/^version:\s*(.+)$/m);
4112
+ if (versionMatch?.[1]) {
4113
+ policy.version = versionMatch[1].trim();
4114
+ }
4115
+ const presetMatch = content.match(/^preset:\s*(.+)$/m);
4116
+ if (presetMatch?.[1]) {
4117
+ const preset = presetMatch[1].trim();
4118
+ if (["strict", "standard", "monitor"].includes(preset)) {
4119
+ policy.preset = preset;
4120
+ }
4121
+ }
4122
+ const overridesBlock = content.match(/severity_overrides:\s*\n((?:\s+\S+.*\n)*)/);
4123
+ if (overridesBlock?.[1]) {
4124
+ policy.severity_overrides = {};
4125
+ const lines = overridesBlock[1].split("\n");
4126
+ for (const line of lines) {
4127
+ const kvMatch = line.match(/^\s+(SF-[A-Z]+-\d+):\s*(.+)$/);
4128
+ if (kvMatch?.[1] && kvMatch[2]) {
4129
+ policy.severity_overrides[kvMatch[1]] = kvMatch[2].trim();
4130
+ }
4131
+ }
4132
+ }
4133
+ const excludeBlock = content.match(/exclude:\s*\n((?:\s+-\s+.+\n)*)/);
4134
+ if (excludeBlock?.[1]) {
4135
+ policy.exclude = [];
4136
+ const lines = excludeBlock[1].split("\n");
4137
+ for (const line of lines) {
4138
+ const itemMatch = line.match(/^\s+-\s+"?([^"]+)"?\s*$/);
4139
+ if (itemMatch?.[1]) {
4140
+ policy.exclude.push(itemMatch[1]);
4141
+ }
4142
+ }
4143
+ }
4144
+ const ignoreBlock = content.match(/ignore:\s*\n([\s\S]*?)(?=\n\w|\n*$)/);
4145
+ if (ignoreBlock?.[1]) {
4146
+ policy.ignore = {};
4147
+ const ruleBlocks = ignoreBlock[1].split(/\n\s{2}(SF-[A-Z]+-\d+):/);
4148
+ for (let i = 1; i < ruleBlocks.length; i += 2) {
4149
+ const ruleId = ruleBlocks[i];
4150
+ const blockContent = ruleBlocks[i + 1] ?? "";
4151
+ if (!ruleId)
4152
+ continue;
4153
+ const entries = [];
4154
+ const entryChunks = blockContent.split(/\n\s{4}- /);
4155
+ for (const chunk of entryChunks) {
4156
+ if (!chunk.trim())
4157
+ continue;
4158
+ const entry = { reason: "" };
4159
+ const pathMatch = chunk.match(/path:\s*"?([^"\n]+)"?/);
4160
+ if (pathMatch?.[1])
4161
+ entry.path = pathMatch[1].trim();
4162
+ const reasonMatch = chunk.match(/reason:\s*"?([^"\n]+)"?/);
4163
+ if (reasonMatch?.[1])
4164
+ entry.reason = reasonMatch[1].trim();
4165
+ const expiresMatch = chunk.match(/expires:\s*"?([^"\n]+)"?/);
4166
+ if (expiresMatch?.[1])
4167
+ entry.expires = expiresMatch[1].trim();
4168
+ const approvedMatch = chunk.match(/approved_by:\s*"?([^"\n]+)"?/);
4169
+ if (approvedMatch?.[1])
4170
+ entry.approved_by = approvedMatch[1].trim();
4171
+ const ticketMatch = chunk.match(/ticket:\s*"?([^"\n]+)"?/);
4172
+ if (ticketMatch?.[1])
4173
+ entry.ticket = ticketMatch[1].trim();
4174
+ if (entry.reason)
4175
+ entries.push(entry);
4176
+ }
4177
+ if (entries.length > 0) {
4178
+ policy.ignore[ruleId] = entries;
4179
+ }
4180
+ }
4181
+ }
4182
+ return policy;
4183
+ }
4184
+ function applySuppressions(findings, configFiles, rootDir, options) {
4185
+ const result = {
4186
+ active: [],
4187
+ suppressed: [],
4188
+ expired_suppressions: [],
4189
+ warnings: []
4190
+ };
4191
+ const { policy, warnings: policyWarnings } = loadPolicyFile(rootDir);
4192
+ result.warnings.push(...policyWarnings);
4193
+ const allInlineSuppressions = /* @__PURE__ */ new Map();
4194
+ for (const file of configFiles) {
4195
+ const fileSups = parseInlineSuppressions(file.path, file.content);
4196
+ for (const [key, sup] of fileSups) {
4197
+ allInlineSuppressions.set(key, sup);
4198
+ }
4199
+ }
4200
+ for (const [location, sup] of allInlineSuppressions) {
4201
+ if (!sup.reason) {
4202
+ result.warnings.push(`Unjustified suppression at ${location}: "# sentinelflow-ignore: ${sup.rule_id}" requires a justification after "--". Example: # sentinelflow-ignore: ${sup.rule_id} -- Accepted risk per SEC-1234`);
4203
+ }
4204
+ }
4205
+ const now = /* @__PURE__ */ new Date();
4206
+ if (policy?.ignore) {
4207
+ for (const [ruleId, entries] of Object.entries(policy.ignore)) {
4208
+ for (const entry of entries) {
4209
+ if (entry.expires) {
4210
+ const expiryDate = new Date(entry.expires);
4211
+ if (expiryDate < now) {
4212
+ result.expired_suppressions.push({
4213
+ rule_id: ruleId,
4214
+ reason: entry.reason,
4215
+ source: "policy",
4216
+ path_pattern: entry.path,
4217
+ expires: entry.expires,
4218
+ approved_by: entry.approved_by,
4219
+ ticket: entry.ticket
4220
+ });
4221
+ }
4222
+ }
4223
+ }
4224
+ }
4225
+ }
4226
+ for (const finding of findings) {
4227
+ let suppression = null;
4228
+ if (policy?.exclude && finding.location?.file) {
4229
+ const relPath = path.relative(rootDir, finding.location.file);
4230
+ for (const pattern of policy.exclude) {
4231
+ if (matchGlob(relPath, pattern)) {
4232
+ suppression = {
4233
+ rule_id: finding.rule_id,
4234
+ reason: `File excluded by policy: ${pattern}`,
4235
+ source: "policy",
4236
+ path_pattern: pattern
4237
+ };
4238
+ break;
4239
+ }
4240
+ }
4241
+ }
4242
+ if (!suppression && finding.location?.file && finding.location?.line) {
4243
+ const key = `${finding.location.file}:${finding.location.line}`;
4244
+ const inlineSup = allInlineSuppressions.get(key);
4245
+ if (inlineSup && inlineSup.rule_id === finding.rule_id && inlineSup.reason) {
4246
+ suppression = inlineSup;
4247
+ }
4248
+ const keyAbove = `${finding.location.file}:${finding.location.line - 1}`;
4249
+ const inlineSupAbove = allInlineSuppressions.get(keyAbove);
4250
+ if (!suppression && inlineSupAbove && inlineSupAbove.rule_id === finding.rule_id && inlineSupAbove.reason) {
4251
+ suppression = inlineSupAbove;
4252
+ }
4253
+ }
4254
+ if (!suppression && policy?.ignore) {
4255
+ const policyEntries = policy.ignore[finding.rule_id];
4256
+ if (policyEntries) {
4257
+ for (const entry of policyEntries) {
4258
+ if (entry.expires && new Date(entry.expires) < now) {
4259
+ continue;
4260
+ }
4261
+ if (entry.path && finding.location?.file) {
4262
+ const relPath = path.relative(rootDir, finding.location.file);
4263
+ if (!matchGlob(relPath, entry.path)) {
4264
+ continue;
4265
+ }
4266
+ }
4267
+ suppression = {
4268
+ rule_id: finding.rule_id,
4269
+ reason: entry.reason,
4270
+ source: "policy",
4271
+ path_pattern: entry.path,
4272
+ expires: entry.expires,
4273
+ approved_by: entry.approved_by,
4274
+ ticket: entry.ticket
4275
+ };
4276
+ break;
4277
+ }
4278
+ }
4279
+ }
4280
+ if (policy?.severity_overrides?.[finding.rule_id]) {
4281
+ const override = policy.severity_overrides[finding.rule_id];
4282
+ if (["critical", "high", "medium", "low", "info"].includes(override)) {
4283
+ finding.severity = override;
4284
+ }
4285
+ }
4286
+ if (suppression) {
4287
+ result.suppressed.push({ finding, suppression });
4288
+ } else {
4289
+ result.active.push(finding);
4290
+ }
4291
+ }
4292
+ return result;
4293
+ }
4294
+ function matchGlob(filePath, pattern) {
4295
+ const regexStr = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "\xA7DOUBLESTAR\xA7").replace(/\*/g, "[^/]*").replace(/§DOUBLESTAR§/g, ".*");
4296
+ const regex = new RegExp(`^${regexStr}$`);
4297
+ return regex.test(filePath);
4298
+ }
4299
+ exports2.PRESETS = {
4300
+ strict: {
4301
+ exitOnSeverities: ["critical", "high", "medium"],
4302
+ description: "Production governance. CI fails on medium and above."
4303
+ },
4304
+ standard: {
4305
+ exitOnSeverities: ["critical", "high"],
4306
+ description: "Active development. CI fails on high and above. (Default)"
4307
+ },
4308
+ monitor: {
4309
+ exitOnSeverities: [],
4310
+ description: "Adoption mode. All findings reported, CI never fails."
4311
+ }
4312
+ };
4313
+ }
4314
+ });
4315
+
4316
+ // packages/scanner/dist/index.js
4317
+ var require_dist3 = __commonJS({
4318
+ "packages/scanner/dist/index.js"(exports2) {
4319
+ "use strict";
4320
+ Object.defineProperty(exports2, "__esModule", { value: true });
4321
+ exports2.PRESETS = exports2.loadPolicyFile = exports2.parseInlineSuppressions = exports2.applySuppressions = exports2.getRulesByCategory = exports2.getRuleById = exports2.BUILT_IN_RULES = exports2.formatSARIF = exports2.formatMarkdown = exports2.formatJSON = exports2.formatTerminal = exports2.scan = void 0;
4322
+ var engine_1 = require_engine();
4323
+ Object.defineProperty(exports2, "scan", { enumerable: true, get: function() {
4324
+ return engine_1.scan;
4325
+ } });
4326
+ var reporter_1 = require_reporter();
4327
+ Object.defineProperty(exports2, "formatTerminal", { enumerable: true, get: function() {
4328
+ return reporter_1.formatTerminal;
4329
+ } });
4330
+ Object.defineProperty(exports2, "formatJSON", { enumerable: true, get: function() {
4331
+ return reporter_1.formatJSON;
4332
+ } });
4333
+ Object.defineProperty(exports2, "formatMarkdown", { enumerable: true, get: function() {
4334
+ return reporter_1.formatMarkdown;
4335
+ } });
4336
+ Object.defineProperty(exports2, "formatSARIF", { enumerable: true, get: function() {
4337
+ return reporter_1.formatSARIF;
4338
+ } });
4339
+ var index_1 = require_rules();
4340
+ Object.defineProperty(exports2, "BUILT_IN_RULES", { enumerable: true, get: function() {
4341
+ return index_1.BUILT_IN_RULES;
4342
+ } });
4343
+ Object.defineProperty(exports2, "getRuleById", { enumerable: true, get: function() {
4344
+ return index_1.getRuleById;
4345
+ } });
4346
+ Object.defineProperty(exports2, "getRulesByCategory", { enumerable: true, get: function() {
4347
+ return index_1.getRulesByCategory;
4348
+ } });
4349
+ var suppression_1 = require_suppression();
4350
+ Object.defineProperty(exports2, "applySuppressions", { enumerable: true, get: function() {
4351
+ return suppression_1.applySuppressions;
4352
+ } });
4353
+ Object.defineProperty(exports2, "parseInlineSuppressions", { enumerable: true, get: function() {
4354
+ return suppression_1.parseInlineSuppressions;
4355
+ } });
4356
+ Object.defineProperty(exports2, "loadPolicyFile", { enumerable: true, get: function() {
4357
+ return suppression_1.loadPolicyFile;
4358
+ } });
4359
+ Object.defineProperty(exports2, "PRESETS", { enumerable: true, get: function() {
4360
+ return suppression_1.PRESETS;
4361
+ } });
4362
+ }
4363
+ });
4364
+
4365
+ // packages/cli/dist/commands/scan.js
4366
+ var require_scan = __commonJS({
4367
+ "packages/cli/dist/commands/scan.js"(exports2) {
4368
+ "use strict";
4369
+ var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
4370
+ if (k2 === void 0) k2 = k;
4371
+ var desc = Object.getOwnPropertyDescriptor(m, k);
4372
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
4373
+ desc = { enumerable: true, get: function() {
4374
+ return m[k];
4375
+ } };
4376
+ }
4377
+ Object.defineProperty(o, k2, desc);
4378
+ }) : (function(o, m, k, k2) {
4379
+ if (k2 === void 0) k2 = k;
4380
+ o[k2] = m[k];
4381
+ }));
4382
+ var __setModuleDefault2 = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
4383
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
4384
+ }) : function(o, v) {
4385
+ o["default"] = v;
4386
+ });
4387
+ var __importStar2 = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
4388
+ var ownKeys = function(o) {
4389
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
4390
+ var ar = [];
4391
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
4392
+ return ar;
4393
+ };
4394
+ return ownKeys(o);
4395
+ };
4396
+ return function(mod) {
4397
+ if (mod && mod.__esModule) return mod;
4398
+ var result = {};
4399
+ if (mod != null) {
4400
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding2(result, mod, k[i]);
4401
+ }
4402
+ __setModuleDefault2(result, mod);
4403
+ return result;
4404
+ };
4405
+ })();
4406
+ Object.defineProperty(exports2, "__esModule", { value: true });
4407
+ exports2.scanCommand = scanCommand;
4408
+ var fs = __importStar2(require("fs"));
4409
+ var path = __importStar2(require("path"));
4410
+ var scanner_1 = require_dist3();
4411
+ var VALID_FORMATS = ["terminal", "json", "md", "sarif"];
4412
+ var VALID_SEVERITIES = ["critical", "high", "medium", "low", "info"];
4413
+ var VALID_PRESETS = ["strict", "standard", "monitor"];
4414
+ async function scanCommand(targetPath, options) {
4415
+ const rootDir = path.resolve(targetPath);
4416
+ if (!fs.existsSync(rootDir)) {
4417
+ console.error(`
4418
+ \x1B[31mError:\x1B[0m Directory not found: ${rootDir}
4419
+ `);
4420
+ process.exit(2);
4421
+ }
4422
+ if (!fs.statSync(rootDir).isDirectory()) {
4423
+ console.error(`
4424
+ \x1B[31mError:\x1B[0m Not a directory: ${rootDir}
4425
+ `);
4426
+ process.exit(2);
4427
+ }
4428
+ const format = options.format;
4429
+ if (!VALID_FORMATS.includes(format)) {
4430
+ console.error(`
4431
+ \x1B[31mError:\x1B[0m Invalid format "${options.format}". Valid formats: ${VALID_FORMATS.join(", ")}
4432
+ `);
4433
+ process.exit(2);
4434
+ }
4435
+ if (options.minSeverity && !VALID_SEVERITIES.includes(options.minSeverity)) {
4436
+ console.error(`
4437
+ \x1B[31mError:\x1B[0m Invalid severity "${options.minSeverity}". Valid values: ${VALID_SEVERITIES.join(", ")}
4438
+ `);
4439
+ process.exit(2);
4440
+ }
4441
+ const preset = options.preset ?? "standard";
4442
+ if (!VALID_PRESETS.includes(preset)) {
4443
+ console.error(`
4444
+ \x1B[31mError:\x1B[0m Invalid preset "${options.preset}". Valid presets: ${VALID_PRESETS.join(", ")}
4445
+ `);
4446
+ process.exit(2);
4447
+ }
4448
+ try {
4449
+ const result = await (0, scanner_1.scan)({
4450
+ rootDir,
4451
+ minSeverity: options.minSeverity,
4452
+ rules: options.rules?.split(",").map((r) => r.trim()),
4453
+ updateRegistry: options.registry
4454
+ });
4455
+ if (result.frameworks.length === 0 && format === "terminal") {
4456
+ console.log(`
4457
+ \x1B[36m\x1B[1mSentinelFlow v0.2.0\x1B[0m \u2014 Agent Governance Scanner
4458
+
4459
+ Scanning ${rootDir}...
4460
+
4461
+ \x1B[33mNo agent frameworks detected.\x1B[0m
4462
+
4463
+ SentinelFlow scans for agents in:
4464
+ \u2022 Claude Code (.claude/ directory, CLAUDE.md, AGENTS.md)
4465
+ \u2022 Cursor (.cursor/ directory, .cursorrules)
4466
+ \u2022 Codex (.codex/ directory, .agents/)
4467
+ \u2022 LangChain (pyproject.toml with langchain dependency)
4468
+ \u2022 CrewAI (crew.yaml, agents.yaml)
4469
+ \u2022 Kiro (.kiro/ directory)
4470
+
4471
+ Run \x1B[1msentinelflow init\x1B[0m to set up governance for this project.
4472
+ `);
4473
+ return;
4474
+ }
4475
+ const { policy, warnings: policyWarnings } = (0, scanner_1.loadPolicyFile)(rootDir);
4476
+ const effectivePreset = options.preset ? preset : policy?.preset ?? "standard";
4477
+ const suppressionResult = (0, scanner_1.applySuppressions)(result.report.findings, [], rootDir);
4478
+ if (!options.showSuppressed) {
4479
+ result.report.findings = suppressionResult.active;
4480
+ result.report.summary = {
4481
+ critical: result.report.findings.filter((f) => f.severity === "critical").length,
4482
+ high: result.report.findings.filter((f) => f.severity === "high").length,
4483
+ medium: result.report.findings.filter((f) => f.severity === "medium").length,
4484
+ low: result.report.findings.filter((f) => f.severity === "low").length,
4485
+ info: result.report.findings.filter((f) => f.severity === "info").length,
4486
+ total: result.report.findings.length
4487
+ };
4488
+ }
4489
+ switch (format) {
4490
+ case "json":
4491
+ console.log((0, scanner_1.formatJSON)(result));
4492
+ break;
4493
+ case "md":
4494
+ console.log((0, scanner_1.formatMarkdown)(result));
4495
+ break;
4496
+ case "sarif":
4497
+ console.log((0, scanner_1.formatSARIF)(result));
4498
+ break;
4499
+ case "terminal":
4500
+ default:
4501
+ console.log((0, scanner_1.formatTerminal)(result));
4502
+ break;
4503
+ }
4504
+ if (format === "terminal" && suppressionResult.suppressed.length > 0) {
4505
+ if (options.showSuppressed) {
4506
+ console.log(` \x1B[33mSuppressed findings (${suppressionResult.suppressed.length}):\x1B[0m`);
4507
+ for (const { finding, suppression } of suppressionResult.suppressed) {
4508
+ console.log(` \x1B[2m${finding.rule_id}\x1B[0m ${finding.title}`);
4509
+ console.log(` Reason: ${suppression.reason}`);
4510
+ if (suppression.expires)
4511
+ console.log(` Expires: ${suppression.expires}`);
4512
+ if (suppression.ticket)
4513
+ console.log(` Ticket: ${suppression.ticket}`);
4514
+ }
4515
+ console.log("");
4516
+ } else {
4517
+ console.log(` \x1B[2m${suppressionResult.suppressed.length} finding(s) suppressed. Use --show-suppressed to review.\x1B[0m`);
4518
+ console.log("");
4519
+ }
4520
+ }
4521
+ if (format === "terminal" && suppressionResult.expired_suppressions.length > 0) {
4522
+ console.log(` \x1B[33m\u26A0 ${suppressionResult.expired_suppressions.length} expired suppression(s):\x1B[0m`);
4523
+ for (const exp of suppressionResult.expired_suppressions) {
4524
+ console.log(` ${exp.rule_id} \u2014 expired ${exp.expires} \u2014 ${exp.reason}`);
4525
+ }
4526
+ console.log(" \x1B[2mRemove expired entries from .sentinelflow-policy.yaml\x1B[0m");
4527
+ console.log("");
4528
+ }
4529
+ if (format === "terminal") {
4530
+ const agentCount = result.agents.length;
4531
+ const configCount = result.report.findings.length;
4532
+ console.log(` \x1B[2m\u250C\u2500 Coverage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\x1B[0m`);
4533
+ console.log(` \x1B[2m\u2502 Static analysis of ${agentCount} agent definition(s). \u2502\x1B[0m`);
4534
+ console.log(` \x1B[2m\u2502 \u2502\x1B[0m`);
4535
+ console.log(` \x1B[2m\u2502 Not analyzed (requires runtime context): \u2502\x1B[0m`);
4536
+ console.log(` \x1B[2m\u2502 \xB7 IAM roles and cloud permissions \u2502\x1B[0m`);
4537
+ console.log(` \x1B[2m\u2502 \xB7 Secrets injected via Vault / env variables \u2502\x1B[0m`);
4538
+ console.log(` \x1B[2m\u2502 \xB7 Network policies and service mesh config \u2502\x1B[0m`);
4539
+ console.log(` \x1B[2m\u2502 \xB7 Feature flags gating agent capabilities \u2502\x1B[0m`);
4540
+ console.log(` \x1B[2m\u2502 \u2502\x1B[0m`);
4541
+ console.log(` \x1B[2m\u2502 Preset: ${effectivePreset.padEnd(10)} Docs: sentinelflow.dev \u2502\x1B[0m`);
4542
+ console.log(` \x1B[2m\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\x1B[0m`);
4543
+ console.log("");
4544
+ }
4545
+ const allWarnings = [...result.warnings, ...policyWarnings];
4546
+ if (allWarnings.length > 0 && format === "terminal") {
4547
+ console.log(" \x1B[33mWarnings:\x1B[0m");
4548
+ for (const warning of allWarnings) {
4549
+ console.log(` \x1B[33m\u26A0\x1B[0m ${warning}`);
4550
+ }
4551
+ console.log("");
4552
+ }
4553
+ const presetConfig = scanner_1.PRESETS[effectivePreset];
4554
+ const { summary } = result.report;
4555
+ const shouldFail = presetConfig.exitOnSeverities.some((sev) => {
4556
+ switch (sev) {
4557
+ case "critical":
4558
+ return summary.critical > 0;
4559
+ case "high":
4560
+ return summary.high > 0;
4561
+ case "medium":
4562
+ return summary.medium > 0;
4563
+ case "low":
4564
+ return summary.low > 0;
4565
+ default:
4566
+ return false;
4567
+ }
4568
+ });
4569
+ if (shouldFail) {
4570
+ process.exit(1);
4571
+ }
4572
+ } catch (error) {
4573
+ const message = error instanceof Error ? error.message : String(error);
4574
+ console.error(`
4575
+ \x1B[31mError:\x1B[0m ${message}
4576
+ `);
4577
+ process.exit(2);
4578
+ }
4579
+ }
4580
+ }
4581
+ });
4582
+
4583
+ // packages/cli/dist/commands/init.js
4584
+ var require_init = __commonJS({
4585
+ "packages/cli/dist/commands/init.js"(exports2) {
4586
+ "use strict";
4587
+ var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
4588
+ if (k2 === void 0) k2 = k;
4589
+ var desc = Object.getOwnPropertyDescriptor(m, k);
4590
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
4591
+ desc = { enumerable: true, get: function() {
4592
+ return m[k];
4593
+ } };
4594
+ }
4595
+ Object.defineProperty(o, k2, desc);
4596
+ }) : (function(o, m, k, k2) {
4597
+ if (k2 === void 0) k2 = k;
4598
+ o[k2] = m[k];
4599
+ }));
4600
+ var __setModuleDefault2 = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
4601
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
4602
+ }) : function(o, v) {
4603
+ o["default"] = v;
4604
+ });
4605
+ var __importStar2 = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
4606
+ var ownKeys = function(o) {
4607
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
4608
+ var ar = [];
4609
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
4610
+ return ar;
4611
+ };
4612
+ return ownKeys(o);
4613
+ };
4614
+ return function(mod) {
4615
+ if (mod && mod.__esModule) return mod;
4616
+ var result = {};
4617
+ if (mod != null) {
4618
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding2(result, mod, k[i]);
4619
+ }
4620
+ __setModuleDefault2(result, mod);
4621
+ return result;
4622
+ };
4623
+ })();
4624
+ Object.defineProperty(exports2, "__esModule", { value: true });
4625
+ exports2.initCommand = initCommand;
4626
+ var fs = __importStar2(require("fs"));
4627
+ var path = __importStar2(require("path"));
4628
+ var DEFAULT_MANIFEST = `# SentinelFlow Configuration
4629
+ # https://github.com/omswaroop/sentinelflow
4630
+
4631
+ # Governance settings for this project
4632
+ governance:
4633
+ # Who is responsible for AI agents in this project?
4634
+ default_owner: ""
4635
+ default_team: ""
4636
+
4637
+ # Minimum scan requirements
4638
+ require_scan_before_commit: false
4639
+ min_severity_to_block: "critical"
4640
+
4641
+ # Compliance standards to enforce
4642
+ compliance:
4643
+ # - eu-ai-act
4644
+ # - soc2
4645
+ # - hipaa
4646
+ # - iso-42001
4647
+
4648
+ # Scanner settings
4649
+ scanner:
4650
+ # Rules to skip (by ID)
4651
+ skip_rules: []
4652
+
4653
+ # Additional paths to scan for agent configs
4654
+ include_paths: []
4655
+
4656
+ # Paths to exclude from scanning
4657
+ exclude_paths:
4658
+ - node_modules
4659
+ - .git
4660
+ - dist
4661
+ - build
4662
+
4663
+ # Registry settings
4664
+ registry:
4665
+ # Where to store the registry (local = SQLite file)
4666
+ backend: local
4667
+
4668
+ # SentinelFlow Cloud URL (for team sync, Phase 2+)
4669
+ # cloud_url: https://api.sentinelflow.ai
4670
+ # cloud_token: $SENTINELFLOW_TOKEN
4671
+ `;
4672
+ async function initCommand(targetPath) {
4673
+ const rootDir = path.resolve(targetPath);
4674
+ const sfDir = path.join(rootDir, ".sentinelflow");
4675
+ if (fs.existsSync(sfDir)) {
4676
+ console.log("\n SentinelFlow already initialized in this project.");
4677
+ console.log(` Config: ${sfDir}/config.yaml`);
4678
+ console.log(" Run 'sentinelflow scan' to scan for agents.\n");
4679
+ return;
4680
+ }
4681
+ fs.mkdirSync(sfDir, { recursive: true });
4682
+ fs.mkdirSync(path.join(sfDir, "reports"), { recursive: true });
4683
+ fs.writeFileSync(path.join(sfDir, "config.yaml"), DEFAULT_MANIFEST, "utf-8");
4684
+ const gitignorePath = path.join(rootDir, ".gitignore");
4685
+ if (fs.existsSync(gitignorePath)) {
4686
+ const gitignore = fs.readFileSync(gitignorePath, "utf-8");
4687
+ if (!gitignore.includes(".sentinelflow/registry")) {
4688
+ fs.appendFileSync(gitignorePath, "\n# SentinelFlow local registry (config is committed, data is not)\n.sentinelflow/agents.json\n.sentinelflow/reports.json\n.sentinelflow/events.json\n.sentinelflow/registry.db\n");
4689
+ }
4690
+ }
4691
+ console.log(`
4692
+ \x1B[32m\u2713\x1B[0m SentinelFlow initialized!
4693
+
4694
+ Created:
4695
+ ${sfDir}/config.yaml \u2014 Governance configuration
4696
+ ${sfDir}/reports/ \u2014 Scan report storage
4697
+
4698
+ Next steps:
4699
+ 1. Edit .sentinelflow/config.yaml to set your team info
4700
+ 2. Run \x1B[1msentinelflow scan\x1B[0m to discover agents
4701
+ 3. Review findings and register agents
4702
+
4703
+ Learn more: https://github.com/omswaroop/sentinelflow
4704
+ `);
4705
+ }
4706
+ }
4707
+ });
4708
+
4709
+ // packages/cli/dist/index.js
4710
+ var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
4711
+ if (k2 === void 0) k2 = k;
4712
+ var desc = Object.getOwnPropertyDescriptor(m, k);
4713
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
4714
+ desc = { enumerable: true, get: function() {
4715
+ return m[k];
4716
+ } };
4717
+ }
4718
+ Object.defineProperty(o, k2, desc);
4719
+ }) : (function(o, m, k, k2) {
4720
+ if (k2 === void 0) k2 = k;
4721
+ o[k2] = m[k];
4722
+ }));
4723
+ var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
4724
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
4725
+ }) : function(o, v) {
4726
+ o["default"] = v;
4727
+ });
4728
+ var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() {
4729
+ var ownKeys = function(o) {
4730
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
4731
+ var ar = [];
4732
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
4733
+ return ar;
4734
+ };
4735
+ return ownKeys(o);
4736
+ };
4737
+ return function(mod) {
4738
+ if (mod && mod.__esModule) return mod;
4739
+ var result = {};
4740
+ if (mod != null) {
4741
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
4742
+ }
4743
+ __setModuleDefault(result, mod);
4744
+ return result;
4745
+ };
4746
+ })();
4747
+ Object.defineProperty(exports, "__esModule", { value: true });
4748
+ var commander_1 = require("commander");
4749
+ var scan_1 = require_scan();
4750
+ var init_1 = require_init();
4751
+ var program = new commander_1.Command();
4752
+ program.name("sentinelflow").description("The vendor-neutral governance layer for enterprise AI agents").version("0.1.0");
4753
+ program.command("scan").description("Scan for AI agents and governance issues").argument("[path]", "Project directory to scan", ".").option("-f, --format <format>", "Output format: terminal, json, md, sarif", "terminal").option("--min-severity <severity>", "Minimum severity: critical, high, medium, low, info").option("--rules <rules>", "Comma-separated rule IDs to run").option("--preset <preset>", "Scan preset: strict, standard, monitor", "standard").option("--show-suppressed", "Show suppressed findings for audit review").option("--no-registry", "Skip updating the local registry").action(scan_1.scanCommand);
4754
+ program.command("init").description("Initialize SentinelFlow in the current project").argument("[path]", "Project directory", ".").action(init_1.initCommand);
4755
+ var registry = program.command("registry").description("Manage the agent registry");
4756
+ registry.command("list").description("List all registered agents").option("--framework <framework>", "Filter by framework").option("--status <status>", "Filter by governance status").option("--json", "Output as JSON").action(async (options) => {
4757
+ const path = await Promise.resolve().then(() => __importStar(require("path")));
4758
+ const { LocalRegistry } = await Promise.resolve().then(() => __importStar(require_dist()));
4759
+ const reg = new LocalRegistry(process.cwd());
4760
+ await reg.initialize();
4761
+ const agents = await reg.listAgents({
4762
+ framework: options.framework,
4763
+ status: options.status
4764
+ });
4765
+ if (options.json) {
4766
+ console.log(JSON.stringify(agents, null, 2));
4767
+ } else {
4768
+ if (agents.length === 0) {
4769
+ console.log("\n No agents registered. Run 'sentinelflow scan' first.\n");
4770
+ return;
4771
+ }
4772
+ console.log(`
4773
+ ${agents.length} agents registered:
4774
+ `);
4775
+ for (const agent of agents) {
4776
+ const risk = agent.governance.risk_level ?? "unassessed";
4777
+ const status = agent.governance.status;
4778
+ console.log(` ${agent.name} (${agent.framework}) \u2014 ${status} \u2014 risk: ${risk}`);
4779
+ }
4780
+ console.log("");
4781
+ }
4782
+ await reg.close();
4783
+ });
4784
+ program.parse();