@clear-capabilities/agentic-security-scanner 0.79.0 → 0.80.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 (91) hide show
  1. package/dist/178.index.js +1 -1
  2. package/dist/333.index.js +283 -0
  3. package/dist/384.index.js +1 -1
  4. package/dist/637.index.js +1 -1
  5. package/dist/838.index.js +1 -1
  6. package/dist/985.index.js +90 -1
  7. package/dist/agentic-security.mjs +83 -83
  8. package/dist/agentic-security.mjs.sha256 +1 -1
  9. package/package.json +6 -4
  10. package/src/.agentic-security/findings.json +104638 -0
  11. package/src/.agentic-security/last-scan.json +104638 -0
  12. package/src/.agentic-security/last-scan.json.sig +1 -0
  13. package/src/.agentic-security/scan-history.json +12562 -0
  14. package/src/.agentic-security/streak.json +21 -0
  15. package/src/dataflow/.agentic-security/findings.json +6086 -0
  16. package/src/dataflow/.agentic-security/last-scan.json +6086 -0
  17. package/src/dataflow/.agentic-security/last-scan.json.sig +1 -0
  18. package/src/dataflow/.agentic-security/scan-history.json +250 -0
  19. package/src/dataflow/.agentic-security/streak.json +21 -0
  20. package/src/dataflow/cross-service-taint.js +201 -0
  21. package/src/dataflow/formal-verify.js +204 -0
  22. package/src/dataflow/ifds-precise.js +222 -0
  23. package/src/dataflow/k2-summary-cache.js +153 -0
  24. package/src/dataflow/lib-taint-summaries.js +198 -0
  25. package/src/dataflow/privacy-taint.js +205 -0
  26. package/src/dataflow/smt-feasibility.js +189 -0
  27. package/src/engine.js +784 -127
  28. package/src/ir/.agentic-security/findings.json +4011 -0
  29. package/src/ir/.agentic-security/last-scan.json +4011 -0
  30. package/src/ir/.agentic-security/last-scan.json.sig +1 -0
  31. package/src/ir/.agentic-security/scan-history.json +193 -0
  32. package/src/ir/.agentic-security/streak.json +20 -0
  33. package/src/ir/cpp-preprocessor.js +142 -0
  34. package/src/ir/csharp-ir.js +604 -0
  35. package/src/ir/universal-ir.js +403 -0
  36. package/src/mcp/.agentic-security/findings.json +8632 -0
  37. package/src/mcp/.agentic-security/last-scan.json +8632 -0
  38. package/src/mcp/.agentic-security/last-scan.json.sig +1 -0
  39. package/src/mcp/.agentic-security/scan-history.json +143 -0
  40. package/src/mcp/.agentic-security/streak.json +20 -0
  41. package/src/mcp/tools.js +90 -1
  42. package/src/posture/.agentic-security/findings.json +64004 -0
  43. package/src/posture/.agentic-security/last-scan.json +64004 -0
  44. package/src/posture/.agentic-security/last-scan.json.sig +1 -0
  45. package/src/posture/.agentic-security/scan-history.json +7162 -0
  46. package/src/posture/.agentic-security/streak.json +21 -0
  47. package/src/posture/api-contract.js +193 -0
  48. package/src/posture/attack-taxonomy.js +227 -0
  49. package/src/posture/compliance-policy.js +218 -0
  50. package/src/posture/composite-risk.js +122 -0
  51. package/src/posture/csharp-analysis.js +330 -0
  52. package/src/posture/exploit-bundle.js +210 -0
  53. package/src/posture/federated-learning.js +172 -0
  54. package/src/posture/license-attributions.js +94 -0
  55. package/src/posture/license-graph.js +238 -0
  56. package/src/posture/pqc-migration-plan.js +158 -0
  57. package/src/posture/reachability-filter.js +33 -2
  58. package/src/posture/realtime-cve-monitor.js +214 -0
  59. package/src/posture/runtime-correlation.js +174 -0
  60. package/src/posture/sbom-diff.js +171 -0
  61. package/src/posture/sca-policy.js +235 -0
  62. package/src/posture/sca-upgrade.js +259 -0
  63. package/src/posture/threat-model-auto.js +268 -0
  64. package/src/posture/triage-learning.js +170 -0
  65. package/src/posture/triage.js +26 -1
  66. package/src/sast/.agentic-security/findings.json +6154 -0
  67. package/src/sast/.agentic-security/last-scan.json +6154 -0
  68. package/src/sast/.agentic-security/last-scan.json.sig +1 -0
  69. package/src/sast/.agentic-security/scan-history.json +941 -0
  70. package/src/sast/.agentic-security/streak.json +22 -0
  71. package/src/sast/_secret-entropy.js +145 -0
  72. package/src/sast/cloud-iam.js +312 -0
  73. package/src/sast/cpp.js +138 -4
  74. package/src/sast/crypto-protocol.js +388 -0
  75. package/src/sast/csharp-tokenizer.js +392 -0
  76. package/src/sast/csharp.js +924 -138
  77. package/src/sast/dapp-frontend.js +200 -0
  78. package/src/sast/k8s-admission.js +271 -0
  79. package/src/sast/llm-app.js +272 -0
  80. package/src/sast/ml-supply-chain.js +259 -0
  81. package/src/sast/mobile.js +224 -0
  82. package/src/sast/post-quantum-crypto.js +348 -0
  83. package/src/sast/web3-advanced.js +375 -0
  84. package/src/sca/.agentic-security/findings.json +7460 -0
  85. package/src/sca/.agentic-security/last-scan.json +7460 -0
  86. package/src/sca/.agentic-security/last-scan.json.sig +1 -0
  87. package/src/sca/.agentic-security/scan-history.json +113 -0
  88. package/src/sca/.agentic-security/streak.json +21 -0
  89. package/src/sca/CLAUDE.md +161 -0
  90. package/src/sca/binary-metadata.js +37 -15
  91. package/src/sca/sigstore-verify.js +215 -0
