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.
Files changed (183) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +170 -0
  3. package/ai-defense/cost-protection.md +292 -0
  4. package/ai-defense/llm-security-checklist.md +324 -0
  5. package/ai-defense/prompt-injection-patterns.js +283 -0
  6. package/ai-defense/system-prompt-armor.md +327 -0
  7. package/checklists/launch-day.md +168 -0
  8. package/cli/agents/abom-generator.js +225 -0
  9. package/cli/agents/agent-attestation-agent.js +318 -0
  10. package/cli/agents/agent-config-scanner.js +787 -0
  11. package/cli/agents/agent-telemetry-agent.js +415 -0
  12. package/cli/agents/agentic-security-agent.js +296 -0
  13. package/cli/agents/agentic-supply-chain-agent.js +463 -0
  14. package/cli/agents/ai-infra-inventory-agent.js +449 -0
  15. package/cli/agents/api-fuzzer.js +345 -0
  16. package/cli/agents/auth-bypass-agent.js +348 -0
  17. package/cli/agents/base-agent.js +280 -0
  18. package/cli/agents/cicd-scanner.js +300 -0
  19. package/cli/agents/config-auditor.js +757 -0
  20. package/cli/agents/deep-analyzer.js +776 -0
  21. package/cli/agents/endpoint-agent-abuse-agent.js +404 -0
  22. package/cli/agents/exception-handler-agent.js +187 -0
  23. package/cli/agents/git-history-scanner.js +169 -0
  24. package/cli/agents/governance-audits.js +138 -0
  25. package/cli/agents/hermes-security-agent.js +536 -0
  26. package/cli/agents/html-reporter.js +1125 -0
  27. package/cli/agents/index.js +147 -0
  28. package/cli/agents/injection-tester.js +502 -0
  29. package/cli/agents/legal-risk-agent.js +328 -0
  30. package/cli/agents/llm-redteam.js +199 -0
  31. package/cli/agents/managed-agent-scanner.js +333 -0
  32. package/cli/agents/mcp-security-agent.js +588 -0
  33. package/cli/agents/memory-poisoning-agent.js +305 -0
  34. package/cli/agents/mobile-scanner.js +231 -0
  35. package/cli/agents/model-file-scanner.js +259 -0
  36. package/cli/agents/orchestrator.js +355 -0
  37. package/cli/agents/pii-compliance-agent.js +301 -0
  38. package/cli/agents/policy-engine.js +229 -0
  39. package/cli/agents/prompt-injection-prober.js +224 -0
  40. package/cli/agents/rag-security-agent.js +204 -0
  41. package/cli/agents/recon-agent.js +207 -0
  42. package/cli/agents/sbom-generator.js +265 -0
  43. package/cli/agents/scoring-engine.js +273 -0
  44. package/cli/agents/ssrf-prober.js +130 -0
  45. package/cli/agents/stateful-watcher.js +238 -0
  46. package/cli/agents/supabase-rls-agent.js +154 -0
  47. package/cli/agents/supply-chain-agent.js +857 -0
  48. package/cli/agents/swarm-orchestrator.js +200 -0
  49. package/cli/agents/verifier-agent.js +303 -0
  50. package/cli/agents/vibe-coding-agent.js +250 -0
  51. package/cli/bin/praxis.js +866 -0
  52. package/cli/commands/abom.js +73 -0
  53. package/cli/commands/agent-fix.js +1245 -0
  54. package/cli/commands/audit.js +1180 -0
  55. package/cli/commands/autofix.js +383 -0
  56. package/cli/commands/baseline.js +193 -0
  57. package/cli/commands/benchmark.js +327 -0
  58. package/cli/commands/checklist.js +223 -0
  59. package/cli/commands/ci.js +403 -0
  60. package/cli/commands/deps.js +516 -0
  61. package/cli/commands/diff.js +200 -0
  62. package/cli/commands/doctor.js +195 -0
  63. package/cli/commands/env-audit.js +349 -0
  64. package/cli/commands/fix.js +218 -0
  65. package/cli/commands/guard.js +396 -0
  66. package/cli/commands/hooks.js +278 -0
  67. package/cli/commands/init.js +514 -0
  68. package/cli/commands/legal.js +158 -0
  69. package/cli/commands/live-advisories.js +241 -0
  70. package/cli/commands/mcp.js +660 -0
  71. package/cli/commands/openclaw.js +386 -0
  72. package/cli/commands/red-team.js +350 -0
  73. package/cli/commands/redteam.js +78 -0
  74. package/cli/commands/remediate.js +797 -0
  75. package/cli/commands/rotate.js +768 -0
  76. package/cli/commands/rules.js +196 -0
  77. package/cli/commands/scan-mcp.js +534 -0
  78. package/cli/commands/scan-skill.js +588 -0
  79. package/cli/commands/scan-standard.js +251 -0
  80. package/cli/commands/scan.js +524 -0
  81. package/cli/commands/score.js +449 -0
  82. package/cli/commands/shell.js +514 -0
  83. package/cli/commands/team-report.js +398 -0
  84. package/cli/commands/undo.js +161 -0
  85. package/cli/commands/update-intel.js +126 -0
  86. package/cli/commands/vibe-check.js +276 -0
  87. package/cli/commands/watch.js +757 -0
  88. package/cli/commands/web.js +63 -0
  89. package/cli/core/ast/guardrail-detector.js +141 -0
  90. package/cli/core/ast/index.js +22 -0
  91. package/cli/core/ast/parser.js +676 -0
  92. package/cli/core/ast/scope-tree.js +287 -0
  93. package/cli/core/ast/taint-tracker.js +158 -0
  94. package/cli/core/branding.js +37 -0
  95. package/cli/core/env.js +38 -0
  96. package/cli/core/errors.js +61 -0
  97. package/cli/core/fs.js +62 -0
  98. package/cli/core/output/compliance.js +90 -0
  99. package/cli/core/output/html-theme.js +158 -0
  100. package/cli/core/output/index.js +57 -0
  101. package/cli/core/output/json.js +48 -0
  102. package/cli/core/output/sarif.js +240 -0
  103. package/cli/core/version.js +67 -0
  104. package/cli/core/web/jobs.js +183 -0
  105. package/cli/core/web/projects.js +146 -0
  106. package/cli/core/web/server.js +439 -0
  107. package/cli/data/atlas-knowledge.json +5640 -0
  108. package/cli/data/eaa-catalog.json +39 -0
  109. package/cli/data/known-mcps.json +26 -0
  110. package/cli/data/probes/prompt-injection-corpus.json +271 -0
  111. package/cli/data/threat-intel.json +85 -0
  112. package/cli/data/threatpacks/latest.json +41 -0
  113. package/cli/hooks/patterns.js +313 -0
  114. package/cli/hooks/post-tool-use.js +140 -0
  115. package/cli/hooks/pre-tool-use.js +186 -0
  116. package/cli/index.js +90 -0
  117. package/cli/providers/llm-provider.js +766 -0
  118. package/cli/utils/autofix-rules.js +74 -0
  119. package/cli/utils/cache-manager.js +310 -0
  120. package/cli/utils/compliance-map.js +191 -0
  121. package/cli/utils/entropy.js +132 -0
  122. package/cli/utils/fix-ledger.js +127 -0
  123. package/cli/utils/hermes-tool-registry.js +252 -0
  124. package/cli/utils/intel/cache.js +61 -0
  125. package/cli/utils/intel/http.js +88 -0
  126. package/cli/utils/intel/index.js +235 -0
  127. package/cli/utils/intel/merge.js +229 -0
  128. package/cli/utils/intel/sources/epss.js +54 -0
  129. package/cli/utils/intel/sources/ghsa.js +81 -0
  130. package/cli/utils/intel/sources/gitguardian.js +40 -0
  131. package/cli/utils/intel/sources/gitleaks.js +101 -0
  132. package/cli/utils/intel/sources/kev.js +38 -0
  133. package/cli/utils/intel/sources/nvd.js +84 -0
  134. package/cli/utils/intel/sources/osv.js +132 -0
  135. package/cli/utils/intel/sources/phylum.js +44 -0
  136. package/cli/utils/intel/sources/snyk.js +46 -0
  137. package/cli/utils/intel/sources/socket.js +69 -0
  138. package/cli/utils/intel/sources/sonatype.js +84 -0
  139. package/cli/utils/intel/sources/threatpack.js +69 -0
  140. package/cli/utils/mcp-trust.js +60 -0
  141. package/cli/utils/output.js +251 -0
  142. package/cli/utils/patterns.js +1130 -0
  143. package/cli/utils/pdf-generator.js +94 -0
  144. package/cli/utils/plugin-loader.js +364 -0
  145. package/cli/utils/rule-import.js +228 -0
  146. package/cli/utils/rule-registry.js +426 -0
  147. package/cli/utils/scan-fingerprint.js +109 -0
  148. package/cli/utils/scan-playbook.js +312 -0
  149. package/cli/utils/score-history.js +119 -0
  150. package/cli/utils/secrets-verifier.js +247 -0
  151. package/cli/utils/security-memory.js +296 -0
  152. package/cli/utils/standards/atlas-knowledge.js +87 -0
  153. package/cli/utils/standards/index.js +127 -0
  154. package/cli/utils/standards/sources/avid.js +45 -0
  155. package/cli/utils/standards/sources/eu-ai-act.js +89 -0
  156. package/cli/utils/standards/sources/google-saif.js +39 -0
  157. package/cli/utils/standards/sources/iso-42001.js +94 -0
  158. package/cli/utils/standards/sources/mitre-atlas.js +54 -0
  159. package/cli/utils/standards/sources/nist-ai-600-1.js +45 -0
  160. package/cli/utils/standards/sources/owasp-llm.js +45 -0
  161. package/cli/utils/standards/sources/owasp-ml.js +45 -0
  162. package/cli/utils/threat-intel.js +265 -0
  163. package/configs/firebase/firestore-rules.txt +215 -0
  164. package/configs/firebase/security-checklist.md +236 -0
  165. package/configs/firebase/storage-rules.txt +206 -0
  166. package/configs/gitignore-template +258 -0
  167. package/configs/nextjs-security-headers.js +220 -0
  168. package/configs/praxisignore-template +50 -0
  169. package/configs/supabase/secure-client.ts +225 -0
  170. package/configs/supabase/security-checklist.md +278 -0
  171. package/docs/THIRD_PARTY_NOTICES.md +26 -0
  172. package/docs/THREAT_INTEL.md +292 -0
  173. package/docs/USAGE.md +1205 -0
  174. package/docs/design/WEB-UI.md +82 -0
  175. package/package.json +71 -0
  176. package/scripts/check-determinism.mjs +119 -0
  177. package/snippets/README.md +122 -0
  178. package/snippets/api-security/api-security-checklist.md +412 -0
  179. package/snippets/api-security/cors-config.ts +322 -0
  180. package/snippets/api-security/input-validation.ts +430 -0
  181. package/snippets/auth/jwt-checklist.md +322 -0
  182. package/snippets/rate-limiting/nextjs-middleware.ts +211 -0
  183. package/snippets/rate-limiting/upstash-ratelimit.ts +229 -0
