For your code, a second pair of eyes.

No one around to review your code? Project Doctor reads your project folder in your own browser. It underlines the lines that look wrong and writes in the margin what to do about them. You make the call; your code goes nowhere.

Project Doctor is an open-source tool under the AGPL-3.0-only licence. No account. No AI: same folder, same result.

app/api/login/route.ts48 lines // DEFECT: MD5 is a fast hash. Commodity hardware tests billions of // candidates per second, so a leaked table is cracked within hours. // Use argon2id or bcrypt. const hash = crypto.createHash('md5').update(password).digest('hex'); const user = await db.user.findFirst({ where: { email, password: hash } }); if (!user) return Response.json({ message: 'Invalid credentials' }, { status: 401 });
lib/server.ts20 lines // DEFECT: origin '*' together with credentials: true means any website your // users visit can make authenticated requests to this API using their // session cookies, and read the responses. app.use(cors({ origin: '*', credentials: true })); app.get('/export', (req: any, res: any) => { // DEFECT: command injection. `req.query.file` is interpolated into a shell
Dockerfile10 lines# DEFECT: unpinned base image — every rebuild can pull a different Node version.FROM node:latest WORKDIR /appCOPY . .

The sample project put 43 papers on the desk.

We scanned a sample project. Each paper has a file, an underlined line and a note in the margin.

app/api/login/route.tsline 21 // candidates per second, so a leaked table is cracked within hours. // Use argon2id or bcrypt. const hash = crypto.createHash('md5').update(password).digest('hex'); const user = await db.user.findFirst({ where: { email, password: hash } }); Critical Passwords hashed with MD5 What to do: Switch password hashing to Argon2id or bcrypt, and re-hash on next login.

The full sample report has all 43 papers, the terminal output and the receipt. Open the sample report

A receipt at the end of every scan.

What was scanned, how many rules ran, what was skipped and how many requests left your device. All of it written out. While a scan runs, outgoing requests stay at zero: the scan page forbids itself from connecting out.

See for yourself: open the Network tab (Ctrl+Shift+I) and scan. Or turn the internet off first. How to check

Sample receipt

vibe-coded-app/sample scan
Files scanned16
Rules run89
Rules skipped4 (no .git)
Files skipped0
Findings43
AInone
Outgoing requests0

This is a sample. A real receipt is made in this tab after each scan.

Which languages does it read?

How much of the code it reads depends on the language. It does not present code it did not read as read.

  • JavaScript, TypeScriptRead in full: .js .jsx .ts .tsx .mjs .cjs .mts .cts
  • PythonRead in part: complexity, secrets, which files are critical, and 7 security rules that read by pattern
  • Dependenciesnpm and PyPI
  • Go, Rust, PHP, Ruby, Java/Kotlin, C#, Swift and othersSecrets and configuration only; the code itself is not read

What it doesn't do

If you don't trust tools that say they find everything, you're right. We measured what we miss.

  • Access to someone else's record (IDOR)Doesn't catch it: 0 of 11 examples in our measurement
  • Python projectsPartly: 7 security rules, by pattern
  • tRPC routesNot recognised
  • Files with syntax errorsLeft out of the security rules; the scan says which
  • Anything outside the sourceCan't see it: backups, server and hosting settings, the production database
  • Rules that need a .git folder4 rules
  • How many users it can takeDoesn't answer that
  • Finding nothingDoesn't mean nothing is wrong

You put the papers on the desk.