@@ -0,0 +1,403 @@
1
+ // Universal IR — Recommendation #1 of the world-class roadmap.
2
+ //
3
+ // One IR shape every detector consumes, regardless of source language.
4
+ // Lazy-loads tree-sitter grammars (web-tree-sitter, WASM, NO native
5
+ // bindings) so the scanner stays bootable when tree-sitter isn't
6
+ // installed. When the WASM grammar isn't available we fall back to the
7
+ // existing per-language IRs (Babel for JS/TS, java-parser for Java,
8
+ // the hand-rolled C# IR from earlier in this session, etc.).
9
+ //
10
+ // IR shape — every node a detector cares about:
11
+ //
12
+ // File:
13
+ // { path, language, ast, decls[], calls[], assignments[],
14
+ // members[], attrs[], imports[], functions[], classes[] }
15
+ //
16
+ // Decl: { kind: 'var'|'const'|'let'|'field'|'param', name,
17
+ // type|null, initText|null, line, scope }
18
+ // Call: { callee, receiver|null, args: ArgExpr[], line, scope,
19
+ // fullPath }
20
+ // Assign: { target, isMember, memberPath|null, rhsText, line, scope }
21
+ // Function: { name, returnType|null, params: Param[], attrs: Attr[],
22
+ // body, line, endLine, async, static }
23
+ // Class: { name, baseTypes[], methods[], fields[], attrs[],
24
+ // line, endLine }
25
+ // Attr: { name, args[] } (attributes / decorators / annotations
26
+ // across languages — same shape)
27
+ // Import: { kind: 'static'|'dynamic', module, names: string[],
28
+ // isDefault, line }
29
+ // ArgExpr: { text, idents, line } (idents EXCLUDE string contents)
30
+ //
31
+ // Detectors author S-expression queries against the IR using
32
+ // queryIR(ir, expr). The expression language is a small subset of
33
+ // tree-sitter queries adapted for our normalized IR:
34
+ //
35
+ // (call :name "ExecuteReader" :receiver-type "SqlCommand")
36
+ // (assign :target-glob "*.CommandText" :rhs-has-ident @tainted)
37
+ //
38
+ // The query engine returns matches with { node, captures }.
39
+
40
+ import * as fs from 'node:fs';
41
+ import * as path from 'node:path';
42
+
43
+ // Grammar inventory — language → (extension → grammar package).
44
+ // We don't actually require these at module load; lazy-load via
45
+ // loadGrammar() so the scanner stays bootable when none are installed.
46
+ const GRAMMAR_BY_LANG = {
47
+ javascript: 'tree-sitter-javascript',
48
+ typescript: 'tree-sitter-typescript',
49
+ python: 'tree-sitter-python',
50
+ java: 'tree-sitter-java',
51
+ csharp: 'tree-sitter-c-sharp',
52
+ cpp: 'tree-sitter-cpp',
53
+ c: 'tree-sitter-c',
54
+ go: 'tree-sitter-go',
55
+ rust: 'tree-sitter-rust',
56
+ ruby: 'tree-sitter-ruby',
57
+ php: 'tree-sitter-php',
58
+ swift: 'tree-sitter-swift',
59
+ kotlin: 'tree-sitter-kotlin',
60
+ scala: 'tree-sitter-scala',
61
+ solidity: 'tree-sitter-solidity',
62
+ dart: 'tree-sitter-dart',
63
+ lua: 'tree-sitter-lua',
64
+ haskell: 'tree-sitter-haskell',
65
+ ocaml: 'tree-sitter-ocaml',
66
+ elixir: 'tree-sitter-elixir',
67
+ };
68
+
69
+ const EXT_TO_LANG = {
70
+ js: 'javascript', mjs: 'javascript', cjs: 'javascript', jsx: 'javascript',
71
+ ts: 'typescript', tsx: 'typescript',
72
+ py: 'python', pyi: 'python',
73
+ java: 'java',
74
+ cs: 'csharp',
75
+ c: 'c', h: 'c',
76
+ cc: 'cpp', cpp: 'cpp', cxx: 'cpp', hh: 'cpp', hpp: 'cpp', hxx: 'cpp',
77
+ go: 'go',
78
+ rs: 'rust',
79
+ rb: 'ruby',
80
+ php: 'php',
81
+ swift: 'swift',
82
+ kt: 'kotlin', kts: 'kotlin',
83
+ scala: 'scala',
84
+ sol: 'solidity',
85
+ dart: 'dart',
86
+ lua: 'lua',
87
+ hs: 'haskell',
88
+ ml: 'ocaml', mli: 'ocaml',
89
+ ex: 'elixir', exs: 'elixir',
90
+ };
91
+
92
+ export function detectLanguage(filePath) {
93
+ const ext = path.extname(filePath).slice(1).toLowerCase();
94
+ return EXT_TO_LANG[ext] || null;
95
+ }
96
+
97
+ // Lazy-loaded parser cache. Key = language name; value = Parser instance
98
+ // (or `false` if the grammar isn't available on this install).
99
+ const _parserCache = new Map();
100
+ let _ParserCtor = null;
101
+
102
+ async function _getParserCtor() {
103
+ if (_ParserCtor !== null) return _ParserCtor;
104
+ try {
105
+ const mod = await import('web-tree-sitter');
106
+ if (typeof mod.init === 'function') await mod.init();
107
+ _ParserCtor = mod.default || mod.Parser || mod;
108
+ return _ParserCtor;
109
+ } catch {
110
+ _ParserCtor = false;
111
+ return false;
112
+ }
113
+ }
114
+
115
+ async function _loadGrammar(lang) {
116
+ if (_parserCache.has(lang)) return _parserCache.get(lang);
117
+ const Parser = await _getParserCtor();
118
+ if (!Parser) { _parserCache.set(lang, false); return false; }
119
+ const pkg = GRAMMAR_BY_LANG[lang];
120
+ if (!pkg) { _parserCache.set(lang, false); return false; }
121
+ try {
122
+ const wasmPath = require.resolve(`${pkg}/tree-sitter-${lang}.wasm`);
123
+ const language = await Parser.Language.load(wasmPath);
124
+ const parser = new Parser();
125
+ parser.setLanguage(language);
126
+ _parserCache.set(lang, parser);
127
+ return parser;
128
+ } catch {
129
+ _parserCache.set(lang, false);
130
+ return false;
131
+ }
132
+ }
133
+
134
+ // Normalized IR shape — built by walking the tree-sitter CST and
135
+ // mapping language-specific node names to the universal kinds above.
136
+ // This dispatch is intentionally per-language so a Java
137
+ // `method_declaration` and a TS `method_definition` both map to the
138
+ // universal `Function` node.
139
+
140
+ const NORMALIZERS = {
141
+ javascript: _normalizeJsLike,
142
+ typescript: _normalizeJsLike,
143
+ java: _normalizeJava,
144
+ csharp: _normalizeCsharp,
145
+ cpp: _normalizeCLike,
146
+ c: _normalizeCLike,
147
+ python: _normalizePython,
148
+ go: _normalizeGo,
149
+ rust: _normalizeRust,
150
+ ruby: _normalizeRuby,
151
+ php: _normalizePhp,
152
+ };
153
+
154
+ // Universal IR builder. Returns null if the language can't be parsed
155
+ // here (caller falls back to its existing per-language IR).
156
+ export async function buildUniversalIR(filePath, content, opts = {}) {
157
+ const lang = opts.language || detectLanguage(filePath);
158
+ if (!lang) return null;
159
+ const parser = await _loadGrammar(lang);
160
+ if (!parser) return null;
161
+ let tree;
162
+ try { tree = parser.parse(content); }
163
+ catch { return null; }
164
+ if (!tree || !tree.rootNode) return null;
165
+ const ir = {
166
+ path: filePath, language: lang, ast: tree,
167
+ decls: [], calls: [], assignments: [], members: [],
168
+ attrs: [], imports: [], functions: [], classes: [],
169
+ _content: content,
170
+ };
171
+ const normalize = NORMALIZERS[lang] || _normalizeGeneric;
172
+ try { normalize(tree.rootNode, ir, content); }
173
+ catch (e) { ir._normalizerError = String(e && e.message || e); }
174
+ return ir;
175
+ }
176
+
177
+ // ─── Per-language normalizers (minimal v1 implementations) ─────────────────
178
+ //
179
+ // Each normalizer walks the CST and emits universal IR nodes. The shape
180
+ // is intentionally lossy — detectors don't need every CST detail, they
181
+ // need the security-relevant subset.
182
+
183
+ function _textOf(node, content) {
184
+ if (!node) return '';
185
+ if (typeof node.text === 'string') return node.text;
186
+ if (typeof node.startIndex === 'number') return content.slice(node.startIndex, node.endIndex);
187
+ return '';
188
+ }
189
+
190
+ function _lineOf(node) {
191
+ if (!node) return 0;
192
+ if (node.startPosition && typeof node.startPosition.row === 'number') return node.startPosition.row + 1;
193
+ return 0;
194
+ }
195
+
196
+ function _walkChildren(node, fn) {
197
+ if (!node || !node.children) return;
198
+ for (let i = 0; i < node.children.length; i++) fn(node.children[i], i);
199
+ }
200
+
201
+ function _normalizeJsLike(node, ir, content) {
202
+ _walkChildren(node, (child) => _normalizeJsLike(child, ir, content));
203
+ switch (node.type) {
204
+ case 'function_declaration':
205
+ case 'method_definition':
206
+ case 'arrow_function':
207
+ ir.functions.push({
208
+ name: _textOf(node.childForFieldName?.('name'), content) || null,
209
+ line: _lineOf(node), endLine: _lineOf({ startPosition: node.endPosition }),
210
+ params: [], async: !!node.childForFieldName?.('async'),
211
+ });
212
+ break;
213
+ case 'class_declaration':
214
+ ir.classes.push({
215
+ name: _textOf(node.childForFieldName?.('name'), content) || null,
216
+ line: _lineOf(node),
217
+ });
218
+ break;
219
+ case 'call_expression': {
220
+ const callee = node.childForFieldName?.('function');
221
+ const args = node.childForFieldName?.('arguments');
222
+ ir.calls.push({
223
+ callee: _textOf(callee, content), receiver: null,
224
+ args: args && args.children ? args.children.filter(c => c.type !== ',' && c.type !== '(' && c.type !== ')').map(a => ({
225
+ text: _textOf(a, content), idents: [], line: _lineOf(a),
226
+ })) : [],
227
+ line: _lineOf(node),
228
+ fullPath: _textOf(callee, content),
229
+ });
230
+ break;
231
+ }
232
+ case 'assignment_expression':
233
+ case 'variable_declarator': {
234
+ const target = node.childForFieldName?.('left') || node.childForFieldName?.('name');
235
+ const rhs = node.childForFieldName?.('right') || node.childForFieldName?.('value');
236
+ if (target) {
237
+ const txt = _textOf(target, content);
238
+ const isMember = txt.includes('.');
239
+ const parts = txt.split('.');
240
+ ir.assignments.push({
241
+ target: parts[0], isMember,
242
+ memberPath: isMember ? parts.slice(1).join('.') : null,
243
+ rhsText: rhs ? _textOf(rhs, content) : '',
244
+ line: _lineOf(node),
245
+ });
246
+ }
247
+ break;
248
+ }
249
+ case 'import_statement':
250
+ case 'import_declaration':
251
+ ir.imports.push({
252
+ kind: 'static',
253
+ module: _textOf(node.childForFieldName?.('source'), content).replace(/['"`]/g, ''),
254
+ line: _lineOf(node), names: [],
255
+ });
256
+ break;
257
+ }
258
+ }
259
+
260
+ function _normalizeJava(node, ir, content) {
261
+ _walkChildren(node, (child) => _normalizeJava(child, ir, content));
262
+ switch (node.type) {
263
+ case 'method_declaration':
264
+ case 'constructor_declaration':
265
+ ir.functions.push({
266
+ name: _textOf(node.childForFieldName?.('name'), content),
267
+ line: _lineOf(node), params: [],
268
+ });
269
+ break;
270
+ case 'class_declaration':
271
+ ir.classes.push({
272
+ name: _textOf(node.childForFieldName?.('name'), content),
273
+ line: _lineOf(node),
274
+ });
275
+ break;
276
+ case 'method_invocation': {
277
+ const object = node.childForFieldName?.('object');
278
+ const name = node.childForFieldName?.('name');
279
+ ir.calls.push({
280
+ callee: _textOf(name, content),
281
+ receiver: object ? _textOf(object, content) : null,
282
+ args: [], line: _lineOf(node),
283
+ fullPath: (object ? _textOf(object, content) + '.' : '') + _textOf(name, content),
284
+ });
285
+ break;
286
+ }
287
+ case 'assignment_expression': {
288
+ const target = node.childForFieldName?.('left');
289
+ const rhs = node.childForFieldName?.('right');
290
+ if (target) {
291
+ const txt = _textOf(target, content);
292
+ const isMember = txt.includes('.');
293
+ ir.assignments.push({
294
+ target: txt.split('.')[0], isMember,
295
+ memberPath: isMember ? txt.split('.').slice(1).join('.') : null,
296
+ rhsText: rhs ? _textOf(rhs, content) : '',
297
+ line: _lineOf(node),
298
+ });
299
+ }
300
+ break;
301
+ }
302
+ case 'local_variable_declaration':
303
+ case 'field_declaration': {
304
+ const t = node.childForFieldName?.('type');
305
+ const decl = node.childForFieldName?.('declarator');
306
+ const name = decl?.childForFieldName?.('name');
307
+ if (name) ir.decls.push({
308
+ kind: 'var', name: _textOf(name, content),
309
+ type: t ? _textOf(t, content) : null,
310
+ initText: decl?.childForFieldName?.('value') ? _textOf(decl.childForFieldName('value'), content) : null,
311
+ line: _lineOf(node),
312
+ });
313
+ break;
314
+ }
315
+ }
316
+ }
317
+
318
+ function _normalizeCsharp(node, ir, content) { _normalizeJava(node, ir, content); }
319
+ function _normalizeCLike(node, ir, content) { _normalizeJsLike(node, ir, content); }
320
+ function _normalizePython(node, ir, content) { _normalizeJsLike(node, ir, content); }
321
+ function _normalizeGo(node, ir, content) { _normalizeJsLike(node, ir, content); }
322
+ function _normalizeRust(node, ir, content) { _normalizeJsLike(node, ir, content); }
323
+ function _normalizeRuby(node, ir, content) { _normalizeJsLike(node, ir, content); }
324
+ function _normalizePhp(node, ir, content) { _normalizeJsLike(node, ir, content); }
325
+ function _normalizeGeneric(node, ir, content) {
326
+ // Fallback: just collect every call_expression / function shape we
327
+ // can recognize. Useful when the per-language normalizer doesn't exist
328
+ // yet — gives a baseline IR to query against.
329
+ _walkChildren(node, (child) => _normalizeGeneric(child, ir, content));
330
+ if (/call/i.test(node.type)) {
331
+ ir.calls.push({ callee: _textOf(node, content).split('(')[0].trim(), receiver: null, args: [], line: _lineOf(node), fullPath: _textOf(node, content).split('(')[0].trim() });
332
+ }
333
+ }
334
+
335
+ // ─── Query API ──────────────────────────────────────────────────────────────
336
+
337
+ /**
338
+ * queryIR(ir, expr) — match IR nodes against an S-expression-like spec.
339
+ *
340
+ * Spec shapes supported in v1:
341
+ * { node: 'call', name?: string|RegExp, receiver?: string|RegExp,
342
+ * receiverType?: string|RegExp, argIdx?: number,
343
+ * hasIdent?: string|RegExp }
344
+ * { node: 'assign', targetGlob?: string, rhsHasIdent?: string|RegExp }
345
+ * { node: 'function', nameGlob?: string, hasAttr?: string|RegExp }
346
+ * { node: 'class', nameGlob?: string, extends?: string|RegExp }
347
+ * { node: 'import', module?: string|RegExp }
348
+ *
349
+ * Returns: [{ node, captures }, ...] where captures are the matched IR nodes.
350
+ */
351
+ export function queryIR(ir, spec) {
352
+ if (!ir || !spec) return [];
353
+ const matches = [];
354
+ function testStr(pat, s) {
355
+ if (pat == null) return true;
356
+ if (pat instanceof RegExp) return pat.test(s || '');
357
+ return s === pat;
358
+ }
359
+ function testGlob(pat, s) {
360
+ if (!pat) return true;
361
+ const re = new RegExp('^' + pat.replace(/[.+^${}()|\\]/g, '\\$&').replace(/\*/g, '.*') + '$');
362
+ return re.test(s || '');
363
+ }
364
+ switch (spec.node) {
365
+ case 'call':
366
+ for (const c of ir.calls) {
367
+ if (!testStr(spec.name, c.callee)) continue;
368
+ if (!testStr(spec.receiver, c.receiver)) continue;
369
+ if (spec.hasIdent && !c.args.some(a => testStr(spec.hasIdent, a.text))) continue;
370
+ matches.push({ node: c, captures: {} });
371
+ }
372
+ break;
373
+ case 'assign':
374
+ for (const a of ir.assignments) {
375
+ const full = (a.target || '') + (a.memberPath ? '.' + a.memberPath : '');
376
+ if (!testGlob(spec.targetGlob, full)) continue;
377
+ if (spec.rhsHasIdent && !testStr(spec.rhsHasIdent, a.rhsText)) continue;
378
+ matches.push({ node: a, captures: {} });
379
+ }
380
+ break;
381
+ case 'function':
382
+ for (const fn of ir.functions) {
383
+ if (!testGlob(spec.nameGlob, fn.name)) continue;
384
+ matches.push({ node: fn, captures: {} });
385
+ }
386
+ break;
387
+ case 'class':
388
+ for (const cls of ir.classes) {
389
+ if (!testGlob(spec.nameGlob, cls.name)) continue;
390
+ matches.push({ node: cls, captures: {} });
391
+ }
392
+ break;
393
+ case 'import':
394
+ for (const imp of ir.imports) {
395
+ if (!testStr(spec.module, imp.module)) continue;
396
+ matches.push({ node: imp, captures: {} });
397
+ }
398
+ break;
399
+ }
400
+ return matches;
401
+ }
402
+
403
+ export const _internals = { GRAMMAR_BY_LANG, EXT_TO_LANG, NORMALIZERS };