Skip to content

Commit cb166ba

Browse files
committed
fix(deno/booking-api): enforce UUID v4 in BOOKING_ID_REGEX
1 parent b906173 commit cb166ba

3 files changed

Lines changed: 50 additions & 8 deletions

File tree

deno/booking-api/postgres-js/handlers.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,14 @@ import type { Sql } from "./db.ts";
3434
const MAX_JSON_BODY_BYTES = 16 * 1024;
3535

3636
/**
37-
* Strict UUID v4 (RFC 4122) path regex for booking IDs. Matching upstream
38-
* returns 404 for obviously malformed IDs without reaching the DB layer.
37+
* Strict UUID v4 (RFC 4122) path regex for booking IDs. Enforces the v4
38+
* version nibble (third group starts with `4`) and variant nibble (fourth
39+
* group starts with `8`, `9`, `a`, or `b`). Aurora DSQL's
40+
* `gen_random_uuid()` emits v4, so valid booking IDs always match; matching
41+
* upstream returns 404 for malformed IDs without reaching the DB layer.
3942
*/
4043
const BOOKING_ID_REGEX =
41-
/^\/bookings\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
44+
/^\/bookings\/[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
4245

4346
// ---------------------------------------------------------------------------
4447
// Types

deno/booking-api/postgres-js/router.property.test.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,11 @@ const VALID_ENDPOINTS = [
4949
const ALL_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
5050

5151
/**
52-
* Generates a valid UUID-like booking ID for parameterized routes.
52+
* Generates a valid UUID v4 booking ID for parameterized routes. Constrained
53+
* to v4 because `handlers.ts` BOOKING_ID_REGEX only accepts v4 (matching
54+
* what Aurora DSQL's `gen_random_uuid()` emits).
5355
*/
54-
const bookingIdArb = fc.uuid();
56+
const bookingIdArb = fc.uuid({ version: 4 });
5557

5658
/**
5759
* Generates a random path that does NOT match any booking endpoint.
@@ -161,6 +163,41 @@ Deno.test("property: wrong method on valid path returns 404", async () => {
161163
);
162164
});
163165

166+
Deno.test("property: non-v4 UUID shapes on /bookings/:id return 404", async () => {
167+
// The router's BOOKING_ID_REGEX enforces UUID v4 (third group starts with
168+
// `4`, fourth with `[89ab]`). Aurora DSQL's gen_random_uuid() always emits
169+
// v4, so anything else is malformed input and is rejected upstream without
170+
// touching the DB. This property covers v1/v3/v5 and the NIL UUID.
171+
const nonV4UuidArb = fc.oneof(
172+
fc.uuid({ version: 1 }),
173+
fc.uuid({ version: 3 }),
174+
fc.uuid({ version: 5 }),
175+
fc.constant("00000000-0000-0000-0000-000000000000"),
176+
);
177+
178+
await fc.assert(
179+
fc.asyncProperty(
180+
fc.constantFrom("GET", "PUT", "DELETE"),
181+
nonV4UuidArb,
182+
async (method, id) => {
183+
const opts: RequestInit = { method };
184+
if (method === "PUT") {
185+
opts.body = JSON.stringify({ booked_by: "test" });
186+
opts.headers = { "Content-Type": "application/json" };
187+
}
188+
189+
const req = new Request(`http://localhost:8000/bookings/${id}`, opts);
190+
const res = await handleRequest(req, CTX);
191+
assertEquals(res.status, 404);
192+
193+
const body = await res.json();
194+
assertEquals(body.error, "Not Found");
195+
},
196+
),
197+
{ numRuns: 50 },
198+
);
199+
});
200+
164201
Deno.test("property: jsonResponse always produces valid JSON with correct content-type", () => {
165202
fc.assert(
166203
fc.property(

deno/booking-api/postgres-js/validation.property.test.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,11 @@ Deno.test("property: POST /bookings rejects malformed JSON with 400", async () =
8383
});
8484

8585
Deno.test("property: PUT /bookings/:id rejects malformed JSON with 400", async () => {
86-
// Use a well-formed UUID so the router dispatches to updateBooking;
87-
// the body parser then rejects malformed JSON with HTTP 400.
88-
const validUuid = "00000000-0000-0000-0000-000000000000";
86+
// Use a well-formed UUID v4 so the router dispatches to updateBooking;
87+
// the body parser then rejects malformed JSON with HTTP 400. The third
88+
// group starts with `4` (version) and the fourth with `8` (variant) to
89+
// satisfy BOOKING_ID_REGEX in handlers.ts.
90+
const validUuid = "00000000-0000-4000-8000-000000000000";
8991
await fc.assert(
9092
fc.asyncProperty(nonJsonStringArb, async (badBody) => {
9193
const req = new Request(

0 commit comments

Comments
 (0)