HaiderMalik12/nestjs-fundamentals
2 critical findings to fix first.
15 errors and 63 warnings in the paths between 58 routes and 15 tables.
- ROUTES
- 58
- TABLES
- 15
- FILES READ
- 1616/1616
- RULES RUN
- 12/12
78 findings
2 critical13 high33 medium30 low
POST /playlists can reach users without authenticating.
Anyone, signed in or not, can read users (email, password) — personal or secret fields.
- Data reached
users(read) · sensitive:email, password- Missing
- Authentication before the handler reaches data, and a query scoped to the caller.
- Confidence
- No middleware, guard or wrapper is applied to this route anywhere in module-06-relations/lesson-02-and-lesson-03/src/playlists/playlists.controller.ts.
- Path
- POST /playlists → users
Prove it
# No cookie, no Authorization header. curl -i -X POST 'https://YOUR_API/playlists' \ -H 'content-type: application/json' -d '{}' # Expect 401. A 2xx means anyone on the internet can do this.Fix
@UseGuards(AuthGuard("jwt")) @Post("/playlists") handler(@Req() req) { // ...scope every query to req.user.id }Regression test
import request from "supertest"; test("POST /playlists rejects a signed-out caller", async () => { const res = await request(app).post("/playlists"); expect(res.status).toBe(401); });Intended? This cannot be declared intentional: public sensitive data is not a design choice the check will bless. Accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.auth-before-dataPOST /applications can reach Customer without authenticating.
Anyone, signed in or not, can write to Customer (email, addressId) — personal or secret fields.
- Data reached
Customer(write) · sensitive:email, addressId- Missing
- Authentication before the handler reaches data, and a query scoped to the caller.
- Confidence
- No middleware, guard or wrapper is applied to this route anywhere in module-20-prisma-integration/lesson-08/src/applications/applications.controller.ts.
- Path
- POST /applications → Customer
Prove it
# No cookie, no Authorization header. curl -i -X POST 'https://YOUR_API/applications' \ -H 'content-type: application/json' -d '{}' # Expect 401. A 2xx means anyone on the internet can do this.Fix
@UseGuards(AuthGuard("jwt")) @Post("/applications") handler(@Req() req) { // ...scope every query to req.user.id }Regression test
import request from "supertest"; test("POST /applications rejects a signed-out caller", async () => { const res = await request(app).post("/applications"); expect(res.status).toBe(401); });Intended? This cannot be declared intentional: a public write is not a design choice the check will bless. Accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.auth-before-dataPOST /playlists can reach playlists without authenticating.
An unauthenticated caller can write to playlists. Writes are how data gets corrupted or planted.
- Data reached
playlists(write)- Missing
- Authentication before the handler reaches data, and a query scoped to the caller.
- Confidence
- No middleware, guard or wrapper is applied to this route anywhere in module-06-relations/lesson-02-and-lesson-03/src/playlists/playlists.controller.ts.
- Path
- POST /playlists → playlists
Prove it
# No cookie, no Authorization header. curl -i -X POST 'https://YOUR_API/playlists' \ -H 'content-type: application/json' -d '{}' # Expect 401. A 2xx means anyone on the internet can do this.Fix
@UseGuards(AuthGuard("jwt")) @Post("/playlists") handler(@Req() req) { // ...scope every query to req.user.id }Regression test
import request from "supertest"; test("POST /playlists rejects a signed-out caller", async () => { const res = await request(app).post("/playlists"); expect(res.status).toBe(401); });Intended? This cannot be declared intentional: a public write is not a design choice the check will bless. Accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.auth-before-dataPATCH /songs/:id can reach Song without authenticating.
An unauthenticated caller can write to Song. Writes are how data gets corrupted or planted.
- Data reached
Song(write)- Missing
- Authentication before the handler reaches data, and a query scoped to the caller.
- Confidence
- No middleware, guard or wrapper is applied to this route anywhere in module-20-prisma-integration/lesson-04-and-05/src/songs/songs.controller.ts.
- Path
- PATCH /songs/:id → Song
Prove it
# No cookie, no Authorization header. curl -i -X PATCH 'https://YOUR_API/songs/1' \ -H 'content-type: application/json' -d '{}' # Expect 401. A 2xx means anyone on the internet can do this.Fix
@UseGuards(AuthGuard("jwt")) @Patch("/songs/:id") handler(@Req() req) { // ...scope every query to req.user.id }Regression test
import request from "supertest"; test("PATCH /songs/:id rejects a signed-out caller", async () => { const res = await request(app).patch("/songs/1"); expect(res.status).toBe(401); });Intended? This cannot be declared intentional: a public write is not a design choice the check will bless. Accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.auth-before-dataPOST /artists can reach Artist without authenticating.
An unauthenticated caller can write to Artist. Writes are how data gets corrupted or planted.
- Data reached
Artist(write)- Missing
- Authentication before the handler reaches data, and a query scoped to the caller.
- Confidence
- No middleware, guard or wrapper is applied to this route anywhere in module-20-prisma-integration/lesson-06/src/artists/artists.controller.ts.
- Path
- POST /artists → Artist
Prove it
# No cookie, no Authorization header. curl -i -X POST 'https://YOUR_API/artists' \ -H 'content-type: application/json' -d '{}' # Expect 401. A 2xx means anyone on the internet can do this.Fix
@UseGuards(AuthGuard("jwt")) @Post("/artists") handler(@Req() req) { // ...scope every query to req.user.id }Regression test
import request from "supertest"; test("POST /artists rejects a signed-out caller", async () => { const res = await request(app).post("/artists"); expect(res.status).toBe(401); });Intended? This cannot be declared intentional: a public write is not a design choice the check will bless. Accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.auth-before-dataPOST /users can reach User without authenticating.
An unauthenticated caller can write to User. Writes are how data gets corrupted or planted.
- Data reached
User(write)- Missing
- Authentication before the handler reaches data, and a query scoped to the caller.
- Confidence
- No middleware, guard or wrapper is applied to this route anywhere in module-20-prisma-integration/lesson-07/src/users/users.controller.ts.
- Path
- POST /users → User
Prove it
# No cookie, no Authorization header. curl -i -X POST 'https://YOUR_API/users' \ -H 'content-type: application/json' -d '{}' # Expect 401. A 2xx means anyone on the internet can do this.Fix
@UseGuards(AuthGuard("jwt")) @Post("/users") handler(@Req() req) { // ...scope every query to req.user.id }Regression test
import request from "supertest"; test("POST /users rejects a signed-out caller", async () => { const res = await request(app).post("/users"); expect(res.status).toBe(401); });Intended? This cannot be declared intentional: a public write is not a design choice the check will bless. Accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.auth-before-dataGET /users can reach User without authenticating.
Anyone can read User.
- Data reached
User(read)- Missing
- Authentication before the handler reaches data, and a query scoped to the caller.
- Confidence
- No middleware, guard or wrapper is applied to this route anywhere in module-20-prisma-integration/lesson-07/src/users/users.controller.ts.
- Path
- GET /users → User
Prove it
# No cookie, no Authorization header. curl -i -X GET 'https://YOUR_API/users' # Expect 401. A 2xx means anyone on the internet can do this.Fix
@UseGuards(AuthGuard("jwt")) @Get("/users") handler(@Req() req) { // ...scope every query to req.user.id }Regression test
import request from "supertest"; test("GET /users rejects a signed-out caller", async () => { const res = await request(app).get("/users"); expect(res.status).toBe(401); });Intended? If this data is meant to be public (a catalogue, published posts), that is a legitimate design. Accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.auth-before-dataPOST /posts can reach Post without authenticating.
An unauthenticated caller can write to Post. Writes are how data gets corrupted or planted.
- Data reached
Post(write)- Missing
- Authentication before the handler reaches data, and a query scoped to the caller.
- Confidence
- No middleware, guard or wrapper is applied to this route anywhere in module-20-prisma-integration/lesson-08/src/posts/posts.controller.ts.
- Path
- POST /posts → Post
Prove it
# No cookie, no Authorization header. curl -i -X POST 'https://YOUR_API/posts' \ -H 'content-type: application/json' -d '{}' # Expect 401. A 2xx means anyone on the internet can do this.Fix
@UseGuards(AuthGuard("jwt")) @Post("/posts") handler(@Req() req) { // ...scope every query to req.user.id }Regression test
import request from "supertest"; test("POST /posts rejects a signed-out caller", async () => { const res = await request(app).post("/posts"); expect(res.status).toBe(401); });Intended? This cannot be declared intentional: a public write is not a design choice the check will bless. Accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.auth-before-dataGET /posts can reach Post without authenticating.
Anyone can read Post.
- Data reached
Post(read)- Missing
- Authentication before the handler reaches data, and a query scoped to the caller.
- Confidence
- No middleware, guard or wrapper is applied to this route anywhere in module-20-prisma-integration/lesson-08/src/posts/posts.controller.ts.
- Path
- GET /posts → Post
Prove it
# No cookie, no Authorization header. curl -i -X GET 'https://YOUR_API/posts' # Expect 401. A 2xx means anyone on the internet can do this.Fix
@UseGuards(AuthGuard("jwt")) @Get("/posts") handler(@Req() req) { // ...scope every query to req.user.id }Regression test
import request from "supertest"; test("GET /posts rejects a signed-out caller", async () => { const res = await request(app).get("/posts"); expect(res.status).toBe(401); });Intended? If this data is meant to be public (a catalogue, published posts), that is a legitimate design. Accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.auth-before-dataGET /sequential can reach Song without authenticating.
Anyone can read Song.
- Data reached
Song(read)- Missing
- Authentication before the handler reaches data, and a query scoped to the caller.
- Confidence
- No middleware, guard or wrapper is applied to this route anywhere in module-20-prisma-integration/lesson-09/src/app.controller.ts.
- Path
- GET /sequential → Song
Prove it
# No cookie, no Authorization header. curl -i -X GET 'https://YOUR_API/sequential' # Expect 401. A 2xx means anyone on the internet can do this.Fix
@UseGuards(AuthGuard("jwt")) @Get("/sequential") handler(@Req() req) { // ...scope every query to req.user.id }Regression test
import request from "supertest"; test("GET /sequential rejects a signed-out caller", async () => { const res = await request(app).get("/sequential"); expect(res.status).toBe(401); });Intended? If this data is meant to be public (a catalogue, published posts), that is a legitimate design. Accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.auth-before-dataGET /sequential can reach Artist without authenticating.
Anyone can read Artist.
- Data reached
Artist(read)- Missing
- Authentication before the handler reaches data, and a query scoped to the caller.
- Confidence
- No middleware, guard or wrapper is applied to this route anywhere in module-20-prisma-integration/lesson-09/src/app.controller.ts.
- Path
- GET /sequential → Artist
Prove it
# No cookie, no Authorization header. curl -i -X GET 'https://YOUR_API/sequential' # Expect 401. A 2xx means anyone on the internet can do this.Fix
@UseGuards(AuthGuard("jwt")) @Get("/sequential") handler(@Req() req) { // ...scope every query to req.user.id }Regression test
import request from "supertest"; test("GET /sequential rejects a signed-out caller", async () => { const res = await request(app).get("/sequential"); expect(res.status).toBe(401); });Intended? If this data is meant to be public (a catalogue, published posts), that is a legitimate design. Accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.auth-before-dataGET /sequential can reach Post without authenticating.
Anyone can read Post.
- Data reached
Post(read)- Missing
- Authentication before the handler reaches data, and a query scoped to the caller.
- Confidence
- No middleware, guard or wrapper is applied to this route anywhere in module-20-prisma-integration/lesson-09/src/app.controller.ts.
- Path
- GET /sequential → Post
Prove it
# No cookie, no Authorization header. curl -i -X GET 'https://YOUR_API/sequential' # Expect 401. A 2xx means anyone on the internet can do this.Fix
@UseGuards(AuthGuard("jwt")) @Get("/sequential") handler(@Req() req) { // ...scope every query to req.user.id }Regression test
import request from "supertest"; test("GET /sequential rejects a signed-out caller", async () => { const res = await request(app).get("/sequential"); expect(res.status).toBe(401); });Intended? If this data is meant to be public (a catalogue, published posts), that is a legitimate design. Accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.auth-before-dataGET /sequential can reach Application without authenticating.
Anyone can read Application.
- Data reached
Application(read)- Missing
- Authentication before the handler reaches data, and a query scoped to the caller.
- Confidence
- No middleware, guard or wrapper is applied to this route anywhere in module-20-prisma-integration/lesson-09/src/app.controller.ts.
- Path
- GET /sequential → Application
Prove it
# No cookie, no Authorization header. curl -i -X GET 'https://YOUR_API/sequential' # Expect 401. A 2xx means anyone on the internet can do this.Fix
@UseGuards(AuthGuard("jwt")) @Get("/sequential") handler(@Req() req) { // ...scope every query to req.user.id }Regression test
import request from "supertest"; test("GET /sequential rejects a signed-out caller", async () => { const res = await request(app).get("/sequential"); expect(res.status).toBe(401); });Intended? If this data is meant to be public (a catalogue, published posts), that is a legitimate design. Accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.auth-before-dataPOST /accounts can reach Account without authenticating.
An unauthenticated caller can write to Account. Writes are how data gets corrupted or planted.
- Data reached
Account(write)- Missing
- Authentication before the handler reaches data, and a query scoped to the caller.
- Confidence
- No middleware, guard or wrapper is applied to this route anywhere in module-20-prisma-integration/lesson-11-interactive-transactions/src/accounts/accounts.controller.ts.
- Path
- POST /accounts → Account
Prove it
# No cookie, no Authorization header. curl -i -X POST 'https://YOUR_API/accounts' \ -H 'content-type: application/json' -d '{}' # Expect 401. A 2xx means anyone on the internet can do this.Fix
@UseGuards(AuthGuard("jwt")) @Post("/accounts") handler(@Req() req) { // ...scope every query to req.user.id }Regression test
import request from "supertest"; test("POST /accounts rejects a signed-out caller", async () => { const res = await request(app).post("/accounts"); expect(res.status).toBe(401); });Intended? This cannot be declared intentional: a public write is not a design choice the check will bless. Accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.auth-before-dataPOST /accounts/transfer can reach Account without authenticating.
An unauthenticated caller can write to Account. Writes are how data gets corrupted or planted.
- Data reached
Account(write)- Missing
- Authentication before the handler reaches data, and a query scoped to the caller.
- Confidence
- No middleware, guard or wrapper is applied to this route anywhere in module-20-prisma-integration/lesson-11-interactive-transactions/src/accounts/accounts.controller.ts.
- Path
- POST /accounts/transfer → Account
Prove it
# No cookie, no Authorization header. curl -i -X POST 'https://YOUR_API/accounts/transfer' \ -H 'content-type: application/json' -d '{}' # Expect 401. A 2xx means anyone on the internet can do this.Fix
@UseGuards(AuthGuard("jwt")) @Post("/accounts/transfer") handler(@Req() req) { // ...scope every query to req.user.id }Regression test
import request from "supertest"; test("POST /accounts/transfer rejects a signed-out caller", async () => { const res = await request(app).post("/accounts/transfer"); expect(res.status).toBe(401); });Intended? This cannot be declared intentional: a public write is not a design choice the check will bless. Accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.auth-before-dataGET /auth/enable-2fa makes calls the check could not follow, so its data access is unknown, not absent: this.userService.findById() — no such method on UsersService; this.userService.updateSecretKey() — no such method on UsersService.
No rule could run on GET /auth/enable-2fa: it calls into code the check could not follow, so whether it reaches data — and whether that is authenticated — is unknown. A route nobody can trace is where a rescue gets hurt.
- Missing
- Evidence of what it touches. Could not follow: this.userService.findById() — no such method on UsersService; this.userService.updateSecretKey() — no such method on UsersService.
- Confidence
- The check is certain it could not follow these calls; it is not claiming the route is wrong.
Intended? Trace the chain by hand or log at the data access and hit the route. If it touches no data, record it in the baseline; if it does, that is the path to review first.
unresolved-data-reachPOST /auth/validate-2fa makes calls the check could not follow, so its data access is unknown, not absent: this.userService.findById() — no such method on UsersService.
No rule could run on POST /auth/validate-2fa: it calls into code the check could not follow, so whether it reaches data — and whether that is authenticated — is unknown. A route nobody can trace is where a rescue gets hurt.
- Missing
- Evidence of what it touches. Could not follow: this.userService.findById() — no such method on UsersService.
- Confidence
- The check is certain it could not follow these calls; it is not claiming the route is wrong.
Intended? Trace the chain by hand or log at the data access and hit the route. If it touches no data, record it in the baseline; if it does, that is the path to review first.
unresolved-data-reachGET /auth/disable-2fa makes calls the check could not follow, so its data access is unknown, not absent: this.userService.disable2FA() — no such method on UsersService.
No rule could run on GET /auth/disable-2fa: it calls into code the check could not follow, so whether it reaches data — and whether that is authenticated — is unknown. A route nobody can trace is where a rescue gets hurt.
- Missing
- Evidence of what it touches. Could not follow: this.userService.disable2FA() — no such method on UsersService.
- Confidence
- The check is certain it could not follow these calls; it is not claiming the route is wrong.
Intended? Trace the chain by hand or log at the data access and hit the route. If it touches no data, record it in the baseline; if it does, that is the path to review first.
unresolved-data-reachPOST /songs has no validator or rate limiter attached.
POST /songs accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X POST 'https://YOUR_API/songs' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("POST /songs rejects an unexpected body", async () => { const res = await request(app).post("/songs").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPUT /songs/:id has no validator or rate limiter attached.
PUT /songs/:id accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X PUT 'https://YOUR_API/songs/1' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("PUT /songs/:id rejects an unexpected body", async () => { const res = await request(app).put("/songs/1").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedDELETE /songs/:id has no validator or rate limiter attached.
DELETE /songs/:id accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X DELETE 'https://YOUR_API/songs/1' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("DELETE /songs/:id rejects an unexpected body", async () => { const res = await request(app).delete("/songs/1").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPOST / has no validator or rate limiter attached.
POST / accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X POST 'https://YOUR_API/' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("POST / rejects an unexpected body", async () => { const res = await request(app).post("/").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPUT /:id has no validator or rate limiter attached.
PUT /:id accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X PUT 'https://YOUR_API/1' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("PUT /:id rejects an unexpected body", async () => { const res = await request(app).put("/1").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedDELETE /:id has no validator or rate limiter attached.
DELETE /:id accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X DELETE 'https://YOUR_API/1' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("DELETE /:id rejects an unexpected body", async () => { const res = await request(app).delete("/1").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPOST /playlists has no validator or rate limiter attached.
POST /playlists accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X POST 'https://YOUR_API/playlists' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("POST /playlists rejects an unexpected body", async () => { const res = await request(app).post("/playlists").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPOST /auth/signup has no validator or rate limiter attached.
POST /auth/signup accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X POST 'https://YOUR_API/auth/signup' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("POST /auth/signup rejects an unexpected body", async () => { const res = await request(app).post("/auth/signup").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPOST /auth/login has no validator or rate limiter attached.
POST /auth/login accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X POST 'https://YOUR_API/auth/login' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("POST /auth/login rejects an unexpected body", async () => { const res = await request(app).post("/auth/login").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPOST /albums has no validator or rate limiter attached.
POST /albums accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X POST 'https://YOUR_API/albums' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("POST /albums rejects an unexpected body", async () => { const res = await request(app).post("/albums").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPATCH /songs/:id has no validator or rate limiter attached.
PATCH /songs/:id accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X PATCH 'https://YOUR_API/songs/1' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("PATCH /songs/:id rejects an unexpected body", async () => { const res = await request(app).patch("/songs/1").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPOST /artists has no validator or rate limiter attached.
POST /artists accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X POST 'https://YOUR_API/artists' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("POST /artists rejects an unexpected body", async () => { const res = await request(app).post("/artists").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPATCH /artists/:id has no validator or rate limiter attached.
PATCH /artists/:id accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X PATCH 'https://YOUR_API/artists/1' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("PATCH /artists/:id rejects an unexpected body", async () => { const res = await request(app).patch("/artists/1").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedDELETE /artists/:id has no validator or rate limiter attached.
DELETE /artists/:id accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X DELETE 'https://YOUR_API/artists/1' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("DELETE /artists/:id rejects an unexpected body", async () => { const res = await request(app).delete("/artists/1").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPOST /users has no validator or rate limiter attached.
POST /users accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X POST 'https://YOUR_API/users' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("POST /users rejects an unexpected body", async () => { const res = await request(app).post("/users").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPATCH /users/:id has no validator or rate limiter attached.
PATCH /users/:id accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X PATCH 'https://YOUR_API/users/1' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("PATCH /users/:id rejects an unexpected body", async () => { const res = await request(app).patch("/users/1").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedDELETE /users/:id has no validator or rate limiter attached.
DELETE /users/:id accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X DELETE 'https://YOUR_API/users/1' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("DELETE /users/:id rejects an unexpected body", async () => { const res = await request(app).delete("/users/1").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPOST /applications has no validator or rate limiter attached.
POST /applications accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X POST 'https://YOUR_API/applications' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("POST /applications rejects an unexpected body", async () => { const res = await request(app).post("/applications").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPATCH /applications/:id has no validator or rate limiter attached.
PATCH /applications/:id accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X PATCH 'https://YOUR_API/applications/1' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("PATCH /applications/:id rejects an unexpected body", async () => { const res = await request(app).patch("/applications/1").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedDELETE /applications/:id has no validator or rate limiter attached.
DELETE /applications/:id accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X DELETE 'https://YOUR_API/applications/1' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("DELETE /applications/:id rejects an unexpected body", async () => { const res = await request(app).delete("/applications/1").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPOST /posts has no validator or rate limiter attached.
POST /posts accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X POST 'https://YOUR_API/posts' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("POST /posts rejects an unexpected body", async () => { const res = await request(app).post("/posts").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPATCH /posts/:id has no validator or rate limiter attached.
PATCH /posts/:id accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X PATCH 'https://YOUR_API/posts/1' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("PATCH /posts/:id rejects an unexpected body", async () => { const res = await request(app).patch("/posts/1").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedDELETE /posts/:id has no validator or rate limiter attached.
DELETE /posts/:id accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X DELETE 'https://YOUR_API/posts/1' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("DELETE /posts/:id rejects an unexpected body", async () => { const res = await request(app).delete("/posts/1").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPOST /accounts has no validator or rate limiter attached.
POST /accounts accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X POST 'https://YOUR_API/accounts' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("POST /accounts rejects an unexpected body", async () => { const res = await request(app).post("/accounts").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPOST /accounts/transfer has no validator or rate limiter attached.
POST /accounts/transfer accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X POST 'https://YOUR_API/accounts/transfer' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("POST /accounts/transfer rejects an unexpected body", async () => { const res = await request(app).post("/accounts/transfer").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPATCH /accounts/:id has no validator or rate limiter attached.
PATCH /accounts/:id accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X PATCH 'https://YOUR_API/accounts/1' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("PATCH /accounts/:id rejects an unexpected body", async () => { const res = await request(app).patch("/accounts/1").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedDELETE /accounts/:id has no validator or rate limiter attached.
DELETE /accounts/:id accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X DELETE 'https://YOUR_API/accounts/1' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("DELETE /accounts/:id rejects an unexpected body", async () => { const res = await request(app).delete("/accounts/1").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPOST /upload has no validator or rate limiter attached.
POST /upload accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X POST 'https://YOUR_API/upload' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("POST /upload rejects an unexpected body", async () => { const res = await request(app).post("/upload").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPOST /upload-png has no validator or rate limiter attached.
POST /upload-png accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X POST 'https://YOUR_API/upload-png' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("POST /upload-png rejects an unexpected body", async () => { const res = await request(app).post("/upload-png").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPOST /audio/convert has no validator or rate limiter attached.
POST /audio/convert accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.
- Missing
- Schema validation of the request body, and a rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Prove it
curl -i -X POST 'https://YOUR_API/audio/convert' \ -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}' # Expect 400. A 2xx or 500 means the body is not validated.Fix
const Body = z.object({ name: z.string().max(200) }).strict(); const parsed = Body.safeParse(req.body); if (!parsed.success) return res.status(400).json(parsed.error.flatten());Regression test
test("POST /audio/convert rejects an unexpected body", async () => { const res = await request(app).post("/audio/convert").send({ unexpected: true }); expect(res.status).toBe(400); });Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedPOST /auth/signup reaches User without auth, which is expected for an authentication endpoint.
POST /auth/signup must be public — it is how people sign in. It is also where credential stuffing and account enumeration arrive.
- Data reached
User(write)- Missing
- A tight rate limit and identical responses for unknown users and wrong passwords.
- Confidence
- Identified as an authentication endpoint by its path, touching identity tables only.
- Path
- POST /auth/signup → User
Fix
// 5 attempts per IP per minute on sign-in. app.post("/auth/signup", rateLimit({ windowMs: 60_000, limit: 5 }), handler);Regression test
test("POST /auth/signup is rate limited", async () => { for (let i = 0; i < 5; i++) await request(app).post("/auth/signup").send({ email: "a@b.c", password: "x" }); const res = await request(app).post("/auth/signup").send({ email: "a@b.c", password: "x" }); expect(res.status).toBe(429); });Intended? Informational: this is reported so the front door stays in view, not because it is wrong.
auth-entry-publicProfile is not connected to anything.
Profile is connected to nothing: dead weight, or a route that was meant to use it and does not.
- Missing
- A route that uses it, or its removal.
- Confidence
- Nothing is wired to it in the design.
Intended? If it is used by a job or another service, accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.no-orphan-datastoreCategory is not connected to anything.
Category is connected to nothing: dead weight, or a route that was meant to use it and does not.
- Missing
- A route that uses it, or its removal.
- Confidence
- Nothing is wired to it in the design.
Intended? If it is used by a job or another service, accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.no-orphan-datastoreCategoriesOnPosts is not connected to anything.
CategoriesOnPosts is connected to nothing: dead weight, or a route that was meant to use it and does not.
- Missing
- A route that uses it, or its removal.
- Confidence
- Nothing is wired to it in the design.
Intended? If it is used by a job or another service, accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.no-orphan-datastoreAddress is not connected to anything.
Address is connected to nothing: dead weight, or a route that was meant to use it and does not.
- Missing
- A route that uses it, or its removal.
- Confidence
- Nothing is wired to it in the design.
Intended? If it is used by a job or another service, accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.no-orphan-datastoresongs is not connected to anything.
songs is connected to nothing: dead weight, or a route that was meant to use it and does not.
- Missing
- A route that uses it, or its removal.
- Confidence
- Nothing is wired to it in the design.
Intended? If it is used by a job or another service, accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.no-orphan-datastoreartists is not connected to anything.
artists is connected to nothing: dead weight, or a route that was meant to use it and does not.
- Missing
- A route that uses it, or its removal.
- Confidence
- Nothing is wired to it in the design.
Intended? If it is used by a job or another service, accept it as known debt: run
wyro-check --update-baselineand commit the ledger. It stays visible and CI fails only on new findings.no-orphan-datastoreGET / has no validator or rate limiter attached.
GET / can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /songs has no validator or rate limiter attached.
GET /songs can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/songs", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /songs/:id has no validator or rate limiter attached.
GET /songs/:id can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/songs/:id", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /:id has no validator or rate limiter attached.
GET /:id can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/:id", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /auth/test has no validator or rate limiter attached.
GET /auth/test can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/auth/test", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /albums has no validator or rate limiter attached.
GET /albums can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/albums", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /artists has no validator or rate limiter attached.
GET /artists can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/artists", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /artists/:id has no validator or rate limiter attached.
GET /artists/:id can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/artists/:id", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /users has no validator or rate limiter attached.
GET /users can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/users", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /users/:id has no validator or rate limiter attached.
GET /users/:id can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/users/:id", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /applications has no validator or rate limiter attached.
GET /applications can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/applications", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /applications/:id has no validator or rate limiter attached.
GET /applications/:id can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/applications/:id", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /posts has no validator or rate limiter attached.
GET /posts can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/posts", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /posts/:id has no validator or rate limiter attached.
GET /posts/:id can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/posts/:id", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /sequential has no validator or rate limiter attached.
GET /sequential can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/sequential", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /accounts has no validator or rate limiter attached.
GET /accounts can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/accounts", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /accounts/:id has no validator or rate limiter attached.
GET /accounts/:id can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/accounts/:id", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /user/:id has no validator or rate limiter attached.
GET /user/:id can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/user/:id", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /get-cookie has no validator or rate limiter attached.
GET /get-cookie can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/get-cookie", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /set-cookie has no validator or rate limiter attached.
GET /set-cookie can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/set-cookie", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /file/stream-file has no validator or rate limiter attached.
GET /file/stream-file can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/file/stream-file", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /file/stream-file-customize has no validator or rate limiter attached.
GET /file/stream-file-customize can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/file/stream-file-customize", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guardedGET /login has no validator or rate limiter attached.
GET /login can be called at any rate. Cheap to fix, rarely urgent on a read.
- Missing
- A rate limit.
- Confidence
- Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.
Fix
// 60 requests per IP per minute. app.get("/login", rateLimit({ windowMs: 60_000, limit: 60 }), handler);Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo:
{ "policy": { "rules": { "public-entry-guarded": "off" } } }in wyro.json.public-entry-guarded
Free account, no card. The repository opens as an editable graph.
Add this check to the README
[](https://wyro.in/scan/HaiderMalik12/nestjs-fundamentals)It updates itself whenever the repository changes and links back to this report.
What this is
Wyro reads the repository’s routes and data models and checks the paths between them: whether a route can reach a table without passing a guard, whether a datastore holding personal data is exposed to a public read, whether an endpoint that issues credentials requires the credentials it issues.
It is rule-based, not a model. The same commit produces the same result every time, and it does not guess at business rules it cannot see. A rule with nothing to look at is reported as not having run — never as a pass.
This check runs on public source through GitHub’s own API and needs no account. A free account adds the editable architecture canvas, private repositories on paid plans, and a CI gate. No card required.
Maintain this repository? You can have this report removed, and a real vulnerability is disclosed to you privately before it is published. How public reports are handled.