fix: surface sentinel analyzer findings
This commit is contained in:
@@ -242,6 +242,11 @@ export function runSentinelReport(state: SentinelCicdState, options: Extract<Web
|
||||
const payload = compactSentinelReportRawPayload(state, body, report, artifactSummary);
|
||||
return rendered(report.ok && body.ok !== false, command, renderSentinelReportSummary(payload, state));
|
||||
}
|
||||
if (options.view === "findings") {
|
||||
const artifactSummary = readSentinelReportArtifactSummary(state, body, Math.min(options.timeoutSeconds, 55));
|
||||
const payload = compactSentinelReportRawPayload(state, body, report, artifactSummary);
|
||||
return rendered(report.ok && body.ok !== false, command, renderSentinelReportFindings(payload));
|
||||
}
|
||||
const renderedText = typeof body.renderedText === "string" ? body.renderedText : renderReportResult({ command, node: state.spec.nodeId, lane: state.spec.lane, report, valuesRedacted: true });
|
||||
return rendered(report.ok && body.ok !== false, command, renderedText);
|
||||
}
|
||||
@@ -256,6 +261,10 @@ function compactSentinelReportRawPayload(
|
||||
const artifact = record(artifactSummary);
|
||||
const findings = Array.isArray(body.findings) ? body.findings.map(record) : [];
|
||||
const artifactFindings = Array.isArray(artifact.findings) ? artifact.findings.map(record) : [];
|
||||
const visibleFindings = mergeSentinelReportFindings(findings, artifactFindings);
|
||||
const storedFindingCount = numberAtNullable(run, "finding_count") ?? numberAtNullable(run, "findingCount") ?? findings.length;
|
||||
const artifactFindingCount = numberAtNullable(artifact, "findingCount") ?? artifactFindings.length;
|
||||
const visibleFindingCount = Math.max(storedFindingCount, artifactFindingCount, visibleFindings.length);
|
||||
const rootCauseSignalFindings = artifactFindings
|
||||
.filter((item) => Object.keys(record(item.rootCauseSignals)).length > 0)
|
||||
.slice(0, 8)
|
||||
@@ -279,7 +288,9 @@ function compactSentinelReportRawPayload(
|
||||
observerId: run.observer_id ?? run.observerId ?? null,
|
||||
stateDir: run.state_dir ?? run.stateDir ?? null,
|
||||
reportJsonSha256: run.report_json_sha256 ?? run.reportJsonSha256 ?? artifact.reportJsonSha256 ?? null,
|
||||
findingCount: run.finding_count ?? run.findingCount ?? findings.length,
|
||||
findingCount: visibleFindingCount,
|
||||
storedFindingCount,
|
||||
artifactFindingCount,
|
||||
artifactCount: run.artifact_count ?? run.artifactCount ?? artifact.artifactCount ?? null,
|
||||
durationMinutes: run.durationMinutes ?? run.runDurationMinutes ?? record(run.timing).durationMinutes ?? null,
|
||||
runDurationMinutes: run.runDurationMinutes ?? run.durationMinutes ?? record(run.timing).durationMinutes ?? null,
|
||||
@@ -292,13 +303,15 @@ function compactSentinelReportRawPayload(
|
||||
valuesRedacted: true,
|
||||
},
|
||||
summary: pickFields(record(body.summary), ["reason", "status", "businessStatus", "failure", "valuesRedacted"]),
|
||||
findings: findings.slice(0, 12).map(compactSentinelReportFinding),
|
||||
findings: visibleFindings.slice(0, 12).map(compactSentinelReportFinding),
|
||||
artifactSummary: Object.keys(artifact).length === 0 ? null : {
|
||||
ok: artifact.ok === true,
|
||||
reportOk: artifact.reportOk === true ? true : artifact.reportOk === false ? false : null,
|
||||
reportJsonPath: artifact.reportJsonPath ?? null,
|
||||
reportJsonSha256: artifact.reportJsonSha256 ?? null,
|
||||
reportMdSha256: artifact.reportMdSha256 ?? null,
|
||||
findingCount: artifactFindingCount,
|
||||
findings: artifactFindings.slice(0, 12).map(compactSentinelReportFinding),
|
||||
screenshot: record(artifact.screenshot),
|
||||
counts: record(artifact.counts),
|
||||
analysisWindow: compactSentinelAnalysisWindow(artifact.analysisWindow),
|
||||
@@ -315,6 +328,27 @@ function compactSentinelReportRawPayload(
|
||||
};
|
||||
}
|
||||
|
||||
function mergeSentinelReportFindings(primary: readonly Record<string, unknown>[], artifact: readonly Record<string, unknown>[]): Record<string, unknown>[] {
|
||||
const merged: Record<string, unknown>[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const item of [...primary, ...artifact]) {
|
||||
const id = sentinelReportFindingIdentity(item);
|
||||
const key = `${id ?? JSON.stringify(item).slice(0, 160)}:${stringAtNullable(item, "severity") ?? stringAtNullable(item, "level") ?? ""}`;
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
merged.push(item);
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
function sentinelReportFindingIdentity(item: Record<string, unknown>): string | null {
|
||||
return stringAtNullable(item, "finding_id")
|
||||
?? stringAtNullable(item, "findingId")
|
||||
?? stringAtNullable(item, "id")
|
||||
?? stringAtNullable(item, "kind")
|
||||
?? stringAtNullable(item, "code");
|
||||
}
|
||||
|
||||
function compactSentinelReportFinding(value: Record<string, unknown>): Record<string, unknown> {
|
||||
const result: Record<string, unknown> = {
|
||||
id: stringAtNullable(value, "finding_id") ?? stringAtNullable(value, "findingId") ?? stringAtNullable(value, "id") ?? stringAtNullable(value, "kind") ?? stringAtNullable(value, "code"),
|
||||
@@ -415,6 +449,35 @@ function renderSentinelReportSummary(payload: Record<string, unknown>, state: Se
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function renderSentinelReportFindings(payload: Record<string, unknown>): string {
|
||||
const run = record(payload.run);
|
||||
const artifactSummary = record(payload.artifactSummary);
|
||||
const findings = Array.isArray(payload.findings) ? payload.findings.map(record) : [];
|
||||
const reportSha = stringAtNullable(run, "reportJsonSha256") ?? stringAtNullable(artifactSummary, "reportJsonSha256");
|
||||
return [
|
||||
"Web Probe Sentinel Findings",
|
||||
"=======================================================",
|
||||
`run=${run.id ?? "-"} report=${reportSha ?? "-"} findings=${run.findingCount ?? findings.length}`,
|
||||
findings.length === 0 ? "-" : findings.map(formatSentinelReportFindingLine).join("\n"),
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function formatSentinelReportFindingLine(item: Record<string, unknown>): string {
|
||||
const check = record(item.check);
|
||||
const code = stringAtNullable(check, "code") ?? stringAtNullable(item, "id") ?? "-";
|
||||
const title = stringAtNullable(check, "titleZh") ?? "";
|
||||
const summary = reportText(item.summary, 180) ?? "";
|
||||
const rootCause = reportText(item.rootCause, 180);
|
||||
const evidence = reportText(item.evidenceSummary, 180);
|
||||
const nextAction = reportText(item.nextAction, 180);
|
||||
return [
|
||||
`${item.severity ?? "-"} ${code} ${title} count=${item.count ?? "-"} ${summary}`.trim(),
|
||||
rootCause === null ? null : `rootCause=${rootCause}`,
|
||||
evidence === null ? null : `evidence=${evidence}`,
|
||||
nextAction === null ? null : `next=${nextAction}`,
|
||||
].filter((part): part is string => part !== null && part.length > 0).join(" | ");
|
||||
}
|
||||
|
||||
function compactRootCauseSignals(value: unknown): Record<string, unknown> | null {
|
||||
const item = record(value);
|
||||
const keys = [
|
||||
|
||||
Reference in New Issue
Block a user