auditor-lambda 0.2.5 → 0.2.8
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/README.md +35 -7
- package/audit-code-wrapper-lib.mjs +1612 -331
- package/dist/cli.js +397 -38
- package/dist/coverage.d.ts +2 -2
- package/dist/coverage.js +5 -5
- package/dist/extractors/disposition.js +10 -1
- package/dist/extractors/flows.js +7 -1
- package/dist/extractors/pathPatterns.d.ts +3 -0
- package/dist/extractors/pathPatterns.js +15 -0
- package/dist/extractors/risk.js +7 -1
- package/dist/io/artifacts.d.ts +6 -6
- package/dist/io/artifacts.js +14 -17
- package/dist/io/json.d.ts +2 -0
- package/dist/io/json.js +15 -0
- package/dist/io/runArtifacts.d.ts +3 -1
- package/dist/io/runArtifacts.js +20 -5
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +579 -0
- package/dist/orchestrator/advance.js +9 -2
- package/dist/orchestrator/dependencyMap.js +9 -13
- package/dist/orchestrator/executors.js +7 -2
- package/dist/orchestrator/flowRequeue.d.ts +2 -2
- package/dist/orchestrator/flowRequeue.js +16 -3
- package/dist/orchestrator/internalExecutors.d.ts +2 -1
- package/dist/orchestrator/internalExecutors.js +129 -48
- package/dist/orchestrator/requeue.js +10 -4
- package/dist/orchestrator/requeueCommand.js +15 -2
- package/dist/orchestrator/resultIngestion.d.ts +2 -1
- package/dist/orchestrator/resultIngestion.js +26 -6
- package/dist/orchestrator/runtimeValidation.d.ts +7 -2
- package/dist/orchestrator/runtimeValidation.js +61 -49
- package/dist/orchestrator/runtimeValidationUpdate.js +2 -4
- package/dist/orchestrator/state.js +28 -14
- package/dist/orchestrator/taskBuilder.js +4 -2
- package/dist/orchestrator/trivialAudit.d.ts +4 -0
- package/dist/orchestrator/trivialAudit.js +49 -0
- package/dist/prompts/renderWorkerPrompt.js +6 -2
- package/dist/providers/spawnLoggedCommand.js +17 -0
- package/dist/reporting/mergeFindings.js +3 -11
- package/dist/reporting/rootCause.js +92 -9
- package/dist/reporting/synthesis.d.ts +25 -22
- package/dist/reporting/synthesis.js +92 -59
- package/dist/reporting/workBlocks.d.ts +12 -3
- package/dist/reporting/workBlocks.js +124 -70
- package/dist/supervisor/sessionConfig.js +4 -2
- package/dist/types/flows.d.ts +2 -0
- package/dist/types/runtimeValidation.d.ts +2 -1
- package/dist/types.d.ts +8 -6
- package/dist/validation/auditResults.d.ts +5 -2
- package/dist/validation/auditResults.js +335 -43
- package/docs/agent-integrations.md +38 -29
- package/docs/artifacts.md +18 -51
- package/docs/bootstrap-install.md +60 -30
- package/docs/contract.md +25 -117
- package/docs/field-trial-bug-report.md +237 -0
- package/docs/next-steps.md +59 -44
- package/docs/packaging.md +13 -3
- package/docs/production-launch-bar.md +2 -2
- package/docs/production-readiness.md +9 -5
- package/docs/releasing.md +81 -0
- package/docs/session-config.md +20 -1
- package/docs/usage.md +22 -0
- package/package.json +4 -1
- package/schemas/audit_result.schema.json +4 -5
- package/schemas/audit_task.schema.json +10 -0
- package/schemas/runtime_validation_report.schema.json +1 -1
- package/skills/audit-code/SKILL.md +11 -2
- package/skills/audit-code/audit-code.prompt.md +11 -10
- package/schemas/merged_findings.schema.json +0 -19
- package/schemas/root_cause_clusters.schema.json +0 -28
- package/schemas/synthesis_report.schema.json +0 -61
|
@@ -12,98 +12,152 @@ function severityRank(severity) {
|
|
|
12
12
|
return 1;
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
function buildFileUnitMap(unitManifest) {
|
|
16
|
+
const map = new Map();
|
|
17
|
+
for (const unit of unitManifest?.units ?? []) {
|
|
18
|
+
for (const path of unit.files) {
|
|
19
|
+
if (!map.has(path)) {
|
|
20
|
+
map.set(path, unit.unit_id);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return map;
|
|
25
|
+
}
|
|
26
|
+
function normalizeOwnedUnits(finding, fileUnitMap) {
|
|
27
|
+
const unitIds = new Set();
|
|
28
|
+
for (const file of finding.affected_files) {
|
|
29
|
+
const mapped = fileUnitMap.get(file.path);
|
|
30
|
+
unitIds.add(mapped ?? `file:${file.path}`);
|
|
31
|
+
}
|
|
32
|
+
return [...unitIds].sort();
|
|
33
|
+
}
|
|
34
|
+
function computeDependencies(params) {
|
|
35
|
+
const blockByFile = new Map();
|
|
36
|
+
for (const block of params.blocks) {
|
|
37
|
+
for (const path of block.owned_files) {
|
|
38
|
+
blockByFile.set(path, block.id);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const dependsOn = new Map();
|
|
42
|
+
for (const block of params.blocks) {
|
|
43
|
+
dependsOn.set(block.id, new Set());
|
|
44
|
+
}
|
|
45
|
+
const graphEdges = [
|
|
46
|
+
...(params.graphBundle?.graphs.imports ?? []),
|
|
47
|
+
...(params.graphBundle?.graphs.calls ?? []),
|
|
48
|
+
];
|
|
49
|
+
for (const edge of graphEdges) {
|
|
50
|
+
const fromBlock = blockByFile.get(edge.from);
|
|
51
|
+
const toBlock = blockByFile.get(edge.to);
|
|
52
|
+
if (fromBlock && toBlock && fromBlock !== toBlock) {
|
|
53
|
+
dependsOn.get(fromBlock)?.add(toBlock);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const flow of params.criticalFlows?.flows ?? []) {
|
|
57
|
+
const flowBlocks = new Set();
|
|
58
|
+
for (const path of flow.paths) {
|
|
59
|
+
const blockId = blockByFile.get(path);
|
|
60
|
+
if (blockId) {
|
|
61
|
+
flowBlocks.add(blockId);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const ordered = [...flowBlocks].sort();
|
|
65
|
+
for (let i = 1; i < ordered.length; i++) {
|
|
66
|
+
dependsOn.get(ordered[i - 1])?.add(ordered[i]);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return params.blocks.map((block) => ({
|
|
70
|
+
...block,
|
|
71
|
+
depends_on: [...(dependsOn.get(block.id) ?? [])].sort(),
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
export function buildWorkBlocks(params) {
|
|
75
|
+
if (params.findings.length === 0) {
|
|
17
76
|
return [];
|
|
77
|
+
}
|
|
78
|
+
const fileUnitMap = buildFileUnitMap(params.unitManifest);
|
|
18
79
|
const parent = new Map();
|
|
80
|
+
const findingUnits = new Map();
|
|
19
81
|
function find(id) {
|
|
20
82
|
if (!parent.has(id))
|
|
21
83
|
parent.set(id, id);
|
|
22
|
-
const
|
|
23
|
-
if (
|
|
84
|
+
const current = parent.get(id);
|
|
85
|
+
if (current === id)
|
|
24
86
|
return id;
|
|
25
|
-
const root = find(
|
|
87
|
+
const root = find(current);
|
|
26
88
|
parent.set(id, root);
|
|
27
89
|
return root;
|
|
28
90
|
}
|
|
29
91
|
function union(a, b) {
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
if (
|
|
33
|
-
parent.set(
|
|
34
|
-
}
|
|
35
|
-
for (const finding of findings)
|
|
36
|
-
find(finding.id);
|
|
37
|
-
// Union findings that share affected files
|
|
38
|
-
const fileToIds = new Map();
|
|
39
|
-
for (const finding of findings) {
|
|
40
|
-
for (const af of finding.affected_files) {
|
|
41
|
-
const ids = fileToIds.get(af.path) ?? [];
|
|
42
|
-
ids.push(finding.id);
|
|
43
|
-
fileToIds.set(af.path, ids);
|
|
92
|
+
const rootA = find(a);
|
|
93
|
+
const rootB = find(b);
|
|
94
|
+
if (rootA !== rootB) {
|
|
95
|
+
parent.set(rootA, rootB);
|
|
44
96
|
}
|
|
45
97
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
98
|
+
const unitToFindingIds = new Map();
|
|
99
|
+
for (const finding of params.findings) {
|
|
100
|
+
parent.set(finding.id, finding.id);
|
|
101
|
+
const ownedUnits = normalizeOwnedUnits(finding, fileUnitMap);
|
|
102
|
+
findingUnits.set(finding.id, ownedUnits);
|
|
103
|
+
for (const unitId of ownedUnits) {
|
|
104
|
+
const ids = unitToFindingIds.get(unitId) ?? [];
|
|
105
|
+
ids.push(finding.id);
|
|
106
|
+
unitToFindingIds.set(unitId, ids);
|
|
49
107
|
}
|
|
50
108
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
for (const relId of finding.related_findings ?? []) {
|
|
55
|
-
if (knownIds.has(relId))
|
|
56
|
-
union(finding.id, relId);
|
|
109
|
+
for (const ids of unitToFindingIds.values()) {
|
|
110
|
+
for (let index = 1; index < ids.length; index++) {
|
|
111
|
+
union(ids[0], ids[index]);
|
|
57
112
|
}
|
|
58
113
|
}
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
for (const finding of findings) {
|
|
114
|
+
const grouped = new Map();
|
|
115
|
+
for (const finding of params.findings) {
|
|
62
116
|
const root = find(finding.id);
|
|
63
|
-
const group =
|
|
117
|
+
const group = grouped.get(root) ?? [];
|
|
64
118
|
group.push(finding);
|
|
65
|
-
|
|
119
|
+
grouped.set(root, group);
|
|
66
120
|
}
|
|
67
|
-
const blocks = []
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return sevDelta;
|
|
74
|
-
const sysA = a.systemic ? 1 : 0;
|
|
75
|
-
const sysB = b.systemic ? 1 : 0;
|
|
76
|
-
if (sysA !== sysB)
|
|
77
|
-
return sysB - sysA;
|
|
78
|
-
return a.title.localeCompare(b.title);
|
|
121
|
+
const blocks = [...grouped.values()].map((group, index) => {
|
|
122
|
+
const orderedFindings = [...group].sort((a, b) => {
|
|
123
|
+
const severityDelta = severityRank(b.severity) - severityRank(a.severity);
|
|
124
|
+
if (severityDelta !== 0)
|
|
125
|
+
return severityDelta;
|
|
126
|
+
return a.id.localeCompare(b.id);
|
|
79
127
|
});
|
|
80
|
-
const
|
|
81
|
-
...new Set(
|
|
128
|
+
const unitIds = [
|
|
129
|
+
...new Set(group.flatMap((finding) => findingUnits.get(finding.id) ?? [])),
|
|
82
130
|
].sort();
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
max_severity:
|
|
92
|
-
rationale
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
131
|
+
const ownedFiles = [
|
|
132
|
+
...new Set(group.flatMap((finding) => finding.affected_files.map((file) => file.path))),
|
|
133
|
+
].sort();
|
|
134
|
+
return {
|
|
135
|
+
id: `block-${index + 1}`,
|
|
136
|
+
finding_ids: orderedFindings.map((finding) => finding.id),
|
|
137
|
+
unit_ids: unitIds,
|
|
138
|
+
owned_files: ownedFiles,
|
|
139
|
+
max_severity: orderedFindings[0].severity,
|
|
140
|
+
rationale: unitIds.length === 1
|
|
141
|
+
? "All findings map to the same owned unit and should be remediated together."
|
|
142
|
+
: "Findings share owned units transitively and should remain one non-overlapping remediation block.",
|
|
143
|
+
depends_on: [],
|
|
144
|
+
};
|
|
145
|
+
});
|
|
96
146
|
blocks.sort((a, b) => {
|
|
97
|
-
const
|
|
98
|
-
if (
|
|
99
|
-
return
|
|
100
|
-
const
|
|
101
|
-
if (
|
|
102
|
-
return
|
|
103
|
-
return
|
|
147
|
+
const severityDelta = severityRank(b.max_severity) - severityRank(a.max_severity);
|
|
148
|
+
if (severityDelta !== 0)
|
|
149
|
+
return severityDelta;
|
|
150
|
+
const findingDelta = b.finding_ids.length - a.finding_ids.length;
|
|
151
|
+
if (findingDelta !== 0)
|
|
152
|
+
return findingDelta;
|
|
153
|
+
return a.id.localeCompare(b.id);
|
|
104
154
|
});
|
|
105
|
-
for (let
|
|
106
|
-
blocks[
|
|
155
|
+
for (let index = 0; index < blocks.length; index++) {
|
|
156
|
+
blocks[index].id = `block-${index + 1}`;
|
|
107
157
|
}
|
|
108
|
-
return
|
|
158
|
+
return computeDependencies({
|
|
159
|
+
blocks,
|
|
160
|
+
graphBundle: params.graphBundle,
|
|
161
|
+
criticalFlows: params.criticalFlows,
|
|
162
|
+
});
|
|
109
163
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readOptionalJsonFile } from "../io/json.js";
|
|
2
2
|
import { validateSessionConfig } from "../validation/sessionConfig.js";
|
|
3
|
+
import { writeJsonFile } from "../io/json.js";
|
|
3
4
|
export function getSessionConfigPath(artifactsDir) {
|
|
4
5
|
return `${artifactsDir}/session-config.json`;
|
|
5
6
|
}
|
|
@@ -16,8 +17,9 @@ export async function loadSessionConfig(artifactsDir) {
|
|
|
16
17
|
const configPath = getSessionConfigPath(artifactsDir);
|
|
17
18
|
const rawConfig = await readOptionalJsonFile(configPath);
|
|
18
19
|
if (rawConfig === undefined) {
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
const defaultConfig = { provider: "local-subprocess" };
|
|
21
|
+
await writeJsonFile(configPath, defaultConfig);
|
|
22
|
+
return defaultConfig;
|
|
21
23
|
}
|
|
22
24
|
const issues = validateSessionConfig(rawConfig);
|
|
23
25
|
if (issues.length > 0) {
|
package/dist/types/flows.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export interface RuntimeValidationTask {
|
|
|
5
5
|
target_paths: string[];
|
|
6
6
|
reason: string;
|
|
7
7
|
priority: "high" | "medium" | "low";
|
|
8
|
+
command?: string[];
|
|
8
9
|
suggested_checks?: string[];
|
|
9
10
|
source_artifacts?: string[];
|
|
10
11
|
}
|
|
@@ -13,7 +14,7 @@ export interface RuntimeValidationTaskManifest {
|
|
|
13
14
|
}
|
|
14
15
|
export interface RuntimeValidationResult {
|
|
15
16
|
task_id: string;
|
|
16
|
-
status: "pending" | "confirmed" | "not_confirmed" | "inconclusive";
|
|
17
|
+
status: "pending" | "confirmed" | "not_confirmed" | "inconclusive" | "not_required";
|
|
17
18
|
summary: string;
|
|
18
19
|
evidence?: string[];
|
|
19
20
|
notes?: string[];
|
package/dist/types.d.ts
CHANGED
|
@@ -28,10 +28,9 @@ export interface AuditUnit {
|
|
|
28
28
|
export interface UnitManifest {
|
|
29
29
|
units: AuditUnit[];
|
|
30
30
|
}
|
|
31
|
-
export interface
|
|
31
|
+
export interface FileCoverageRecord {
|
|
32
32
|
path: string;
|
|
33
|
-
|
|
34
|
-
end: number;
|
|
33
|
+
total_lines: number;
|
|
35
34
|
pass_id: string;
|
|
36
35
|
lens?: Lens;
|
|
37
36
|
agent_role?: string;
|
|
@@ -47,6 +46,7 @@ export interface CoverageFileRecord {
|
|
|
47
46
|
export interface CoverageMatrix {
|
|
48
47
|
files: CoverageFileRecord[];
|
|
49
48
|
}
|
|
49
|
+
export type AuditTaskStatus = "pending" | "complete";
|
|
50
50
|
export interface AuditTask {
|
|
51
51
|
task_id: string;
|
|
52
52
|
unit_id: string;
|
|
@@ -62,6 +62,9 @@ export interface AuditTask {
|
|
|
62
62
|
rationale: string;
|
|
63
63
|
priority?: "high" | "medium" | "low";
|
|
64
64
|
tags?: string[];
|
|
65
|
+
status?: AuditTaskStatus;
|
|
66
|
+
completed_at?: string;
|
|
67
|
+
completion_reason?: string;
|
|
65
68
|
}
|
|
66
69
|
export interface Finding {
|
|
67
70
|
id: string;
|
|
@@ -90,10 +93,9 @@ export interface AuditResult {
|
|
|
90
93
|
pass_id: string;
|
|
91
94
|
lens: Lens;
|
|
92
95
|
agent_role?: string;
|
|
93
|
-
|
|
96
|
+
file_coverage: Array<{
|
|
94
97
|
path: string;
|
|
95
|
-
|
|
96
|
-
end: number;
|
|
98
|
+
total_lines: number;
|
|
97
99
|
}>;
|
|
98
100
|
findings: Finding[];
|
|
99
101
|
notes?: string[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AuditTask } from "../types.js";
|
|
2
2
|
export type IssueSeverity = "error" | "warning";
|
|
3
3
|
export interface AuditResultIssue {
|
|
4
4
|
result_index: number;
|
|
@@ -7,5 +7,8 @@ export interface AuditResultIssue {
|
|
|
7
7
|
field: string;
|
|
8
8
|
message: string;
|
|
9
9
|
}
|
|
10
|
-
export
|
|
10
|
+
export interface ValidateAuditResultOptions {
|
|
11
|
+
lineIndex?: Record<string, number>;
|
|
12
|
+
}
|
|
13
|
+
export declare function validateAuditResults(results: unknown, tasks: AuditTask[], options?: ValidateAuditResultOptions): AuditResultIssue[];
|
|
11
14
|
export declare function formatAuditResultIssues(issues: AuditResultIssue[]): string;
|