Code reviewer, incl. AI-written code
An independent read-only pull request review: 42 published rules on security, migrations, tests, architecture and reliability. Every finding has the file and line, a code excerpt, an explanation and a way to verify it. The code is never changed or executed.
Free, no sign-up
git diff, git format-patch or a GitHub/GitLab PR .diff/.patch. The code is not executed, changed or sent anywhere.
Project context
Rule packs switch on automatically from signals in the diff. You can force them on or off.
No signals found — pack not applied. Signals: .sql, migrations, pg/psycopg/prisma
No signals found — pack not applied. Signals: supabase/ directory, @supabase/*, SUPABASE_*
No signals found — pack not applied. Signals: *.rules, firebase.json, firebase
No signals found — pack not applied. Signals: app/**/page|route, next.config, "use client", next/*
How the reviewer works
Diff
Paste a unified diff (git diff, git format-patch, a GitHub or GitLab PR .diff/.patch) or upload the file.
Project context
Rule packs for PostgreSQL, Supabase, Firebase and the Next.js App Router switch on automatically from signals in the diff (PostgreSQL — .sql, migrations, pg/psycopg/prisma; Supabase — supabase/ directory, @supabase/*, SUPABASE_*; Firebase — *.rules, firebase.json, firebase; Next.js App Router — app/**/page|route, next.config, "use client", next/*); you can force them on or off. You can also limit the check to some languages.
Rules
Each rule looks at added lines (exceptions are stated) and reports the file, the new-version line, an excerpt with context, why it matters and how to verify. There are no auto-fixes — a person decides.
Reviewer decision
A finding gets “to fix”, “accepted risk” or “false positive”, a note and a name. Decisions survive a re-check: the finding key is built from the rule, file and line text, not the line number.
Export
Markdown for a PR comment, PDF via print, JSON to come back to the review, and SARIF 2.1.0 for GitHub code scanning.
Rules
The tables below are generated from the same code as the check — rules v1.0 (2026-10-13). Severity: critical, high, medium, low, info.
CR-SEC-001
- What
- Looks like a secret in an added line
- How it is detected
- Key patterns (AWS, GitHub, GitLab, Slack, Stripe, Google, Anthropic, OpenAI, JWT, Telegram, private keys, connection strings with a password) and assignments like password=… (placeholders such as process.env, ${VAR}, changeme are ignored).
- Severity
- critical
- How to verify
- Check whether the value is real. If so, revoke and reissue the key, move it to environment variables or a secret store, and clean the history.
CR-SEC-002
- What
- A .env file is added or changed in the repository
- How it is detected
- A .env, .env.local, .env.production or similar file (except .env.example/.sample/.template) is added or changed.
- Severity
- high
- How to verify
- Make sure the file is in .gitignore and only an .env.example without values is committed. If the values are real, rotate them.
CR-SEC-003
- What
- Code executed from a string (eval and friends)
- How it is detected
- JS/TS: eval(), new Function(), setTimeout/setInterval with a string; Python: eval()/exec(); PHP, Ruby: eval().
- Severity
- high
- How to verify
- Trace where the string comes from. eval can usually be replaced by JSON parsing, a function lookup table or a safe expression parser.
CR-SEC-004
- What
- Shell command built from variables
- How it is detected
- child_process exec/execSync with a template string or concatenation, spawn/execFile with shell: true; subprocess with shell=True, os.system/os.popen with interpolation; exec.Command("sh", "-c", …); PHP shell_exec/system with a variable; Ruby backticks with #{}.
- Severity
- high
- How to verify
- Pass arguments as an array (execFile/spawn without a shell, subprocess.run([...])) and check whether the value can come from a user.
CR-SEC-005
- What
- SQL query built by inserting values into a string
- How it is detected
- A line with SELECT … FROM / INSERT INTO / UPDATE … SET / DELETE FROM plus interpolation on the same line: ${} in a template string, + concatenation, an f-string, %, .format(), fmt.Sprintf, a PHP $variable, Ruby #{}. Safe tagged templates (sql
…, Prisma.sql…, $queryRaw…) are ignored. - Severity
- high
- How to verify
- Use parameters ($1, ?, :name) or a query builder. If the value is a column name from an allow-list, that is acceptable: check the list.
CR-SEC-006
- What
- HTML inserted without escaping
- How it is detected
- dangerouslySetInnerHTML, innerHTML/outerHTML =, insertAdjacentHTML, document.write, v-html, the |safe filter, autoescape off, mark_safe, Markup(), Blade {!! !!}, .html_safe, <%==, {{{ }}}. Clearing with innerHTML = "" is ignored.
- Severity
- medium
- How to verify
- Check where the HTML comes from. User data must go through a sanitizer (for example DOMPurify) or be rendered as text.
CR-SEC-007
- What
- TLS certificate verification disabled
- How it is detected
- rejectUnauthorized: false, NODE_TLS_REJECT_UNAUTHORIZED=0, verify=False, InsecureSkipVerify: true, CURLOPT_SSL_VERIFYPEER false, ssl._create_unverified_context, strictSSL: false, check_hostname=False, VERIFY_NONE, ServerCertificateValidationCallback => true.
- Severity
- high
- How to verify
- If this is local debugging, limit it to the development environment. For a private CA, pass the certificate explicitly (ca, verify=path).
CR-SEC-008
- What
- CORS: any origin together with credentials
- How it is detected
- Added lines of one file contain both an any-origin setting (Access-Control-Allow-Origin: *, origin: '*' or true, allow_origins=["*"], AllowAllOrigins, AllowAnyOrigin) and a credentials setting (credentials: true, Allow-Credentials: true, allow_credentials=True).
- Severity
- high
- How to verify
- Set an explicit list of trusted origins. Make sure origin: true does not reflect any incoming Origin.
CR-SEC-009
- What
- JWT read without signature verification
- How it is detected
- jwt.decode() without jwt.verify() in the same file's added lines (jsonwebtoken), algorithm(s) "none", ignoreExpiration: true, PyJWT decode with verify_signature=False / verify=False, Go ParseUnverified.
- Severity
- high
- How to verify
- Use verify with an explicit algorithm list and key; decode only reads a token that was already verified.
CR-SEC-010
- What
- MD5 or SHA-1 for passwords
- How it is detected
- An md5/sha1 call (createHash, hashlib, MessageDigest, Digest::MD5, md5()) on a line with a password or secret mentioned nearby (±3 added lines).
- Severity
- high
- How to verify
- Use bcrypt, scrypt or Argon2 with a salt for passwords. MD5 for a file checksum is fine — then mark it a false positive.
CR-SEC-011
- What
- Non-cryptographic random for a token or code
- How it is detected
- Math.random(), Python random.*, PHP rand()/mt_rand()/uniqid(), Java new Random(), Go math/rand — on a line with token, secret, password, otp, nonce, salt, session, key, code, invite, reset or uuid.
- Severity
- medium
- How to verify
- Use crypto.randomUUID()/crypto.getRandomValues(), Python secrets, PHP random_bytes(), Java SecureRandom, Go crypto/rand.
CR-SEC-012
- What
- Redirect to an address taken from the request
- How it is detected
- redirect(...) with req.query/params/body, request.args/GET, searchParams.get(), $_GET; location = params.get(...); redirect_to params[...].
- Severity
- medium
- How to verify
- Allow only relative paths or allow-listed hosts; check that the target starts with a single “/”, not “//”.
CR-SEC-013
- What
- Supabase service_role key in client code (pack: Supabase)
- How it is detected
- service_role / SERVICE_ROLE in a "use client" file (or *.client.tsx), or a NEXT_PUBLIC_/VITE_/REACT_APP_/EXPO_PUBLIC_…SERVICE_ROLE variable in any file.
- Severity
- critical
- How to verify
- Keep service_role on the server only (route handler, server action, edge function) without a NEXT_PUBLIC_ prefix. Use the anon key and RLS policies in the client. If the key already shipped in a build, rotate it.
CR-SEC-014
- What
- RLS disabled or a policy that allows everyone (pack: Supabase / PostgreSQL)
- How it is detected
- ALTER TABLE … DISABLE ROW LEVEL SECURITY, USING (true), WITH CHECK (true), GRANT ALL … TO anon/public in .sql files and migrations.
- Severity
- high
- How to verify
- Check that the table really is public (a reference list, say). Otherwise write a policy conditioned on auth.uid() and role.
CR-SEC-015
- What
- Firebase rules open data to everyone (pack: Firebase)
- How it is detected
- allow … : if true; allow read, write; with no condition; test mode if request.time < timestamp.date(…); ".read"/".write": true in the Realtime Database.
- Severity
- critical
- How to verify
- Grant access based on request.auth and document ownership; test the rules in the Firebase emulator.
CR-SEC-016
- What
- Debug mode in a production configuration
- How it is detected
- DEBUG = True in settings/config, app.run(debug=True), and FLASK_DEBUG/APP_DEBUG/DEBUG=true or debug: true in files with prod/production in the path.
- Severity
- medium
- How to verify
- Enable debugging only via a development environment variable and check the value on the production server.
CR-SEC-017
- What
- A password, token or key is written to the log
- How it is detected
- A logging call (console.*, logger.*, logging.*, print, fmt.Print*, System.out, puts, error_log) with a variable named like password, secret, token, api_key, authorization, cookie, card, cvv or ssn. Words inside string literals do not count.
- Severity
- medium
- How to verify
- Remove the value from the log or mask it; check whether values were already written.
CR-SEC-018
- What
- Unsafe deserialization
- How it is detected
- pickle.load(s), yaml.load without SafeLoader, yaml.unsafe_load, Marshal.load, unserialize($_GET/$_POST/…), new ObjectInputStream.
- Severity
- high
- How to verify
- Make sure the data only comes from a trusted source; for external data use JSON or yaml.safe_load.
CR-SEC-019
- What
- 777 permissions on files or directories
- How it is detected
- chmod 777, chmod(…, 0o777), os.chmod(…, 0o777), os.Chmod/MkdirAll/WriteFile with 0777.
- Severity
- medium
- How to verify
- Grant the minimum permissions needed (usually 644 for files, 755 for directories) and set the owner.
CR-MIG-001
- What
- Table or column dropped
- How it is detected
- DROP TABLE, DROP COLUMN, TRUNCATE TABLE, dropTable/dropColumn, op.drop_table/drop_column, remove_column, drop_table, dropIfExists.
- Severity
- high
- How to verify
- Check the backup and rollback plan; drop in two releases: stop using the column first, then drop it. If this is a down migration, mark it a false positive.
CR-MIG-002
- What
- New NOT NULL column without a default
- How it is detected
- ADD [COLUMN] … NOT NULL without DEFAULT on the same line; add_column … null: false without default:; op.add_column(… nullable=False) without server_default.
- Severity
- high
- How to verify
- Add the column as nullable or with a DEFAULT, backfill, then enforce NOT NULL as a separate step.
CR-MIG-003
- What
- Column or table renamed
- How it is detected
- RENAME COLUMN, ALTER TABLE … RENAME TO, RENAME TABLE, renameColumn, rename_column/rename_table, op.alter_column(new_column_name=…), sp_rename.
- Severity
- medium
- How to verify
- Find every use of the old name; for zero-downtime rollouts — new column, dual writes, switch, drop the old one.
CR-MIG-004
- What
- Column type changed
- How it is detected
- ALTER COLUMN … [SET DATA] TYPE, MODIFY COLUMN, change_column, op.alter_column(type_=…).
- Severity
- medium
- How to verify
- Estimate table size and lock time on a data copy; check that every value fits the new type.
CR-MIG-005
- What
- CREATE INDEX without CONCURRENTLY (PostgreSQL) (pack: PostgreSQL)
- How it is detected
- CREATE [UNIQUE] INDEX without CONCURRENTLY on a table that is not created in the same file.
- Severity
- medium
- How to verify
- For large tables use CREATE INDEX CONCURRENTLY (outside the migration transaction). For small or new tables mark it an accepted risk.
CR-MIG-006
- What
- An existing migration was edited
- How it is detected
- A file in a migrations directory (migrations/, db/migrate/, alembic/versions/, prisma/migrations/, supabase/migrations/) is modified rather than added.
- Severity
- high
- How to verify
- If the migration was already applied anywhere, revert it and make the change in a new migration.
CR-MIG-007
- What
- DELETE or UPDATE without WHERE
- How it is detected
- In .sql — a DELETE FROM / UPDATE … SET statement without WHERE (added lines up to “;”); in code — such a query entirely on one line without WHERE.
- Severity
- high
- How to verify
- Make sure changing every row is intended (a backfill, say) and test it on a copy of the data.
CR-TST-001
- What
- Source changed, tests did not
- How it is detected
- The diff changes code files (not migrations or generated files) but no test file (by path conventions).
- Severity
- medium
- How to verify
- Check whether existing tests cover the behaviour; if not, add a test for the changed branch of logic.
CR-TST-002
- What
- Focused test (.only)
- How it is detected
- describe/it/test/context.only(…), fit(, fdescribe( in test files.
- Severity
- high
- How to verify
- Remove .only; make CI forbid it (for example --forbid-only in Mocha/Playwright, a no-focused-tests lint rule).
CR-TST-003
- What
- Test disabled (skip)
- How it is detected
- .skip/.todo, xit/xdescribe/xtest, @pytest.mark.skip/skipif/xfail, @unittest.skip, t.Skip, @Disabled, @Ignore, RSpec skip/pending.
- Severity
- medium
- How to verify
- There should be a reason and a ticket to restore the test. If it blocked a green CI, look into why it failed.
CR-TST-004
- What
- Assertions removed from tests
- How it is detected
- A test file loses more assertion lines (expect, assert*, should, t.Error/Fatal, require.*, assertThat) than it gains. Removed lines are checked.
- Severity
- medium
- How to verify
- Compare removed and new assertions: do the new ones cover the same behaviour?
CR-TST-005
- What
- Large snapshot update
- How it is detected
- A snapshot file changes by 200+ lines.
- Severity
- low
- How to verify
- Spot-check the snapshot differences; make sure the update is expected from this PR's changes.
CR-ARC-001
- What
- New dependencies
- How it is detected
- New packages in package.json, requirements*.txt, pyproject.toml, Pipfile, go.mod, Cargo.toml, Gemfile, composer.json (in added lines and not in removed ones).
- Severity
- info
- How to verify
- Check that the package exists, is maintained, has a clear licence and the exact name (no typos); see whether existing dependencies already cover it.
CR-ARC-002
- What
- Dependencies changed, lock file did not
- How it is detected
- Dependencies are added or changed in a manifest, but the matching lock file (package-lock.json, yarn.lock, pnpm-lock.yaml, poetry.lock, go.sum, Cargo.lock, Gemfile.lock, composer.lock…) is not in the diff.
- Severity
- medium
- How to verify
- Update the lock file with the same package manager and commit it with the manifest.
CR-ARC-003
- What
- Server-only module imported into a client component (pack: Next.js App Router)
- How it is detected
- A "use client" file adds an import of server-only, next/headers, fs, child_process, node:*, @prisma/client, pg, mysql2, mongoose, bcrypt, nodemailer, firebase-admin or stripe.
- Severity
- high
- How to verify
- Move the module use into a server component, route handler or server action and pass only data to the client.
CR-ARC-004
- What
- Non-public environment variable in a client component (pack: Next.js App Router)
- How it is detected
- process.env.X without the NEXT_PUBLIC_ prefix (except NODE_ENV) in a "use client" file.
- Severity
- medium
- How to verify
- If the value is secret, read it on the server. If it is public, rename it with the NEXT_PUBLIC_ prefix.
CR-REL-001
- What
- Exception caught and silently ignored
- How it is detected
- An empty catch {} (on one line, or catch (…) { followed by “}”), .catch(() => {}), except: pass / except Exception: pass (one or two lines), an empty if err != nil {} in Go.
- Severity
- medium
- How to verify
- Handle the error explicitly: log with context, retry, tell the user or rethrow. If ignoring is intended, leave a comment.
CR-REL-002
- What
- localhost or private IP address in code
- How it is detected
- http(s)://localhost, 127.0.0.1, 0.0.0.0, [::1], 10.x, 192.168.x, 172.16–31.x in added code lines (tests excluded). Next to process.env/os.environ the confidence is low.
- Severity
- low
- How to verify
- Move the address to an environment variable or config and check its value for each environment.
CR-MNT-001
- What
- Very large change in one file
- How it is detected
- More than 400 added lines in one code file (tests and generated files excluded).
- Severity
- low
- How to verify
- Split the change into logical parts or review it section by section; look for duplicates of existing code.
CR-MNT-002
- What
- TODO / FIXME / HACK added
- How it is detected
- The words TODO, FIXME, HACK, XXX (upper case) in added code lines.
- Severity
- info
- How to verify
- Check that the note does not hide missing logic; file a ticket or finish it before merging.
CR-MNT-003
- What
- Debug output or breakpoint
- How it is detected
- console.log/debug/trace/dir, debugger, Python print(, breakpoint(), pdb.set_trace(), var_dump/print_r/dd, binding.pry, puts, System.out.print, printStackTrace, Console.Write — except tests and scripts/, bin/, cli/, tools/ directories.
- Severity
- low
- How to verify
- Remove it or replace it with structured logging at a proper level.
CR-MNT-004
- What
- Type checking or lint suppressed
- How it is detected
- any / as any, @ts-ignore, @ts-expect-error, @ts-nocheck, eslint-disable, # type: ignore, # noqa, pylint: disable, //nolint, @SuppressWarnings, rubocop:disable, #pragma warning disable. File-wide @ts-nocheck and eslint-disable are medium.
- Severity
- low
- How to verify
- See whether the type can be described precisely or the warning's cause fixed; leave a comment explaining why the suppression is needed.
CR-MNT-005
- What
- Large commented-out code block added
- How it is detected
- 5+ consecutive added comment lines that look like code (; { } ( ) = or return/if/for/const/def/import at the start).
- Severity
- low
- How to verify
- Delete the block or explain why it is kept.
Very large file change (CR-MNT-001)
- Value
- more than 400 added lines
Large snapshot update (CR-TST-005)
- Value
- 200+ changed lines
Commented-out code (CR-MNT-005)
- Value
- 5+ consecutive lines
Findings per rule per file
- Value
- up to 20, the rest are counted
Test files are detected by path: tests/…, __tests__/…, e2e/…; login.test.ts, cart.spec.js; auth_test.go; test_auth.py, auth_test.py; AuthServiceTest.java; AuthTests.cs; user_spec.rb; UserTest.php; LoginTests.swift; __snapshots__/x.snap.
Limitations
- The code is not executed; diff lines are matched against patterns. Patterns can miss a problem (for example SQL built over several lines) or fire falsely.
- Cross-file relations, types and data flow are not analysed: a rule sees a line, not the program. Files outside the diff are not considered.
- This does not replace SAST, tests or an engineering review — it is a fast first pass with evidence that helps you not miss common mistakes.
Privacy
- The diff is parsed in your browser: the code is not sent to a server or put into the page address.
- The draft, decisions and versions are stored only in this browser (localStorage); “Clear all” deletes them.
- Values that look like secrets are replaced with [REDACTED] in evidence and exports; a warning is shown before export.
- Analytics receives only the fact of a review or an export and a category (highest severity, file format) — no code or paths.
- The result reaches us only if you send a request yourself and keep the “Attach the result” box ticked — the form shows exactly what will be sent. The summary holds counts and rule · file:line · title, never code.
Related tools
All tools · 40
MVP health check16 questions about code, access, data, tests and releases. Shows what to check first — no “score out of 100”, no sign-up.
Context VaultProject docs, decisions and constraints in one place: source search, staleness control and task-specific context packs for an AI agent or a new developer.
Spec Drift DetectorRequirements ↔ Git/PR changes: a traceability map, likely drift, changes without requirements and requirements without test evidence — with a reviewer workflow.
Project handoffRepository snapshot, architecture, progress, blockers, risks, tests and next tasks → a verifiable handoff package for a developer or an AI agent.
Release readinessAn evidence-backed check matrix: CI, migrations, backups, a rollback plan, monitoring, risk exceptions and sign-off → a GO / NO-GO decision.Need a review of code written with AI agents?
A code audit: access and roles, secrets, database and RLS, APIs, tests and deployment — with a risk map and a fix plan. The first short call is free; scope and cost come after a brief.
- The first short conversation is free — we choose the format
- NDA — yours or ours
- Pricing after a brief and an agreed scope