Files
edr-platform/apps/edr-passenger-api/test/auth-gaps.e2e-spec.ts
Muluhabt c4f54a666b test: add EDR passenger pricing/config E2E bug-hunt harness
Hermetic E2E harness targeting pricing integrity and backoffice config:
- e2e/ docker Postgres (5544) + prepare.sh/run.sh one-command runner + HTML report
- 6 suites / 23 tests reproducing pricing, FX, wallet, refund, config and auth
  defects (see docs/ISSUES.md); docs/e2e-test-matrix.md documents the matrix
- two-tier harness (slim module boot + direct service instantiation) to work
  around the IAM/RabbitMQ/file-type boot wall
- .env.test.example tracked; loader falls back to it for fresh checkouts

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 16:22:40 +03:00

30 lines
1.4 KiB
TypeScript

/**
* 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);
});
});