/security-review and Claude Code Review are AI reviewers: they comment, and Code Review's check run always completes as neutral, so neither blocks a merge. For checks that can block, add hooks in .claude/settings.json that run on every edit, and a required status check on the pull request.


What Anthropic ships, and what it doesn't do

ToolWhat it doesBlocks a merge?
Permission modesDecide whether Claude may run a command, edit a file or call a toolNo; they govern actions, not code
/security-reviewAsks Claude to review your pending changes for security issuesNo; it's a review in your session
anthropics/claude-code-security-reviewGitHub Action that runs an AI security review on a PR with your Claude API key and comments on itOnly if you build that into your workflow
Claude Code ReviewManaged PR reviewer; posts findings by severity in a check runNo: the check run always completes as neutral. Anthropic's docs suggest reading the severity counts from the check output in your own CI if you want to gate

These are good tools. They are also model-based, so their findings can vary between runs. The rest of this page adds the deterministic part: rule IDs, the same result on the same code, and a check that can fail.


Inside Claude Code: hooks on every edit, install and stop

pip install skylos
skylos agent install-hooks --skylos-bin skylos   # writes .claude/settings.json

Run /hooks in Claude Code to see what was added. From then on:

  • After each Edit/Write/MultiEdit, a PostToolUse hook checks only the lines Claude just changed. When Claude wrote os.system('echo hi ' + name) into a Click command, Claude got back: Skylos found 1 issue(s) in the lines you just changed. Fix them before moving on: - pkg/tool.py:8 SKY-D212/SKY-D203 Possible command injection (os.system): tainted input.
  • Before a Read of a file containing a hard-coded secret, a PreToolUse hook denies it, so the key isn't sent to the model provider.
  • Before a Bash install of a package that doesn't exist (pip install reqeusts, uv add numpyy), a PreToolUse hook denies it.
  • When Claude tries to stop with an issue it introduced still open, the Stop hook sends it back with the list. With [tool.skylos.done] committed, the Stop hook also re-runs your tests and blocks on deleted or skipped tests (real output).

The full settings.json, the JSON each hook returns and the latency numbers are on Claude Code hooks examples for security.

Committing the hooks for a team

.claude/settings.json is usually committed. By default the installer writes the absolute path of the Skylos that ran it; on a teammate's machine without that path the hooks do nothing (they never block). --skylos-bin skylos writes a plain skylos instead, so each machine uses the one on its own PATH. Personal overrides go in .claude/settings.local.json.

Claude can edit .claude/settings.json like any file. skylos done --base origin/main reports a change to it as SKY-A114 ("Skylos settings and hooks left alone"), and blocks by default.


Before you accept "done"

Run two commands on Claude's branch. They answer different questions:

skylos done --base origin/main                          # did it delete, skip or weaken tests?
skylos . --diff-base origin/main --danger --quality     # did it remove an auth, CSRF or rate-limit check?

skylos done re-runs your tests and compares them with the base (what it checks). It does not look at security controls: a branch whose only change was deleting @login_required got Verdict: PASS. The --diff-base scan reported that same deletion as SKY-L021 Security control regression: Auth decorator @login_required was removed (details).

If the repository is noisy, record today's findings with skylos baseline . and scan with --baseline, so only new findings show.


On the pull request: a check Claude can't edit

skylos cicd init --no-upload

This writes a GitHub Actions workflow with a changed-line scan ("Skylos Quality Gate") and, when it finds a pytest project, a "Skylos Done" job. No Skylos account or API key is needed. Two things to add in 4.47.1:

  1. The removed-controls job. The generated scan uses --diff, which keeps only findings on changed lines, so it did not report a deleted @login_required in our test and the gate passed. Add the removed-controls job.
  2. Branch protection. Make the Skylos jobs required status checks. Claude Code Review's neutral check can sit next to them as advice.

What to look for in Claude Code diffs

RiskWhy broad refactors produce itCovered by
Removed auth or permission checksRefactors optimize structure, not security intent--diff-base scan (SKY-L021), named patterns only
Insecure convenience flags (verify=False, debug=True)"Working now" beats "safe in production"Edit hook and --danger scan
Helpers nobody calls after extractionThe agent leaves plausible but unused codeskylos . dead-code scan
Calls to APIs or helpers that don't existThe code looks valid even when the call is wrongSKY-D224, SKY-L012 (details)
Deleted or skipped testsA green run is the goalskylos done
Tenant filters, business rules, CORSApplication-specificYour tests and human review

What this setup doesn't do

  • It is not a sandbox. The hooks don't restrict commands, files or network access. Use Claude Code's permission settings for that.
  • It doesn't see application-specific authorization, such as a tenant filter dropped from a query.
  • It doesn't replace a reviewer. It removes the machine-checkable problems before one reads the diff.

Where to go next

References

Running this across a team? These checks are free in the CLI. Skylos Cloud keeps the results in one place: the Free plan adds a pass/fail check on pull requests and scan history for one project, and the Workspace plan adds up to 10 projects, approvals for findings you decide not to fix, pull request comments, and audit exports.