next-evolve 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -19,5 +19,7 @@ export declare function sharedReplayGuard(): ReplayGuard;
|
|
|
19
19
|
* a fresh id within the signature window (T12.5).
|
|
20
20
|
*/
|
|
21
21
|
export declare function firstDelivery(guard: ReplayGuard, delivery: string | null, t: number, body: string): boolean;
|
|
22
|
+
/** True the first time this signed message (`t` + body) is seen in the replay window. */
|
|
23
|
+
export declare function firstMessage(guard: ReplayGuard, t: number, body: string): boolean;
|
|
22
24
|
export declare function handleAggregate(req: Request, deps: AggregateRouteDeps): Promise<Response>;
|
|
23
25
|
export {};
|
|
@@ -17,7 +17,10 @@ function sharedReplayGuard() {
|
|
|
17
17
|
* a fresh id within the signature window (T12.5).
|
|
18
18
|
*/
|
|
19
19
|
function firstDelivery(guard, delivery, t, body) {
|
|
20
|
-
|
|
20
|
+
return !!delivery && guard.check(delivery) && firstMessage(guard, t, body);
|
|
21
|
+
}
|
|
22
|
+
/** True the first time this signed message (`t` + body) is seen in the replay window. */
|
|
23
|
+
function firstMessage(guard, t, body) {
|
|
21
24
|
return guard.check(`msg:${t}:${createHash("sha256").update(body).digest("base64url")}`);
|
|
22
25
|
}
|
|
23
26
|
const json = (status, body, headers = {}) => Response.json(body, {
|
|
@@ -75,4 +78,4 @@ async function handleAggregate(req, deps) {
|
|
|
75
78
|
}
|
|
76
79
|
});
|
|
77
80
|
}
|
|
78
|
-
export { firstDelivery, handleAggregate, sharedReplayGuard };
|
|
81
|
+
export { firstDelivery, firstMessage, handleAggregate, sharedReplayGuard };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logRateLimited } from "../core/log.js";
|
|
2
|
-
import {
|
|
2
|
+
import { firstMessage, sharedReplayGuard } from "./aggregate-route.js";
|
|
3
3
|
import { DELIVERY_HEADER, SIGNATURE_HEADER, SUPPORTED_PROTOCOLS, isSupportedProtocol, parseSecretKey, syncWebhookSchema, unsupportedProtocolBody, verifyBody } from "@next-evolve/protocol";
|
|
4
4
|
// src/server/sync-route.ts
|
|
5
5
|
/** Cache tag on the render-side bundle fetch (spec §7.7). */
|
|
@@ -32,7 +32,8 @@ async function handleSync(req, deps) {
|
|
|
32
32
|
reason: sig.reason
|
|
33
33
|
});
|
|
34
34
|
const guard = deps.replay ?? sharedReplayGuard();
|
|
35
|
-
|
|
35
|
+
const delivery = req.headers.get(DELIVERY_HEADER);
|
|
36
|
+
if (!delivery || !guard.check(delivery)) return json(409, { error: "replayed" });
|
|
36
37
|
let parsed;
|
|
37
38
|
try {
|
|
38
39
|
parsed = JSON.parse(body);
|
|
@@ -49,6 +50,7 @@ async function handleSync(req, deps) {
|
|
|
49
50
|
protocol: { supported: SUPPORTED_PROTOCOLS }
|
|
50
51
|
});
|
|
51
52
|
if (r.data.site !== site) return json(403, { error: "site_mismatch" });
|
|
53
|
+
if (!firstMessage(guard, sig.t, body)) return json(409, { error: "replayed" });
|
|
52
54
|
if (!(!current || r.data.ver > current.ver || r.data.ver === current.ver && r.data.iat > current.iat)) return json(202, {
|
|
53
55
|
accepted: false,
|
|
54
56
|
reason: "current"
|