praxis-sec 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +170 -0
- package/ai-defense/cost-protection.md +292 -0
- package/ai-defense/llm-security-checklist.md +324 -0
- package/ai-defense/prompt-injection-patterns.js +283 -0
- package/ai-defense/system-prompt-armor.md +327 -0
- package/checklists/launch-day.md +168 -0
- package/cli/agents/abom-generator.js +225 -0
- package/cli/agents/agent-attestation-agent.js +318 -0
- package/cli/agents/agent-config-scanner.js +787 -0
- package/cli/agents/agent-telemetry-agent.js +415 -0
- package/cli/agents/agentic-security-agent.js +296 -0
- package/cli/agents/agentic-supply-chain-agent.js +463 -0
- package/cli/agents/ai-infra-inventory-agent.js +449 -0
- package/cli/agents/api-fuzzer.js +345 -0
- package/cli/agents/auth-bypass-agent.js +348 -0
- package/cli/agents/base-agent.js +280 -0
- package/cli/agents/cicd-scanner.js +300 -0
- package/cli/agents/config-auditor.js +757 -0
- package/cli/agents/deep-analyzer.js +776 -0
- package/cli/agents/endpoint-agent-abuse-agent.js +404 -0
- package/cli/agents/exception-handler-agent.js +187 -0
- package/cli/agents/git-history-scanner.js +169 -0
- package/cli/agents/governance-audits.js +138 -0
- package/cli/agents/hermes-security-agent.js +536 -0
- package/cli/agents/html-reporter.js +1125 -0
- package/cli/agents/index.js +147 -0
- package/cli/agents/injection-tester.js +502 -0
- package/cli/agents/legal-risk-agent.js +328 -0
- package/cli/agents/llm-redteam.js +199 -0
- package/cli/agents/managed-agent-scanner.js +333 -0
- package/cli/agents/mcp-security-agent.js +588 -0
- package/cli/agents/memory-poisoning-agent.js +305 -0
- package/cli/agents/mobile-scanner.js +231 -0
- package/cli/agents/model-file-scanner.js +259 -0
- package/cli/agents/orchestrator.js +355 -0
- package/cli/agents/pii-compliance-agent.js +301 -0
- package/cli/agents/policy-engine.js +229 -0
- package/cli/agents/prompt-injection-prober.js +224 -0
- package/cli/agents/rag-security-agent.js +204 -0
- package/cli/agents/recon-agent.js +207 -0
- package/cli/agents/sbom-generator.js +265 -0
- package/cli/agents/scoring-engine.js +273 -0
- package/cli/agents/ssrf-prober.js +130 -0
- package/cli/agents/stateful-watcher.js +238 -0
- package/cli/agents/supabase-rls-agent.js +154 -0
- package/cli/agents/supply-chain-agent.js +857 -0
- package/cli/agents/swarm-orchestrator.js +200 -0
- package/cli/agents/verifier-agent.js +303 -0
- package/cli/agents/vibe-coding-agent.js +250 -0
- package/cli/bin/praxis.js +866 -0
- package/cli/commands/abom.js +73 -0
- package/cli/commands/agent-fix.js +1245 -0
- package/cli/commands/audit.js +1180 -0
- package/cli/commands/autofix.js +383 -0
- package/cli/commands/baseline.js +193 -0
- package/cli/commands/benchmark.js +327 -0
- package/cli/commands/checklist.js +223 -0
- package/cli/commands/ci.js +403 -0
- package/cli/commands/deps.js +516 -0
- package/cli/commands/diff.js +200 -0
- package/cli/commands/doctor.js +195 -0
- package/cli/commands/env-audit.js +349 -0
- package/cli/commands/fix.js +218 -0
- package/cli/commands/guard.js +396 -0
- package/cli/commands/hooks.js +278 -0
- package/cli/commands/init.js +514 -0
- package/cli/commands/legal.js +158 -0
- package/cli/commands/live-advisories.js +241 -0
- package/cli/commands/mcp.js +660 -0
- package/cli/commands/openclaw.js +386 -0
- package/cli/commands/red-team.js +350 -0
- package/cli/commands/redteam.js +78 -0
- package/cli/commands/remediate.js +797 -0
- package/cli/commands/rotate.js +768 -0
- package/cli/commands/rules.js +196 -0
- package/cli/commands/scan-mcp.js +534 -0
- package/cli/commands/scan-skill.js +588 -0
- package/cli/commands/scan-standard.js +251 -0
- package/cli/commands/scan.js +524 -0
- package/cli/commands/score.js +449 -0
- package/cli/commands/shell.js +514 -0
- package/cli/commands/team-report.js +398 -0
- package/cli/commands/undo.js +161 -0
- package/cli/commands/update-intel.js +126 -0
- package/cli/commands/vibe-check.js +276 -0
- package/cli/commands/watch.js +757 -0
- package/cli/commands/web.js +63 -0
- package/cli/core/ast/guardrail-detector.js +141 -0
- package/cli/core/ast/index.js +22 -0
- package/cli/core/ast/parser.js +676 -0
- package/cli/core/ast/scope-tree.js +287 -0
- package/cli/core/ast/taint-tracker.js +158 -0
- package/cli/core/branding.js +37 -0
- package/cli/core/env.js +38 -0
- package/cli/core/errors.js +61 -0
- package/cli/core/fs.js +62 -0
- package/cli/core/output/compliance.js +90 -0
- package/cli/core/output/html-theme.js +158 -0
- package/cli/core/output/index.js +57 -0
- package/cli/core/output/json.js +48 -0
- package/cli/core/output/sarif.js +240 -0
- package/cli/core/version.js +67 -0
- package/cli/core/web/jobs.js +183 -0
- package/cli/core/web/projects.js +146 -0
- package/cli/core/web/server.js +439 -0
- package/cli/data/atlas-knowledge.json +5640 -0
- package/cli/data/eaa-catalog.json +39 -0
- package/cli/data/known-mcps.json +26 -0
- package/cli/data/probes/prompt-injection-corpus.json +271 -0
- package/cli/data/threat-intel.json +85 -0
- package/cli/data/threatpacks/latest.json +41 -0
- package/cli/hooks/patterns.js +313 -0
- package/cli/hooks/post-tool-use.js +140 -0
- package/cli/hooks/pre-tool-use.js +186 -0
- package/cli/index.js +90 -0
- package/cli/providers/llm-provider.js +766 -0
- package/cli/utils/autofix-rules.js +74 -0
- package/cli/utils/cache-manager.js +310 -0
- package/cli/utils/compliance-map.js +191 -0
- package/cli/utils/entropy.js +132 -0
- package/cli/utils/fix-ledger.js +127 -0
- package/cli/utils/hermes-tool-registry.js +252 -0
- package/cli/utils/intel/cache.js +61 -0
- package/cli/utils/intel/http.js +88 -0
- package/cli/utils/intel/index.js +235 -0
- package/cli/utils/intel/merge.js +229 -0
- package/cli/utils/intel/sources/epss.js +54 -0
- package/cli/utils/intel/sources/ghsa.js +81 -0
- package/cli/utils/intel/sources/gitguardian.js +40 -0
- package/cli/utils/intel/sources/gitleaks.js +101 -0
- package/cli/utils/intel/sources/kev.js +38 -0
- package/cli/utils/intel/sources/nvd.js +84 -0
- package/cli/utils/intel/sources/osv.js +132 -0
- package/cli/utils/intel/sources/phylum.js +44 -0
- package/cli/utils/intel/sources/snyk.js +46 -0
- package/cli/utils/intel/sources/socket.js +69 -0
- package/cli/utils/intel/sources/sonatype.js +84 -0
- package/cli/utils/intel/sources/threatpack.js +69 -0
- package/cli/utils/mcp-trust.js +60 -0
- package/cli/utils/output.js +251 -0
- package/cli/utils/patterns.js +1130 -0
- package/cli/utils/pdf-generator.js +94 -0
- package/cli/utils/plugin-loader.js +364 -0
- package/cli/utils/rule-import.js +228 -0
- package/cli/utils/rule-registry.js +426 -0
- package/cli/utils/scan-fingerprint.js +109 -0
- package/cli/utils/scan-playbook.js +312 -0
- package/cli/utils/score-history.js +119 -0
- package/cli/utils/secrets-verifier.js +247 -0
- package/cli/utils/security-memory.js +296 -0
- package/cli/utils/standards/atlas-knowledge.js +87 -0
- package/cli/utils/standards/index.js +127 -0
- package/cli/utils/standards/sources/avid.js +45 -0
- package/cli/utils/standards/sources/eu-ai-act.js +89 -0
- package/cli/utils/standards/sources/google-saif.js +39 -0
- package/cli/utils/standards/sources/iso-42001.js +94 -0
- package/cli/utils/standards/sources/mitre-atlas.js +54 -0
- package/cli/utils/standards/sources/nist-ai-600-1.js +45 -0
- package/cli/utils/standards/sources/owasp-llm.js +45 -0
- package/cli/utils/standards/sources/owasp-ml.js +45 -0
- package/cli/utils/threat-intel.js +265 -0
- package/configs/firebase/firestore-rules.txt +215 -0
- package/configs/firebase/security-checklist.md +236 -0
- package/configs/firebase/storage-rules.txt +206 -0
- package/configs/gitignore-template +258 -0
- package/configs/nextjs-security-headers.js +220 -0
- package/configs/praxisignore-template +50 -0
- package/configs/supabase/secure-client.ts +225 -0
- package/configs/supabase/security-checklist.md +278 -0
- package/docs/THIRD_PARTY_NOTICES.md +26 -0
- package/docs/THREAT_INTEL.md +292 -0
- package/docs/USAGE.md +1205 -0
- package/docs/design/WEB-UI.md +82 -0
- package/package.json +71 -0
- package/scripts/check-determinism.mjs +119 -0
- package/snippets/README.md +122 -0
- package/snippets/api-security/api-security-checklist.md +412 -0
- package/snippets/api-security/cors-config.ts +322 -0
- package/snippets/api-security/input-validation.ts +430 -0
- package/snippets/auth/jwt-checklist.md +322 -0
- package/snippets/rate-limiting/nextjs-middleware.ts +211 -0
- package/snippets/rate-limiting/upstash-ratelimit.ts +229 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Endpoint Agent Abuse Agent
|
|
3
|
+
* ==========================
|
|
4
|
+
*
|
|
5
|
+
* Static detection of local-AI-agent abuse techniques from the Endpoint AI
|
|
6
|
+
* Agent Abuse (EAA) catalog — the attack surface where malware, malicious
|
|
7
|
+
* packages, and repo-borne payloads compromise coding agents (Claude Code,
|
|
8
|
+
* Cursor, Codex, Gemini CLI, etc.) through their config, hooks, MCP servers,
|
|
9
|
+
* environment, and committed state.
|
|
10
|
+
*
|
|
11
|
+
* Techniques covered (see cli/data/eaa-catalog.json, CC0):
|
|
12
|
+
* EAA-001 Agent CLI invocation by untrusted parent (lifecycle scripts)
|
|
13
|
+
* EAA-002 Permissive or unattended agent execution (bypass flags)
|
|
14
|
+
* EAA-003 Lifecycle hook persistence (observed: Mini Shai-Hulud)
|
|
15
|
+
* EAA-005 Committed agent transcripts/state (secret-harvest surface)
|
|
16
|
+
* EAA-007 Hostile model/API gateway routing (base-URL overrides)
|
|
17
|
+
* EAA-008 Shadow agent config directory (CLAUDE_CONFIG_DIR overrides)
|
|
18
|
+
* EAA-011 Environment-expanded MCP activation (${VAR} in MCP config)
|
|
19
|
+
* EAA-012 Observability/logging exfiltration (OTEL endpoints)
|
|
20
|
+
*
|
|
21
|
+
* Rule confidence is backed by the catalog's evidence tier:
|
|
22
|
+
* observed / malicious-artifact → high, research / documented → medium.
|
|
23
|
+
*
|
|
24
|
+
* Complements (does not duplicate): AgentConfigScanner + MemoryPoisoningAgent
|
|
25
|
+
* (instruction content), MCPSecurityAgent (MCP tool semantics).
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import fs from 'fs';
|
|
29
|
+
import path from 'path';
|
|
30
|
+
import fg from 'fast-glob';
|
|
31
|
+
import { BaseAgent, createFinding } from './base-agent.js';
|
|
32
|
+
|
|
33
|
+
// =============================================================================
|
|
34
|
+
// FILE TARGETS
|
|
35
|
+
// =============================================================================
|
|
36
|
+
|
|
37
|
+
const HOOK_CONFIG_GLOBS = [
|
|
38
|
+
'.claude/settings.json',
|
|
39
|
+
'.claude/settings.local.json',
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const MCP_CONFIG_GLOBS = [
|
|
43
|
+
'.mcp.json',
|
|
44
|
+
'mcp.json',
|
|
45
|
+
'.cursor/mcp.json',
|
|
46
|
+
'claude_desktop_config.json',
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const ENV_FILE_GLOBS = [
|
|
50
|
+
'.env', '.env.*', '*.env', '.envrc',
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const SCRIPT_GLOBS = [
|
|
54
|
+
'.github/workflows/*.yml', '.github/workflows/*.yaml',
|
|
55
|
+
'Makefile', '*.mk',
|
|
56
|
+
'Dockerfile', 'docker-compose.yml', 'docker-compose.yaml',
|
|
57
|
+
'scripts/**/*.sh', 'setup.sh', 'install.sh', 'bootstrap.sh',
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
const AGENT_STATE_GLOBS = [
|
|
61
|
+
'.claude/projects/**/*.jsonl',
|
|
62
|
+
'.claude/history.jsonl',
|
|
63
|
+
'.claude/todos/**/*.jsonl',
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
// =============================================================================
|
|
67
|
+
// DETECTION LOGIC
|
|
68
|
+
// =============================================================================
|
|
69
|
+
|
|
70
|
+
// EAA-003: hook commands that fetch, decode, or execute opaque payloads
|
|
71
|
+
const HOOK_DANGEROUS_CMD = /(?:curl|wget|iwr|Invoke-WebRequest|nc\s|netcat|base64\s+-d|base64\s+--decode|\beval\b|bash\s+-c|sh\s+-c|powershell\s+-(?:e|enc)|npx\s|pip\s+install|pip3\s+install|\/tmp\/|%TEMP%|\$\{?TEMP\}?)/i;
|
|
72
|
+
|
|
73
|
+
// EAA-002: agent CLI bypass flags in checked-in automation
|
|
74
|
+
const PERMISSIVE_FLAGS = /--dangerously-skip-permissions|--yolo|--trust-all|--full-auto|--no-sandbox|--allow-all-tools/i;
|
|
75
|
+
const AGENT_CLI = /\b(?:claude|claude-code|codex|gemini|cursor-agent|opencode)\b/i;
|
|
76
|
+
|
|
77
|
+
// EAA-001: lifecycle script names in package.json
|
|
78
|
+
const LIFECYCLE_SCRIPTS = new Set(['preinstall', 'install', 'postinstall', 'prepare', 'preprepare', 'postprepare']);
|
|
79
|
+
|
|
80
|
+
// EAA-011: env-var interpolation inside MCP config values
|
|
81
|
+
const ENV_EXPANSION = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
|
|
82
|
+
const CREDENTIAL_VAR = /(?:TOKEN|SECRET|KEY|PASSWORD|PASSWD|CRED|AWS_|GH_|GITHUB_|ANTHROPIC_|OPENAI_)/i;
|
|
83
|
+
|
|
84
|
+
// EAA-007: provider endpoint overrides
|
|
85
|
+
const GATEWAY_VARS = /^(?:ANTHROPIC_BASE_URL|ANTHROPIC_API_BASE|ANTHROPIC_AUTH_TOKEN|OPENAI_BASE_URL|OPENAI_API_BASE|GEMINI_API_BASE|GOOGLE_AI_BASE_URL)$/;
|
|
86
|
+
const KNOWN_ENDPOINTS = /(?:api\.anthropic\.com|api\.openai\.com|generativelanguage\.googleapis\.com|localhost|127\.0\.0\.1|0\.0\.0\.0)/i;
|
|
87
|
+
const KNOWN_GATEWAYS = /(?:openrouter\.ai|together\.xyz|api\.groq\.com|api\.deepseek\.com|api\.moonshot\.ai|api\.mistral\.ai|api\.x\.ai|api\.perplexity\.ai|api\.cohere\.com|api\.fireworks\.ai|api\.together\.xyz)/i;
|
|
88
|
+
|
|
89
|
+
// EAA-012: telemetry/observability endpoints and raw-content logging
|
|
90
|
+
const TELEMETRY_VARS = /^(?:OTEL_EXPORTER_OTLP_ENDPOINT|OTEL_EXPORTER_OTLP_TRACES_ENDPOINT|CLAUDE_CODE_ENABLE_TELEMETRY|OTEL_LOG_TOOL_CONTENT|OTEL_LOG_PROMPTS)$/;
|
|
91
|
+
|
|
92
|
+
// EAA-008: alternate agent config directory overrides
|
|
93
|
+
const SHADOW_DIR_VARS = /^(?:CLAUDE_CONFIG_DIR|CURSOR_CONFIG_DIR|CODEX_CONFIG_DIR)$/;
|
|
94
|
+
|
|
95
|
+
// EAA-005: secret patterns worth escalating committed transcripts with
|
|
96
|
+
const TRANSCRIPT_SECRET = /(?:sk-[a-zA-Z0-9_-]{20,}|AKIA[0-9A-Z]{16}|ghp_[a-zA-Z0-9]{30,}|xox[baprs]-[a-zA-Z0-9-]{10,})/;
|
|
97
|
+
|
|
98
|
+
const VAR_LINE = /^\s*(?:export\s+)?([A-Z_][A-Z0-9_]*)\s*[=:]\s*(.*)$/;
|
|
99
|
+
|
|
100
|
+
// =============================================================================
|
|
101
|
+
// AGENT
|
|
102
|
+
// =============================================================================
|
|
103
|
+
|
|
104
|
+
export class EndpointAgentAbuseAgent extends BaseAgent {
|
|
105
|
+
constructor() {
|
|
106
|
+
super(
|
|
107
|
+
'EndpointAgentAbuseAgent',
|
|
108
|
+
'Detects local AI agent abuse techniques (EAA catalog): hook persistence, MCP env-expansion, gateway overrides, committed agent state',
|
|
109
|
+
'agentic'
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
shouldRun() {
|
|
114
|
+
return true; // agent config surfaces exist in any modern repo
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async analyze(context) {
|
|
118
|
+
const { rootPath } = context;
|
|
119
|
+
const findings = [];
|
|
120
|
+
|
|
121
|
+
const opts = { cwd: rootPath, absolute: true, dot: true, onlyFiles: true };
|
|
122
|
+
|
|
123
|
+
const hookFiles = await fg(HOOK_CONFIG_GLOBS, opts).catch(() => []);
|
|
124
|
+
for (const f of hookFiles) findings.push(...this._scanHookConfig(f));
|
|
125
|
+
|
|
126
|
+
const mcpFiles = await fg(MCP_CONFIG_GLOBS, opts).catch(() => []);
|
|
127
|
+
for (const f of mcpFiles) findings.push(...this._scanMcpEnvExpansion(f));
|
|
128
|
+
|
|
129
|
+
const pkgPath = path.join(rootPath, 'package.json');
|
|
130
|
+
if (fs.existsSync(pkgPath)) findings.push(...this._scanPackageLifecycle(pkgPath));
|
|
131
|
+
|
|
132
|
+
const scriptFiles = await fg(SCRIPT_GLOBS, opts).catch(() => []);
|
|
133
|
+
for (const f of scriptFiles) findings.push(...this._scanScriptsForPermissiveAgent(f));
|
|
134
|
+
|
|
135
|
+
const envFiles = await fg(ENV_FILE_GLOBS, opts).catch(() => []);
|
|
136
|
+
for (const f of [...envFiles, ...scriptFiles]) findings.push(...this._scanEnvOverrides(f));
|
|
137
|
+
|
|
138
|
+
const stateFiles = await fg(AGENT_STATE_GLOBS, opts).catch(() => []);
|
|
139
|
+
for (const f of stateFiles) findings.push(...this._scanCommittedState(f, rootPath));
|
|
140
|
+
|
|
141
|
+
return findings;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// -------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
_scanHookConfig(file) {
|
|
147
|
+
const findings = [];
|
|
148
|
+
let settings;
|
|
149
|
+
try {
|
|
150
|
+
settings = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
151
|
+
} catch {
|
|
152
|
+
return findings;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const hooks = settings?.hooks;
|
|
156
|
+
if (!hooks || typeof hooks !== 'object') return findings;
|
|
157
|
+
|
|
158
|
+
for (const [event, entries] of Object.entries(hooks)) {
|
|
159
|
+
const commands = this._collectHookCommands(entries);
|
|
160
|
+
for (const cmd of commands) {
|
|
161
|
+
if (!HOOK_DANGEROUS_CMD.test(cmd)) continue;
|
|
162
|
+
findings.push(createFinding({
|
|
163
|
+
file, line: 1,
|
|
164
|
+
severity: 'critical',
|
|
165
|
+
category: this.category,
|
|
166
|
+
rule: 'EAA_HOOK_SUSPICIOUS_COMMAND',
|
|
167
|
+
title: `Agent Hook (${event}) Runs Suspicious Command`,
|
|
168
|
+
description: `A ${event} agent hook executes a command that fetches, decodes, or runs opaque payloads. Malicious packages have planted SessionStart hooks for persistence (EAA-003, observed in the Mini Shai-Hulud incident).`,
|
|
169
|
+
matched: cmd.slice(0, 200),
|
|
170
|
+
confidence: 'high',
|
|
171
|
+
cwe: 'CWE-506',
|
|
172
|
+
owasp: 'ASI04',
|
|
173
|
+
eaa: 'EAA-003',
|
|
174
|
+
fix: 'Remove the hook or replace the command with a reviewed, pinned local script. Hooks should never fetch and execute remote content.',
|
|
175
|
+
}));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return findings;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
_collectHookCommands(entries) {
|
|
182
|
+
const commands = [];
|
|
183
|
+
const walk = (node) => {
|
|
184
|
+
if (!node) return;
|
|
185
|
+
if (Array.isArray(node)) { node.forEach(walk); return; }
|
|
186
|
+
if (typeof node === 'object') {
|
|
187
|
+
if (typeof node.command === 'string') commands.push(node.command);
|
|
188
|
+
for (const v of Object.values(node)) walk(v);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
walk(entries);
|
|
192
|
+
return commands;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
_scanMcpEnvExpansion(file) {
|
|
196
|
+
const findings = [];
|
|
197
|
+
let content;
|
|
198
|
+
try {
|
|
199
|
+
content = fs.readFileSync(file, 'utf-8');
|
|
200
|
+
JSON.parse(content); // only well-formed config
|
|
201
|
+
} catch {
|
|
202
|
+
return findings;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const lines = content.split('\n');
|
|
206
|
+
for (let i = 0; i < lines.length; i++) {
|
|
207
|
+
ENV_EXPANSION.lastIndex = 0;
|
|
208
|
+
let m;
|
|
209
|
+
while ((m = ENV_EXPANSION.exec(lines[i])) !== null) {
|
|
210
|
+
const varName = m[1];
|
|
211
|
+
const credLike = CREDENTIAL_VAR.test(varName);
|
|
212
|
+
findings.push(createFinding({
|
|
213
|
+
file, line: i + 1,
|
|
214
|
+
severity: credLike ? 'high' : 'medium',
|
|
215
|
+
category: this.category,
|
|
216
|
+
rule: 'EAA_MCP_ENV_EXPANSION',
|
|
217
|
+
title: 'MCP Config Uses Environment Variable Expansion',
|
|
218
|
+
description: `Checked-in MCP config interpolates \${${varName}} at activation time, so the server that actually starts depends on the victim machine's environment (EAA-011).${credLike ? ' The variable looks credential-bearing — a repo-shipped config can resolve to attacker-controlled values.' : ''}`,
|
|
219
|
+
matched: m[0],
|
|
220
|
+
confidence: 'medium',
|
|
221
|
+
cwe: 'CWE-15',
|
|
222
|
+
owasp: 'ASI04',
|
|
223
|
+
eaa: 'EAA-011',
|
|
224
|
+
fix: 'Pin literal commands/URLs in checked-in MCP config. If per-machine values are required, document the expansion and review what each variable can resolve to.',
|
|
225
|
+
}));
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return findings;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
_scanPackageLifecycle(file) {
|
|
232
|
+
const findings = [];
|
|
233
|
+
let pkg;
|
|
234
|
+
try {
|
|
235
|
+
pkg = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
236
|
+
} catch {
|
|
237
|
+
return findings;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const scripts = pkg?.scripts || {};
|
|
241
|
+
for (const [name, cmd] of Object.entries(scripts)) {
|
|
242
|
+
if (!LIFECYCLE_SCRIPTS.has(name) || typeof cmd !== 'string') continue;
|
|
243
|
+
if (!AGENT_CLI.test(cmd)) continue;
|
|
244
|
+
findings.push(createFinding({
|
|
245
|
+
file, line: 1,
|
|
246
|
+
severity: 'medium',
|
|
247
|
+
category: this.category,
|
|
248
|
+
rule: 'EAA_AGENT_CLI_IN_LIFECYCLE',
|
|
249
|
+
title: `Lifecycle Script "${name}" Invokes an AI Agent CLI`,
|
|
250
|
+
description: `An npm lifecycle hook launches a coding-agent CLI. Package installs run without user review, making this a vector for driving a trusted agent from an untrusted parent (EAA-001, observed in the Nx s1ngularity and Trivy OpenVSX incidents).`,
|
|
251
|
+
matched: cmd.slice(0, 200),
|
|
252
|
+
confidence: 'medium',
|
|
253
|
+
cwe: 'CWE-506',
|
|
254
|
+
owasp: 'ASI04',
|
|
255
|
+
eaa: 'EAA-001',
|
|
256
|
+
fix: 'Remove agent invocations from lifecycle scripts. If automation is intended, run it as an explicit, reviewed step (CI job with approval), not an install side effect.',
|
|
257
|
+
}));
|
|
258
|
+
}
|
|
259
|
+
return findings;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
_scanScriptsForPermissiveAgent(file) {
|
|
263
|
+
const findings = [];
|
|
264
|
+
let content;
|
|
265
|
+
try {
|
|
266
|
+
content = fs.readFileSync(file, 'utf-8');
|
|
267
|
+
} catch {
|
|
268
|
+
return findings;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const lines = content.split('\n');
|
|
272
|
+
for (let i = 0; i < lines.length; i++) {
|
|
273
|
+
const line = lines[i];
|
|
274
|
+
if (!AGENT_CLI.test(line) || !PERMISSIVE_FLAGS.test(line)) continue;
|
|
275
|
+
findings.push(createFinding({
|
|
276
|
+
file, line: i + 1,
|
|
277
|
+
severity: 'high',
|
|
278
|
+
category: this.category,
|
|
279
|
+
rule: 'EAA_PERMISSIVE_AGENT_INVOCATION',
|
|
280
|
+
title: 'Checked-in Automation Runs an Agent with Permission Bypass',
|
|
281
|
+
description: 'A repo script/CI step launches a coding agent with approval/sandbox bypass flags. Unattended permissive agents are the execution primitive behind agent-abuse campaigns (EAA-002, malicious-artifact evidence).',
|
|
282
|
+
matched: line.trim().slice(0, 200),
|
|
283
|
+
confidence: 'high',
|
|
284
|
+
cwe: 'CWE-862',
|
|
285
|
+
owasp: 'ASI02',
|
|
286
|
+
eaa: 'EAA-002',
|
|
287
|
+
fix: 'Drop bypass flags from checked-in automation. Use scoped permissions, an allowlisted tool set, and a human-approval step for anything sensitive.',
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
290
|
+
return findings;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
_scanEnvOverrides(file) {
|
|
294
|
+
const findings = [];
|
|
295
|
+
let content;
|
|
296
|
+
try {
|
|
297
|
+
content = fs.readFileSync(file, 'utf-8');
|
|
298
|
+
} catch {
|
|
299
|
+
return findings;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const lines = content.split('\n');
|
|
303
|
+
for (let i = 0; i < lines.length; i++) {
|
|
304
|
+
const m = lines[i].match(VAR_LINE);
|
|
305
|
+
if (!m) continue;
|
|
306
|
+
const [, name, rawValue] = m;
|
|
307
|
+
const value = rawValue.trim().replace(/^["']|["']$/g, '');
|
|
308
|
+
|
|
309
|
+
if (GATEWAY_VARS.test(name) && value) {
|
|
310
|
+
const known = KNOWN_ENDPOINTS.test(value) || KNOWN_GATEWAYS.test(value);
|
|
311
|
+
findings.push(createFinding({
|
|
312
|
+
file, line: i + 1,
|
|
313
|
+
severity: known ? 'low' : 'medium',
|
|
314
|
+
category: this.category,
|
|
315
|
+
rule: 'EAA_PROVIDER_GATEWAY_OVERRIDE',
|
|
316
|
+
title: 'AI Provider Endpoint Override',
|
|
317
|
+
description: `${name} routes agent traffic to ${known ? 'a known gateway' : 'a non-vendor endpoint'}. Hostile routing points a trusted agent at an attacker-controlled gateway that can read or alter every prompt and response (EAA-007).${known ? ' Recognized gateway — verify it is intentional.' : ' Unrecognized host — verify before trusting.'}`,
|
|
318
|
+
matched: `${name}=${value.slice(0, 120)}`,
|
|
319
|
+
confidence: known ? 'low' : 'medium',
|
|
320
|
+
cwe: 'CWE-441',
|
|
321
|
+
owasp: 'ASI04',
|
|
322
|
+
eaa: 'EAA-007',
|
|
323
|
+
fix: 'Keep provider base URLs on vendor or explicitly approved gateway domains. Treat unexpected overrides in repo-shipped files as suspicious.',
|
|
324
|
+
}));
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (TELEMETRY_VARS.test(name) && value) {
|
|
328
|
+
const rawLogging = /OTEL_LOG_(?:TOOL_CONTENT|PROMPTS)/.test(name);
|
|
329
|
+
findings.push(createFinding({
|
|
330
|
+
file, line: i + 1,
|
|
331
|
+
severity: rawLogging ? 'high' : 'medium',
|
|
332
|
+
category: this.category,
|
|
333
|
+
rule: 'EAA_TELEMETRY_EXTERNAL',
|
|
334
|
+
title: rawLogging ? 'Raw Agent Content Logging to External Collector' : 'Agent Telemetry Endpoint Configured',
|
|
335
|
+
description: rawLogging
|
|
336
|
+
? `${name} enables logging of raw prompts/tool content. Combined with an external collector this exfiltrates everything the agent sees (EAA-012).`
|
|
337
|
+
: `${name} sends agent telemetry to ${value}. Prompts and tool output may include secrets and source code (EAA-012).`,
|
|
338
|
+
matched: `${name}=${value.slice(0, 120)}`,
|
|
339
|
+
confidence: 'medium',
|
|
340
|
+
cwe: 'CWE-532',
|
|
341
|
+
owasp: 'ASI03',
|
|
342
|
+
eaa: 'EAA-012',
|
|
343
|
+
fix: 'Point telemetry at an approved internal collector and disable raw prompt/tool-content logging unless explicitly required and reviewed.',
|
|
344
|
+
}));
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (SHADOW_DIR_VARS.test(name) && value) {
|
|
348
|
+
findings.push(createFinding({
|
|
349
|
+
file, line: i + 1,
|
|
350
|
+
severity: 'medium',
|
|
351
|
+
category: this.category,
|
|
352
|
+
rule: 'EAA_SHADOW_CONFIG_DIR',
|
|
353
|
+
title: 'Agent Config Directory Override',
|
|
354
|
+
description: `${name} redirects the agent to an alternate config directory. A shadow profile can carry attacker-controlled settings, hooks, and MCP servers past the user's normal environment (EAA-008).`,
|
|
355
|
+
matched: `${name}=${value.slice(0, 120)}`,
|
|
356
|
+
confidence: 'medium',
|
|
357
|
+
cwe: 'CWE-15',
|
|
358
|
+
owasp: 'ASI04',
|
|
359
|
+
eaa: 'EAA-008',
|
|
360
|
+
fix: 'Remove the override unless a reviewed workflow requires it. Never accept config-dir overrides from repo-shipped scripts or untrusted environments.',
|
|
361
|
+
}));
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return findings;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
_scanCommittedState(file, rootPath) {
|
|
368
|
+
const findings = [];
|
|
369
|
+
let stat;
|
|
370
|
+
try {
|
|
371
|
+
stat = fs.statSync(file);
|
|
372
|
+
} catch {
|
|
373
|
+
return findings;
|
|
374
|
+
}
|
|
375
|
+
if (stat.size > 2 * 1024 * 1024) return findings; // skip huge transcripts
|
|
376
|
+
|
|
377
|
+
let content;
|
|
378
|
+
try {
|
|
379
|
+
content = fs.readFileSync(file, 'utf-8');
|
|
380
|
+
} catch {
|
|
381
|
+
return findings;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const rel = path.relative(rootPath, file).replace(/\\/g, '/');
|
|
385
|
+
const hasSecret = TRANSCRIPT_SECRET.test(content);
|
|
386
|
+
findings.push(createFinding({
|
|
387
|
+
file, line: 1,
|
|
388
|
+
severity: hasSecret ? 'high' : 'medium',
|
|
389
|
+
category: this.category,
|
|
390
|
+
rule: 'EAA_COMMITTED_AGENT_STATE',
|
|
391
|
+
title: hasSecret ? 'Committed Agent Transcript Contains Secrets' : 'Committed Agent Session State',
|
|
392
|
+
description: hasSecret
|
|
393
|
+
? `Agent session state (${rel}) is committed and contains what looks like a live secret. Transcripts capture prompts, tool output, and credentials shown during sessions — a harvest target (EAA-005).`
|
|
394
|
+
: `Agent session state (${rel}) is committed to the repo. Transcripts and history files capture prompts and tool output and are a collection target for agent-state harvesting (EAA-005).`,
|
|
395
|
+
matched: rel,
|
|
396
|
+
confidence: hasSecret ? 'high' : 'medium',
|
|
397
|
+
cwe: 'CWE-538',
|
|
398
|
+
owasp: 'ASI03',
|
|
399
|
+
eaa: 'EAA-005',
|
|
400
|
+
fix: 'Add agent state directories (.claude/, session logs) to .gitignore and purge them from history. Rotate any secret visible in the transcript.',
|
|
401
|
+
}));
|
|
402
|
+
return findings;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exception Handling Agent
|
|
3
|
+
* =========================
|
|
4
|
+
*
|
|
5
|
+
* Detects mishandling of exceptional conditions — OWASP A10:2025.
|
|
6
|
+
*
|
|
7
|
+
* This is a NEW OWASP Top 10 2025 category that addresses:
|
|
8
|
+
* - Empty catch blocks that swallow errors silently
|
|
9
|
+
* - Generic catch-all without proper handling
|
|
10
|
+
* - Unhandled promise rejections
|
|
11
|
+
* - Missing React/Vue error boundaries
|
|
12
|
+
* - Stack traces leaked to production responses
|
|
13
|
+
* - Error responses that expose internal details
|
|
14
|
+
* - Missing finally/cleanup in resource handling
|
|
15
|
+
*
|
|
16
|
+
* Maps to: OWASP A10:2025, CWE-390, CWE-754, CWE-755
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import path from 'path';
|
|
20
|
+
import { BaseAgent, createFinding } from './base-agent.js';
|
|
21
|
+
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// EXCEPTION HANDLING PATTERNS
|
|
24
|
+
// =============================================================================
|
|
25
|
+
|
|
26
|
+
export const PATTERNS = [
|
|
27
|
+
// ── Empty/Silent Error Handling ────────────────────────────────────────────
|
|
28
|
+
{
|
|
29
|
+
rule: 'EXCEPTION_EMPTY_CATCH',
|
|
30
|
+
title: 'Exception: Empty catch Block',
|
|
31
|
+
regex: /catch\s*\(\s*(?:e|err|error|ex|exception|_)?\s*\)\s*\{\s*\}/g,
|
|
32
|
+
severity: 'medium',
|
|
33
|
+
cwe: 'CWE-390',
|
|
34
|
+
owasp: 'A10:2025',
|
|
35
|
+
description: 'Empty catch block silently swallows errors. Failures go undetected, masking bugs and security issues.',
|
|
36
|
+
fix: 'Handle the error: catch (err) { logger.error("Context:", err); throw err; }',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
rule: 'EXCEPTION_CATCH_COMMENT_ONLY',
|
|
40
|
+
title: 'Exception: catch Block With Only a Comment',
|
|
41
|
+
regex: /catch\s*\(\s*\w*\s*\)\s*\{\s*\/\/[^\n]*\s*\}/g,
|
|
42
|
+
severity: 'low',
|
|
43
|
+
cwe: 'CWE-390',
|
|
44
|
+
owasp: 'A10:2025',
|
|
45
|
+
confidence: 'medium',
|
|
46
|
+
description: 'Catch block contains only a comment — error is still silently swallowed.',
|
|
47
|
+
fix: 'At minimum, log the error for debugging: catch (err) { /* expected for X */ logger.debug(err); }',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
rule: 'EXCEPTION_PYTHON_BARE_EXCEPT',
|
|
51
|
+
title: 'Exception: Python Bare except: (catches everything)',
|
|
52
|
+
regex: /^(\s*)except\s*:\s*$/gm,
|
|
53
|
+
severity: 'high',
|
|
54
|
+
cwe: 'CWE-396',
|
|
55
|
+
owasp: 'A10:2025',
|
|
56
|
+
description: 'Bare except: catches all exceptions including SystemExit and KeyboardInterrupt. Use except Exception instead.',
|
|
57
|
+
fix: 'Use specific exception types: except ValueError as e: or at least except Exception as e:',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
rule: 'EXCEPTION_PYTHON_PASS',
|
|
61
|
+
title: 'Exception: Python except with pass',
|
|
62
|
+
regex: /except\s+\w+(?:\s+as\s+\w+)?:\s*\n\s+pass\s*$/gm,
|
|
63
|
+
severity: 'medium',
|
|
64
|
+
cwe: 'CWE-390',
|
|
65
|
+
owasp: 'A10:2025',
|
|
66
|
+
description: 'Python except block with only pass — error is silently ignored.',
|
|
67
|
+
fix: 'Log the error: except Exception as e: logger.warning(f"Handled: {e}")',
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
// ── Unhandled Async Errors ────────────────────────────────────────────────
|
|
71
|
+
{
|
|
72
|
+
rule: 'EXCEPTION_UNHANDLED_PROMISE',
|
|
73
|
+
title: 'Exception: Async Function Without Error Handling',
|
|
74
|
+
regex: /(?:app|router)\.(?:get|post|put|patch|delete)\s*\(\s*['"][^'"]+['"]\s*,\s*async\s+(?:\(\s*(?:req|request|ctx)\s*(?:,\s*(?:res|response|next))?\s*\)|(?:req|request|ctx))\s*=>\s*\{(?:(?!try\s*\{|\.catch\b).){0,500}\}/gs,
|
|
75
|
+
severity: 'medium',
|
|
76
|
+
cwe: 'CWE-755',
|
|
77
|
+
owasp: 'A10:2025',
|
|
78
|
+
confidence: 'low',
|
|
79
|
+
description: 'Async route handler without try/catch or .catch(). Unhandled rejection will crash the process or return 500.',
|
|
80
|
+
fix: 'Wrap in try/catch or use an async error wrapper: app.get("/path", asyncHandler(async (req, res) => { ... }))',
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// ── Error Information Leakage ─────────────────────────────────────────────
|
|
84
|
+
{
|
|
85
|
+
rule: 'EXCEPTION_STACK_IN_RESPONSE',
|
|
86
|
+
title: 'Exception: Stack Trace in Response',
|
|
87
|
+
regex: /(?:res\.(?:json|send|status)|response\.(?:json|send))\s*\([^)]*(?:\.stack|stackTrace|stack_trace|err\.message)/g,
|
|
88
|
+
severity: 'high',
|
|
89
|
+
cwe: 'CWE-209',
|
|
90
|
+
owasp: 'A10:2025',
|
|
91
|
+
description: 'Error stack trace or raw error message sent in API response. Reveals internal paths, versions, and logic to attackers.',
|
|
92
|
+
fix: 'Return generic error to client, log details server-side: res.status(500).json({ error: "Internal error", id: errorId })',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
rule: 'EXCEPTION_FULL_ERROR_RESPONSE',
|
|
96
|
+
title: 'Exception: Full Error Object in Response',
|
|
97
|
+
regex: /(?:res\.(?:json|send)|response\.(?:json|send))\s*\(\s*(?:err|error|e|ex)\s*\)/g,
|
|
98
|
+
severity: 'high',
|
|
99
|
+
cwe: 'CWE-209',
|
|
100
|
+
owasp: 'A10:2025',
|
|
101
|
+
description: 'Full error object sent directly in response. May contain stack traces, file paths, and sensitive context.',
|
|
102
|
+
fix: 'Send only safe fields: res.json({ error: err.message, code: err.code })',
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// ── Missing Error Boundaries ──────────────────────────────────────────────
|
|
106
|
+
{
|
|
107
|
+
rule: 'EXCEPTION_NO_ERROR_BOUNDARY',
|
|
108
|
+
title: 'Exception: React App Without Error Boundary',
|
|
109
|
+
regex: /(?:createRoot|ReactDOM\.render)\s*\(\s*(?:(?!ErrorBoundary|error-boundary|Sentry\.ErrorBoundary)[\s\S]){0,200}\)/g,
|
|
110
|
+
severity: 'medium',
|
|
111
|
+
cwe: 'CWE-755',
|
|
112
|
+
owasp: 'A10:2025',
|
|
113
|
+
confidence: 'low',
|
|
114
|
+
description: 'React app rendered without an Error Boundary. Unhandled component errors will crash the entire UI.',
|
|
115
|
+
fix: 'Wrap your app: <ErrorBoundary fallback={<ErrorPage />}><App /></ErrorBoundary>',
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
// ── Generic Catch-All Without Rethrow ─────────────────────────────────────
|
|
119
|
+
{
|
|
120
|
+
rule: 'EXCEPTION_CATCH_ALL_NO_RETHROW',
|
|
121
|
+
title: 'Exception: catch(Exception) Without Rethrow',
|
|
122
|
+
regex: /catch\s*\(\s*(?:Exception|Error|Throwable|BaseException)\s+\w+\s*\)\s*\{(?:(?!throw\b|rethrow\b).){0,200}\}/gs,
|
|
123
|
+
severity: 'medium',
|
|
124
|
+
cwe: 'CWE-396',
|
|
125
|
+
owasp: 'A10:2025',
|
|
126
|
+
confidence: 'low',
|
|
127
|
+
description: 'Catching broad Exception/Error without rethrowing. Unexpected errors are silently absorbed.',
|
|
128
|
+
fix: 'Either rethrow unexpected exceptions or catch only specific exception types you can handle.',
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
// ── Process-Level Missing Handlers ────────────────────────────────────────
|
|
132
|
+
{
|
|
133
|
+
rule: 'EXCEPTION_NO_UNCAUGHT_HANDLER',
|
|
134
|
+
title: 'Exception: Missing uncaughtException Handler',
|
|
135
|
+
regex: /(?:http\.createServer|express\(\)|new\s+Koa|fastify\(\)|new\s+Hono)(?:(?!uncaughtException|unhandledRejection).){0,2000}(?:\.listen|module\.exports)/gs,
|
|
136
|
+
severity: 'medium',
|
|
137
|
+
cwe: 'CWE-755',
|
|
138
|
+
owasp: 'A10:2025',
|
|
139
|
+
confidence: 'low',
|
|
140
|
+
description: 'Server application without process.on("uncaughtException") handler. Unhandled errors will crash the process.',
|
|
141
|
+
fix: 'Add: process.on("uncaughtException", (err) => { logger.error("Uncaught:", err); process.exit(1); })',
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
// ── Resource Cleanup ──────────────────────────────────────────────────────
|
|
145
|
+
{
|
|
146
|
+
rule: 'EXCEPTION_OPEN_WITHOUT_CLOSE',
|
|
147
|
+
title: 'Exception: Resource Opened Without finally/close',
|
|
148
|
+
regex: /(?:createConnection|createPool|open\(|connect\()[\s\S]{0,500}(?:(?!\.finally|\.close\(|\.end\(|\.release\(|\.destroy\(|finally\s*\{|with\s).){200,}$/gm,
|
|
149
|
+
severity: 'low',
|
|
150
|
+
cwe: 'CWE-404',
|
|
151
|
+
owasp: 'A10:2025',
|
|
152
|
+
confidence: 'low',
|
|
153
|
+
description: 'Database connection or resource opened without visible close/finally block. Resource leaks under error conditions.',
|
|
154
|
+
fix: 'Use try/finally or a connection pool: try { conn = await pool.connect(); ... } finally { conn.release(); }',
|
|
155
|
+
},
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
// =============================================================================
|
|
159
|
+
// EXCEPTION HANDLER AGENT CLASS
|
|
160
|
+
// =============================================================================
|
|
161
|
+
|
|
162
|
+
export class ExceptionHandlerAgent extends BaseAgent {
|
|
163
|
+
constructor() {
|
|
164
|
+
super(
|
|
165
|
+
'ExceptionHandlerAgent',
|
|
166
|
+
'Detects mishandling of exceptional conditions (OWASP A10:2025)',
|
|
167
|
+
'injection', // maps to Code Vulnerabilities
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async analyze(context) {
|
|
172
|
+
const files = this.getFilesToScan(context);
|
|
173
|
+
const findings = [];
|
|
174
|
+
|
|
175
|
+
for (const file of files) {
|
|
176
|
+
const ext = path.extname(file).toLowerCase();
|
|
177
|
+
if (!['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs',
|
|
178
|
+
'.py', '.rb', '.go', '.java', '.rs', '.php',
|
|
179
|
+
'.vue', '.svelte'].includes(ext)) continue;
|
|
180
|
+
|
|
181
|
+
const results = this.scanFileWithPatterns(file, PATTERNS);
|
|
182
|
+
findings.push(...results);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return findings;
|
|
186
|
+
}
|
|
187
|
+
}
|