audit-tools 0.30.0 → 0.30.2

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 (141) hide show
  1. package/dist/audit/adapters/astGrep.d.ts +30 -0
  2. package/dist/audit/adapters/astGrep.d.ts.map +1 -0
  3. package/dist/audit/adapters/astGrep.js +46 -0
  4. package/dist/audit/adapters/astGrep.js.map +1 -0
  5. package/dist/audit/adapters/codeql.d.ts +47 -0
  6. package/dist/audit/adapters/codeql.d.ts.map +1 -0
  7. package/dist/audit/adapters/codeql.js +52 -0
  8. package/dist/audit/adapters/codeql.js.map +1 -0
  9. package/dist/audit/adapters/normalizeExternal.d.ts +24 -1
  10. package/dist/audit/adapters/normalizeExternal.d.ts.map +1 -1
  11. package/dist/audit/adapters/normalizeExternal.js +50 -0
  12. package/dist/audit/adapters/normalizeExternal.js.map +1 -1
  13. package/dist/audit/adapters/semgrep.d.ts +34 -15
  14. package/dist/audit/adapters/semgrep.d.ts.map +1 -1
  15. package/dist/audit/adapters/semgrep.js +54 -1
  16. package/dist/audit/adapters/semgrep.js.map +1 -1
  17. package/dist/audit/cli/mergeAndIngestCommand.d.ts +29 -1
  18. package/dist/audit/cli/mergeAndIngestCommand.d.ts.map +1 -1
  19. package/dist/audit/cli/mergeAndIngestCommand.js +71 -5
  20. package/dist/audit/cli/mergeAndIngestCommand.js.map +1 -1
  21. package/dist/audit/cli/nextStepHelpers.d.ts.map +1 -1
  22. package/dist/audit/cli/nextStepHelpers.js +7 -6
  23. package/dist/audit/cli/nextStepHelpers.js.map +1 -1
  24. package/dist/audit/cli/resynthesizeCommand.d.ts.map +1 -1
  25. package/dist/audit/cli/resynthesizeCommand.js +3 -3
  26. package/dist/audit/cli/resynthesizeCommand.js.map +1 -1
  27. package/dist/audit/cli/submitPacketCommand.d.ts.map +1 -1
  28. package/dist/audit/cli/submitPacketCommand.js +6 -1
  29. package/dist/audit/cli/submitPacketCommand.js.map +1 -1
  30. package/dist/audit/contracts/workerSchemas.d.ts +13 -13
  31. package/dist/audit/contracts/workerSchemas.d.ts.map +1 -1
  32. package/dist/audit/contracts/workerSchemas.js +1 -1
  33. package/dist/audit/contracts/workerSchemas.js.map +1 -1
  34. package/dist/audit/contracts/wrapperResponse.d.ts +7 -0
  35. package/dist/audit/contracts/wrapperResponse.d.ts.map +1 -1
  36. package/dist/audit/contracts/wrapperResponse.js +1 -0
  37. package/dist/audit/contracts/wrapperResponse.js.map +1 -1
  38. package/dist/audit/extractors/graph.d.ts.map +1 -1
  39. package/dist/audit/extractors/graph.js +47 -0
  40. package/dist/audit/extractors/graph.js.map +1 -1
  41. package/dist/audit/io/artifacts.d.ts +2 -2
  42. package/dist/audit/io/artifacts.d.ts.map +1 -1
  43. package/dist/audit/io/artifacts.js +14 -11
  44. package/dist/audit/io/artifacts.js.map +1 -1
  45. package/dist/audit/orchestrator/designReviewPrompt.d.ts +17 -0
  46. package/dist/audit/orchestrator/designReviewPrompt.d.ts.map +1 -1
  47. package/dist/audit/orchestrator/designReviewPrompt.js +26 -0
  48. package/dist/audit/orchestrator/designReviewPrompt.js.map +1 -1
  49. package/dist/audit/orchestrator/executorRunners.d.ts.map +1 -1
  50. package/dist/audit/orchestrator/executorRunners.js +20 -0
  51. package/dist/audit/orchestrator/executorRunners.js.map +1 -1
  52. package/dist/audit/orchestrator/executors.d.ts.map +1 -1
  53. package/dist/audit/orchestrator/executors.js +5 -0
  54. package/dist/audit/orchestrator/executors.js.map +1 -1
  55. package/dist/audit/orchestrator/nextStep.d.ts +37 -0
  56. package/dist/audit/orchestrator/nextStep.d.ts.map +1 -1
  57. package/dist/audit/orchestrator/nextStep.js +45 -0
  58. package/dist/audit/orchestrator/nextStep.js.map +1 -1
  59. package/dist/audit/supervisor/operatorHandoff.d.ts +12 -0
  60. package/dist/audit/supervisor/operatorHandoff.d.ts.map +1 -1
  61. package/dist/audit/supervisor/operatorHandoff.js +3 -1
  62. package/dist/audit/supervisor/operatorHandoff.js.map +1 -1
  63. package/dist/audit/types/externalAnalyzer.d.ts +68 -2
  64. package/dist/audit/types/externalAnalyzer.d.ts.map +1 -1
  65. package/dist/audit/types/externalAnalyzer.js +23 -0
  66. package/dist/audit/types/externalAnalyzer.js.map +1 -1
  67. package/dist/audit/types/reviewPlanning.d.ts +14 -14
  68. package/dist/audit/validation/auditResults.d.ts +17 -0
  69. package/dist/audit/validation/auditResults.d.ts.map +1 -1
  70. package/dist/audit/validation/auditResults.js +39 -0
  71. package/dist/audit/validation/auditResults.js.map +1 -1
  72. package/dist/audit/validation/sessionConfig.d.ts.map +1 -1
  73. package/dist/audit/validation/sessionConfig.js +4 -0
  74. package/dist/audit/validation/sessionConfig.js.map +1 -1
  75. package/dist/remediate/review/autonomousGate.d.ts +74 -0
  76. package/dist/remediate/review/autonomousGate.d.ts.map +1 -0
  77. package/dist/remediate/review/autonomousGate.js +179 -0
  78. package/dist/remediate/review/autonomousGate.js.map +1 -0
  79. package/dist/remediate/steps/contractPipeline.d.ts +8 -0
  80. package/dist/remediate/steps/contractPipeline.d.ts.map +1 -1
  81. package/dist/remediate/steps/contractPipeline.js +1 -0
  82. package/dist/remediate/steps/contractPipeline.js.map +1 -1
  83. package/dist/remediate/steps/contractPipelinePrompts.d.ts +10 -0
  84. package/dist/remediate/steps/contractPipelinePrompts.d.ts.map +1 -1
  85. package/dist/remediate/steps/contractPipelinePrompts.js +32 -1
  86. package/dist/remediate/steps/contractPipelinePrompts.js.map +1 -1
  87. package/dist/remediate/steps/nextStep.d.ts +34 -0
  88. package/dist/remediate/steps/nextStep.d.ts.map +1 -1
  89. package/dist/remediate/steps/nextStep.js +144 -8
  90. package/dist/remediate/steps/nextStep.js.map +1 -1
  91. package/dist/shared/index.d.ts +11 -2
  92. package/dist/shared/index.d.ts.map +1 -1
  93. package/dist/shared/index.js +6 -2
  94. package/dist/shared/index.js.map +1 -1
  95. package/dist/shared/io/auditToolsPaths.d.ts +35 -0
  96. package/dist/shared/io/auditToolsPaths.d.ts.map +1 -1
  97. package/dist/shared/io/auditToolsPaths.js +46 -1
  98. package/dist/shared/io/auditToolsPaths.js.map +1 -1
  99. package/dist/shared/io/frictionCapture.d.ts +83 -0
  100. package/dist/shared/io/frictionCapture.d.ts.map +1 -0
  101. package/dist/shared/io/frictionCapture.js +79 -0
  102. package/dist/shared/io/frictionCapture.js.map +1 -0
  103. package/dist/shared/io/gitignoreArtifacts.d.ts +94 -0
  104. package/dist/shared/io/gitignoreArtifacts.d.ts.map +1 -0
  105. package/dist/shared/io/gitignoreArtifacts.js +147 -0
  106. package/dist/shared/io/gitignoreArtifacts.js.map +1 -0
  107. package/dist/shared/quota/antigravityQuotaSource.d.ts.map +1 -1
  108. package/dist/shared/quota/antigravityQuotaSource.js +2 -0
  109. package/dist/shared/quota/antigravityQuotaSource.js.map +1 -1
  110. package/dist/shared/quota/claudeOAuthQuotaSource.d.ts +35 -2
  111. package/dist/shared/quota/claudeOAuthQuotaSource.d.ts.map +1 -1
  112. package/dist/shared/quota/claudeOAuthQuotaSource.js +157 -17
  113. package/dist/shared/quota/claudeOAuthQuotaSource.js.map +1 -1
  114. package/dist/shared/quota/codexQuotaSource.d.ts.map +1 -1
  115. package/dist/shared/quota/codexQuotaSource.js +2 -0
  116. package/dist/shared/quota/codexQuotaSource.js.map +1 -1
  117. package/dist/shared/quota/copilotQuotaSource.d.ts.map +1 -1
  118. package/dist/shared/quota/copilotQuotaSource.js +2 -0
  119. package/dist/shared/quota/copilotQuotaSource.js.map +1 -1
  120. package/dist/shared/quota/errorParsing.d.ts +19 -0
  121. package/dist/shared/quota/errorParsing.d.ts.map +1 -1
  122. package/dist/shared/quota/errorParsing.js +14 -1
  123. package/dist/shared/quota/errorParsing.js.map +1 -1
  124. package/dist/shared/quota/hostSessionQuotaSource.d.ts +138 -0
  125. package/dist/shared/quota/hostSessionQuotaSource.d.ts.map +1 -0
  126. package/dist/shared/quota/hostSessionQuotaSource.js +213 -0
  127. package/dist/shared/quota/hostSessionQuotaSource.js.map +1 -0
  128. package/dist/shared/quota/httpQuotaSource.d.ts +7 -3
  129. package/dist/shared/quota/httpQuotaSource.d.ts.map +1 -1
  130. package/dist/shared/quota/httpQuotaSource.js.map +1 -1
  131. package/dist/shared/reporting/auditDeliverable.d.ts +36 -0
  132. package/dist/shared/reporting/auditDeliverable.d.ts.map +1 -0
  133. package/dist/shared/reporting/auditDeliverable.js +85 -0
  134. package/dist/shared/reporting/auditDeliverable.js.map +1 -0
  135. package/dist/shared/types/sessionConfig.d.ts +13 -0
  136. package/dist/shared/types/sessionConfig.d.ts.map +1 -1
  137. package/package.json +1 -1
  138. package/schemas/audit_result.schema.json +2 -2
  139. package/schemas/audit_results.schema.json +2 -2
  140. package/schemas/finding.schema.json +2 -2
  141. package/scripts/postinstall.mjs +64 -0
