fix(medications): bound the RxNorm code length

The RxCUI field validates against `^\d+$`, which is unbounded — a caller
could submit an arbitrarily long digit string. Add a `.max(20)` ceiling
as defence-in-depth so the column write stays tight; real RxCUIs are at
most a handful of digits. `atcCode` needs no max — its regex already
fixes the length. Regenerate the OpenAPI contract for the new
`maxLength` on both the create and update bodies.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(cherry picked from commit 8b97d35bc375ef3b2e70f938c0d6729a5fa6bccb)
This commit is contained in:
Marc-André Bombeck committed 2026-06-02 17:34:52 +02:00
1 parent bfdf0a12a0
commit f0f2caab79
3 files changed
+25

No files matched your search

+2
View File
@@ -2564,6 +2564,7 @@ components:
NULL clears it; absent leaves it untouched.
anyOf:
- type: string
maxLength: 20
pattern: ^\d+$
- type: "null"
startsOn:
@@ -2782,6 +2783,7 @@ components:
NULL clears it; absent leaves it untouched.
anyOf:
- type: string
maxLength: 20
pattern: ^\d+$
- type: "null"
startsOn:
@@ -71,6 +71,24 @@ describe("medication drug-code validation", () => {
.success,
).toBe(false);
});
it("rejects an over-long RxNorm code (defence-in-depth length bound)", () => {
// The `^\d+$` regex alone is unbounded; the `.max(20)` ceiling
// rejects an absurd digit string while still admitting real RxCUIs.
expect(
createMedicationSchema.safeParse({
...baseCreate,
rxNormCode: "1".repeat(21),
}).success,
).toBe(false);
// 20 digits is the boundary and stays valid.
expect(
createMedicationSchema.safeParse({
...baseCreate,
rxNormCode: "1".repeat(20),
}).success,
).toBe(true);
});
});
describe("updateMedicationSchema", () => {
+5
View File
@@ -51,6 +51,11 @@ const atcCodeField = z
const rxNormCodeField = z
.string()
.regex(RXCUI_REGEX, "Invalid RxNorm code (expected a numeric RxCUI)")
// Defence-in-depth: the `^\d+$` regex is otherwise unbounded. Real
// RxCUIs are at most 7 digits today; 20 is a generous ceiling that
// still bounds the column write tightly. (`atcCode` needs no max —
// its regex already fixes the length at 7.)
.max(20, "RxNorm code is too long")
.nullable()
.optional()
.describe(