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,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHistoryScanner Agent
|
|
3
|
+
* ========================
|
|
4
|
+
*
|
|
5
|
+
* Scans git commit history for secrets that were committed
|
|
6
|
+
* and later removed but remain in repository history.
|
|
7
|
+
* These are the most dangerous secrets — developers think
|
|
8
|
+
* they're deleted but they're still accessible.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { execSync, execFileSync } from 'child_process';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { BaseAgent, createFinding } from './base-agent.js';
|
|
14
|
+
import { SECRET_PATTERNS } from '../utils/patterns.js';
|
|
15
|
+
|
|
16
|
+
// Compile a fast combined regex from all secret patterns
|
|
17
|
+
const FAST_SECRET_PATTERNS = SECRET_PATTERNS.map(p => ({
|
|
18
|
+
name: p.name,
|
|
19
|
+
pattern: p.pattern,
|
|
20
|
+
severity: p.severity,
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
export class GitHistoryScanner extends BaseAgent {
|
|
24
|
+
constructor() {
|
|
25
|
+
super('GitHistoryScanner', 'Scan git history for leaked secrets', 'history');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async analyze(context) {
|
|
29
|
+
const { rootPath, options } = context;
|
|
30
|
+
const findings = [];
|
|
31
|
+
|
|
32
|
+
// Check if this is a git repository
|
|
33
|
+
if (!this.isGitRepo(rootPath)) return [];
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
// Get recent commits (default: last 50, configurable)
|
|
37
|
+
const maxCommits = options?.maxCommits || 50;
|
|
38
|
+
const since = options?.since || null;
|
|
39
|
+
|
|
40
|
+
const gitArgs = ['-C', rootPath, 'log', '--all', '--diff-filter=A', '--diff-filter=M', '-p', '--no-color', `--max-count=${parseInt(maxCommits, 10)}`];
|
|
41
|
+
if (since) {
|
|
42
|
+
gitArgs.push(`--since=${since}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let diffOutput;
|
|
46
|
+
try {
|
|
47
|
+
diffOutput = execFileSync('git', gitArgs, {
|
|
48
|
+
cwd: rootPath,
|
|
49
|
+
encoding: 'utf-8',
|
|
50
|
+
maxBuffer: 50 * 1024 * 1024, // 50MB buffer
|
|
51
|
+
timeout: 60000, // 60s timeout
|
|
52
|
+
});
|
|
53
|
+
} catch {
|
|
54
|
+
// git log failed — might be a shallow clone or no history
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!diffOutput) return [];
|
|
59
|
+
|
|
60
|
+
// Parse the diff output
|
|
61
|
+
let currentFile = '';
|
|
62
|
+
let currentCommit = '';
|
|
63
|
+
let currentDate = '';
|
|
64
|
+
const lines = diffOutput.split('\n');
|
|
65
|
+
|
|
66
|
+
for (let i = 0; i < lines.length; i++) {
|
|
67
|
+
const line = lines[i];
|
|
68
|
+
|
|
69
|
+
// Track current commit
|
|
70
|
+
if (line.startsWith('commit ')) {
|
|
71
|
+
currentCommit = line.slice(7, 17); // First 10 chars of hash
|
|
72
|
+
}
|
|
73
|
+
if (line.startsWith('Date:')) {
|
|
74
|
+
currentDate = line.slice(5).trim();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Track current file
|
|
78
|
+
if (line.startsWith('diff --git ')) {
|
|
79
|
+
const match = line.match(/diff --git a\/(.+) b\//);
|
|
80
|
+
if (match) currentFile = match[1];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Only check added lines (lines starting with +)
|
|
84
|
+
if (!line.startsWith('+') || line.startsWith('+++')) continue;
|
|
85
|
+
|
|
86
|
+
const addedLine = line.slice(1); // Remove the leading +
|
|
87
|
+
|
|
88
|
+
// Check against all secret patterns
|
|
89
|
+
for (const p of FAST_SECRET_PATTERNS) {
|
|
90
|
+
p.pattern.lastIndex = 0;
|
|
91
|
+
const match = p.pattern.exec(addedLine);
|
|
92
|
+
if (match) {
|
|
93
|
+
// Check if this secret still exists in current working tree
|
|
94
|
+
const stillExists = this.existsInWorkingTree(rootPath, match[0]);
|
|
95
|
+
|
|
96
|
+
findings.push(createFinding({
|
|
97
|
+
file: path.join(rootPath, currentFile),
|
|
98
|
+
line: 0, // Line number not meaningful in history
|
|
99
|
+
severity: stillExists ? p.severity : this.elevateSeverity(p.severity),
|
|
100
|
+
category: 'history',
|
|
101
|
+
rule: 'GIT_HISTORY_SECRET',
|
|
102
|
+
title: `Historical Secret: ${p.name}`,
|
|
103
|
+
description: stillExists
|
|
104
|
+
? `Secret found in current code AND in git history (commit ${currentCommit}).`
|
|
105
|
+
: `Secret was removed from code but still exists in git history (commit ${currentCommit}, ${currentDate}). Anyone with repo access can retrieve it.`,
|
|
106
|
+
matched: this.maskSecret(match[0]),
|
|
107
|
+
confidence: 'high',
|
|
108
|
+
fix: stillExists
|
|
109
|
+
? 'Remove from code, rotate the credential, then clean git history with BFG or git filter-repo'
|
|
110
|
+
: 'Rotate this credential immediately, then clean history: npx bfg --replace-text passwords.txt',
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Deduplicate by matched value (same secret in multiple commits)
|
|
117
|
+
const seen = new Set();
|
|
118
|
+
return findings.filter(f => {
|
|
119
|
+
const key = `${f.matched}:${f.title}`;
|
|
120
|
+
if (seen.has(key)) return false;
|
|
121
|
+
seen.add(key);
|
|
122
|
+
return true;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
} catch (err) {
|
|
126
|
+
// Don't fail the entire scan if git history scan fails
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
isGitRepo(dir) {
|
|
132
|
+
try {
|
|
133
|
+
execSync('git rev-parse --is-inside-work-tree', { cwd: dir, stdio: 'pipe' });
|
|
134
|
+
return true;
|
|
135
|
+
} catch {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
existsInWorkingTree(rootPath, secret) {
|
|
141
|
+
try {
|
|
142
|
+
const result = execFileSync('git', [
|
|
143
|
+
'-C', rootPath, 'grep', '-F', '-l', secret
|
|
144
|
+
], {
|
|
145
|
+
cwd: rootPath,
|
|
146
|
+
encoding: 'utf-8',
|
|
147
|
+
timeout: 5000,
|
|
148
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
149
|
+
});
|
|
150
|
+
return result.trim().length > 0;
|
|
151
|
+
} catch {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
elevateSeverity(sev) {
|
|
157
|
+
// Secrets in history-only are MORE dangerous (developer thinks they're gone)
|
|
158
|
+
if (sev === 'medium') return 'high';
|
|
159
|
+
if (sev === 'high') return 'critical';
|
|
160
|
+
return sev;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
maskSecret(secret) {
|
|
164
|
+
if (secret.length <= 10) return secret.slice(0, 4) + '***';
|
|
165
|
+
return secret.slice(0, 8) + '***' + secret.slice(-4);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export default GitHistoryScanner;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Governance Absence-Audits (post-processor)
|
|
3
|
+
* ==========================================
|
|
4
|
+
*
|
|
5
|
+
* Detects MISSING controls rather than present bugs — evidence no generic
|
|
6
|
+
* regex scanner produces:
|
|
7
|
+
*
|
|
8
|
+
* no-human-oversight — high-blast-radius agent tool authority (financial /
|
|
9
|
+
* destructive actions, excessive agency) with no
|
|
10
|
+
* approval-gate pattern anywhere in the repo.
|
|
11
|
+
* → EU AI Act Art. 14 / ISO 42001 A.12.4 evidence.
|
|
12
|
+
*
|
|
13
|
+
* no-observability — a repo that uses AI (LLM findings exist) but wires
|
|
14
|
+
* no AI tracing/observability stack at all.
|
|
15
|
+
* → EU AI Act Art. 12 / ISO 42001 A.6.2.6 evidence.
|
|
16
|
+
*
|
|
17
|
+
* Runs after the agent pool; findings are merged into the scan results.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import fs from 'fs';
|
|
21
|
+
import path from 'path';
|
|
22
|
+
|
|
23
|
+
// Approval-gate patterns — presence of any of these near high-risk agent
|
|
24
|
+
// actions counts as a human-oversight control.
|
|
25
|
+
export const OVERSIGHT_PATTERNS = [
|
|
26
|
+
/interrupt_before/i, /interrupt_after/i, /human[_-]?in[_-]?the[_-]?loop/i,
|
|
27
|
+
/requires_approval/i, /confirmation_required/i, /requires_confirmation/i,
|
|
28
|
+
/approval[_-]?gate/i, /human[_-]?approval/i, /ask[_-]?for[_-]?confirmation/i,
|
|
29
|
+
/confirm[_-]?before/i, /await[_-]?confirmation/i, /approval[_-]?required/i,
|
|
30
|
+
/manual[_-]?review/i, /two[_-]?person[_-]?rule/i, /maker[_-]?checker/i,
|
|
31
|
+
/human[_-]?check/i, /approve[_-]?tool/i, /permission[_-]?request/i,
|
|
32
|
+
/consent[_-]?check/i, /guardrail/i,
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
// Financial / destructive / high-blast-radius agent action signals.
|
|
36
|
+
const HIGH_RISK_ACTION = /financial|refund|payment|charge|transfer|withdraw|payout|invoice|delete|drop|truncate|purge|destroy|write_db|update_record|insert|modify|send_email|send_slack|send_sms|post_to|excessive.?agency|blast.?radius/i;
|
|
37
|
+
|
|
38
|
+
// AI observability/tracing wiring indicators.
|
|
39
|
+
const OBSERVABILITY_INDICATORS = [
|
|
40
|
+
/langsmith/i, /langfuse/i, /helicone/i, /traceloop/i, /openllmetry/i,
|
|
41
|
+
/openinference/i, /opentelemetry/i, /otel/i, /wandb\b/i, /weave\b/i,
|
|
42
|
+
/mlflow/i, /logfire/i, /arize/i, /phoenix\.arize/i, /braintrust/i,
|
|
43
|
+
/langtrace/i, /agentops/i, /lunary/i, /promptfoo/i, /deepeval/i,
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
// Files excluded from the observability check: dependency manifests and
|
|
47
|
+
// lockfiles are not proof of wiring (a transitive dep isn't your tracing).
|
|
48
|
+
const OBSERVABILITY_EXCLUDE = /(?:package-lock\.json|yarn\.lock|pnpm-lock\.yaml|poetry\.lock|requirements[^/]*\.txt|Cargo\.lock|go\.sum|composer\.lock|Gemfile\.lock|node_modules[\\/])/i;
|
|
49
|
+
|
|
50
|
+
// Manifest-style files where tracing-adjacent names commonly appear as
|
|
51
|
+
// dependencies even when nothing is wired.
|
|
52
|
+
const MANIFEST_FILES = /(?:package\.json|requirements[^/]*\.txt|pyproject\.toml|Cargo\.toml|go\.mod|Gemfile)$/i;
|
|
53
|
+
|
|
54
|
+
function readFileSafe(file) {
|
|
55
|
+
try {
|
|
56
|
+
return fs.readFileSync(file, 'utf8');
|
|
57
|
+
} catch {
|
|
58
|
+
return '';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {object} args
|
|
64
|
+
* @param {string} args.rootPath — project root
|
|
65
|
+
* @param {string[]} args.files — files discovered by the orchestrator
|
|
66
|
+
* @param {object[]} args.findings — agent-pool findings for this run
|
|
67
|
+
* @returns {object[]} governance findings to merge
|
|
68
|
+
*/
|
|
69
|
+
export function runGovernanceAudits({ rootPath, files = [], findings = [] }) {
|
|
70
|
+
const audits = [];
|
|
71
|
+
|
|
72
|
+
// ── no-human-oversight ─────────────────────────────────────────────────
|
|
73
|
+
const riskyActions = findings.filter(f =>
|
|
74
|
+
HIGH_RISK_ACTION.test(`${f.rule || ''} ${f.title || ''}`)
|
|
75
|
+
&& (f.category === 'llm' || f.category === 'agentic' || f.category === 'mcp' || f.rule?.startsWith('MCP_'))
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (riskyActions.length > 0) {
|
|
79
|
+
const repoHasOversight = files.some(f => OVERSIGHT_PATTERNS.some(re => re.test(readFileSafe(f))));
|
|
80
|
+
if (!repoHasOversight) {
|
|
81
|
+
const example = riskyActions[0];
|
|
82
|
+
audits.push({
|
|
83
|
+
file: example.file || path.join(rootPath, 'agent-config'),
|
|
84
|
+
line: 1,
|
|
85
|
+
column: 0,
|
|
86
|
+
severity: 'high',
|
|
87
|
+
category: 'llm',
|
|
88
|
+
rule: 'NO_HUMAN_OVERSIGHT',
|
|
89
|
+
title: 'High-Risk Agent Actions Without Human Oversight Gate',
|
|
90
|
+
description: `${riskyActions.length} finding(s) grant the agent high-blast-radius actions (financial, destructive, or broad tool authority), but no approval-gate pattern (interrupt_before, requires_approval, human_in_the_loop, ...) was found anywhere in the repository. EU AI Act Art. 14 requires effective human oversight for high-risk AI systems.`,
|
|
91
|
+
matched: `oversight patterns absent for ${riskyActions.length} risky action(s)`,
|
|
92
|
+
confidence: 'medium',
|
|
93
|
+
cwe: 'CWE-862',
|
|
94
|
+
owasp: 'ASI08',
|
|
95
|
+
eaa: 'EAA-002',
|
|
96
|
+
fix: 'Add human-approval gates before every financial/destructive agent action (framework interrupts, requires_approval flags, or a two-person rule).',
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ── no-observability ───────────────────────────────────────────────────
|
|
102
|
+
const usesAi = findings.some(f =>
|
|
103
|
+
f.category === 'llm' || f.category === 'agentic'
|
|
104
|
+
|| /(?:llm|openai|anthropic|gemini|prompt|mcp|model|rag)/i.test(`${f.rule || ''} ${f.title || ''}`)
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
if (usesAi) {
|
|
108
|
+
const evidenceFiles = files.filter(f => {
|
|
109
|
+
const rel = path.relative(rootPath, f).replace(/\\/g, '/');
|
|
110
|
+
if (OBSERVABILITY_EXCLUDE.test(rel)) return false;
|
|
111
|
+
if (MANIFEST_FILES.test(path.basename(f))) return false;
|
|
112
|
+
return true;
|
|
113
|
+
});
|
|
114
|
+
const wired = evidenceFiles.some(f =>
|
|
115
|
+
OBSERVABILITY_INDICATORS.some(re => re.test(readFileSafe(f)))
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
if (!wired) {
|
|
119
|
+
audits.push({
|
|
120
|
+
file: path.join(rootPath, 'observability'),
|
|
121
|
+
line: 1,
|
|
122
|
+
column: 0,
|
|
123
|
+
severity: 'medium',
|
|
124
|
+
category: 'llm',
|
|
125
|
+
rule: 'NO_OBSERVABILITY',
|
|
126
|
+
title: 'AI in Use Without Observability/Tracing',
|
|
127
|
+
description: 'This project calls LLMs, but no AI tracing/observability wiring (LangSmith, Langfuse, Helicone, OpenTelemetry, ...) was found outside dependency manifests. Without traces you cannot detect prompt-injection events, monitor output quality, or respond to AI incidents — EU AI Act Art. 12 / ISO 42001 A.6.2.6.',
|
|
128
|
+
matched: `no tracing wiring in ${evidenceFiles.length} evidence file(s)`,
|
|
129
|
+
confidence: 'medium',
|
|
130
|
+
cwe: 'CWE-778',
|
|
131
|
+
owasp: 'ASI03',
|
|
132
|
+
fix: 'Wire an AI tracing/observability stack (e.g., Langfuse, Helicone, or OpenTelemetry) and log prompts, tool calls, and outputs with retention.',
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return audits;
|
|
138
|
+
}
|