Skip to content

Commit 728386e

Browse files
fix(referrals): validate positive cent amounts
1 parent 39794dc commit 728386e

2 files changed

Lines changed: 39 additions & 2 deletions

File tree

src/referrals/index.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,8 +355,16 @@ export function createReferralsRouteHandler(options: ReferralsRouteHandlerOption
355355
const userId = await getUserId(req);
356356
if (!userId) return jsonResponse({ error: "Unauthorized" }, { status: 401 });
357357
const code = typeof body["code"] === "string" ? body["code"] : null;
358-
const amount = typeof body["amount"] === "number" ? body["amount"] : null;
359-
if (!code || !amount) return jsonResponse({ error: "Missing code or amount" }, { status: 400 });
358+
const rawAmount = body["amount"];
359+
const amount =
360+
typeof rawAmount === "number" &&
361+
Number.isSafeInteger(rawAmount) &&
362+
rawAmount > 0
363+
? rawAmount
364+
: null;
365+
if (!code || amount === null) {
366+
return jsonResponse({ error: "Missing code or amount" }, { status: 400 });
367+
}
360368
const usage = await applyReferral({
361369
code,
362370
newUserId: userId,

tests/referrals.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,35 @@ describe("createReferralsRouteHandler (authenticated)", () => {
567567
expect(await missing.json()).toEqual({ error: "Missing code or amount" });
568568
});
569569

570+
it.each([-100, 0, 1.5, Number.MAX_SAFE_INTEGER + 1, "100", null])(
571+
"POST apply rejects invalid cent amount %j without saving a usage",
572+
async (amount) => {
573+
const { store, usages } = makeMemoryStore();
574+
await store.saveCode({
575+
code: "ABC123",
576+
ownerId: "user-1",
577+
createdAt: new Date(),
578+
expiresAt: null,
579+
});
580+
const { POST } = createReferralsRouteHandler({
581+
store,
582+
getUserId: () => "user-2",
583+
});
584+
585+
const res = await POST(
586+
makeReq("https://app.test/api/referrals", {
587+
action: "apply",
588+
code: "ABC123",
589+
amount,
590+
})
591+
);
592+
593+
expect(res.status).toBe(400);
594+
expect(await res.json()).toEqual({ error: "Missing code or amount" });
595+
expect(usages).toHaveLength(0);
596+
}
597+
);
598+
570599
it("POST apply records the usage with the default 60/20 split", async () => {
571600
const { store, usages } = makeMemoryStore();
572601
await store.saveCode({

0 commit comments

Comments
 (0)