All jobs

Run a Security Audit on a NestJS API

Yes. An agent boots your NestJS API in its own sandbox, confirms each finding with a real request against the running app, and writes a report without changing code.

Run this on RushIn a Project that has your repository.

What the agent does

The agent starts by reading where the security decisions live, because a NestJS audit that only greps for helmet misses the configuration behind it. In an API like brocoders/nestjs-boilerplate, src/main.ts installs helmet with contentSecurityPolicy: false and a cross-origin resource policy, enables CORS from app.corsOrigins, sets the api prefix and URI versioning, and registers a global ValidationPipe from src/utils/validation-options.ts with whitelist and transform on and 422 as the error status. src/config/app.config.ts shows that corsOrigins falls back to FRONTEND_DOMAIN and then to * when APP_CORS_ORIGINS is unset. src/auth/config/auth.config.ts refuses to start in production while any AUTH_*_SECRET still holds its placeholder, and AUTH_UNIFORM_ERRORS is an opt-in that docs/auth.md explains. That document's own section on security hardening says rate limiting is deliberately not bundled, which is the first thing the audit confirms rather than discovers.

Then it boots the API the way the repository does, with docker-compose.yaml and env-example-relational, which start Postgres 17, a mail catcher, Adminer and the api service built from the Dockerfile. With the app answering, it registers through POST /api/v1/auth/email/register, confirms the account using the message the mail catcher received, and logs in, which gives it a token for the authenticated routes in src/auth/auth.controller.ts: me, refresh, logout, PATCH me and DELETE me. It keeps a second account with the admin role from the seeds for the routes guarded by RolesGuard in src/users/users.controller.ts.

Each check is a request with a recorded answer. For brute force it sends repeated wrong passwords to email/login and forgot/password and records that nothing slows them down. For account enumeration it compares the responses for a registered and an unregistered email with AUTH_UNIFORM_ERRORS unset, the default. For CORS it sends an Origin header from an unrelated site and records the Access-Control-Allow-Origin that comes back. For tokens it checks that src/auth/strategies/jwt.strategy.ts pins HS256 and that a token signed with none or a different algorithm is refused, that refresh rejects a reused session hash the way auth.service.ts intends, and that a password reset link stops working once the password changes. For uploads it reads src/files/infrastructure/uploader/local/files.module.ts, where fileFilter tests the original filename against jpg|jpeg|png|gif rather than the bytes, uploads a renamed file to POST /api/v1/files/upload, and tries GET /api/v1/files/:path with traversal segments to see how sendFile with root: './files' treats them. For authorization it calls the admin-only user routes with an ordinary token.

It also reviews what the running app exposes by default: Swagger UI at /docs with bearer auth, the x-custom-lang header, the / info route, and the response headers helmet does and does not set with CSP off. Every finding goes into security-audit.md with the file and line it comes from, the exact request that demonstrates it, the response, a severity, and the fix, citing the repository's own guidance where it exists, such as the @nestjs/throttler suggestion in docs/auth.md. Things that hold up, such as the algorithm pin or the production secret check, are listed as confirmed so the report is a record of what was tested, not only of what failed. The agent changes no code and opens no pull request.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "Audit this NestJS API for security problems without changing any code. Read src/main.ts, src/config/app.config.ts, src/auth/auth.controller.ts, src/auth/auth.service.ts, src/auth/config/auth.config.ts, src/auth/strategies/*.ts, src/users/users.controller.ts, src/files/infrastructure/uploader/local/files.module.ts and files.controller.ts, src/utils/validation-options.ts, docs/auth.md, docker-compose.yaml and env-example-relational first. Start the stack in the sandbox with docker compose and the example env file, register and confirm a user through the mail catcher, log in, and get an admin token from the seeded admin. Then test with real requests: repeated failed logins and forgot-password calls for rate limiting, registered versus unregistered emails for account enumeration, a foreign Origin header for CORS, tokens with a changed algorithm or no signature, refresh token reuse, password reset link reuse after a password change, an upload of a non-image renamed to .png, path traversal on the file download route, admin-only routes with an ordinary token, and the headers helmet sets with CSP disabled. Write security-audit.md: for each finding the file and line, the request that demonstrates it, the response, severity and fix, and a section listing the checks that passed. Change no code and open no pull request.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
  1. Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
  2. Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
  3. Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

