@vyuhlabs/dxkit 2.7.1 → 2.9.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 (133) hide show
  1. package/CHANGELOG.md +145 -0
  2. package/README.md +20 -9
  3. package/dist/analyzers/cache.js +11 -0
  4. package/dist/analyzers/cache.js.map +1 -1
  5. package/dist/analyzers/security/aggregator.d.ts +20 -0
  6. package/dist/analyzers/security/aggregator.d.ts.map +1 -1
  7. package/dist/analyzers/security/aggregator.js +5 -0
  8. package/dist/analyzers/security/aggregator.js.map +1 -1
  9. package/dist/analyzers/security/gather.d.ts.map +1 -1
  10. package/dist/analyzers/security/gather.js +8 -0
  11. package/dist/analyzers/security/gather.js.map +1 -1
  12. package/dist/analyzers/tools/grep-secrets.d.ts +6 -1
  13. package/dist/analyzers/tools/grep-secrets.d.ts.map +1 -1
  14. package/dist/analyzers/tools/grep-secrets.js +80 -60
  15. package/dist/analyzers/tools/grep-secrets.js.map +1 -1
  16. package/dist/analyzers/tools/tool-registry.d.ts.map +1 -1
  17. package/dist/analyzers/tools/tool-registry.js +50 -0
  18. package/dist/analyzers/tools/tool-registry.js.map +1 -1
  19. package/dist/baseline/create.d.ts.map +1 -1
  20. package/dist/baseline/create.js +18 -6
  21. package/dist/baseline/create.js.map +1 -1
  22. package/dist/cli.d.ts.map +1 -1
  23. package/dist/cli.js +58 -0
  24. package/dist/cli.js.map +1 -1
  25. package/dist/doctor.d.ts.map +1 -1
  26. package/dist/doctor.js +85 -7
  27. package/dist/doctor.js.map +1 -1
  28. package/dist/explore/cli/context.d.ts +1 -1
  29. package/dist/explore/cli/context.d.ts.map +1 -1
  30. package/dist/explore/cli/context.js +173 -4
  31. package/dist/explore/cli/context.js.map +1 -1
  32. package/dist/explore/queries.d.ts +71 -0
  33. package/dist/explore/queries.d.ts.map +1 -1
  34. package/dist/explore/queries.js +76 -0
  35. package/dist/explore/queries.js.map +1 -1
  36. package/dist/explore/source-slice.d.ts +51 -0
  37. package/dist/explore/source-slice.d.ts.map +1 -0
  38. package/dist/explore/source-slice.js +88 -0
  39. package/dist/explore/source-slice.js.map +1 -0
  40. package/dist/explore-cli.js +6 -4
  41. package/dist/explore-cli.js.map +1 -1
  42. package/dist/generator.d.ts.map +1 -1
  43. package/dist/generator.js +18 -0
  44. package/dist/generator.js.map +1 -1
  45. package/dist/hooks-cli.d.ts.map +1 -1
  46. package/dist/hooks-cli.js +43 -0
  47. package/dist/hooks-cli.js.map +1 -1
  48. package/dist/ingest/codeql.d.ts +36 -0
  49. package/dist/ingest/codeql.d.ts.map +1 -0
  50. package/dist/ingest/codeql.js +166 -0
  51. package/dist/ingest/codeql.js.map +1 -0
  52. package/dist/ingest/config.d.ts +10 -0
  53. package/dist/ingest/config.d.ts.map +1 -0
  54. package/dist/ingest/config.js +69 -0
  55. package/dist/ingest/config.js.map +1 -0
  56. package/dist/ingest/engine-resolver.d.ts +42 -0
  57. package/dist/ingest/engine-resolver.d.ts.map +1 -0
  58. package/dist/ingest/engine-resolver.js +89 -0
  59. package/dist/ingest/engine-resolver.js.map +1 -0
  60. package/dist/ingest/normalize.d.ts +23 -0
  61. package/dist/ingest/normalize.d.ts.map +1 -0
  62. package/dist/ingest/normalize.js +18 -0
  63. package/dist/ingest/normalize.js.map +1 -0
  64. package/dist/ingest/sarif.d.ts +29 -0
  65. package/dist/ingest/sarif.d.ts.map +1 -0
  66. package/dist/ingest/sarif.js +136 -0
  67. package/dist/ingest/sarif.js.map +1 -0
  68. package/dist/ingest/snapshot.d.ts +26 -0
  69. package/dist/ingest/snapshot.d.ts.map +1 -0
  70. package/dist/ingest/snapshot.js +114 -0
  71. package/dist/ingest/snapshot.js.map +1 -0
  72. package/dist/ingest/snyk-api.d.ts +82 -0
  73. package/dist/ingest/snyk-api.d.ts.map +1 -0
  74. package/dist/ingest/snyk-api.js +114 -0
  75. package/dist/ingest/snyk-api.js.map +1 -0
  76. package/dist/ingest/snyk-cli.d.ts +22 -0
  77. package/dist/ingest/snyk-cli.d.ts.map +1 -0
  78. package/dist/ingest/snyk-cli.js +135 -0
  79. package/dist/ingest/snyk-cli.js.map +1 -0
  80. package/dist/ingest/types.d.ts +68 -0
  81. package/dist/ingest/types.d.ts.map +1 -0
  82. package/dist/ingest/types.js +3 -0
  83. package/dist/ingest/types.js.map +1 -0
  84. package/dist/ingest-cli.d.ts +21 -0
  85. package/dist/ingest-cli.d.ts.map +1 -0
  86. package/dist/ingest-cli.js +232 -0
  87. package/dist/ingest-cli.js.map +1 -0
  88. package/dist/languages/csharp.d.ts +9 -0
  89. package/dist/languages/csharp.d.ts.map +1 -1
  90. package/dist/languages/csharp.js +87 -7
  91. package/dist/languages/csharp.js.map +1 -1
  92. package/dist/languages/go.d.ts.map +1 -1
  93. package/dist/languages/go.js +2 -0
  94. package/dist/languages/go.js.map +1 -1
  95. package/dist/languages/index.d.ts +21 -1
  96. package/dist/languages/index.d.ts.map +1 -1
  97. package/dist/languages/index.js +32 -0
  98. package/dist/languages/index.js.map +1 -1
  99. package/dist/languages/java.d.ts.map +1 -1
  100. package/dist/languages/java.js +2 -0
  101. package/dist/languages/java.js.map +1 -1
  102. package/dist/languages/kotlin.d.ts.map +1 -1
  103. package/dist/languages/kotlin.js +8 -0
  104. package/dist/languages/kotlin.js.map +1 -1
  105. package/dist/languages/python.d.ts.map +1 -1
  106. package/dist/languages/python.js +2 -0
  107. package/dist/languages/python.js.map +1 -1
  108. package/dist/languages/ruby.d.ts.map +1 -1
  109. package/dist/languages/ruby.js +2 -0
  110. package/dist/languages/ruby.js.map +1 -1
  111. package/dist/languages/rust.d.ts.map +1 -1
  112. package/dist/languages/rust.js +3 -0
  113. package/dist/languages/rust.js.map +1 -1
  114. package/dist/languages/types.d.ts +40 -0
  115. package/dist/languages/types.d.ts.map +1 -1
  116. package/dist/languages/typescript.d.ts.map +1 -1
  117. package/dist/languages/typescript.js +3 -0
  118. package/dist/languages/typescript.js.map +1 -1
  119. package/dist/ship-installers.d.ts +22 -0
  120. package/dist/ship-installers.d.ts.map +1 -1
  121. package/dist/ship-installers.js +83 -3
  122. package/dist/ship-installers.js.map +1 -1
  123. package/dist/update.d.ts.map +1 -1
  124. package/dist/update.js +8 -0
  125. package/dist/update.js.map +1 -1
  126. package/package.json +1 -1
  127. package/templates/.claude/skills/dxkit-action/SKILL.md +9 -0
  128. package/templates/.claude/skills/dxkit-config/SKILL.md +23 -0
  129. package/templates/.claude/skills/dxkit-docs/SKILL.md +148 -0
  130. package/templates/.claude/skills/dxkit-feature/SKILL.md +189 -0
  131. package/templates/.claude/skills/dxkit-ingest/SKILL.md +99 -0
  132. package/templates/.claude/skills/dxkit-update/SKILL.md +10 -0
  133. package/templates/.github/workflows/dxkit-deep-sast-refresh.yml +104 -0
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveDeepSastEngine = resolveDeepSastEngine;
4
+ /**
5
+ * License-aware deep-SAST engine resolver.
6
+ *
7
+ * Interprocedural SAST is not free-and-unconditional the way dxkit's
8
+ * bundled scanners are: CodeQL's CLI license permits free analysis only
9
+ * for open-source repos or under GitHub Advanced Security; Snyk Code is
10
+ * the customer's own licensed engine. So "which engine do we use for
11
+ * deep SAST?" is a decision that depends on repo visibility, what the
12
+ * customer already licenses, and explicit consent — exactly the shape
13
+ * of the baseline-mode resolver (see `src/baseline/modes.ts`), and kept
14
+ * deliberately parallel to it.
15
+ *
16
+ * This module decides; it does not run anything. The caller (the
17
+ * `ingest` CLI, the `dxkit-ingest` skill) acts on the decision —
18
+ * including prompting for consent when `requiresConsent` is set, so an
19
+ * engine never runs on private code without the user accepting the
20
+ * license terms.
21
+ *
22
+ * Pure: the only I/O is the visibility probe, which is injectable for
23
+ * tests.
24
+ */
25
+ const visibility_1 = require("../baseline/visibility");
26
+ const GHAS_NOTE = "CodeQL's license permits free use on open-source repos, academic " +
27
+ 'research, or under GitHub Advanced Security. This repo is not public — ' +
28
+ 'confirm you have GitHub Advanced Security (or ingest an engine you ' +
29
+ 'already license, e.g. Snyk) before running CodeQL.';
30
+ /**
31
+ * Resolve which deep-SAST engine to use. Precedence:
32
+ * 1. explicit `--engine` flag
33
+ * 2. a configured Snyk token (ingest the customer's own results —
34
+ * license-safe, no consent)
35
+ * 3. repo visibility: public → CodeQL (licensed for OSS); otherwise
36
+ * CodeQL gated behind consent (GHAS), so the caller can prompt or
37
+ * fall back.
38
+ */
39
+ function resolveDeepSastEngine(opts) {
40
+ const probe = opts.visibilityProbe ?? visibility_1.detectRepoVisibility;
41
+ // 1. Explicit override.
42
+ if (opts.engineFlag) {
43
+ const engine = opts.engineFlag;
44
+ if (engine === 'codeql') {
45
+ const visibility = probe(opts.cwd);
46
+ const consent = visibility !== 'public';
47
+ return {
48
+ engine,
49
+ source: 'flag',
50
+ requiresConsent: consent,
51
+ explanation: `engine=codeql (--engine)${consent ? '; consent required (non-public repo)' : ''}`,
52
+ ...(consent ? { licenseNote: GHAS_NOTE } : {}),
53
+ };
54
+ }
55
+ return {
56
+ engine,
57
+ source: 'flag',
58
+ requiresConsent: false,
59
+ explanation: `engine=${engine} (--engine)`,
60
+ };
61
+ }
62
+ // 2. Snyk configured → ingest the customer's own licensed results.
63
+ if (opts.snykConfigured) {
64
+ return {
65
+ engine: 'snyk-code',
66
+ source: 'snyk-configured',
67
+ requiresConsent: false,
68
+ explanation: 'engine=snyk-code (SNYK_TOKEN + project configured; quota-free read)',
69
+ };
70
+ }
71
+ // 3. Visibility-derived default.
72
+ const visibility = probe(opts.cwd);
73
+ if (visibility === 'public') {
74
+ return {
75
+ engine: 'codeql',
76
+ source: 'visibility-public',
77
+ requiresConsent: false,
78
+ explanation: 'engine=codeql (public repo; CodeQL is licensed for open source)',
79
+ };
80
+ }
81
+ return {
82
+ engine: 'codeql',
83
+ source: 'visibility-private',
84
+ requiresConsent: true,
85
+ explanation: `engine=codeql (visibility=${visibility}; consent required before running)`,
86
+ licenseNote: GHAS_NOTE,
87
+ };
88
+ }
89
+ //# sourceMappingURL=engine-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-resolver.js","sourceRoot":"","sources":["../../src/ingest/engine-resolver.ts"],"names":[],"mappings":";;AA6EA,sDAoDC;AAjID;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,uDAA8D;AAyC9D,MAAM,SAAS,GACb,mEAAmE;IACnE,yEAAyE;IACzE,qEAAqE;IACrE,oDAAoD,CAAC;AAEvD;;;;;;;;GAQG;AACH,SAAgB,qBAAqB,CAAC,IAA4B;IAChE,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,IAAI,iCAAoB,CAAC;IAE3D,wBAAwB;IACxB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC;QAC/B,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YACxB,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnC,MAAM,OAAO,GAAG,UAAU,KAAK,QAAQ,CAAC;YACxC,OAAO;gBACL,MAAM;gBACN,MAAM,EAAE,MAAM;gBACd,eAAe,EAAE,OAAO;gBACxB,WAAW,EAAE,2BAA2B,OAAO,CAAC,CAAC,CAAC,sCAAsC,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC/F,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC/C,CAAC;QACJ,CAAC;QACD,OAAO;YACL,MAAM;YACN,MAAM,EAAE,MAAM;YACd,eAAe,EAAE,KAAK;YACtB,WAAW,EAAE,UAAU,MAAM,aAAa;SAC3C,CAAC;IACJ,CAAC;IAED,mEAAmE;IACnE,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,OAAO;YACL,MAAM,EAAE,WAAW;YACnB,MAAM,EAAE,iBAAiB;YACzB,eAAe,EAAE,KAAK;YACtB,WAAW,EAAE,qEAAqE;SACnF,CAAC;IACJ,CAAC;IAED,iCAAiC;IACjC,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO;YACL,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,mBAAmB;YAC3B,eAAe,EAAE,KAAK;YACtB,WAAW,EAAE,iEAAiE;SAC/E,CAAC;IACJ,CAAC;IACD,OAAO;QACL,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,oBAAoB;QAC5B,eAAe,EAAE,IAAI;QACrB,WAAW,EAAE,6BAA6B,UAAU,oCAAoC;QACxF,WAAW,EAAE,SAAS;KACvB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Normalize ingested external findings into the security pipeline's
3
+ * `SecurityFinding` shape.
4
+ *
5
+ * Deliberately a pure field-map and nothing more. In particular it does
6
+ * NOT compute identity: the security aggregator owns fingerprinting for
7
+ * every code finding (`canonicalRuleFor(tool, rule)` →
8
+ * `computeCodeFingerprint`) and the cross-tool dedup that collapses,
9
+ * say, a Snyk Code and a semgrep finding on the same line into one
10
+ * `CodeFinding`. Routing ingested findings through that same path is
11
+ * what keeps one fingerprint scheme across native + ingested findings
12
+ * (Rule 9) — an ingest-local hash would silently fork the contract.
13
+ *
14
+ * The engine name becomes the finding's `tool` provenance, so the
15
+ * canonical rule (`canonicalRuleFor(engine, rule)`) and the report's
16
+ * attribution both trace back to the producing engine.
17
+ */
18
+ import type { SecurityFinding } from '../analyzers/security/types';
19
+ import type { ExternalFinding } from './types';
20
+ /** Map normalized external findings to `SecurityFinding[]` for the
21
+ * aggregator. Identity + dedup happen downstream (Rule 9). */
22
+ export declare function externalToSecurityFindings(findings: ReadonlyArray<ExternalFinding>): SecurityFinding[];
23
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/ingest/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;+DAC+D;AAC/D,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,aAAa,CAAC,eAAe,CAAC,GACvC,eAAe,EAAE,CAWnB"}
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.externalToSecurityFindings = externalToSecurityFindings;
4
+ /** Map normalized external findings to `SecurityFinding[]` for the
5
+ * aggregator. Identity + dedup happen downstream (Rule 9). */
6
+ function externalToSecurityFindings(findings) {
7
+ return findings.map((f) => ({
8
+ severity: f.severity,
9
+ category: f.category,
10
+ cwe: f.cwe,
11
+ rule: f.rule,
12
+ title: f.title,
13
+ file: f.file,
14
+ line: f.line,
15
+ tool: f.engine,
16
+ }));
17
+ }
18
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/ingest/normalize.ts"],"names":[],"mappings":";;AAsBA,gEAaC;AAfD;+DAC+D;AAC/D,SAAgB,0BAA0B,CACxC,QAAwC;IAExC,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1B,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,IAAI,EAAE,CAAC,CAAC,MAAM;KACf,CAAC,CAAC,CAAC;AACN,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * SARIF 2.1.0 → `ExternalFinding[]`.
3
+ *
4
+ * SARIF is the lingua franca of SAST: CodeQL, Snyk Code (`snyk code
5
+ * test --sarif`), Semgrep Pro, and Bearer all emit it. Parsing it once
6
+ * here means every current and future interprocedural engine funnels
7
+ * into dxkit through the same door — the engine-agnostic core of the
8
+ * deep-SAST tier.
9
+ *
10
+ * Severity resolution prefers the numeric `security-severity` property
11
+ * (a CVSS-style 0–10 score most security rules carry) and falls back to
12
+ * the SARIF result `level`. CWE comes from the rule's `tags`
13
+ * (`external/cwe/cwe-022` → `CWE-22`). Rules can live on the driver or
14
+ * on a tool extension (CodeQL puts the query pack's rules under
15
+ * `extensions`), so we index both.
16
+ *
17
+ * Pure + defensive: a malformed run, a result with no location, or a
18
+ * missing rule never throws — it's skipped or filled with a safe
19
+ * default, because ingestion runs against third-party output we don't
20
+ * control.
21
+ */
22
+ import type { SourceEngine, ExternalFinding } from './types';
23
+ /**
24
+ * Parse a SARIF 2.1.0 document. `engine` overrides the auto-detected
25
+ * driver name (callers usually know which engine produced the file);
26
+ * pass `undefined` to infer from the SARIF tool driver.
27
+ */
28
+ export declare function parseSarif(raw: string, engine?: SourceEngine): ExternalFinding[];
29
+ //# sourceMappingURL=sarif.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sarif.d.ts","sourceRoot":"","sources":["../../src/ingest/sarif.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAuG7D;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,eAAe,EAAE,CAqDhF"}
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseSarif = parseSarif;
4
+ /** Map a SARIF tool driver name to a known engine, when the caller
5
+ * didn't pass one explicitly. Unknown drivers fall back to the generic
6
+ * `sarif` engine so attribution is still honest. */
7
+ function engineFromDriver(name) {
8
+ const n = (name || '').toLowerCase();
9
+ if (n.includes('codeql'))
10
+ return 'codeql';
11
+ if (n.includes('snyk'))
12
+ return 'snyk-code';
13
+ if (n.includes('semgrep'))
14
+ return 'semgrep-pro';
15
+ return 'sarif';
16
+ }
17
+ /** Normalize any CWE-ish string to canonical `CWE-<n>` (leading zeros
18
+ * stripped), or '' when none present. */
19
+ function normalizeCwe(s) {
20
+ if (!s)
21
+ return '';
22
+ const m = /cwe[-/_]?(\d+)/i.exec(s);
23
+ return m ? `CWE-${parseInt(m[1], 10)}` : '';
24
+ }
25
+ /** Resolve the CWE for a rule across engine conventions:
26
+ * Snyk Code uses `properties.cwe: ["CWE-94"]`; CodeQL uses
27
+ * `properties.tags: ["external/cwe/cwe-094"]`. Checks both. */
28
+ function cweFromRule(rule) {
29
+ const direct = rule?.properties?.cwe;
30
+ if (Array.isArray(direct)) {
31
+ for (const c of direct) {
32
+ const n = normalizeCwe(c);
33
+ if (n)
34
+ return n;
35
+ }
36
+ }
37
+ for (const tag of rule?.properties?.tags ?? []) {
38
+ const n = normalizeCwe(tag);
39
+ if (n)
40
+ return n;
41
+ }
42
+ return '';
43
+ }
44
+ /** Resolve four-tier severity. Prefer numeric `security-severity`
45
+ * (CVSS-like 0–10); else the rule's `problem.severity`; else the
46
+ * result `level`. */
47
+ function resolveSeverity(rule, level) {
48
+ const num = rule?.properties?.['security-severity'];
49
+ if (num !== undefined && num !== null && num !== '') {
50
+ const score = typeof num === 'number' ? num : parseFloat(String(num));
51
+ if (!Number.isNaN(score)) {
52
+ if (score >= 9.0)
53
+ return 'critical';
54
+ if (score >= 7.0)
55
+ return 'high';
56
+ if (score >= 4.0)
57
+ return 'medium';
58
+ return 'low';
59
+ }
60
+ }
61
+ const ps = rule?.properties?.['problem.severity'];
62
+ if (ps === 'error')
63
+ return 'high';
64
+ if (ps === 'warning' || ps === 'recommendation')
65
+ return 'medium';
66
+ // SARIF result level is the last resort.
67
+ if (level === 'error')
68
+ return 'high';
69
+ if (level === 'note')
70
+ return 'low';
71
+ if (level === 'warning')
72
+ return 'medium';
73
+ return 'medium';
74
+ }
75
+ /**
76
+ * Parse a SARIF 2.1.0 document. `engine` overrides the auto-detected
77
+ * driver name (callers usually know which engine produced the file);
78
+ * pass `undefined` to infer from the SARIF tool driver.
79
+ */
80
+ function parseSarif(raw, engine) {
81
+ let log;
82
+ try {
83
+ log = JSON.parse(raw);
84
+ }
85
+ catch {
86
+ return [];
87
+ }
88
+ const out = [];
89
+ for (const run of log.runs || []) {
90
+ const driverName = run.tool?.driver?.name;
91
+ const resolvedEngine = engine ?? engineFromDriver(driverName);
92
+ // Index rules by id from driver + every extension.
93
+ const rulesById = new Map();
94
+ const ruleLists = [];
95
+ if (run.tool?.driver?.rules)
96
+ ruleLists.push(run.tool.driver.rules);
97
+ for (const ext of run.tool?.extensions || []) {
98
+ if (ext.rules)
99
+ ruleLists.push(ext.rules);
100
+ }
101
+ const flatRules = [];
102
+ for (const list of ruleLists) {
103
+ for (const r of list) {
104
+ flatRules.push(r);
105
+ if (r.id)
106
+ rulesById.set(r.id, r);
107
+ }
108
+ }
109
+ for (const res of run.results || []) {
110
+ const ruleId = res.ruleId || res.rule?.id;
111
+ // Rule can be referenced by id or by index into the (flattened) list.
112
+ let rule = ruleId ? rulesById.get(ruleId) : undefined;
113
+ if (!rule && typeof res.rule?.index === 'number')
114
+ rule = flatRules[res.rule.index];
115
+ const loc = res.locations?.[0]?.physicalLocation;
116
+ const file = loc?.artifactLocation?.uri;
117
+ const line = loc?.region?.startLine;
118
+ // A finding with no source location can't be fingerprinted or
119
+ // fixed — skip rather than emit a phantom at line 0.
120
+ if (!file || !line)
121
+ continue;
122
+ out.push({
123
+ engine: resolvedEngine,
124
+ severity: resolveSeverity(rule, res.level),
125
+ category: 'code',
126
+ cwe: cweFromRule(rule),
127
+ rule: ruleId || rule?.name || 'unknown',
128
+ title: res.message?.text || rule?.shortDescription?.text || ruleId || 'SAST finding',
129
+ file,
130
+ line,
131
+ });
132
+ }
133
+ }
134
+ return out;
135
+ }
136
+ //# sourceMappingURL=sarif.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sarif.js","sourceRoot":"","sources":["../../src/ingest/sarif.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { SourceEngine, ExternalFinding } from './types';
2
+ /** Directory (relative to repo root) where ingestion snapshots live. */
3
+ export declare const EXTERNAL_DIR: string;
4
+ export interface ExternalSnapshot {
5
+ schemaVersion: 1;
6
+ engine: SourceEngine;
7
+ /** ISO timestamp the snapshot was produced. Stamped by the caller so
8
+ * this module stays free of clock access (testability). */
9
+ generatedAt: string;
10
+ /** Commit the snapshot was produced against, when known. */
11
+ commitSha?: string;
12
+ findings: ExternalFinding[];
13
+ }
14
+ /** Write (overwrite) an engine's snapshot. Creates `.dxkit/external/`
15
+ * if needed. */
16
+ export declare function writeSnapshot(cwd: string, snapshot: ExternalSnapshot): string;
17
+ /**
18
+ * Read every snapshot under `.dxkit/external/` and return the union of
19
+ * their findings. Fail-open: a missing directory, an unreadable file,
20
+ * or a malformed snapshot yields no findings from that file rather than
21
+ * throwing — ingestion is optional and must never break a scan.
22
+ */
23
+ export declare function readAllSnapshots(cwd: string): ExternalFinding[];
24
+ /** Distinct engines present in `.dxkit/external/` (for provenance). */
25
+ export declare function snapshotEngines(cwd: string): string[];
26
+ //# sourceMappingURL=snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/ingest/snapshot.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE7D,wEAAwE;AACxE,eAAO,MAAM,YAAY,QAAkC,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,CAAC,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;IACrB;gEAC4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAOD;iBACiB;AACjB,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAM7E;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,EAAE,CAmB/D;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAUrD"}
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.EXTERNAL_DIR = void 0;
37
+ exports.writeSnapshot = writeSnapshot;
38
+ exports.readAllSnapshots = readAllSnapshots;
39
+ exports.snapshotEngines = snapshotEngines;
40
+ /**
41
+ * Persisted ingestion snapshots under `.dxkit/external/`.
42
+ *
43
+ * `vyuh-dxkit ingest` writes one snapshot file per engine
44
+ * (`.dxkit/external/<engine>.json`). The snapshot is committed to the
45
+ * repo so every developer and every CI run reads the ingested findings
46
+ * WITHOUT needing the engine's token — only the one CI refresh job that
47
+ * produced it needs `SNYK_TOKEN` (or a CodeQL license). This is what
48
+ * makes "an admin adds the token once and everyone benefits" true.
49
+ *
50
+ * The snapshot is the normalized `ExternalFinding[]` plus light
51
+ * metadata for provenance/diagnostics. It deliberately carries no
52
+ * engine-token or account identifier — only finding data — so it is
53
+ * safe to commit.
54
+ */
55
+ const fs = __importStar(require("fs"));
56
+ const path = __importStar(require("path"));
57
+ /** Directory (relative to repo root) where ingestion snapshots live. */
58
+ exports.EXTERNAL_DIR = path.join('.dxkit', 'external');
59
+ /** Absolute path to an engine's snapshot file. */
60
+ function snapshotPath(cwd, engine) {
61
+ return path.join(cwd, exports.EXTERNAL_DIR, `${engine}.json`);
62
+ }
63
+ /** Write (overwrite) an engine's snapshot. Creates `.dxkit/external/`
64
+ * if needed. */
65
+ function writeSnapshot(cwd, snapshot) {
66
+ const dir = path.join(cwd, exports.EXTERNAL_DIR);
67
+ fs.mkdirSync(dir, { recursive: true });
68
+ const file = snapshotPath(cwd, snapshot.engine);
69
+ fs.writeFileSync(file, JSON.stringify(snapshot, null, 2) + '\n', 'utf-8');
70
+ return file;
71
+ }
72
+ /**
73
+ * Read every snapshot under `.dxkit/external/` and return the union of
74
+ * their findings. Fail-open: a missing directory, an unreadable file,
75
+ * or a malformed snapshot yields no findings from that file rather than
76
+ * throwing — ingestion is optional and must never break a scan.
77
+ */
78
+ function readAllSnapshots(cwd) {
79
+ const dir = path.join(cwd, exports.EXTERNAL_DIR);
80
+ let entries;
81
+ try {
82
+ entries = fs.readdirSync(dir).filter((f) => f.endsWith('.json'));
83
+ }
84
+ catch {
85
+ return [];
86
+ }
87
+ const out = [];
88
+ for (const name of entries) {
89
+ try {
90
+ const raw = fs.readFileSync(path.join(dir, name), 'utf-8');
91
+ const snap = JSON.parse(raw);
92
+ if (Array.isArray(snap.findings))
93
+ out.push(...snap.findings);
94
+ }
95
+ catch {
96
+ // skip unreadable / malformed snapshot
97
+ }
98
+ }
99
+ return out;
100
+ }
101
+ /** Distinct engines present in `.dxkit/external/` (for provenance). */
102
+ function snapshotEngines(cwd) {
103
+ const dir = path.join(cwd, exports.EXTERNAL_DIR);
104
+ try {
105
+ return fs
106
+ .readdirSync(dir)
107
+ .filter((f) => f.endsWith('.json'))
108
+ .map((f) => f.replace(/\.json$/, ''));
109
+ }
110
+ catch {
111
+ return [];
112
+ }
113
+ }
114
+ //# sourceMappingURL=snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/ingest/snapshot.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,sCAMC;AAQD,4CAmBC;AAGD,0CAUC;AAtFD;;;;;;;;;;;;;;GAcG;AACH,uCAAyB;AACzB,2CAA6B;AAG7B,wEAAwE;AAC3D,QAAA,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;AAa5D,kDAAkD;AAClD,SAAS,YAAY,CAAC,GAAW,EAAE,MAAoB;IACrD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,oBAAY,EAAE,GAAG,MAAM,OAAO,CAAC,CAAC;AACxD,CAAC;AAED;iBACiB;AACjB,SAAgB,aAAa,CAAC,GAAW,EAAE,QAA0B;IACnE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,oBAAY,CAAC,CAAC;IACzC,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IAC1E,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,SAAgB,gBAAgB,CAAC,GAAW;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,oBAAY,CAAC,CAAC;IACzC,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,GAAG,GAAsB,EAAE,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;YAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAqB,CAAC;YACjD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/D,CAAC;QAAC,MAAM,CAAC;YACP,uCAAuC;QACzC,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,uEAAuE;AACvE,SAAgB,eAAe,CAAC,GAAW;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,oBAAY,CAAC,CAAC;IACzC,IAAI,CAAC;QACH,OAAO,EAAE;aACN,WAAW,CAAC,GAAG,CAAC;aAChB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;aAClC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Snyk Code (SAST) reader — pulls a project's already-computed findings
3
+ * from the Snyk REST API.
4
+ *
5
+ * Why API-read (not `snyk code test`): reading stored issues does NOT
6
+ * consume the org's Snyk Code test quota. On a free/limited tier where
7
+ * tests are capped per billing period, this is the only repeatable way
8
+ * to get the findings out. An admin provides a `SNYK_TOKEN` once
9
+ * (ideally as a CI secret); the refresh job commits a snapshot so every
10
+ * other developer and CI run reads it without a token.
11
+ *
12
+ * Endpoint (REST):
13
+ * GET /rest/orgs/{org_id}/issues
14
+ * ?version=<date>&type=code
15
+ * &scan_item.type=project&scan_item.id=<project_id>
16
+ * Authorization: token <SNYK_TOKEN>
17
+ *
18
+ * Field paths (confirmed against the published OpenAPI):
19
+ * - severity → attributes.effective_severity_level
20
+ * (info|low|medium|high|critical)
21
+ * - cwe → attributes.classes[] where source == 'CWE' (id 'CWE-23')
22
+ * - title → attributes.title
23
+ * - location → attributes.coordinates[].representations[].sourceLocation
24
+ * .file + .region.start.line
25
+ *
26
+ * VALIDATION NOTE: the precise auth scheme (`token` vs `Bearer`) and the
27
+ * exact nesting of `sourceLocation` for code issues must be confirmed
28
+ * against a REAL response from a live token before this is trusted in
29
+ * production — synthetic-schema parsers drift from real output. The
30
+ * parser below is defensive (missing fields skip, never throw) so a
31
+ * schema surprise degrades to "fewer findings", never a crash.
32
+ */
33
+ import type { ExternalFinding } from './types';
34
+ export interface SnykReadOptions {
35
+ token: string;
36
+ orgId: string;
37
+ projectId: string;
38
+ /** REST API base; override for self-hosted/regional tenants
39
+ * (e.g. https://api.eu.snyk.io). */
40
+ apiBase?: string;
41
+ /** REST API version date. Snyk requires an explicit version. */
42
+ version?: string;
43
+ }
44
+ interface SnykClass {
45
+ id?: string;
46
+ source?: string;
47
+ }
48
+ interface SnykRepresentation {
49
+ sourceLocation?: {
50
+ file?: string;
51
+ region?: {
52
+ start?: {
53
+ line?: number;
54
+ };
55
+ };
56
+ };
57
+ }
58
+ interface SnykCoordinate {
59
+ representations?: SnykRepresentation[];
60
+ }
61
+ interface SnykIssue {
62
+ id?: string;
63
+ attributes?: {
64
+ title?: string;
65
+ type?: string;
66
+ effective_severity_level?: string;
67
+ classes?: SnykClass[];
68
+ coordinates?: SnykCoordinate[];
69
+ };
70
+ }
71
+ /** Map one Snyk issue → `ExternalFinding`, or null when it has no
72
+ * usable source location (can't be fingerprinted/fixed). */
73
+ export declare function snykIssueToFinding(issue: SnykIssue): ExternalFinding | null;
74
+ /**
75
+ * Fetch all Snyk Code findings for a project, following pagination.
76
+ * Pure-ish: the only side effect is the network read. Throws on auth /
77
+ * network failure so the CLI can surface a clear message; a successful
78
+ * call with zero issues returns `[]`.
79
+ */
80
+ export declare function fetchSnykCodeFindings(opts: SnykReadOptions): Promise<ExternalFinding[]>;
81
+ export {};
82
+ //# sourceMappingURL=snyk-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snyk-api.d.ts","sourceRoot":"","sources":["../../src/ingest/snyk-api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAG/C,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB;yCACqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAuBD,UAAU,SAAS;IACjB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AACD,UAAU,kBAAkB;IAC1B,cAAc,CAAC,EAAE;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE;YAAE,KAAK,CAAC,EAAE;gBAAE,IAAI,CAAC,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,CAAC;KACxC,CAAC;CACH;AACD,UAAU,cAAc;IACtB,eAAe,CAAC,EAAE,kBAAkB,EAAE,CAAC;CACxC;AACD,UAAU,SAAS;IACjB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE;QACX,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,wBAAwB,CAAC,EAAE,MAAM,CAAC;QAClC,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC;QACtB,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;KAChC,CAAC;CACH;AA8BD;6DAC6D;AAC7D,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,GAAG,eAAe,GAAG,IAAI,CAe3E;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAsC7F"}