@edgehero/pi-dispatch-receiver 4.3.0 → 5.0.0

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.
package/src/http-body.mjs DELETED
@@ -1,73 +0,0 @@
1
- /**
2
- * The forge-neutral half of a webhook endpoint: read a bounded raw body, recognise a JSON content type,
3
- * and write a small JSON response.
4
- *
5
- * Extracted from verify.mjs when a second forge arrived (issue #42), and extracted rather than copied for
6
- * one reason: `readRawBody`'s per-chunk accounting is a memory bound on attacker-controlled input. Two
7
- * copies of a security limit drift -- one gets the fix, the other keeps the bug -- and the copy that keeps
8
- * it is the one nobody is looking at. Verification itself is NOT here; each forge's gate owns its own,
9
- * because that is exactly the part that differs.
10
- *
11
- * Nothing here reads a header that identifies a forge, and nothing here decides whether a request is
12
- * trusted. A caller must still verify before parsing a single field (CONST-HMAC-OVER-RAW-BODY).
13
- */
14
-
15
- /** Accept `application/json`, ignoring any parameters and case. */
16
- export function isJsonContentType(contentType) {
17
- return String(contentType ?? "").split(";")[0].trim().toLowerCase() === "application/json";
18
- }
19
-
20
- /**
21
- * Collect the request stream into a Buffer, bounded by `limit`. Accumulation is checked on every
22
- * chunk so the buffer can never grow past the cap: exceeding it destroys the request and rejects
23
- * with `code: "E_PAYLOAD_TOO_LARGE"` rather than reading an unbounded body into memory.
24
- */
25
- export function readRawBody(req, limit) {
26
- return new Promise((resolve, reject) => {
27
- const chunks = [];
28
- let size = 0;
29
- let settled = false;
30
-
31
- const cleanup = () => {
32
- req.removeListener("data", onData);
33
- req.removeListener("end", onEnd);
34
- req.removeListener("error", onError);
35
- };
36
- const onData = (chunk) => {
37
- if (settled) return;
38
- size += chunk.length;
39
- if (size > limit) {
40
- settled = true;
41
- cleanup();
42
- req.destroy();
43
- const err = new Error("payload too large");
44
- err.code = "E_PAYLOAD_TOO_LARGE";
45
- reject(err);
46
- return;
47
- }
48
- chunks.push(chunk);
49
- };
50
- const onEnd = () => {
51
- if (settled) return;
52
- settled = true;
53
- cleanup();
54
- resolve(Buffer.concat(chunks));
55
- };
56
- const onError = (err) => {
57
- if (settled) return;
58
- settled = true;
59
- cleanup();
60
- reject(err);
61
- };
62
-
63
- req.on("data", onData);
64
- req.on("end", onEnd);
65
- req.on("error", onError);
66
- });
67
- }
68
-
69
- /** Write a small JSON body with an explicit status. `writeHead` sets the status code on the response. */
70
- export function respond(res, status, body) {
71
- res.writeHead(status, { "content-type": "application/json" });
72
- res.end(JSON.stringify(body));
73
- }