文件
hyb-oa/scripts/qa/check-sensitive-recovery-contract.cjs
wangsli 1f8d5b658a 完善借款、编号、付款与预算并发及验收(09-06-release-gate-integrity)
汇总已落盘的借款本金与还款、兼容历史编号的周期化编号、付款责任与回单核验、报销进度和排序、PWA/采购事务、错误原因与日期校验及ERP退役。
统一共享预算池锁序和锁后判断,恢复差旅原预算选择,补原因码固定夹具及反向完整性检查。
纳入配套迁移、测试、受管QA、分层验收合同、规约及任务记录;原始证据留在ignored artifacts。

关联任务:09-17-company-loan-principal-repayment、09-17-erp-exclusive-retirement、09-18-reimbursement-periodic-numbering、09-08-reimbursement-payment-closeout、09-18-local-concurrency-product-fixes、09-19-budget-pool-concurrency、09-05-error-explainability-payment-readiness。
预算补审由Claude定向验证2文件928项通过,原4446/28及r6结果仍仅对应各自历史输入;本轮未重跑测试。
完整新候选门禁、R1真实入口接入与12项公网义务仍开放。生产编号未启用、差旅仍冻结;不推送、不部署,不宣称发布资格。
2026-09-19 13:11:04 +08:00

92 行
3.6 KiB
JavaScript

#!/usr/bin/env node
// K0 only: synthetic, in-memory inputs. No env/config/backup loader or K2 entry.
const { randomBytes } = require("node:crypto");
const {
resolveFieldEncryptionKey,
encryptStringOrThrow,
decryptStringOrThrow,
} = require("../../server/src/services/crypto");
function result(status, reason) {
return { status, reason };
}
// The expected value belongs to a synthetic fixture, never a real-data probe.
// Return fixed categories only, including when crypto throws or returns null.
function checkSyntheticSample(config, encrypted, expectedPlaintext) {
try {
const key = resolveFieldEncryptionKey(config);
if (!key) return result("STOP", "KEY_UNAVAILABLE_OR_INVALID");
key.fill(0);
if (encrypted === null || encrypted === undefined || encrypted === "") {
return result("NOT_TESTED", "EMPTY_FIELD");
}
if (typeof encrypted !== "string") return result("STOP", "INVALID_PAYLOAD");
if (typeof expectedPlaintext !== "string" || !expectedPlaintext) {
return result("STOP", "SYNTHETIC_EXPECTATION_REQUIRED");
}
const plaintext = decryptStringOrThrow(config, encrypted);
if (plaintext === null) return result("STOP", "INVALID_PAYLOAD");
if (plaintext !== expectedPlaintext) return result("STOP", "PLAINTEXT_MISMATCH");
return result("PASS", "SYNTHETIC_MATCH");
} catch {
// Never call decryptStringSafe: its warning can contain a raw Error object.
return result("STOP", "DECRYPTION_FAILED");
}
}
function runSyntheticContract() {
const key = randomBytes(32);
const otherKey = Buffer.from(key);
otherKey[0] ^= 1;
try {
const config = { fieldEncryptionKey: key.toString("base64") };
const wrongConfig = { fieldEncryptionKey: otherKey.toString("base64") };
const plaintext = `synthetic-only-${randomBytes(16).toString("hex")}`;
const encrypted = encryptStringOrThrow(config, plaintext);
const parts = encrypted.split(":");
const tag = Buffer.from(parts[2], "base64url");
tag[0] ^= 1;
parts[2] = tag.toString("base64url");
const cases = [
["correct", config, encrypted, "PASS", "SYNTHETIC_MATCH"],
["missing", {}, encrypted, "STOP", "KEY_UNAVAILABLE_OR_INVALID"],
["invalid_key", { fieldEncryptionKey: "invalid" }, encrypted, "STOP", "KEY_UNAVAILABLE_OR_INVALID"],
["wrong", wrongConfig, encrypted, "STOP", "DECRYPTION_FAILED"],
["tampered", config, parts.join(":"), "STOP", "DECRYPTION_FAILED"],
["empty", config, "", "NOT_TESTED", "EMPTY_FIELD"],
["malformed", config, "v1:invalid", "STOP", "INVALID_PAYLOAD"],
];
const checks = cases.map(([category, inputConfig, payload, status, reason]) => {
const actual = checkSyntheticSample(inputConfig, payload, plaintext);
return { category, ...actual, matched: actual.status === status && actual.reason === reason };
});
return {
scope: "K0_SYNTHETIC_ONLY",
status: checks.every((check) => check.matched) ? "PASS" : "FAIL",
checks,
originalKeySource: "UNKNOWN",
custody: "NOT_TESTED",
realDataDecryption: "NOT_TESTED",
};
} finally {
key.fill(0);
otherKey.fill(0);
}
}
if (require.main === module) {
let report;
try {
report = process.argv.length === 2
? runSyntheticContract()
: { scope: "K0_SYNTHETIC_ONLY", status: "FAIL", reason: "ARGUMENTS_NOT_ALLOWED" };
} catch {
report = { scope: "K0_SYNTHETIC_ONLY", status: "FAIL", reason: "SYNTHETIC_CHECK_FAILED" };
}
process.stdout.write(`${JSON.stringify(report)}\n`);
process.exitCode = report.status === "PASS" ? 0 : 1;
}
module.exports = { checkSyntheticSample, runSyntheticContract };