@@ -0,0 +1,296 @@
1
+ /**
2
+ * Security Memory
3
+ * ================
4
+ *
5
+ * Hermes-inspired persistent memory for false-positive learning.
6
+ *
7
+ * After deep analysis confirms a finding is a false-positive, the verdict is
8
+ * written to `.praxis/memory.json`. On the next scan, the SecurityMemory
9
+ * filter runs before scoring and suppresses any finding whose (rule + file +
10
+ * matched-snippet hash) appears in memory — avoiding the same LLM call twice.
11
+ *
12
+ * Memory entries are per-repo (stored in `.praxis/` inside the project
13
+ * root), so suppressions don't bleed across unrelated projects.
14
+ *
15
+ * USAGE:
16
+ * import { SecurityMemory } from '../utils/security-memory.js';
17
+ *
18
+ * const memory = new SecurityMemory(rootPath);
19
+ *
20
+ * // After deep analysis — auto-learn false positives
21
+ * memory.learnFromAnalysis(findings);
22
+ *
23
+ * // Filter findings against memory before scoring
24
+ * const { kept, suppressed } = memory.filter(findings);
25
+ *
26
+ * // Manual suppression (from `praxis memory add`)
27
+ * memory.suppress(finding, 'Not reachable from user input');
28
+ *
29
+ * // CLI commands
30
+ * memory.list() → all suppressed entries
31
+ * memory.forget(id) → remove one entry
32
+ * memory.clear() → wipe all entries
33
+ */
34
+
35
+ import fs from 'fs';
36
+ import path from 'path';
37
+ import { createHash } from 'crypto';
38
+
39
+ const MEMORY_DIR = '.praxis';
40
+ const MEMORY_FILE = 'memory.json';
41
+
42
+ /** How many chars of the matched snippet to hash (stable across reformatting) */
43
+ const SNIPPET_HASH_LEN = 120;
44
+
45
+ export class SecurityMemory {
46
+ constructor(rootPath) {
47
+ this.rootPath = rootPath;
48
+ this.memoryDir = path.join(rootPath, MEMORY_DIR);
49
+ this.memoryPath = path.join(this.memoryDir, MEMORY_FILE);
50
+ this._data = null; // lazy-loaded
51
+ }
52
+
53
+ // ===========================================================================
54
+ // PERSISTENCE
55
+ // ===========================================================================
56
+
57
+ _load() {
58
+ if (this._data) return this._data;
59
+
60
+ try {
61
+ if (fs.existsSync(this.memoryPath)) {
62
+ this._data = JSON.parse(fs.readFileSync(this.memoryPath, 'utf-8'));
63
+ }
64
+ } catch { /* corrupt file — start fresh */ }
65
+
66
+ if (!this._data || typeof this._data !== 'object') {
67
+ this._data = { version: 1, entries: [] };
68
+ }
69
+ if (!Array.isArray(this._data.entries)) {
70
+ this._data.entries = [];
71
+ }
72
+
73
+ return this._data;
74
+ }
75
+
76
+ _save() {
77
+ try {
78
+ if (!fs.existsSync(this.memoryDir)) {
79
+ fs.mkdirSync(this.memoryDir, { recursive: true });
80
+ }
81
+ fs.writeFileSync(this.memoryPath, JSON.stringify(this._data, null, 2), 'utf-8');
82
+ } catch { /* non-fatal */ }
83
+ }
84
+
85
+ // ===========================================================================
86
+ // KEY GENERATION
87
+ // ===========================================================================
88
+
89
+ /**
90
+ * Generate a stable key for a finding.
91
+ * Key = SHA-256(rule + file-basename + first N chars of matched).
92
+ * Deliberately excludes line number so the suppression survives minor refactors.
93
+ */
94
+ static keyOf(finding) {
95
+ const rule = finding.rule || '';
96
+ const file = finding.file ? path.basename(finding.file) : '';
97
+ const snippet = (finding.matched || finding.title || '').slice(0, SNIPPET_HASH_LEN);
98
+ return createHash('sha256').update(`${rule}::${file}::${snippet}`).digest('hex').slice(0, 16);
99
+ }
100
+
101
+ // ===========================================================================
102
+ // CORE API
103
+ // ===========================================================================
104
+
105
+ /**
106
+ * Learn from deep analysis results.
107
+ * Any finding with exploitability === 'false_positive' is auto-added to memory.
108
+ *
109
+ * @param {object[]} findings — findings array (with deepAnalysis attached)
110
+ * @returns {number} — count of new entries added
111
+ */
112
+ learnFromAnalysis(findings) {
113
+ const data = this._load();
114
+ const existingKeys = new Set(data.entries.map(e => e.key));
115
+ let added = 0;
116
+
117
+ for (const f of findings) {
118
+ if (f.deepAnalysis?.exploitability !== 'false_positive') continue;
119
+
120
+ const key = SecurityMemory.keyOf(f);
121
+ if (existingKeys.has(key)) continue;
122
+
123
+ data.entries.push({
124
+ key,
125
+ rule: f.rule,
126
+ file: f.file ? path.basename(f.file) : '',
127
+ title: f.title,
128
+ severity: f.severity,
129
+ reason: f.deepAnalysis.reasoning || 'Auto-detected false positive by deep analysis',
130
+ source: 'deep-analysis',
131
+ suppressedAt: new Date().toISOString(),
132
+ });
133
+
134
+ existingKeys.add(key);
135
+ added++;
136
+ }
137
+
138
+ if (added > 0) this._save();
139
+ return added;
140
+ }
141
+
142
+ /**
143
+ * Manually suppress a finding with a reason.
144
+ *
145
+ * @param {object} finding
146
+ * @param {string} reason
147
+ * @returns {string} — the key added
148
+ */
149
+ suppress(finding, reason = '') {
150
+ const data = this._load();
151
+ const key = SecurityMemory.keyOf(finding);
152
+
153
+ if (data.entries.some(e => e.key === key)) {
154
+ return key; // already suppressed
155
+ }
156
+
157
+ data.entries.push({
158
+ key,
159
+ rule: finding.rule,
160
+ file: finding.file ? path.basename(finding.file) : '',
161
+ title: finding.title,
162
+ severity: finding.severity,
163
+ reason: reason || 'Manually suppressed',
164
+ source: 'manual',
165
+ suppressedAt: new Date().toISOString(),
166
+ });
167
+
168
+ this._save();
169
+ return key;
170
+ }
171
+
172
+ /**
173
+ * Filter findings against memory.
174
+ * Returns { kept, suppressed } — suppressed findings get a .memorySuppressed flag.
175
+ *
176
+ * @param {object[]} findings
177
+ * @returns {{ kept: object[], suppressed: object[], suppressedCount: number }}
178
+ */
179
+ filter(findings) {
180
+ const data = this._load();
181
+ if (data.entries.length === 0) {
182
+ return { kept: findings, suppressed: [], suppressedCount: 0 };
183
+ }
184
+
185
+ const keySet = new Set(data.entries.map(e => e.key));
186
+ const kept = [];
187
+ const suppressed = [];
188
+
189
+ for (const f of findings) {
190
+ const key = SecurityMemory.keyOf(f);
191
+ const entry = data.entries.find(e => e.key === key);
192
+ if (entry) {
193
+ suppressed.push({ ...f, memorySuppressed: true, suppressionReason: entry.reason });
194
+ } else {
195
+ kept.push(f);
196
+ }
197
+ }
198
+
199
+ return { kept, suppressed, suppressedCount: suppressed.length };
200
+ }
201
+
202
+ /**
203
+ * Remove a single entry by key.
204
+ */
205
+ forget(key) {
206
+ const data = this._load();
207
+ const before = data.entries.length;
208
+ data.entries = data.entries.filter(e => e.key !== key);
209
+ const removed = before - data.entries.length;
210
+ if (removed > 0) this._save();
211
+ return removed;
212
+ }
213
+
214
+ /**
215
+ * Wipe all memory entries.
216
+ */
217
+ clear() {
218
+ this._data = { version: 1, entries: [] };
219
+ this._save();
220
+ }
221
+
222
+ /**
223
+ * Return all entries.
224
+ */
225
+ list() {
226
+ return this._load().entries;
227
+ }
228
+
229
+ /**
230
+ * Count of suppressed entries.
231
+ */
232
+ get size() {
233
+ return this._load().entries.length;
234
+ }
235
+ }
236
+
237
+ // =============================================================================
238
+ // CLI COMMAND (praxis memory)
239
+ // =============================================================================
240
+
241
+ import chalk from 'chalk';
242
+ import * as output from './output.js';
243
+
244
+ export async function memoryCommand(subcommand, args = [], options = {}) {
245
+ const rootPath = path.resolve(options.path || '.');
246
+ const memory = new SecurityMemory(rootPath);
247
+
248
+ switch (subcommand) {
249
+ case 'list':
250
+ case undefined: {
251
+ const entries = memory.list();
252
+ if (entries.length === 0) {
253
+ console.log('\n No suppressed findings in memory.\n');
254
+ return;
255
+ }
256
+ console.log(`\n ${chalk.cyan.bold('Security Memory')} — ${entries.length} suppressed finding(s)\n`);
257
+ for (const e of entries) {
258
+ const sev = e.severity === 'critical' ? chalk.red.bold(e.severity)
259
+ : e.severity === 'high' ? chalk.yellow(e.severity)
260
+ : chalk.gray(e.severity);
261
+ console.log(` ${chalk.gray(e.key)} ${sev} ${chalk.white(e.rule)}`);
262
+ console.log(` ${chalk.gray('File:')} ${e.file} ${chalk.gray('Source:')} ${e.source} ${chalk.gray('Date:')} ${e.suppressedAt.slice(0, 10)}`);
263
+ console.log(` ${chalk.gray('Reason:')} ${e.reason}`);
264
+ console.log();
265
+ }
266
+ break;
267
+ }
268
+
269
+ case 'forget': {
270
+ const key = args[0];
271
+ if (!key) {
272
+ output.error('Usage: praxis memory forget <key>');
273
+ process.exit(1);
274
+ }
275
+ const removed = memory.forget(key);
276
+ if (removed) {
277
+ console.log(chalk.green(` Removed memory entry: ${key}`));
278
+ } else {
279
+ console.log(chalk.yellow(` Key not found in memory: ${key}`));
280
+ }
281
+ break;
282
+ }
283
+
284
+ case 'clear': {
285
+ const before = memory.size;
286
+ memory.clear();
287
+ console.log(chalk.green(` Cleared ${before} memory entry/entries.`));
288
+ break;
289
+ }
290
+
291
+ default:
292
+ output.error(`Unknown memory subcommand: ${subcommand}`);
293
+ console.log(' Usage: praxis memory [list|forget <key>|clear]');
294
+ process.exit(1);
295
+ }
296
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * MITRE ATLAS knowledge lookup — vendored enrichment data.
3
+ *
4
+ * Loads cli/data/atlas-knowledge.json (Apache-2.0, The MITRE Corporation,
5
+ * snapshot 2026-04). Provides hydrated technique details for reports:
6
+ * technique name/description/URL, parent tactics, mitigations (with
7
+ * parent-technique fallback), and real-world case studies.
8
+ */
9
+
10
+ import fs from 'fs';
11
+ import path from 'path';
12
+ import { fileURLToPath } from 'url';
13
+
14
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
+ const DATA_PATH = path.join(__dirname, '..', '..', 'data', 'atlas-knowledge.json');
16
+
17
+ let _cache = null;
18
+
19
+ function load() {
20
+ if (_cache) return _cache;
21
+ try {
22
+ _cache = JSON.parse(fs.readFileSync(DATA_PATH, 'utf-8'));
23
+ } catch {
24
+ _cache = { entities: {}, snapshot: 'unknown' };
25
+ }
26
+ return _cache;
27
+ }
28
+
29
+ export function atlasSnapshot() {
30
+ return load().snapshot || 'unknown';
31
+ }
32
+
33
+ export function atlasAttribution() {
34
+ return load()._attribution || null;
35
+ }
36
+
37
+ export function isKnownTechnique(id) {
38
+ const e = load().entities[id];
39
+ return Boolean(e && e.entity === 'technique');
40
+ }
41
+
42
+ /**
43
+ * Hydrate a technique with readable references. Mitigations fall back to the
44
+ * parent technique when the (sub-)technique itself carries none.
45
+ */
46
+ export function getTechniqueDetails(id) {
47
+ const data = load();
48
+ const entity = data.entities[id];
49
+ if (!entity) return null;
50
+
51
+ const out = {
52
+ id,
53
+ name: entity.name,
54
+ description: entity.description,
55
+ url: entity.url || `https://atlas.mitre.org/techniques/${id}`,
56
+ tactics: [],
57
+ mitigations: [],
58
+ caseStudies: [],
59
+ };
60
+
61
+ for (const t of entity.parentTactics || []) {
62
+ const tactic = data.entities[t];
63
+ if (tactic) out.tactics.push({ id: t, name: tactic.name, url: tactic.url });
64
+ }
65
+
66
+ let mitigationIds = entity.parentMitigations || [];
67
+ if (mitigationIds.length === 0 && (entity.parentTechniques || []).length > 0) {
68
+ const parent = data.entities[entity.parentTechniques[0]];
69
+ mitigationIds = parent?.parentMitigations || [];
70
+ }
71
+ for (const m of mitigationIds) {
72
+ const mit = data.entities[m];
73
+ if (mit) out.mitigations.push({ id: m, name: mit.name, url: mit.url });
74
+ }
75
+
76
+ for (const c of entity.parentCaseStudies || []) {
77
+ const cs = data.entities[c];
78
+ if (cs) out.caseStudies.push({ id: c, name: cs.name, url: cs.url });
79
+ }
80
+
81
+ return out;
82
+ }
83
+
84
+ export function listTechniques() {
85
+ const data = load();
86
+ return Object.values(data.entities).filter(e => e.entity === 'technique');
87
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Standards registry.
3
+ *
4
+ * Each standard is a self-contained ESM module exporting:
5
+ * name, version, title, description, url, controls, mapFinding(finding)
6
+ *
7
+ * `mapFindingToStandards(finding)` returns:
8
+ * { 'owasp-llm': ['LLM01'], 'mitre-atlas': ['AML.T0051'], ... }
9
+ *
10
+ * Adding a standard: drop a file under ./sources, register in ALL_STANDARDS.
11
+ */
12
+
13
+ import * as owaspLlm from './sources/owasp-llm.js';
14
+ import * as mitreAtlas from './sources/mitre-atlas.js';
15
+ import * as nistAi6001 from './sources/nist-ai-600-1.js';
16
+ import * as avid from './sources/avid.js';
17
+ import * as owaspMl from './sources/owasp-ml.js';
18
+ import * as euAiAct from './sources/eu-ai-act.js';
19
+ import * as iso42001 from './sources/iso-42001.js';
20
+ import * as googleSaif from './sources/google-saif.js';
21
+
22
+ export const ALL_STANDARDS = [
23
+ owaspLlm,
24
+ mitreAtlas,
25
+ nistAi6001,
26
+ avid,
27
+ owaspMl,
28
+ euAiAct,
29
+ iso42001,
30
+ googleSaif,
31
+ ];
32
+
33
+ export function listStandards() {
34
+ return ALL_STANDARDS.map(s => ({
35
+ name: s.name,
36
+ version: s.version,
37
+ title: s.title,
38
+ description: s.description,
39
+ url: s.url,
40
+ controlCount: (s.controls || []).length,
41
+ }));
42
+ }
43
+
44
+ export function getStandard(name) {
45
+ return ALL_STANDARDS.find(s => s.name === name) || null;
46
+ }
47
+
48
+ /**
49
+ * Tag a single finding with all standard control IDs it maps to.
50
+ * @returns {Object<string, string[]>} keyed by standard name; empty arrays omitted.
51
+ */
52
+ export function mapFindingToStandards(finding) {
53
+ const out = {};
54
+ for (const s of ALL_STANDARDS) {
55
+ let ids;
56
+ try {
57
+ ids = s.mapFinding(finding) || [];
58
+ } catch {
59
+ ids = [];
60
+ }
61
+ if (ids.length > 0) out[s.name] = ids;
62
+ }
63
+ return out;
64
+ }
65
+
66
+ /**
67
+ * Aggregate coverage across a finding set.
68
+ * Returns:
69
+ * {
70
+ * [standardName]: {
71
+ * title, version, totalControls, flaggedControls, coverage,
72
+ * controls: [{ id, title, findingCount, status }]
73
+ * }
74
+ * }
75
+ */
76
+ export function getStandardsSummary(findings = []) {
77
+ const summary = {};
78
+
79
+ for (const s of ALL_STANDARDS) {
80
+ summary[s.name] = {
81
+ name: s.name,
82
+ title: s.title,
83
+ version: s.version,
84
+ url: s.url,
85
+ totalControls: (s.controls || []).length,
86
+ flaggedControls: 0,
87
+ coverage: `0/${(s.controls || []).length}`,
88
+ controls: (s.controls || []).map(c => ({ ...c, findingCount: 0, status: 'clear' })),
89
+ };
90
+ }
91
+
92
+ for (const f of findings) {
93
+ const tagged = mapFindingToStandards(f);
94
+ for (const [stdName, ids] of Object.entries(tagged)) {
95
+ const std = summary[stdName];
96
+ if (!std) continue;
97
+ for (const id of ids) {
98
+ const ctrl = std.controls.find(c => c.id === id);
99
+ if (ctrl) {
100
+ ctrl.findingCount += 1;
101
+ ctrl.status = 'flagged';
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ for (const std of Object.values(summary)) {
108
+ std.flaggedControls = std.controls.filter(c => c.findingCount > 0).length;
109
+ std.coverage = `${std.flaggedControls}/${std.totalControls}`;
110
+ }
111
+
112
+ return summary;
113
+ }
114
+
115
+ /**
116
+ * Filter a finding list to only those tagged with a given standard (and
117
+ * optionally a specific control within that standard).
118
+ */
119
+ export function filterFindingsByStandard(findings, standardName, controlId = null) {
120
+ return findings.filter(f => {
121
+ const tagged = f.standards || mapFindingToStandards(f);
122
+ const ids = tagged[standardName];
123
+ if (!ids || ids.length === 0) return false;
124
+ if (controlId) return ids.includes(controlId);
125
+ return true;
126
+ });
127
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * AVID — AI Vulnerability Database taxonomy.
3
+ * https://avidml.org/
4
+ *
5
+ * AVID classifies vulnerabilities under three top-level categories:
6
+ * S — Security P — Performance E — Ethics
7
+ */
8
+
9
+ export const name = 'avid';
10
+ export const version = '2024';
11
+ export const title = 'AI Vulnerability Database (AVID) Taxonomy';
12
+ export const description = 'Open taxonomy for AI failure modes across security, performance, and ethics.';
13
+ export const url = 'https://avidml.org/';
14
+
15
+ export const controls = [
16
+ { id: 'S0100', title: 'Software Vulnerability', description: 'Classical software flaws in AI systems (injection, IDOR, etc.).' },
17
+ { id: 'S0200', title: 'Supply Chain Compromise', description: 'Tampered model, dataset, or upstream package.' },
18
+ { id: 'S0301', title: 'Information Leakage', description: 'Model leaks training data, secrets, or system prompts.' },
19
+ { id: 'S0400', title: 'Model Bypass / Evasion', description: 'Crafted inputs bypass model safety or filters.' },
20
+ { id: 'S0500', title: 'Exfiltration via Model Outputs', description: 'Adversary uses model channel to exfiltrate data.' },
21
+ { id: 'P0201', title: 'Robustness — Adversarial', description: 'Model degrades under adversarial / OOD inputs.' },
22
+ { id: 'P0204', title: 'Data Drift', description: 'Inputs diverge from training distribution.' },
23
+ { id: 'P0301', title: 'Hallucination / Confabulation', description: 'Confidently incorrect outputs.' },
24
+ { id: 'E0101', title: 'Group Fairness', description: 'Outputs disadvantage protected groups.' },
25
+ ];
26
+
27
+ export function mapFinding(finding) {
28
+ const ids = new Set();
29
+ const cwe = finding.cwe || '';
30
+ const owasp = finding.owasp || '';
31
+ const cat = finding.category || '';
32
+ const rule = (finding.rule || '').toLowerCase();
33
+ const title = (finding.title || '').toLowerCase();
34
+
35
+ if (cat === 'injection' || cat === 'auth' || cat === 'config' || /^CWE-/.test(cwe)) ids.add('S0100');
36
+ if (cat === 'supply-chain' || cat === 'deps' || owasp === 'ASI04') ids.add('S0200');
37
+ if (cat === 'secrets' || cat === 'git-history' || cwe === 'CWE-200' || cwe === 'CWE-312' || owasp === 'ASI06') ids.add('S0301');
38
+ if (/jailbreak|prompt.?inject|bypass/.test(rule + title) || owasp === 'ASI01') ids.add('S0400');
39
+ if (owasp === 'ASI05' || /poison|tamper|memory/.test(rule + title)) ids.add('P0201');
40
+ if (/retrieval|vector.?store|rag\b|embedding/.test(rule + title)) ids.add('S0500');
41
+ if (/halluc|confabul|misinform/.test(rule + title) || owasp === 'ASI10') ids.add('P0301');
42
+ if (/bias|fairness|discriminat/.test(rule + title)) ids.add('E0101');
43
+
44
+ return [...ids];
45
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * EU AI Act (Regulation (EU) 2024/1689).
3
+ * https://eur-lex.europa.eu/eli/reg/2024/1689/oj
4
+ *
5
+ * Control text authored by Praxis from the public text of the Regulation
6
+ * (EUR-Lex). For static analysis we tag findings against the cybersecurity,
7
+ * transparency, data-governance, oversight, and post-market monitoring
8
+ * obligations most relevant to code-level signals.
9
+ */
10
+
11
+ export const name = 'eu-ai-act';
12
+ export const version = '2024/1689';
13
+ export const title = 'EU AI Act';
14
+ export const description = 'Regulation laying down harmonised rules on artificial intelligence in the EU.';
15
+ export const url = 'https://eur-lex.europa.eu/eli/reg/2024/1689/oj';
16
+
17
+ export const controls = [
18
+ { id: 'Art.5', title: 'Prohibited AI practices', description: 'Subliminal/manipulative techniques, exploitative, social scoring, and similar prohibited uses (Art. 5(1)(a)-(h)).' },
19
+ { id: 'Art.9', title: 'Risk management system', description: 'Continuous risk management for high-risk systems covering identification, analysis, mitigation, and testing (Art. 9).' },
20
+ { id: 'Art.10', title: 'Data and data governance', description: 'Training/validation data must meet quality, integrity, and relevance criteria; governance for bias and gaps (Art. 10).' },
21
+ { id: 'Art.11', title: 'Technical documentation', description: 'Documentation demonstrating compliance with high-risk requirements before and during market placement (Art. 11, Annex IV).' },
22
+ { id: 'Art.12', title: 'Record keeping / logging', description: 'Automatic logging of system events sufficient for post-market traceability and oversight (Art. 12).' },
23
+ { id: 'Art.13', title: 'Transparency to deployers', description: 'High-risk AI must be designed for transparent operation; instructions for use (Art. 13).' },
24
+ { id: 'Art.14', title: 'Human oversight', description: 'Effective oversight by natural persons during AI use; intervention and override capability (Art. 14).' },
25
+ { id: 'Art.15', title: 'Accuracy, robustness and cybersecurity', description: 'AI systems must be resilient against errors, faults, and adversarial manipulation (Art. 15).' },
26
+ { id: 'Art.17', title: 'Quality management system', description: 'Provider QMS covering design control, verification, data management, and post-market monitoring (Art. 17).' },
27
+ { id: 'Art.25', title: 'Responsibilities along the value chain', description: 'Obligations distributed among providers, deployers, importers, and distributors (Art. 25).' },
28
+ { id: 'Art.26', title: 'Obligations of deployers', detectable: false, description: 'Deployers must use high-risk systems per instructions, ensure input relevance, and monitor operation (Art. 26).' },
29
+ { id: 'Art.27', title: 'Fundamental rights impact assessment', detectable: false, description: 'Deployers of high-risk systems assess impact on fundamental rights before deployment (Art. 27).' },
30
+ { id: 'Art.49', title: 'Registration', detectable: false, description: 'High-risk systems registered in the EU database before market placement (Art. 49).' },
31
+ { id: 'Art.50', title: 'Transparency for GenAI / chatbots', description: 'Disclose AI interaction; mark synthetic and deepfake content (Art. 50).' },
32
+ { id: 'Art.53', title: 'GPAI provider obligations', description: 'General-purpose AI model documentation, copyright policy, training-data summary (Art. 53).' },
33
+ { id: 'Art.55', title: 'GPAI systemic-risk obligations', description: 'Adversarial testing, incident reporting, and cybersecurity for systemic-risk models (Art. 55).' },
34
+ { id: 'Art.72', title: 'Post-market monitoring', description: 'Continuous monitoring of deployed AI for new risks; corrective action and incident reporting (Art. 72).' },
35
+ { id: 'Art.73', title: 'Reporting of serious incidents', description: 'Serious incidents reported to market surveillance authorities, with investigations (Art. 73).' },
36
+ ];
37
+
38
+ export function mapFinding(finding) {
39
+ const ids = new Set();
40
+ const cwe = finding.cwe || '';
41
+ const owasp = finding.owasp || '';
42
+ const cat = finding.category || '';
43
+ const rule = (finding.rule || '').toLowerCase();
44
+ const title = (finding.title || '').toLowerCase();
45
+ const text = rule + title;
46
+
47
+ // Art. 5 — prohibited-practice adjacent signals (deception, manipulation, social scoring)
48
+ if (/social.?scor|subliminal|manipulat|deceptiv|emotion.?recogn/.test(text)) ids.add('Art.5');
49
+
50
+ // Art. 9/10 — data governance, model/data/context poisoning, RAG content
51
+ if (/poison|embedding|rag\b|retrieval|memory/.test(text)) {
52
+ ids.add('Art.9'); ids.add('Art.10');
53
+ }
54
+
55
+ // Art. 11/17 — technical documentation / QMS evidence gaps
56
+ if (/no.?model.?card|missing.?documentation|no.?readme/.test(text)) ids.add('Art.11');
57
+ if (/unpinned|attestation|unsigned|integrity.?hash/.test(text)) ids.add('Art.17');
58
+
59
+ // Art. 12 — logging / record keeping
60
+ if (/no.?logging|no.?observability|telemetry|no.?audit.?log/.test(text)) ids.add('Art.12');
61
+
62
+ // Art. 13 — transparency / opaqueness / system prompt leakage
63
+ if (/system.?prompt.?leak|opaqu|disclos|transparen/.test(text)) ids.add('Art.13');
64
+
65
+ // Art. 14 — human oversight / auto-approval / excessive agency
66
+ if (owasp === 'ASI08' || /human.?oversight|auto.?accept|yolo|excessive.?agency|no.?approval|permissionless/.test(text)) ids.add('Art.14');
67
+
68
+ // Art. 15 — accuracy/robustness/cybersecurity (all technical vuln classes)
69
+ if (cat === 'injection' || cat === 'supply-chain' || cat === 'auth' || cat === 'config'
70
+ || /prompt.?inject|jailbreak/.test(text) || owasp === 'ASI01' || owasp === 'ASI04'
71
+ || cwe === 'CWE-94' || cwe === 'CWE-78' || cwe === 'CWE-502') ids.add('Art.15');
72
+
73
+ // Art. 25/26 — value chain, third-party components, deployer obligations
74
+ if (/third.?party|supplier|vendor|deployer/.test(text) && (cat === 'supply-chain' || /mcp/.test(text))) ids.add('Art.25');
75
+
76
+ // Art. 50 — GenAI/chatbot disclosure, synthetic content
77
+ if (/chatbot|user.?facing|disclos|synthetic|deepfake/.test(text)) ids.add('Art.50');
78
+
79
+ // Art. 53/55 — GPAI obligations, model provenance, systemic risk
80
+ if (cat === 'supply-chain' && /(model|dataset|gpai|foundation|training.?data)/.test(text)) ids.add('Art.53');
81
+ if (owasp === 'ASI10' || /systemic.?risk|capabilit|frontier/.test(text)) ids.add('Art.55');
82
+
83
+ // Art. 72/73 — post-market monitoring / incident reporting / MCP runtime risk
84
+ if (/mcp|tool.?poison/.test(text) || owasp === 'ASI06' || /post.?market|incident.?report|no.?monitoring/.test(text)) {
85
+ ids.add('Art.72'); ids.add('Art.73');
86
+ }
87
+
88
+ return [...ids];
89
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Google Secure AI Framework (SAIF).
3
+ * https://safety.google/cybersecurity-advancements/saif/
4
+ *
5
+ * Six core elements for securing AI systems end-to-end.
6
+ */
7
+
8
+ export const name = 'google-saif';
9
+ export const version = '1.0';
10
+ export const title = 'Google Secure AI Framework (SAIF)';
11
+ export const description = 'Conceptual framework for securing AI systems across foundations, detection, defense, controls, adaptation, and context.';
12
+ export const url = 'https://safety.google/cybersecurity-advancements/saif/';
13
+
14
+ export const controls = [
15
+ { id: 'SAIF-1', title: 'Expand strong security foundations to the AI ecosystem', description: 'Apply secure-by-default infra and IAM to AI workloads.' },
16
+ { id: 'SAIF-2', title: 'Extend detection and response to AI threats', description: 'Bring AI into the SOC: monitor model I/O, prompt anomalies.' },
17
+ { id: 'SAIF-3', title: 'Automate defenses to keep pace with new threats', description: 'Automate scanning, patching, and adversarial testing.' },
18
+ { id: 'SAIF-4', title: 'Harmonize platform-level controls across AI systems', description: 'Consistent controls across model serving, RAG, and agents.' },
19
+ { id: 'SAIF-5', title: 'Adapt controls and create faster feedback loops', description: 'Continuously tune mitigations using deployment signals.' },
20
+ { id: 'SAIF-6', title: 'Contextualize AI risks in surrounding business processes', description: 'Tie AI risk to actual use case, data, and business impact.' },
21
+ ];
22
+
23
+ export function mapFinding(finding) {
24
+ const ids = new Set();
25
+ const cwe = finding.cwe || '';
26
+ const owasp = finding.owasp || '';
27
+ const cat = finding.category || '';
28
+ const rule = (finding.rule || '').toLowerCase();
29
+ const title = (finding.title || '').toLowerCase();
30
+
31
+ if (cat === 'auth' || cat === 'config' || cat === 'secrets' || /^CWE-(287|306|269|250|798|312)$/.test(cwe)) ids.add('SAIF-1');
32
+ if (/log.?inject|missing.?logg|silent|hide.?output/.test(rule + title) || owasp === 'ASI06') ids.add('SAIF-2');
33
+ if (cat === 'supply-chain' || cat === 'deps' || cat === 'git-history' || owasp === 'ASI04') ids.add('SAIF-3');
34
+ if (/mcp|tool.?poison|rag\b|retrieval|vector.?store|agent.?config|\.cursorrules|CLAUDE\.md/.test(rule + title) || cat === 'agentic') ids.add('SAIF-4');
35
+ if (owasp === 'ASI05' || owasp === 'ASI10' || /poison|tamper|memory/.test(rule + title)) ids.add('SAIF-5');
36
+ if (owasp === 'ASI08' || cat === 'llm') ids.add('SAIF-6');
37
+
38
+ return [...ids];
39
+ }