What comes back

What comes back is security-audit.md as an artifact on the session, which the session read returns with a download link good for an hour. It opens with how the API was run: the compose file, the env file, the Postgres and Node images from docker-compose.yaml and the Dockerfile, the accounts it created and the routes it reached, so a reader knows the conditions each finding was checked under. Then come the findings, grouped by authentication, authorization, input handling, transport and headers, and configuration, each with its request and response copied from the transcript.

Each finding names the file and line, the severity and the fix in terms of this codebase: the @nestjs/throttler module on the three auth routes docs/auth.md already names, AUTH_UNIFORM_ERRORS=true for enumeration, an explicit APP_CORS_ORIGINS list, a content check on uploads instead of the filename regex, and whatever the download route test showed. A closing section lists what passed, such as the HS256 pin, the whitelist on the validation pipe and the production placeholder-secret check, so the next audit can start from the same list.

Before acting on it, a reviewer replays one or two of the requests from the report against a staging instance to confirm they reproduce outside the sandbox, checks that each severity matches how the API is exposed in production, and turns the fixes into issues. A follow-up message to the same session can ask the agent to open a pull request for any one of them, since the sandbox still has the running stack.

Run it on every release candidate branch

A team that wants the audit before each release starts one run per candidate: a POST /o/{org}/sessions with the repository at that branch in resources, as https://github.com/your-org/your-app/tree/release-candidate, and this prompt. A GitHub Actions job on the release branch can send that request with the key from its secrets.

Each run boots its own copy of the stack in its own sandbox, so audits of two branches never share a database or a port. A webhook on the key receives run.completed with the session_url, and reading the session returns the artifact's download link; the job can post that link on the release pull request.

The use case this job follows, with its automation and trigger: Produce a report or file, no repo.

Why run it on Rush?

An audit that only reads code produces guesses; one that runs the API produces evidence. Running this stack means Postgres, a mail catcher and the API in Docker, a confirmed account, an admin account and a few hundred requests, which is an afternoon on a laptop and competes with everything else on it. In a Rush sandbox the agent has its own Docker daemon and a browser for the Swagger UI and the mail catcher, and the whole thing is discarded when the turn ends.

The report carries its own proof. Every request in it is also in the transcript with the response the running app gave, so a reviewer can tell a confirmed finding from a suspicion, and a finding that did not reproduce is listed as such rather than padding the report.

Frequently asked questions

Does the audit change my code or open a pull request?

No. The prompt asks for a report only, and the result is a Markdown artifact on the session. If you want a fix, send a follow-up message to the same session naming the finding, and the agent opens a pull request for that change with the same sandbox and running stack.

What does it do with the secrets in the example env file?

It uses env-example-relational as the repository's own compose setup does, inside a sandbox that is discarded afterwards. It never needs your production secrets. One of its checks is that auth.config.ts refuses the placeholder secrets when NODE_ENV=production, which it confirms by starting the app that way and recording the error.

Will it flag things the repository documents as deliberate?

It reports them, with the documentation quoted. docs/auth.md says rate limiting is left to the proxy or to @nestjs/throttler and that CSP is off because of Swagger UI; the report records that the running app behaves that way, rates the risk for an API exposed as yours is, and cites the document so the team can decide rather than rediscover it.

Does it test the social login and the S3 uploaders?

Only as far as the code shows. Apple, Google and Facebook sign-in need provider credentials the sandbox does not have, so the report reviews validateSocialLogin against the email verification rules in docs/auth.md and says those paths were read, not exercised. The local uploader is the one that runs with the example env; the S3 and presigned variants are reviewed from src/files/infrastructure/uploader/ and marked as untested.