146 行
4.8 KiB
JavaScript
146 行
4.8 KiB
JavaScript
const fs = require("node:fs/promises");
|
|
const path = require("node:path");
|
|
|
|
const {
|
|
buildCallbackConfigCheck,
|
|
executeCallbackPoc,
|
|
parseArgs,
|
|
runSyntheticCheck,
|
|
runUrlVerificationCheck,
|
|
} = require("../../../src/scripts/check-wecom-approval-callback-poc");
|
|
|
|
const TOKEN = "callback-token";
|
|
const AES_KEY = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG";
|
|
const CORP_ID = "ww-callback-corp";
|
|
const SECRET_VALUE = "secret-value-should-not-leak";
|
|
|
|
function baseConfig(overrides = {}) {
|
|
return {
|
|
wecomCorpId: CORP_ID,
|
|
wecomApprovalCallbackUrl: "https://oa.hyinda.com/api/wecom/approval/callback",
|
|
wecomCallbackToken: TOKEN,
|
|
wecomCallbackEncodingAesKey: AES_KEY,
|
|
wecomApprovalCallbackEvidenceFile: path.join("/tmp", `wcbk1-script-${Date.now()}-${Math.random()}.jsonl`),
|
|
wecomRequestTimeoutMs: 1000,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function textResponse(body, status = 200) {
|
|
return {
|
|
ok: status >= 200 && status < 300,
|
|
status,
|
|
text: async () => body,
|
|
};
|
|
}
|
|
|
|
describe("check-wecom-approval-callback-poc", () => {
|
|
it("parses CLI options", () => {
|
|
const options = parseArgs([
|
|
"--live",
|
|
"--format=json",
|
|
"--callback-url=https://oa.hyinda.com/api/wecom/approval/callback",
|
|
"--report-dir=/tmp/wcbk1",
|
|
]);
|
|
|
|
expect(options).toMatchObject({
|
|
live: true,
|
|
format: "json",
|
|
callbackUrl: "https://oa.hyinda.com/api/wecom/approval/callback",
|
|
reportDir: "/tmp/wcbk1",
|
|
});
|
|
expect(() => parseArgs(["--format=xml"])).toThrow(/unsupported format/);
|
|
});
|
|
|
|
it("classifies missing callback config as NEEDS_CONFIG", () => {
|
|
const check = buildCallbackConfigCheck(baseConfig({ wecomCallbackToken: "", wecomApprovalCallbackUrl: "" }), "");
|
|
|
|
expect(check.status).toBe("NEEDS_CONFIG");
|
|
expect(check.missing).toEqual(expect.arrayContaining([
|
|
"WECOM_APPROVAL_CALLBACK_URL",
|
|
"WECOM_CALLBACK_TOKEN",
|
|
]));
|
|
expect(JSON.stringify(check)).not.toContain(SECRET_VALUE);
|
|
});
|
|
|
|
it("runs synthetic crypto check without leaking raw identifiers", () => {
|
|
const check = runSyntheticCheck(baseConfig());
|
|
|
|
expect(check.status).toBe("OK");
|
|
expect(check.summary).toMatchObject({
|
|
eventType: "open_approval_change",
|
|
approvalType: "leave",
|
|
statusLabel: "已通过",
|
|
});
|
|
expect(JSON.stringify(check)).not.toContain("SPWCBK1SYNTHETIC000001");
|
|
expect(JSON.stringify(check)).not.toContain("TPLWCBK1SYNTHETIC000001");
|
|
});
|
|
|
|
it("verifies live callback URL with generated echostr", async () => {
|
|
const fetchImpl = vi.fn(async (url) => {
|
|
expect(String(url)).toContain("msg_signature=");
|
|
expect(String(url)).not.toContain(TOKEN);
|
|
expect(String(url)).not.toContain(AES_KEY);
|
|
return textResponse("wcbk1-url-verify-ok");
|
|
});
|
|
|
|
const check = await runUrlVerificationCheck({
|
|
config: baseConfig(),
|
|
callbackUrl: "https://oa.hyinda.com/api/wecom/approval/callback",
|
|
fetchImpl,
|
|
timeoutMs: 1000,
|
|
});
|
|
|
|
expect(check).toMatchObject({
|
|
status: "OK",
|
|
matched: true,
|
|
callbackUrlPreview: "https://oa.hyinda.com/api/wecom/approval/callback",
|
|
});
|
|
});
|
|
|
|
it("returns PARTIAL_CALLBACK_NEEDS_CONFIG when real callback config is missing", async () => {
|
|
const report = await executeCallbackPoc({
|
|
live: false,
|
|
format: "json",
|
|
}, {
|
|
config: baseConfig({
|
|
wecomApprovalCallbackUrl: "",
|
|
wecomCallbackToken: "",
|
|
wecomCallbackEncodingAesKey: "",
|
|
}),
|
|
});
|
|
|
|
expect(report.status).toBe("PARTIAL_CALLBACK_NEEDS_CONFIG");
|
|
expect(report.checks.synthetic.status).toBe("OK");
|
|
expect(report.configGaps.map((item) => item.name)).toEqual(expect.arrayContaining([
|
|
"WECOM_APPROVAL_CALLBACK_URL",
|
|
"WECOM_CALLBACK_TOKEN",
|
|
"WECOM_CALLBACK_ENCODING_AES_KEY",
|
|
]));
|
|
});
|
|
|
|
it("returns PASS_CALLBACK_EVENT_CONFIRMED when sanitized event evidence exists", async () => {
|
|
const config = baseConfig();
|
|
await fs.mkdir(path.dirname(config.wecomApprovalCallbackEvidenceFile), { recursive: true });
|
|
await fs.writeFile(config.wecomApprovalCallbackEvidenceFile, `${JSON.stringify({
|
|
recordedAt: "2026-05-19T06:00:00.000Z",
|
|
eventType: "open_approval_change",
|
|
callbackKind: "approval_status_change",
|
|
approvalType: "leave",
|
|
spStatus: "2",
|
|
statusLabel: "已通过",
|
|
spNoPrefix: "SPFULLID***",
|
|
templateIdPrefix: "TPLFULLI***",
|
|
idempotencyKey: "idem",
|
|
})}\n`);
|
|
const fetchImpl = vi.fn(async () => textResponse("wcbk1-url-verify-ok"));
|
|
|
|
const report = await executeCallbackPoc({ live: true }, { config, fetchImpl });
|
|
|
|
expect(report.status).toBe("PASS_CALLBACK_EVENT_CONFIRMED");
|
|
expect(report.checks.eventEvidence.eventCount).toBe(1);
|
|
expect(JSON.stringify(report)).not.toContain(TOKEN);
|
|
expect(JSON.stringify(report)).not.toContain(AES_KEY);
|
|
});
|
|
});
|