fix(sentinel): harden post-deploy report and ssh summaries
This commit is contained in:
@@ -325,10 +325,14 @@ export function runSentinelQuickVerify(state: SentinelCicdState, reason: string,
|
||||
cleanupFindings,
|
||||
);
|
||||
const blockingFindings = findings.filter(isQuickVerifyBlockingFinding);
|
||||
const analysisWarnings = analysis.ok ? [] : ["quick verify analyze command returned non-zero but a readable analysis artifact was produced; targetValidation is using artifact severity plus control blockers."];
|
||||
const ok = record(artifactSummary).ok === true && controlFindings.length === 0 && blockingFindings.length === 0;
|
||||
const analysisReadable = stringAtNullable(artifactSummary, "reportJsonSha256") !== null;
|
||||
const analysisWarnings = mergeWarnings(
|
||||
analysis.ok ? [] : ["quick verify analyze command returned non-zero but a readable analysis artifact was produced; targetValidation is using artifact severity plus control blockers."],
|
||||
analysisReadable && artifactSummaryRecord.ok !== true ? ["quick verify analysis report contains non-blocking findings; targetValidation keeps them in report but does not fail the CronJob without red/blocking findings."] : [],
|
||||
);
|
||||
const ok = analysisReadable && controlFindings.length === 0 && blockingFindings.length === 0;
|
||||
const businessStatus = quickVerifyBusinessStatus(null, promptIndex, turnSummary, traceFrame, elapsedMs(), maxSeconds);
|
||||
printQuickVerifyProgress(state, runId, "record-report", ok ? "succeeded" : "blocked", { observerId, reportJsonSha256: stringAtNullable(artifactSummary, "reportJsonSha256"), findingCount: findings.length, blockingFindingCount: blockingFindings.length, controlFindingCount: controlFindings.length, elapsedMs: elapsedMs() });
|
||||
printQuickVerifyProgress(state, runId, "record-report", ok ? "succeeded" : "blocked", { observerId, reportJsonSha256: stringAtNullable(artifactSummary, "reportJsonSha256"), analysisReadable, findingCount: findings.length, blockingFindingCount: blockingFindings.length, controlFindingCount: controlFindings.length, elapsedMs: elapsedMs() });
|
||||
return recordQuickVerify(state, {
|
||||
ok,
|
||||
runId,
|
||||
@@ -342,7 +346,7 @@ export function runSentinelQuickVerify(state: SentinelCicdState, reason: string,
|
||||
reportJsonSha256: stringAtNullable(artifactSummary, "reportJsonSha256"),
|
||||
findingCount: findings.length,
|
||||
artifactCount: numberAtNullable(artifactSummary, "artifactCount") ?? 0,
|
||||
failure: controlFindings.length > 0 ? "quick-verify-no-business-turn" : blockingFindings.length > 0 ? "quick-verify-blocking-findings" : null,
|
||||
failure: !analysisReadable ? "quick-verify-analysis-missing" : controlFindings.length > 0 ? "quick-verify-no-business-turn" : blockingFindings.length > 0 ? "quick-verify-blocking-findings" : null,
|
||||
promptSource: prompts.summary,
|
||||
accountEnv: accountEnv.summary,
|
||||
steps: [...steps, cleanupStep],
|
||||
@@ -611,7 +615,9 @@ function callSentinelService(state: SentinelCicdState, method: "GET" | "POST", p
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
||||
result = runCommand(["trans", stringAt(state.controlPlaneNode, "kubeRoute"), "sh", "--", script], repoRoot, { timeoutMs: attemptTimeoutSeconds * 1000 });
|
||||
parsed = parseJsonObject(result.stdout);
|
||||
attempts.push({ attempt, ...compactCommand(result), parsedOk: parsed !== null, valuesRedacted: true });
|
||||
const recovered = parsed === null ? recoverTruncatedSshStdoutJson(result) : null;
|
||||
if (recovered !== null) parsed = recovered.parsed;
|
||||
attempts.push({ attempt, ...compactCommand(result), parsedOk: parsed !== null, parsedFromDump: recovered !== null, dumpPath: recovered?.dumpPath ?? null, valuesRedacted: true });
|
||||
if (result.exitCode === 0) break;
|
||||
}
|
||||
return {
|
||||
@@ -631,6 +637,29 @@ function callSentinelService(state: SentinelCicdState, method: "GET" | "POST", p
|
||||
};
|
||||
}
|
||||
|
||||
function recoverTruncatedSshStdoutJson(result: CommandResult): { parsed: Record<string, unknown>; dumpPath: string } | null {
|
||||
const dumpPath = sshStdoutDumpPathFromStderr(result.stderr);
|
||||
if (dumpPath === null || !existsSync(dumpPath)) return null;
|
||||
const parsed = parseJsonObject(readFileSync(dumpPath, "utf8"));
|
||||
return parsed === null ? null : { parsed, dumpPath };
|
||||
}
|
||||
|
||||
function sshStdoutDumpPathFromStderr(value: string): string | null {
|
||||
for (const rawLine of value.split(/\r?\n/u)) {
|
||||
const line = rawLine.trim();
|
||||
const prefix = "UNIDESK_SSH_STDOUT_TRUNCATED ";
|
||||
if (!line.startsWith(prefix)) continue;
|
||||
try {
|
||||
const payload = JSON.parse(line.slice(prefix.length)) as unknown;
|
||||
const dumpPath = stringAtNullable(record(payload), "dumpPath");
|
||||
if (dumpPath !== null) return dumpPath;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function compactSentinelServiceBodyJson(value: Record<string, unknown> | null): unknown {
|
||||
if (value === null || typeof value.renderedText !== "string") return value;
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user