oaops1-patrol-feature-review 改进 OAOPS1 巡检报告传播与脱敏

这个提交包含在:
2026-05-26 14:03:51 +08:00
父节点 267240bd81
当前提交 d61b254064
共修改 15 个文件,包含 1448 行新增和 14 行删除
+71 -14
查看文件
@@ -22,17 +22,27 @@ write_step_json() {
local kind="$3"
local code="${4:-0}"
local detail="${5:-}"
node - "$REPORT_DIR" "$name" "$status" "$kind" "$code" "$detail" <<'NODE' || true
node - "$ROOT_DIR" "$REPORT_DIR" "$name" "$status" "$kind" "$code" "$detail" <<'NODE' || true
const fs = require("node:fs");
const path = require("node:path");
const [reportDir, name, status, kind, code, detail] = process.argv.slice(2);
const [rootDir, reportDir, name, status, kind, code, detail] = process.argv.slice(2);
function reportRef(file) {
const absolute = path.resolve(file);
const relative = path.relative(rootDir, absolute);
if (relative && !relative.startsWith("..") && !path.isAbsolute(relative)) {
return relative;
}
return path.basename(absolute);
}
fs.writeFileSync(path.join(reportDir, `${name}.step.json`), `${JSON.stringify({
name,
status,
kind,
code: Number(code || 0),
detail: String(detail || "").slice(0, 600),
logPath: path.resolve(path.join(reportDir, `${name}.log`)),
logRef: reportRef(path.join(reportDir, `${name}.log`)),
checkedAt: new Date().toISOString(),
}, null, 2)}\n`, "utf8");
NODE
@@ -201,9 +211,16 @@ function summarizePayload(json) {
if (typeof data === "object" && data) {
const summary = {};
for (const [key, value] of Object.entries(data)) {
if (/token|secret|password|phone|email|payload|attachment|comment|form/i.test(key)) continue;
if (typeof value === "number" || typeof value === "boolean") summary[key] = value;
if (typeof value === "string" && value.length <= 60) summary[key] = value;
if (/token|secret|password|phone|email|payload|attachment|comment|form|amount|money|price|cost|balance|bank|receipt|account|invoice|tax|fee|paid|payment/i.test(key)) continue;
if (typeof value === "number" && /(count|total|size|days|pending|open|closed|passed|failed)$/i.test(key)) {
summary[key] = value;
}
if (typeof value === "boolean" && /^(enabled|disabled|ready|ok|healthy|configured|available|has[A-Z]|is[A-Z]|can[A-Z])/i.test(key)) {
summary[key] = value;
}
if (typeof value === "string" && /^(status|mode|conclusion)$/i.test(key) && /^[a-z0-9_/-]{1,60}$/i.test(value)) {
summary[key] = value;
}
}
return summary;
}
@@ -347,11 +364,19 @@ run_privacy_smoke() {
local matches
matches="$(grep -RInE "$sensitive_pattern" "$REPORT_DIR" || true)"
if [[ -n "$matches" ]]; then
printf '%s\n' "$matches"
while IFS= read -r line; do
local match_file="${line%%:*}"
local rest="${line#*:}"
local match_line="${rest%%:*}"
local match_ref="${match_file#$ROOT_DIR/}"
printf '%s:%s:REDACTED\n' "$match_ref" "$match_line"
done <<<"$matches" >"$REPORT_DIR/privacy-findings.txt"
log "[oaops1][FAIL] report-privacy-smoke matched sensitive pattern"
STATUS=1
write_step_json "report-privacy-smoke" "FAIL" "required" 1 "sensitive pattern matched; see privacy-findings.txt"
else
log "[oaops1][PASS] report-privacy-smoke"
write_step_json "report-privacy-smoke" "PASS" "required" 0 "ok"
fi
}
@@ -372,21 +397,33 @@ function readJson(file) {
}
}
function reportRef(file) {
const absolute = path.resolve(file);
const relative = path.relative(rootDir, absolute);
if (relative && !relative.startsWith("..") && !path.isAbsolute(relative)) {
return relative;
}
return path.basename(absolute);
}
function step(name) {
return readJson(path.join(reportDir, `${name}.step.json`)) || {
name,
status: "MISSING",
kind: "unknown",
logPath: path.resolve(path.join(reportDir, `${name}.log`)),
logRef: reportRef(path.join(reportDir, `${name}.log`)),
};
}
function child(relativePath) {
const file = path.join(reportDir, relativePath, "summary.json");
const json = readJson(file);
const findingCount = Number(json?.findingCount ?? json?.findings?.length ?? json?.gates?.findings?.length ?? 0);
return {
status: json?.status || json?.conclusion || "MISSING",
reportPath: fs.existsSync(file) ? path.resolve(file) : null,
reportRef: fs.existsSync(file) ? reportRef(file) : null,
findingCount: Number.isFinite(findingCount) ? findingCount : 0,
openCount: Number(json?.gates?.openCount ?? 0),
};
}
@@ -405,9 +442,14 @@ const stepNames = [
"mobile-entry-matrix",
"wecom-rendering-matrix",
"wecom-entry",
"report-privacy-smoke",
];
const steps = Object.fromEntries(stepNames.map((name) => [name, step(name)]));
const readonlyProbe = readJson(path.join(reportDir, "authenticated-readonly-probe.json"));
const childReports = {
liveReadiness: child("live-smoke-readiness"),
oaOnlySmokeReadiness: child("oa-only-smoke-readiness"),
};
const findings = [];
for (const [name, item] of Object.entries(steps)) {
@@ -424,6 +466,17 @@ for (const [name, item] of Object.entries(steps)) {
if (readonlyProbe?.status && readonlyProbe.status !== "PASS") {
findings.push({ source: "authenticated-readonly-probe", status: readonlyProbe.status, severity: readonlyProbe.status === "FAIL" ? "finding" : "manual_or_config" });
}
for (const [name, item] of Object.entries(childReports)) {
const status = String(item.status || "UNKNOWN");
if (status === "PASS" && item.findingCount <= 0 && item.openCount <= 0) continue;
if (/BLOCKED|FAIL|ERROR/i.test(status)) {
findings.push({ source: `child:${name}`, status, severity: "finding", reportRef: item.reportRef });
} else if (/FINDING/i.test(status) || item.findingCount > 0) {
findings.push({ source: `child:${name}`, status, severity: "finding", findingCount: item.findingCount, reportRef: item.reportRef });
} else if (/SKIP|MISSING|PAUSED|PARTIAL/i.test(status) || item.openCount > 0) {
findings.push({ source: `child:${name}`, status, severity: "manual_or_config", openCount: item.openCount, reportRef: item.reportRef });
}
}
let status = "PASS_OA_REAL_USE_DAILY_READY";
if (Number(statusInput || 0) !== 0 || findings.some((item) => item.severity === "blocker")) {
@@ -440,7 +493,7 @@ const summary = {
baseURL,
commit,
checkedAt: new Date().toISOString(),
reportDir: path.resolve(reportDir),
reportRef: reportRef(reportDir),
window: {
durationDays: 7,
scope: "small-team-all-employees",
@@ -479,9 +532,7 @@ const summary = {
actualPaymentChannel: "offline_bank_channel",
contractSensitiveFiles: "do_not_upload_confidential_body_sensitive_quote_cost_detail_to_public_oa",
},
childReports: {
liveReadiness: child("live-smoke-readiness"),
},
childReports,
steps,
readonlyProbe,
findingCount: findings.length,
@@ -498,7 +549,7 @@ const summary = {
};
fs.writeFileSync(path.join(reportDir, "summary.json"), `${JSON.stringify(summary, null, 2)}\n`, "utf8");
console.log(`[oaops1] summary=${path.join(reportDir, "summary.json")}`);
console.log(`[oaops1] summary=${reportRef(path.join(reportDir, "summary.json"))}`);
NODE
}
@@ -506,6 +557,12 @@ finish() {
write_manual_checklist
write_summary_json || STATUS=1
run_privacy_smoke
write_summary_json || true
local before_second_privacy_status="$STATUS"
run_privacy_smoke
if [[ "$STATUS" != "$before_second_privacy_status" ]]; then
write_summary_json || true
fi
log "[oaops1] report=$REPORT_DIR"
exit "$STATUS"
}