@@ -0,0 +1,30 @@
1
+ import type { ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
2
+ /**
3
+ * One match from `ast-grep scan --json`. The matched file is `file`; a capture
4
+ * naming the edge target (e.g. a module specifier, a referenced path) is read
5
+ * from `metaVariables.single.<TARGET_META_VAR>.text`. Both are optional so a
6
+ * partial / malformed payload degrades rather than throwing.
7
+ */
8
+ interface AstGrepMatch {
9
+ file?: string;
10
+ ruleId?: string;
11
+ metaVariables?: {
12
+ single?: Record<string, {
13
+ text?: string;
14
+ } | undefined>;
15
+ };
16
+ }
17
+ /**
18
+ * Normalize `ast-grep scan --json` output into language-neutral graph edges:
19
+ * one `matched-file → captured-target` edge per match whose target meta-variable
20
+ * resolves to a non-empty string distinct from the matched file. ast-grep is a
21
+ * structural matcher, so the target capture is whatever the operator's rule
22
+ * binds (a module specifier, a referenced symbol path); endpoint resolution
23
+ * against the repo path lookup happens downstream in the graph extractor.
24
+ *
25
+ * Degrades to an empty edge list on any malformed / missing field and never
26
+ * throws; `normalizeGenericExternalEdges` dedupes + sorts for determinism.
27
+ */
28
+ export declare function normalizeAstGrepJson(matches: AstGrepMatch[] | undefined, targetMetaVar?: string): ExternalAnalyzerResults;
29
+ export {};
30
+ //# sourceMappingURL=astGrep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"astGrep.d.ts","sourceRoot":"","sources":["../../../src/audit/adapters/astGrep.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,uBAAuB,EACxB,MAAM,8BAA8B,CAAC;AAGtC;;;;;GAKG;AACH,UAAU,YAAY;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE;QACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,SAAS,CAAC,CAAC;KACxD,CAAC;CACH;AAKD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,YAAY,EAAE,GAAG,SAAS,EACnC,aAAa,GAAE,MAAgC,GAC9C,uBAAuB,CA+BzB"}
@@ -0,0 +1,46 @@
1
+ import { normalizeGenericExternalEdges } from "./normalizeExternal.js";
2
+ /** Default capture name holding the edge target path/specifier. */
3
+ const DEFAULT_TARGET_META_VAR = "TARGET";
4
+ /**
5
+ * Normalize `ast-grep scan --json` output into language-neutral graph edges:
6
+ * one `matched-file → captured-target` edge per match whose target meta-variable
7
+ * resolves to a non-empty string distinct from the matched file. ast-grep is a
8
+ * structural matcher, so the target capture is whatever the operator's rule
9
+ * binds (a module specifier, a referenced symbol path); endpoint resolution
10
+ * against the repo path lookup happens downstream in the graph extractor.
11
+ *
12
+ * Degrades to an empty edge list on any malformed / missing field and never
13
+ * throws; `normalizeGenericExternalEdges` dedupes + sorts for determinism.
14
+ */
15
+ export function normalizeAstGrepJson(matches, targetMetaVar = DEFAULT_TARGET_META_VAR) {
16
+ const candidates = [];
17
+ for (const match of Array.isArray(matches) ? matches : []) {
18
+ if (!match || typeof match !== "object")
19
+ continue;
20
+ const from = typeof match.file === "string" && match.file.trim().length > 0
21
+ ? match.file.trim()
22
+ : undefined;
23
+ const target = match.metaVariables?.single?.[targetMetaVar]?.text;
24
+ const to = typeof target === "string" && target.trim().length > 0
25
+ ? target.trim()
26
+ : undefined;
27
+ if (!from || !to)
28
+ continue;
29
+ candidates.push({
30
+ from,
31
+ to,
32
+ kind: "analyzer-dataflow-edge",
33
+ confidence: 0.7,
34
+ reason: typeof match.ruleId === "string"
35
+ ? `ast-grep rule '${match.ruleId}' links match to captured target.`
36
+ : "ast-grep structural match links file to captured target.",
37
+ });
38
+ }
39
+ return {
40
+ tool: "ast-grep",
41
+ generated_at: new Date().toISOString(),
42
+ graph_edges: normalizeGenericExternalEdges(candidates),
43
+ results: [],
44
+ };
45
+ }
46
+ //# sourceMappingURL=astGrep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"astGrep.js","sourceRoot":"","sources":["../../../src/audit/adapters/astGrep.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AAgBvE,mEAAmE;AACnE,MAAM,uBAAuB,GAAG,QAAQ,CAAC;AAEzC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAmC,EACnC,gBAAwB,uBAAuB;IAE/C,MAAM,UAAU,GAA8C,EAAE,CAAC;IACjE,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAC1D,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,SAAS;QAClD,MAAM,IAAI,GACR,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAC5D,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE;YACnB,CAAC,CAAC,SAAS,CAAC;QAChB,MAAM,MAAM,GAAG,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC;QAClE,MAAM,EAAE,GACN,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YACpD,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE;YACf,CAAC,CAAC,SAAS,CAAC;QAChB,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE;YAAE,SAAS;QAC3B,UAAU,CAAC,IAAI,CAAC;YACd,IAAI;YACJ,EAAE;YACF,IAAI,EAAE,wBAAwB;YAC9B,UAAU,EAAE,GAAG;YACf,MAAM,EACJ,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;gBAC9B,CAAC,CAAC,kBAAkB,KAAK,CAAC,MAAM,mCAAmC;gBACnE,CAAC,CAAC,0DAA0D;SACjE,CAAC,CAAC;IACL,CAAC;IACD,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACtC,WAAW,EAAE,6BAA6B,CAAC,UAAU,CAAC;QACtD,OAAO,EAAE,EAAE;KACZ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,47 @@
1
+ import type { ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
2
+ /**
3
+ * Minimal SARIF 2.1.0 shape for CodeQL dataflow queries. Only the fields needed
4
+ * to recover source→sink file edges from `codeFlows` are typed; everything is
5
+ * optional so a partial / malformed report degrades rather than throwing.
6
+ */
7
+ interface SarifArtifactLocation {
8
+ uri?: string;
9
+ }
10
+ interface SarifLocation {
11
+ physicalLocation?: {
12
+ artifactLocation?: SarifArtifactLocation;
13
+ };
14
+ }
15
+ interface SarifThreadFlowLocation {
16
+ location?: SarifLocation;
17
+ }
18
+ interface SarifThreadFlow {
19
+ locations?: SarifThreadFlowLocation[];
20
+ }
21
+ interface SarifCodeFlow {
22
+ threadFlows?: SarifThreadFlow[];
23
+ }
24
+ interface SarifResult {
25
+ ruleId?: string;
26
+ codeFlows?: SarifCodeFlow[];
27
+ }
28
+ interface SarifRun {
29
+ results?: SarifResult[];
30
+ }
31
+ export interface CodeqlSarif {
32
+ runs?: SarifRun[];
33
+ }
34
+ /**
35
+ * Normalize a CodeQL SARIF dataflow report into language-neutral graph edges:
36
+ * for each result's code flow, emit one `source-file → sink-file` edge from the
37
+ * first to the last thread-flow location whose file paths differ. CodeQL's
38
+ * dataflow queries already model taint propagation, so the first/last locations
39
+ * are the source and sink; downstream the graph extractor resolves the URIs
40
+ * against the repo path lookup.
41
+ *
42
+ * Degrades to an empty edge list on any malformed / missing SARIF field and
43
+ * never throws; `normalizeGenericExternalEdges` dedupes + sorts for determinism.
44
+ */
45
+ export declare function normalizeCodeqlSarif(input: CodeqlSarif): ExternalAnalyzerResults;
46
+ export {};
47
+ //# sourceMappingURL=codeql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codeql.d.ts","sourceRoot":"","sources":["../../../src/audit/adapters/codeql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,uBAAuB,EACxB,MAAM,8BAA8B,CAAC;AAGtC;;;;GAIG;AACH,UAAU,qBAAqB;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AACD,UAAU,aAAa;IACrB,gBAAgB,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,qBAAqB,CAAA;KAAE,CAAC;CACjE;AACD,UAAU,uBAAuB;IAC/B,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B;AACD,UAAU,eAAe;IACvB,SAAS,CAAC,EAAE,uBAAuB,EAAE,CAAC;CACvC;AACD,UAAU,aAAa;IACrB,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;CACjC;AACD,UAAU,WAAW;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;CAC7B;AACD,UAAU,QAAQ;IAChB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;CACzB;AACD,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC;CACnB;AAOD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,uBAAuB,CAiChF"}
@@ -0,0 +1,52 @@
1
+ import { normalizeGenericExternalEdges } from "./normalizeExternal.js";
2
+ function locationUri(location) {
3
+ const uri = location?.location?.physicalLocation?.artifactLocation?.uri;
4
+ return typeof uri === "string" && uri.trim().length > 0 ? uri.trim() : undefined;
5
+ }
6
+ /**
7
+ * Normalize a CodeQL SARIF dataflow report into language-neutral graph edges:
8
+ * for each result's code flow, emit one `source-file → sink-file` edge from the
9
+ * first to the last thread-flow location whose file paths differ. CodeQL's
10
+ * dataflow queries already model taint propagation, so the first/last locations
11
+ * are the source and sink; downstream the graph extractor resolves the URIs
12
+ * against the repo path lookup.
13
+ *
14
+ * Degrades to an empty edge list on any malformed / missing SARIF field and
15
+ * never throws; `normalizeGenericExternalEdges` dedupes + sorts for determinism.
16
+ */
17
+ export function normalizeCodeqlSarif(input) {
18
+ const candidates = [];
19
+ for (const run of input.runs ?? []) {
20
+ for (const result of run?.results ?? []) {
21
+ for (const codeFlow of result?.codeFlows ?? []) {
22
+ for (const threadFlow of codeFlow?.threadFlows ?? []) {
23
+ if (!threadFlow || typeof threadFlow !== "object")
24
+ continue;
25
+ const locations = Array.isArray(threadFlow.locations)
26
+ ? threadFlow.locations
27
+ : [];
28
+ const from = locationUri(locations[0]);
29
+ const to = locationUri(locations[locations.length - 1]);
30
+ if (!from || !to)
31
+ continue;
32
+ candidates.push({
33
+ from,
34
+ to,
35
+ kind: "analyzer-dataflow-edge",
36
+ confidence: 0.7,
37
+ reason: typeof result.ruleId === "string"
38
+ ? `CodeQL query '${result.ruleId}' flows source → sink.`
39
+ : "CodeQL dataflow flows source → sink.",
40
+ });
41
+ }
42
+ }
43
+ }
44
+ }
45
+ return {
46
+ tool: "codeql",
47
+ generated_at: new Date().toISOString(),
48
+ graph_edges: normalizeGenericExternalEdges(candidates),
49
+ results: [],
50
+ };
51
+ }
52
+ //# sourceMappingURL=codeql.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codeql.js","sourceRoot":"","sources":["../../../src/audit/adapters/codeql.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AAiCvE,SAAS,WAAW,CAAC,QAA6C;IAChE,MAAM,GAAG,GAAG,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,GAAG,CAAC;IACxE,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACnF,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAkB;IACrD,MAAM,UAAU,GAA8C,EAAE,CAAC;IACjE,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;QACnC,KAAK,MAAM,MAAM,IAAI,GAAG,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC;YACxC,KAAK,MAAM,QAAQ,IAAI,MAAM,EAAE,SAAS,IAAI,EAAE,EAAE,CAAC;gBAC/C,KAAK,MAAM,UAAU,IAAI,QAAQ,EAAE,WAAW,IAAI,EAAE,EAAE,CAAC;oBACrD,IAAI,CAAC,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ;wBAAE,SAAS;oBAC5D,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC;wBACnD,CAAC,CAAC,UAAU,CAAC,SAAS;wBACtB,CAAC,CAAC,EAAE,CAAC;oBACP,MAAM,IAAI,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;oBACvC,MAAM,EAAE,GAAG,WAAW,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;oBACxD,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE;wBAAE,SAAS;oBAC3B,UAAU,CAAC,IAAI,CAAC;wBACd,IAAI;wBACJ,EAAE;wBACF,IAAI,EAAE,wBAAwB;wBAC9B,UAAU,EAAE,GAAG;wBACf,MAAM,EACJ,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ;4BAC/B,CAAC,CAAC,iBAAiB,MAAM,CAAC,MAAM,wBAAwB;4BACxD,CAAC,CAAC,sCAAsC;qBAC7C,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACtC,WAAW,EAAE,6BAA6B,CAAC,UAAU,CAAC;QACtD,OAAO,EAAE,EAAE;KACZ,CAAC;AACJ,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
1
+ import type { ExternalAnalyzerGraphEdge, ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
2
2
  export declare function normalizeGenericExternalResults(tool: string, items: Array<{
3
3
  id?: string;
4
4
  category?: string;
@@ -10,4 +10,27 @@ export declare function normalizeGenericExternalResults(tool: string, items: Arr
10
10
  rule?: string;
11
11
  raw?: unknown;
12
12
  }>): ExternalAnalyzerResults;
13
+ /**
14
+ * Normalize a raw list of edge candidates (from an external dataflow analyzer:
15
+ * ast-grep / broader-semgrep dataflow / CodeQL) into the language-neutral
16
+ * {@link ExternalAnalyzerGraphEdge} contract.
17
+ *
18
+ * Degrade-to-empty + deterministic by construction:
19
+ * - any candidate missing a non-empty string `from`/`to`, or a self-edge
20
+ * (`from === to`), is dropped — never throws on a malformed payload;
21
+ * - duplicate (from,to,kind) triples collapse to one;
22
+ * - output is sorted by from-then-to-then-kind so identical input yields
23
+ * byte-identical output run to run.
24
+ *
25
+ * The returned shape is the wire contract carried on
26
+ * `ExternalAnalyzerResults.graph_edges`; the graph extractor resolves the
27
+ * endpoints against the repo path lookup and merges them into the edge set.
28
+ */
29
+ export declare function normalizeGenericExternalEdges(candidates: Array<{
30
+ from?: unknown;
31
+ to?: unknown;
32
+ kind?: unknown;
33
+ confidence?: unknown;
34
+ reason?: unknown;
35
+ }>): ExternalAnalyzerGraphEdge[];
13
36
  //# sourceMappingURL=normalizeExternal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"normalizeExternal.d.ts","sourceRoot":"","sources":["../../../src/audit/adapters/normalizeExternal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAoB5E,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,KAAK,CAAC;IACX,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC,GACD,uBAAuB,CA6BzB"}
1
+ {"version":3,"file":"normalizeExternal.d.ts","sourceRoot":"","sources":["../../../src/audit/adapters/normalizeExternal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EACzB,uBAAuB,EACxB,MAAM,8BAA8B,CAAC;AAoBtC,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,KAAK,CAAC;IACX,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC,GACD,uBAAuB,CA6BzB;AAQD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,KAAK,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC,GACD,yBAAyB,EAAE,CA4B7B"}
@@ -41,4 +41,54 @@ export function normalizeGenericExternalResults(tool, items) {
41
41
  })),
42
42
  };
43
43
  }
44
+ function clampUnitInterval(value) {
45
+ return typeof value === "number" && Number.isFinite(value)
46
+ ? Math.min(1, Math.max(0, value))
47
+ : undefined;
48
+ }
49
+ /**
50
+ * Normalize a raw list of edge candidates (from an external dataflow analyzer:
51
+ * ast-grep / broader-semgrep dataflow / CodeQL) into the language-neutral
52
+ * {@link ExternalAnalyzerGraphEdge} contract.
53
+ *
54
+ * Degrade-to-empty + deterministic by construction:
55
+ * - any candidate missing a non-empty string `from`/`to`, or a self-edge
56
+ * (`from === to`), is dropped — never throws on a malformed payload;
57
+ * - duplicate (from,to,kind) triples collapse to one;
58
+ * - output is sorted by from-then-to-then-kind so identical input yields
59
+ * byte-identical output run to run.
60
+ *
61
+ * The returned shape is the wire contract carried on
62
+ * `ExternalAnalyzerResults.graph_edges`; the graph extractor resolves the
63
+ * endpoints against the repo path lookup and merges them into the edge set.
64
+ */
65
+ export function normalizeGenericExternalEdges(candidates) {
66
+ const deduped = new Map();
67
+ for (const candidate of Array.isArray(candidates) ? candidates : []) {
68
+ if (!candidate || typeof candidate !== "object")
69
+ continue;
70
+ const from = typeof candidate.from === "string" ? candidate.from.trim() : "";
71
+ const to = typeof candidate.to === "string" ? candidate.to.trim() : "";
72
+ if (from.length === 0 || to.length === 0 || from === to)
73
+ continue;
74
+ const kind = typeof candidate.kind === "string" && candidate.kind.trim().length > 0
75
+ ? candidate.kind.trim()
76
+ : undefined;
77
+ const confidence = clampUnitInterval(candidate.confidence);
78
+ const reason = typeof candidate.reason === "string" && candidate.reason.trim().length > 0
79
+ ? candidate.reason.trim()
80
+ : undefined;
81
+ const edge = { from, to };
82
+ if (kind !== undefined)
83
+ edge.kind = kind;
84
+ if (confidence !== undefined)
85
+ edge.confidence = confidence;
86
+ if (reason !== undefined)
87
+ edge.reason = reason;
88
+ deduped.set(`${from}\0${to}\0${kind ?? ""}`, edge);
89
+ }
90
+ return [...deduped.values()].sort((a, b) => a.from.localeCompare(b.from) ||
91
+ a.to.localeCompare(b.to) ||
92
+ (a.kind ?? "").localeCompare(b.kind ?? ""));
93
+ }
44
94
  //# sourceMappingURL=normalizeExternal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"normalizeExternal.js","sourceRoot":"","sources":["../../../src/audit/adapters/normalizeExternal.ts"],"names":[],"mappings":"AAIA,SAAS,yBAAyB,CAAC,KAAyB;IAC1D,QAAQ,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC;QAC7B,KAAK,UAAU,CAAC,CAAC,OAAO,UAAU,CAAC;QACnC,KAAK,OAAO,CAAC;QACb,KAAK,MAAM,CAAC,CAAC,OAAO,MAAM,CAAC;QAC3B,KAAK,SAAS,CAAC;QACf,KAAK,UAAU,CAAC;QAChB,KAAK,QAAQ,CAAC,CAAC,OAAO,QAAQ,CAAC;QAC/B,KAAK,KAAK,CAAC,CAAC,OAAO,KAAK,CAAC;QACzB,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC,CAAC,OAAO,MAAM,CAAC;QAC3B,OAAO,CAAC,CAAC,OAAO,MAAM,CAAC;IACzB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,+BAA+B,CAC7C,IAAY,EACZ,KAUE;IAEF,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;IAChE,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC5C,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,IAAI,CAAC,SAAS,CAAC;YACb,KAAK,EAAE,6BAA6B;YACpC,IAAI;YACJ,OAAO;YACP,KAAK,EAAE,KAAK,CAAC,MAAM;YACnB,MAAM,EAAE,yBAAyB;SAClC,CAAC,GAAG,IAAI,CACV,CAAC;IACJ,CAAC;IACD,OAAO;QACL,IAAI;QACJ,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACtC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YACnC,EAAE,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC,EAAE;YACrC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,SAAS;YACpC,QAAQ,EAAE,yBAAyB,CAAC,IAAI,CAAC,QAAQ,CAAC;YAClD,IAAI,EAAE,IAAI,CAAC,IAAc;YACzB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,OAAO,EAAE,IAAI,CAAC,OAAiB;YAC/B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,GAAG,EAAE,IAAI,CAAC,GAAG;SACd,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"normalizeExternal.js","sourceRoot":"","sources":["../../../src/audit/adapters/normalizeExternal.ts"],"names":[],"mappings":"AAOA,SAAS,yBAAyB,CAAC,KAAyB;IAC1D,QAAQ,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC;QAC7B,KAAK,UAAU,CAAC,CAAC,OAAO,UAAU,CAAC;QACnC,KAAK,OAAO,CAAC;QACb,KAAK,MAAM,CAAC,CAAC,OAAO,MAAM,CAAC;QAC3B,KAAK,SAAS,CAAC;QACf,KAAK,UAAU,CAAC;QAChB,KAAK,QAAQ,CAAC,CAAC,OAAO,QAAQ,CAAC;QAC/B,KAAK,KAAK,CAAC,CAAC,OAAO,KAAK,CAAC;QACzB,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC,CAAC,OAAO,MAAM,CAAC;QAC3B,OAAO,CAAC,CAAC,OAAO,MAAM,CAAC;IACzB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,+BAA+B,CAC7C,IAAY,EACZ,KAUE;IAEF,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;IAChE,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC5C,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,IAAI,CAAC,SAAS,CAAC;YACb,KAAK,EAAE,6BAA6B;YACpC,IAAI;YACJ,OAAO;YACP,KAAK,EAAE,KAAK,CAAC,MAAM;YACnB,MAAM,EAAE,yBAAyB;SAClC,CAAC,GAAG,IAAI,CACV,CAAC;IACJ,CAAC;IACD,OAAO;QACL,IAAI;QACJ,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACtC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YACnC,EAAE,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC,EAAE;YACrC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,SAAS;YACpC,QAAQ,EAAE,yBAAyB,CAAC,IAAI,CAAC,QAAQ,CAAC;YAClD,IAAI,EAAE,IAAI,CAAC,IAAc;YACzB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,OAAO,EAAE,IAAI,CAAC,OAAiB;YAC/B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,GAAG,EAAE,IAAI,CAAC,GAAG;SACd,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAc;IACvC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QACxD,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACjC,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,6BAA6B,CAC3C,UAME;IAEF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAqC,CAAC;IAC7D,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACpE,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ;YAAE,SAAS;QAC1D,MAAM,IAAI,GAAG,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7E,MAAM,EAAE,GAAG,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,KAAK,EAAE;YAAE,SAAS;QAClE,MAAM,IAAI,GACR,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YACpE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE;YACvB,CAAC,CAAC,SAAS,CAAC;QAChB,MAAM,UAAU,GAAG,iBAAiB,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAC3D,MAAM,MAAM,GACV,OAAO,SAAS,CAAC,MAAM,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YACxE,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE;YACzB,CAAC,CAAC,SAAS,CAAC;QAChB,MAAM,IAAI,GAA8B,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACrD,IAAI,IAAI,KAAK,SAAS;YAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACzC,IAAI,UAAU,KAAK,SAAS;YAAE,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC3D,IAAI,MAAM,KAAK,SAAS;YAAE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,KAAK,EAAE,KAAK,IAAI,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAC/B,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5B,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAC7C,CAAC;AACJ,CAAC"}
@@ -1,23 +1,42 @@
1
1
  import type { ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
2
- interface SemgrepJson {
3
- results?: Array<{
4
- check_id?: string;
2
+ interface SemgrepDataflowTraceNode {
3
+ location?: {
5
4
  path?: string;
6
- start?: {
7
- line?: number;
8
- };
9
- end?: {
10
- line?: number;
5
+ };
6
+ }
7
+ interface SemgrepResult {
8
+ check_id?: string;
9
+ path?: string;
10
+ start?: {
11
+ line?: number;
12
+ };
13
+ end?: {
14
+ line?: number;
15
+ };
16
+ extra?: {
17
+ severity?: string;
18
+ message?: string;
19
+ metadata?: {
20
+ category?: string;
11
21
  };
12
- extra?: {
13
- severity?: string;
14
- message?: string;
15
- metadata?: {
16
- category?: string;
17
- };
22
+ dataflow_trace?: {
23
+ taint_source?: SemgrepDataflowTraceNode | SemgrepDataflowTraceNode[];
24
+ intermediate_vars?: SemgrepDataflowTraceNode[];
25
+ taint_sink?: SemgrepDataflowTraceNode | SemgrepDataflowTraceNode[];
18
26
  };
19
- }>;
27
+ };
28
+ }
29
+ interface SemgrepJson {
30
+ results?: SemgrepResult[];
20
31
  }
21
32
  export declare function normalizeSemgrepJson(input: SemgrepJson): ExternalAnalyzerResults;
33
+ /**
34
+ * Normalize semgrep's broader `--dataflow-traces` output into language-neutral
35
+ * graph edges: one `taint-source → taint-sink` edge per result whose source and
36
+ * sink resolve to distinct file paths. Intermediate vars are not edges (they are
37
+ * within-file steps). Malformed / missing traces degrade to an empty edge list;
38
+ * `normalizeGenericExternalEdges` dedupes + sorts so output is deterministic.
39
+ */
40
+ export declare function normalizeSemgrepDataflowJson(input: SemgrepJson): ExternalAnalyzerResults;
22
41
  export {};
23
42
  //# sourceMappingURL=semgrep.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"semgrep.d.ts","sourceRoot":"","sources":["../../../src/audit/adapters/semgrep.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAG5E,UAAU,WAAW;IACnB,OAAO,CAAC,EAAE,KAAK,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1B,GAAG,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACxB,KAAK,CAAC,EAAE;YACN,QAAQ,CAAC,EAAE,MAAM,CAAC;YAClB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,QAAQ,CAAC,EAAE;gBAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;SAClC,CAAC;KACH,CAAC,CAAC;CACJ;AA6BD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,GACjB,uBAAuB,CAezB"}
1
+ {"version":3,"file":"semgrep.d.ts","sourceRoot":"","sources":["../../../src/audit/adapters/semgrep.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,uBAAuB,EACxB,MAAM,8BAA8B,CAAC;AAMtC,UAAU,wBAAwB;IAChC,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9B;AAED,UAAU,aAAa;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1B,GAAG,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxB,KAAK,CAAC,EAAE;QACN,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE;YAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACjC,cAAc,CAAC,EAAE;YACf,YAAY,CAAC,EAAE,wBAAwB,GAAG,wBAAwB,EAAE,CAAC;YACrE,iBAAiB,CAAC,EAAE,wBAAwB,EAAE,CAAC;YAC/C,UAAU,CAAC,EAAE,wBAAwB,GAAG,wBAAwB,EAAE,CAAC;SACpE,CAAC;KACH,CAAC;CACH;AAED,UAAU,WAAW;IACnB,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;CAC3B;AA6BD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,GACjB,uBAAuB,CAezB;AAwBD;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,WAAW,GACjB,uBAAuB,CA0BzB"}
@@ -1,4 +1,4 @@
1
- import { normalizeGenericExternalResults } from "./normalizeExternal.js";
1
+ import { normalizeGenericExternalEdges, normalizeGenericExternalResults, } from "./normalizeExternal.js";
2
2
  /**
3
3
  * Maps semgrep's native uppercase severity strings to the lowercase enum values
4
4
  * required by ExternalAnalyzerResultsSchema. Case-insensitive lookup so
@@ -37,4 +37,57 @@ export function normalizeSemgrepJson(input) {
37
37
  raw: result,
38
38
  })));
39
39
  }
40
+ function dataflowNodePath(node) {
41
+ const path = node?.location?.path;
42
+ return typeof path === "string" && path.trim().length > 0
43
+ ? path.trim()
44
+ : undefined;
45
+ }
46
+ function firstDataflowPath(value) {
47
+ if (Array.isArray(value)) {
48
+ for (const node of value) {
49
+ const path = dataflowNodePath(node);
50
+ if (path)
51
+ return path;
52
+ }
53
+ return undefined;
54
+ }
55
+ return dataflowNodePath(value);
56
+ }
57
+ /**
58
+ * Normalize semgrep's broader `--dataflow-traces` output into language-neutral
59
+ * graph edges: one `taint-source → taint-sink` edge per result whose source and
60
+ * sink resolve to distinct file paths. Intermediate vars are not edges (they are
61
+ * within-file steps). Malformed / missing traces degrade to an empty edge list;
62
+ * `normalizeGenericExternalEdges` dedupes + sorts so output is deterministic.
63
+ */
64
+ export function normalizeSemgrepDataflowJson(input) {
65
+ const candidates = [];
66
+ for (const result of input.results ?? []) {
67
+ if (!result || typeof result !== "object")
68
+ continue;
69
+ const trace = result.extra?.dataflow_trace;
70
+ if (!trace)
71
+ continue;
72
+ const from = firstDataflowPath(trace.taint_source);
73
+ const to = firstDataflowPath(trace.taint_sink);
74
+ if (!from || !to)
75
+ continue;
76
+ candidates.push({
77
+ from,
78
+ to,
79
+ kind: "analyzer-dataflow-edge",
80
+ confidence: 0.7,
81
+ reason: typeof result.check_id === "string"
82
+ ? `semgrep dataflow trace '${result.check_id}' flows source → sink.`
83
+ : "semgrep dataflow trace flows source → sink.",
84
+ });
85
+ }
86
+ return {
87
+ tool: "semgrep-dataflow",
88
+ generated_at: new Date().toISOString(),
89
+ graph_edges: normalizeGenericExternalEdges(candidates),
90
+ results: [],
91
+ };
92
+ }
40
93
  //# sourceMappingURL=semgrep.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"semgrep.js","sourceRoot":"","sources":["../../../src/audit/adapters/semgrep.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAC;AAgBzE;;;;;;;;;;;;;GAaG;AACH,SAAS,wBAAwB,CAC/B,GAAuB;IAEvB,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACxC,QAAQ,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC;QAC1B,KAAK,UAAU,CAAC,CAAC,OAAO,UAAU,CAAC;QACnC,KAAK,OAAO,CAAC,CAAI,OAAO,MAAM,CAAC;QAC/B,KAAK,SAAS,CAAC,CAAE,OAAO,QAAQ,CAAC;QACjC,KAAK,MAAM,CAAC,CAAK,OAAO,MAAM,CAAC;QAC/B,OAAO,CAAC,CAAS,OAAO,SAAS,CAAC;IACpC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,KAAkB;IAElB,OAAO,+BAA+B,CACpC,SAAS,EACT,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACrC,EAAE,EAAE,MAAM,CAAC,QAAQ;QACnB,QAAQ,EAAE,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,IAAI,UAAU;QACxD,QAAQ,EAAE,wBAAwB,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC;QAC1D,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,UAAU,EAAE,MAAM,CAAC,KAAK,EAAE,IAAI;QAC9B,QAAQ,EAAE,MAAM,CAAC,GAAG,EAAE,IAAI;QAC1B,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO;QAC9B,IAAI,EAAE,MAAM,CAAC,QAAQ;QACrB,GAAG,EAAE,MAAM;KACZ,CAAC,CAAC,CACJ,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"semgrep.js","sourceRoot":"","sources":["../../../src/audit/adapters/semgrep.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,32 @@
1
- import type { AuditResult } from "../types.js";
1
+ import type { AuditResult, AuditTask } from "../types.js";
2
+ /**
3
+ * Validate each pending task's result, classifying into passing/failing/notDispatched.
4
+ * Reads results from the result-map paths or falls back to the task_id lookup table
5
+ * for tasks recovered from non-canonical files.
6
+ */
7
+ /**
8
+ * Build, per dispatched packet_id, the list of member task_ids assigned to it
9
+ * (excluding the synthetic `__prior_dispatch__` sentinel, which is not a real
10
+ * packet). Used to rebind a worker result mistakenly keyed under the packet_id
11
+ * onto its sole OUTSTANDING member.
12
+ */
13
+ export declare function packetMembersByPacketId(entries: Array<{
14
+ task_id: string;
15
+ packet_id: string;
16
+ }>): Map<string, string[]>;
17
+ export declare function validateAndCollectResults(allTasks: AuditTask[], entryByTaskId: Map<string, {
18
+ result_path: string;
19
+ task_id: string;
20
+ packet_id: string;
21
+ }>, fallbackByTaskId: Map<string, unknown>, packetMembers: Map<string, string[]>): Promise<{
22
+ passing: AuditResult[];
23
+ failing: Array<{
24
+ task_id: string;
25
+ errors: string[];
26
+ }>;
27
+ notDispatched: string[];
28
+ recoveredCount: number;
29
+ }>;
2
30
  /**
3
31
  * Grounding pass (S7): re-verify each finding against disk before it is admitted
4
32
  * as fact. Tier-1 (quote-and-verify) re-reads the cited verbatim span and
@@ -1 +1 @@
1
- {"version":3,"file":"mergeAndIngestCommand.d.ts","sourceRoot":"","sources":["../../../src/audit/cli/mergeAndIngestCommand.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAa,MAAM,aAAa,CAAC;AA6T1D;;;;;;;;;;;;;GAaG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,WAAW,EAAE,GACrB,OAAO,CAAC,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CA2CvE;AAED,qFAAqF;AACrF,MAAM,WAAW,oBAAoB;IACnC,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,4EAA4E;IAC5E,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAsOhC;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAUrE"}
1
+ {"version":3,"file":"mergeAndIngestCommand.d.ts","sourceRoot":"","sources":["../../../src/audit/cli/mergeAndIngestCommand.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAqN1D;;;;GAIG;AACH;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,GACrD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CASvB;AAED,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,SAAS,EAAE,EACrB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,EACvF,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACnC,OAAO,CAAC;IACT,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACtD,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC,CA0ID;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,WAAW,EAAE,GACrB,OAAO,CAAC,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CA2CvE;AAED,qFAAqF;AACrF,MAAM,WAAW,oBAAoB;IACnC,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,4EAA4E;IAC5E,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAuOhC;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAUrE"}
@@ -1,7 +1,7 @@
1
1
  import { readFile, readdir, rm } from "node:fs/promises";
2
2
  import { join, resolve } from "node:path";
3
3
  import { isFileMissingError, mapWithConcurrency, readJsonFile, writeJsonFile } from "audit-tools/shared";
4
- import { validateAuditResults } from "../validation/auditResults.js";
4
+ import { validateAuditResults, defaultFindingLensFromResult } from "../validation/auditResults.js";
5
5
  import { verifyFindingGrounding } from "../validation/quoteGrounding.js";
6
6
  import { ANCHOR_GROUNDING_CONCURRENCY, anchorEvidenceLine, combineGroundingWithAnchor, verifyFindingAnchor, } from "../validation/anchorGrounding.js";
7
7
  import { runAuditStep } from "./auditStep.js";
@@ -192,7 +192,24 @@ async function scanTaskResults(taskResultsDir, expectedPaths) {
192
192
  * Reads results from the result-map paths or falls back to the task_id lookup table
193
193
  * for tasks recovered from non-canonical files.
194
194
  */
195
- async function validateAndCollectResults(allTasks, entryByTaskId, fallbackByTaskId) {
195
+ /**
196
+ * Build, per dispatched packet_id, the list of member task_ids assigned to it
197
+ * (excluding the synthetic `__prior_dispatch__` sentinel, which is not a real
198
+ * packet). Used to rebind a worker result mistakenly keyed under the packet_id
199
+ * onto its sole OUTSTANDING member.
200
+ */
201
+ export function packetMembersByPacketId(entries) {
202
+ const byPacket = new Map();
203
+ for (const entry of entries) {
204
+ if (entry.packet_id === "__prior_dispatch__")
205
+ continue;
206
+ const members = byPacket.get(entry.packet_id) ?? [];
207
+ members.push(entry.task_id);
208
+ byPacket.set(entry.packet_id, members);
209
+ }
210
+ return byPacket;
211
+ }
212
+ export async function validateAndCollectResults(allTasks, entryByTaskId, fallbackByTaskId, packetMembers) {
196
213
  const passing = [];
197
214
  const failing = [];
198
215
  // Pending tasks that were NOT dispatched this round. Not failures — they
@@ -202,6 +219,51 @@ async function validateAndCollectResults(allTasks, entryByTaskId, fallbackByTask
202
219
  // Results recovered by task_id from packet result files. The host writes one
203
220
  // array file per packet, so this is the normal collection path, not an error.
204
221
  let recoveredCount = 0;
222
+ // OUTSTANDING (still-pending) members keyed by their dispatched packet_id.
223
+ // A multi-member packet that was partially answered then re-queued keeps only
224
+ // its still-pending members here, so a result keyed under the packet_id is
225
+ // rebound to the SOLE outstanding member (never to an already-answered one,
226
+ // and never when >1 member is still outstanding — that would be ambiguous).
227
+ const outstandingIds = new Set(allTasks.map((t) => t.task_id));
228
+ const outstandingMembersByPacketId = new Map();
229
+ for (const [packetId, members] of packetMembers) {
230
+ const stillOutstanding = members.filter((id) => outstandingIds.has(id));
231
+ if (stillOutstanding.length > 0) {
232
+ outstandingMembersByPacketId.set(packetId, stillOutstanding);
233
+ }
234
+ }
235
+ /**
236
+ * A worker that keyed its result under the synthetic packet_id (not the
237
+ * assigned member task_id — the deepening non-convergence leak) leaves the
238
+ * member's own result missing. Recover it by binding the packet-id-keyed
239
+ * result to the packet's SOLE outstanding member, then rewrite its task_id to
240
+ * the member id so the identity gates pass. Returns the rebound object or null.
241
+ */
242
+ function rebindPacketIdKeyedResult(member) {
243
+ const entry = entryByTaskId.get(member.task_id);
244
+ const packetId = entry?.packet_id;
245
+ if (!packetId || packetId === "__prior_dispatch__")
246
+ return null;
247
+ const stillOutstanding = outstandingMembersByPacketId.get(packetId) ?? [];
248
+ if (stillOutstanding.length !== 1 || stillOutstanding[0] !== member.task_id) {
249
+ return null; // ambiguous (≠1 outstanding member) — never guess.
250
+ }
251
+ const keyed = fallbackByTaskId.get(packetId);
252
+ if (!keyed || typeof keyed !== "object" || Array.isArray(keyed))
253
+ return null;
254
+ // The worker stamped EVERY identity field from the packet (task_id, unit_id,
255
+ // pass_id, lens), not just task_id — so rebinding only task_id leaves the
256
+ // pass_id/unit_id/lens mismatching the member and the result still rejects.
257
+ // Identity is the tool's authority (not the worker's), so force all four from
258
+ // the assigned member's metadata. The findings/coverage payload is untouched.
259
+ return {
260
+ ...keyed,
261
+ task_id: member.task_id,
262
+ unit_id: member.unit_id,
263
+ pass_id: member.pass_id,
264
+ lens: member.lens,
265
+ };
266
+ }
205
267
  for (const task of allTasks) {
206
268
  const entry = entryByTaskId.get(task.task_id);
207
269
  let obj;
@@ -212,7 +274,7 @@ async function validateAndCollectResults(allTasks, entryByTaskId, fallbackByTask
212
274
  }
213
275
  catch (e) {
214
276
  if (isFileMissingError(e)) {
215
- const fallback = fallbackByTaskId.get(task.task_id);
277
+ const fallback = fallbackByTaskId.get(task.task_id) ?? rebindPacketIdKeyedResult(task);
216
278
  if (fallback) {
217
279
  recoveredCount++;
218
280
  obj = fallback;
@@ -239,7 +301,7 @@ async function validateAndCollectResults(allTasks, entryByTaskId, fallbackByTask
239
301
  // against). Recover it by task_id so it ingests instead of looping forever
240
302
  // as "pending"; only when no such file exists is the task genuinely held
241
303
  // back for the next dispatch (not a failure).
242
- const fallback = fallbackByTaskId.get(task.task_id);
304
+ const fallback = fallbackByTaskId.get(task.task_id) ?? rebindPacketIdKeyedResult(task);
243
305
  if (!fallback) {
244
306
  notDispatched.push(task.task_id);
245
307
  continue;
@@ -247,6 +309,10 @@ async function validateAndCollectResults(allTasks, entryByTaskId, fallbackByTask
247
309
  recoveredCount++;
248
310
  obj = fallback;
249
311
  }
312
+ // Force-default findings[].lens from the AuditResult lens before validation
313
+ // (weaker/deepening workers set only the top-level lens; the validator
314
+ // requires a non-empty per-finding lens). Mutates the recovered object.
315
+ defaultFindingLensFromResult([obj]);
250
316
  const record = obj && typeof obj === "object" && !Array.isArray(obj)
251
317
  ? obj
252
318
  : undefined;
@@ -377,7 +443,7 @@ export async function mergeAndIngest(params) {
377
443
  `task-results/ ignored: ${spuriousFiles.join(", ")}\n`);
378
444
  }
379
445
  // Phase 3: validate each task's result and classify into passing/failing/notDispatched.
380
- const { passing, failing, notDispatched, recoveredCount } = await validateAndCollectResults(allTasks, entryByTaskId, fallbackByTaskId);
446
+ const { passing, failing, notDispatched, recoveredCount } = await validateAndCollectResults(allTasks, entryByTaskId, fallbackByTaskId, packetMembersByPacketId(resultMap.entries));
381
447
  if (recoveredCount > 0) {
382
448
  process.stderr.write(`[merge-and-ingest] Recovered ${recoveredCount} result(s) by task_id from packet result files.\n`);
383
449
  }