/** * Auth/authorization gaps (matrix Suite J), proven via route guard metadata — no boot needed. * J1 🔴 The exchange-rate controller's write routes (PUT upsert, PATCH update) carry NO guard, * so USD/ETB/DJF rates — which every international fare multiplies by — can be rewritten by * an unauthenticated caller. Only DELETE is guarded (@PassengerAdmin). fare-engine/currency.controller.ts:25,32,42 */ import "reflect-metadata"; import { CurrencyController } from "../src/modules/fare-engine/currency.controller"; // Nest stores @UseGuards under the "__guards__" metadata key on the route handler. const GUARDS_METADATA = "__guards__"; function guardsOn(handler: unknown): unknown[] { return (Reflect.getMetadata(GUARDS_METADATA, handler as object) as unknown[]) ?? []; } describe("Auth gaps (Suite J)", () => { it("J1 🔴 PUT upsert exchange-rate has NO guard (unauthenticated FX write)", () => { expect(guardsOn(CurrencyController.prototype.upsert)).toHaveLength(0); }); it("J1 🔴 PATCH update exchange-rate has NO guard (unauthenticated FX write)", () => { expect(guardsOn(CurrencyController.prototype.update)).toHaveLength(0); }); it("J1 control: DELETE exchange-rate IS guarded — proving the omission on writes is not global", () => { expect(guardsOn(CurrencyController.prototype.remove).length).toBeGreaterThan(0); }); });