← Blog /Engineering
Hack yourself first: why grayhat runs on a model that doesn’t refuse
Ask a frontier model to review your code for security bugs and you’ll get a polite list of things that might be wrong. Ask it to prove one, by writing the request that actually breaks in, and it will usually stop. The people attacking your app don’t have that problem.
The reviewer that won’t attack
Frontier models are remarkable engineers. They write good code, refactor it carefully and explain it well. They’re also trained, deliberately, to be safe for everyone who uses them, which means they’re trained to decline offensive work. For a general-purpose assistant that’s the right call. For a security reviewer it’s a handicap, and it shows up in three ways.
They hedge. Without the ability to test a theory, a cautious model flags everything that looks vaguely risky. You get “consider validating this input” on code that’s fine, sitting next to “ensure proper authorization” on the one route that’s actually open. Teams quickly learn to skim these reviews, and that’s how the real bug gets merged.
They stop at the important part. The difference between a scary comment and a security finding is proof: a request that reads someone else’s data, a race that lets a coupon apply twice. Writing that request is exactly what safety training teaches a model to avoid. Push, and you get a refusal or a “conceptual example” that doesn’t run.
> Prove that GET /invoices/:id is missing an ownership check.
I can't help with writing exploits against an application. Instead, consider
reviewing your authorization middleware and adding tests that ...
They can’t tell you what’s real. A reviewer that never attacks can’t separate the one exploitable bug from the forty plausible ones. So it reports all of them, or quietly drops the ones it’s unsure about. Either way, you’re the one doing the triage.
None of this is a criticism of the models. They’re doing what they were built to do. But a security review that won’t think like an attacker is a linter with nicer prose.
Attackers already use models that don’t refuse
Refusal isn’t a law of nature. It’s a behavior learned in training, and researchers have shown that in many open-weight models it’s controlled by a single direction in the model’s internal activations. Remove that direction from the weights, a technique known as abliteration, and you get a model that is just as capable but no longer declines.
Abliterated versions of strong open-weight models are a download away. Run one on a rented GPU and there’s no usage policy, no rate limit and no one reviewing the prompts. It will happily read your public API, guess how your tenancy works and write the requests to test it, as many times as it takes.
That’s the asymmetry. The attacker’s model explores freely. The defender’s model is trained to stop just before the answer. If your security review runs on the polite model, you’re defending with weaker tools than the people you’re defending against.
So point the same model at yourself
The answer isn’t to hope attackers play fair. It’s to run the same class of model, unfiltered and willing to write the exploit, against your own code, before anyone else does. In a place you control.
An unfiltered model is a tool. What makes it safe is where it’s pointed, so grayhat puts the guardrails in the system around the model rather than in the model’s weights:
- Consent. grayhat only touches repositories where you’ve installed the GitHub App. Remove a repo and scanning stops.
- A throwaway sandbox. Your app boots in an isolated virtual machine, from a clone made with a token scoped to that one repository. Outbound network is denied by default. The machine holds no model keys and no credentials, and it’s destroyed when the run ends. Nothing ever runs against production.
- Proof, or it didn’t happen. A suspicion only becomes a finding if the exploit works in the sandbox. Anything that can’t be proven is labelled likely, never passed off as confirmed.
- Your code stays yours. We keep the security index and the findings, not a copy of your source, and nothing is ever used to train a model.
What it does on every pull request
1. Index. grayhat reads the repository without running it and maps what an attacker cares about: routes and the guards that actually protect them, untrusted input and the dangerous sinks it can reach, what each module can do, and the supply chain. The index persists, so a new commit only re-reads what changed.
2. Diff. Instead of a text diff, it compares the index before and after your change and ranks what moved in security terms. A guard that disappeared is a first-class event, even though nothing “dangerous” was added:
- router.get("/invoices/:id", requireAuth, requireOwner, show)
+ router.get("/invoices/:id", requireAuth, show)
3. Hunt. The abliterated model gets the ranked targets and the full call paths, and writes real attacks against them: IDORs, broken auth, races, injection, business-logic abuse. A second, skeptical pass throws out anything it can’t justify.
4. Prove. The app boots in the sandbox and each exploit runs against it. Here, a user from one tenant reads another tenant’s invoice:
$ curl -s -H "Cookie: sid=$TENANT_B" localhost:3000/invoices/inv_7Hq2
HTTP/1.1 200 OK
{"id":"inv_7Hq2","tenant":"tenant_a","total":"48200.00", ...}
That’s no longer a “consider adding authorization” comment. It’s a critical finding, posted inline on the line that caused it, with the request above as its reproduction.
5. Fix. Here’s the part we like most: the fix is written by a regular coding model, because that’s what those models are great at. Then grayhat attacks the patch. The fix PR opens only when the original exploit fails and your test suite passes, in the same sandbox. If either check fails, you still get the finding and a ready-to-use fix prompt, and you aren’t charged.
Built for teams that ship every day
grayhat is meant to be left on. A review takes a single credit per push, any size. Docs-only and formatting-only changes are skipped for free, and a new push cancels the old run so you pay once. A verified fix PR costs five credits, and only if it opens. There are no seats and no repo limits.
The Hobby plan includes 50 free credits every month, so you can install it on a repo today and watch the first scan. Most teams find something in that first scan. We’d rather you hear about it from us.
grayhat attacks your code before someone else does. Install the GitHub App and run your first scan free.