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.
- package/dist/audit/adapters/astGrep.d.ts +30 -0
- package/dist/audit/adapters/astGrep.d.ts.map +1 -0
- package/dist/audit/adapters/astGrep.js +46 -0
- package/dist/audit/adapters/astGrep.js.map +1 -0
- package/dist/audit/adapters/codeql.d.ts +47 -0
- package/dist/audit/adapters/codeql.d.ts.map +1 -0
- package/dist/audit/adapters/codeql.js +52 -0
- package/dist/audit/adapters/codeql.js.map +1 -0
- package/dist/audit/adapters/normalizeExternal.d.ts +24 -1
- package/dist/audit/adapters/normalizeExternal.d.ts.map +1 -1
- package/dist/audit/adapters/normalizeExternal.js +50 -0
- package/dist/audit/adapters/normalizeExternal.js.map +1 -1
- package/dist/audit/adapters/semgrep.d.ts +34 -15
- package/dist/audit/adapters/semgrep.d.ts.map +1 -1
- package/dist/audit/adapters/semgrep.js +54 -1
- package/dist/audit/adapters/semgrep.js.map +1 -1
- package/dist/audit/cli/mergeAndIngestCommand.d.ts +29 -1
- package/dist/audit/cli/mergeAndIngestCommand.d.ts.map +1 -1
- package/dist/audit/cli/mergeAndIngestCommand.js +71 -5
- package/dist/audit/cli/mergeAndIngestCommand.js.map +1 -1
- package/dist/audit/cli/nextStepHelpers.d.ts.map +1 -1
- package/dist/audit/cli/nextStepHelpers.js +7 -6
- package/dist/audit/cli/nextStepHelpers.js.map +1 -1
- package/dist/audit/cli/resynthesizeCommand.d.ts.map +1 -1
- package/dist/audit/cli/resynthesizeCommand.js +3 -3
- package/dist/audit/cli/resynthesizeCommand.js.map +1 -1
- package/dist/audit/cli/submitPacketCommand.d.ts.map +1 -1
- package/dist/audit/cli/submitPacketCommand.js +6 -1
- package/dist/audit/cli/submitPacketCommand.js.map +1 -1
- package/dist/audit/contracts/workerSchemas.d.ts +13 -13
- package/dist/audit/contracts/workerSchemas.d.ts.map +1 -1
- package/dist/audit/contracts/workerSchemas.js +1 -1
- package/dist/audit/contracts/workerSchemas.js.map +1 -1
- package/dist/audit/contracts/wrapperResponse.d.ts +7 -0
- package/dist/audit/contracts/wrapperResponse.d.ts.map +1 -1
- package/dist/audit/contracts/wrapperResponse.js +1 -0
- package/dist/audit/contracts/wrapperResponse.js.map +1 -1
- package/dist/audit/extractors/graph.d.ts.map +1 -1
- package/dist/audit/extractors/graph.js +47 -0
- package/dist/audit/extractors/graph.js.map +1 -1
- package/dist/audit/io/artifacts.d.ts +2 -2
- package/dist/audit/io/artifacts.d.ts.map +1 -1
- package/dist/audit/io/artifacts.js +14 -11
- package/dist/audit/io/artifacts.js.map +1 -1
- package/dist/audit/orchestrator/designReviewPrompt.d.ts +17 -0
- package/dist/audit/orchestrator/designReviewPrompt.d.ts.map +1 -1
- package/dist/audit/orchestrator/designReviewPrompt.js +26 -0
- package/dist/audit/orchestrator/designReviewPrompt.js.map +1 -1
- package/dist/audit/orchestrator/executorRunners.d.ts.map +1 -1
- package/dist/audit/orchestrator/executorRunners.js +20 -0
- package/dist/audit/orchestrator/executorRunners.js.map +1 -1
- package/dist/audit/orchestrator/executors.d.ts.map +1 -1
- package/dist/audit/orchestrator/executors.js +5 -0
- package/dist/audit/orchestrator/executors.js.map +1 -1
- package/dist/audit/orchestrator/nextStep.d.ts +37 -0
- package/dist/audit/orchestrator/nextStep.d.ts.map +1 -1
- package/dist/audit/orchestrator/nextStep.js +45 -0
- package/dist/audit/orchestrator/nextStep.js.map +1 -1
- package/dist/audit/supervisor/operatorHandoff.d.ts +12 -0
- package/dist/audit/supervisor/operatorHandoff.d.ts.map +1 -1
- package/dist/audit/supervisor/operatorHandoff.js +3 -1
- package/dist/audit/supervisor/operatorHandoff.js.map +1 -1
- package/dist/audit/types/externalAnalyzer.d.ts +68 -2
- package/dist/audit/types/externalAnalyzer.d.ts.map +1 -1
- package/dist/audit/types/externalAnalyzer.js +23 -0
- package/dist/audit/types/externalAnalyzer.js.map +1 -1
- package/dist/audit/types/reviewPlanning.d.ts +14 -14
- package/dist/audit/validation/auditResults.d.ts +17 -0
- package/dist/audit/validation/auditResults.d.ts.map +1 -1
- package/dist/audit/validation/auditResults.js +39 -0
- package/dist/audit/validation/auditResults.js.map +1 -1
- package/dist/audit/validation/sessionConfig.d.ts.map +1 -1
- package/dist/audit/validation/sessionConfig.js +4 -0
- package/dist/audit/validation/sessionConfig.js.map +1 -1
- package/dist/remediate/review/autonomousGate.d.ts +74 -0
- package/dist/remediate/review/autonomousGate.d.ts.map +1 -0
- package/dist/remediate/review/autonomousGate.js +179 -0
- package/dist/remediate/review/autonomousGate.js.map +1 -0
- package/dist/remediate/steps/contractPipeline.d.ts +8 -0
- package/dist/remediate/steps/contractPipeline.d.ts.map +1 -1
- package/dist/remediate/steps/contractPipeline.js +1 -0
- package/dist/remediate/steps/contractPipeline.js.map +1 -1
- package/dist/remediate/steps/contractPipelinePrompts.d.ts +10 -0
- package/dist/remediate/steps/contractPipelinePrompts.d.ts.map +1 -1
- package/dist/remediate/steps/contractPipelinePrompts.js +32 -1
- package/dist/remediate/steps/contractPipelinePrompts.js.map +1 -1
- package/dist/remediate/steps/nextStep.d.ts +34 -0
- package/dist/remediate/steps/nextStep.d.ts.map +1 -1
- package/dist/remediate/steps/nextStep.js +144 -8
- package/dist/remediate/steps/nextStep.js.map +1 -1
- package/dist/shared/index.d.ts +11 -2
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +6 -2
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/io/auditToolsPaths.d.ts +35 -0
- package/dist/shared/io/auditToolsPaths.d.ts.map +1 -1
- package/dist/shared/io/auditToolsPaths.js +46 -1
- package/dist/shared/io/auditToolsPaths.js.map +1 -1
- package/dist/shared/io/frictionCapture.d.ts +83 -0
- package/dist/shared/io/frictionCapture.d.ts.map +1 -0
- package/dist/shared/io/frictionCapture.js +79 -0
- package/dist/shared/io/frictionCapture.js.map +1 -0
- package/dist/shared/io/gitignoreArtifacts.d.ts +94 -0
- package/dist/shared/io/gitignoreArtifacts.d.ts.map +1 -0
- package/dist/shared/io/gitignoreArtifacts.js +147 -0
- package/dist/shared/io/gitignoreArtifacts.js.map +1 -0
- package/dist/shared/quota/antigravityQuotaSource.d.ts.map +1 -1
- package/dist/shared/quota/antigravityQuotaSource.js +2 -0
- package/dist/shared/quota/antigravityQuotaSource.js.map +1 -1
- package/dist/shared/quota/claudeOAuthQuotaSource.d.ts +35 -2
- package/dist/shared/quota/claudeOAuthQuotaSource.d.ts.map +1 -1
- package/dist/shared/quota/claudeOAuthQuotaSource.js +157 -17
- package/dist/shared/quota/claudeOAuthQuotaSource.js.map +1 -1
- package/dist/shared/quota/codexQuotaSource.d.ts.map +1 -1
- package/dist/shared/quota/codexQuotaSource.js +2 -0
- package/dist/shared/quota/codexQuotaSource.js.map +1 -1
- package/dist/shared/quota/copilotQuotaSource.d.ts.map +1 -1
- package/dist/shared/quota/copilotQuotaSource.js +2 -0
- package/dist/shared/quota/copilotQuotaSource.js.map +1 -1
- package/dist/shared/quota/errorParsing.d.ts +19 -0
- package/dist/shared/quota/errorParsing.d.ts.map +1 -1
- package/dist/shared/quota/errorParsing.js +14 -1
- package/dist/shared/quota/errorParsing.js.map +1 -1
- package/dist/shared/quota/hostSessionQuotaSource.d.ts +138 -0
- package/dist/shared/quota/hostSessionQuotaSource.d.ts.map +1 -0
- package/dist/shared/quota/hostSessionQuotaSource.js +213 -0
- package/dist/shared/quota/hostSessionQuotaSource.js.map +1 -0
- package/dist/shared/quota/httpQuotaSource.d.ts +7 -3
- package/dist/shared/quota/httpQuotaSource.d.ts.map +1 -1
- package/dist/shared/quota/httpQuotaSource.js.map +1 -1
- package/dist/shared/reporting/auditDeliverable.d.ts +36 -0
- package/dist/shared/reporting/auditDeliverable.d.ts.map +1 -0
- package/dist/shared/reporting/auditDeliverable.js +85 -0
- package/dist/shared/reporting/auditDeliverable.js.map +1 -0
- package/dist/shared/types/sessionConfig.d.ts +13 -0
- package/dist/shared/types/sessionConfig.d.ts.map +1 -1
- package/package.json +1 -1
- package/schemas/audit_result.schema.json +2 -2
- package/schemas/audit_results.schema.json +2 -2
- package/schemas/finding.schema.json +2 -2
- 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,
|
|
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":"
|
|
1
|
+
{"version":3,"file":"normalizeExternal.js","sourceRoot":"","sources":["../../../src/audit/adapters/normalizeExternal.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,23 +1,42 @@
|
|
|
1
1
|
import type { ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
|
|
2
|
-
interface
|
|
3
|
-
|
|
4
|
-
check_id?: string;
|
|
2
|
+
interface SemgrepDataflowTraceNode {
|
|
3
|
+
location?: {
|
|
5
4
|
path?: string;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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,
|
|
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":"
|
|
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,
|
|
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
|
-
|
|
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
|
}
|