I've been writing code with AI assistants for a while now. Copilot, Claude, ChatGPT — I've used them all. And for the most part, they're genuinely ...
For further actions, you may consider blocking this person and/or reporting abuse
Seven of your eight rules are "this is a vulnerability."
async-no-catchis the odd one out, and I'd think hard before shipping it alongside the others. Plenty of correct code has an async function with no try/catch because the caller handles it, or because it's an Express route behind an error middleware, or because throwing is the point. Medium severity won't save you there, since the first time someone runs hallint on a real codebase and gets forty of those next to one genuine hardcoded key, the signal you worked for is gone and the tool reads as noisy. The other seven all share a property that one doesn't: a human looking at the flagged line agrees it's wrong. That's the bar that makes a security linter trustworthy enough to gate CI on, and I'd protect it.That's a fair call. async-no-catch breaks the "a human looking at the flagged line agrees it's wrong" bar — and that bar is exactly what makes the other seven gateable. The plan is to move it behind a separate rule set (non-recommended) so it doesn't ship alongside the security rules by default. Running --rules all opts you in, recommended stays clean.
The three-layer split makes sense, though the optional LLM pass reintroduces the exact non-determinism the regex and AST layers avoid, so I would pin it behind a flag that never blocks a merge on its own. The missing-auth-on-route case is the one I find most valuable, because hardcoded secrets and raw SQL have existing scanners but "structurally complete router, half the handlers unprotected" is genuinely an AST-shaped problem. Does it track auth middleware across router composition, or only flag handlers defined in the same file where the middleware is registered?
Agreed on the LLM flag — that's exactly how it's planned, opt-in only and never a merge blocker. Non-determinism in a CI gate is a dealbreaker.
On the auth tracking question: currently same-file only. If middleware is registered in
app.tsand the router is defined inroutes/users.ts, hallint doesn't connect the two yet. Cross-file composition tracking is the main reason the AST layer (tree-sitter) is on the v0.2 roadmap — regex can't follow imports.That's the honest limitation right now. github.com/Asyncinnovator/hallint
The categories split cleanly by how catchable they are. Hardcoded secrets, template-string SQL,
cors: '*',innerHTMLwith user input are all syntactic. An AST rule nails them and you get real signal with near-zero false positives.The hard one on your list is missing auth on route handlers. That is not a pattern, it is an absence, and absence is context-dependent. A health check or a public webhook route is supposed to have no auth middleware. The moment you flag every unauthed route you drown people in false positives on exactly the routes meant to be open, and they turn the rule off.
So how does hallint scope that one? Allowlist of public routes, a convention (a
// publicmarker), or does it try to infer intent from the handler body? That single rule is where AI-focused linters live or die, because it is the one class ESLint genuinely cannot touch.Great point — that rule is the hardest one for exactly the reason you described. Right now it skips routes where auth middleware is applied at the router level, which reduces noise, but intentionally public routes (health checks, webhooks) still produce false positives.
The fix planned for v0.2 is an allowlist in config plus a
// hallint-disable missing-auth-checkinline escape. Your// publicmarker idea is cleaner though — opening an issue on it.If you want to weigh in: github.com/Asyncinnovator/hallint
A linter for AI-written security mistakes is exactly the kind of boring layer teams need. The best guardrails are not motivational prompts; they are repeatable checks around the classes of bugs the assistant keeps producing. That turns anecdotal distrust into a measurable feedback loop.
A linter for AI-written security mistakes is a good direction because it turns a vague concern into a repeatable check. The strongest rules are probably the ones tied to patterns assistants repeat: unsafe defaults, missing validation, secret handling, auth bypass assumptions, and trusting client input too early.
Interesting take on the growing dependency on AI-generated code. I've noticed similar patterns when working with GPU drivers and kernel modules — even high-quality AI outputs sometimes miss subtle race conditions or memory leaks. It's reassuring to see tools evolving to catch these slips, especially in security-sensitive contexts like confidential computing where the stakes are high.
This is exactly where AI coding needs boring deterministic tools around it. The model can generate patterns faster than humans can review them, so the review layer needs checks that do not get tired. A linter for recurring AI-written security mistakes is much more useful than another reminder to “review carefully.”
I gate CI on lint rules for the same reason, the agent takes a failing exit code seriously in a way it doesn't take review comments. The missing-auth false positives question below is the one I'd want answered too before wiring it in.
Exactly — exit codes are the only feedback loop agents actually respect. That's the whole reason hallint exits 1 on critical/high by default.
On the false positives question, answered in the thread above. Short version: v0.1 is same-file only, allowlist config is coming in v0.2 before most people would want to hard-gate on that rule.
This is exactly what the AI coding ecosystem needs not just better models, but better guardrails around them. AI generates code fast. It also generates vulnerabilities fast. The same speed that makes it useful is the same speed that makes it dangerous.
The security bugs AI assistants keep writing the fact that this is a consistent pattern means it's not a bug in any single model. It's a structural gap. And fixing it requires tools like this, not just better prompts.
Curious: what were the most common vulnerability patterns you found? SQL injection? Hard-coded secrets? Unsafe deserialization?
Thanks for building this and for sharing it.
hardcoded secrets and template-string SQL were by far the most common in my experience. Secrets because AI will complete whatever pattern you start — start typing const API_KEY = and it fills in a real-looking value. SQL because string interpolation is the obvious, readable solution and the model has no reason to prefer parameterized queries unless you explicitly ask.
Missing auth on route handlers was third, and the most dangerous because it's invisible — the code looks complete and correct, it just has a structural absence.
Unsafe deserialization is on the roadmap but didn't make v0.1 — it's harder to detect without deeper AST analysis.
Glad it's useful. github.com/Asyncinnovator/hallint
The pattern I keep catching in agent diffs is quieter than a classic vulnerability, a bare except swallowing a real error, or a fallback path that silently masks a failed auth check. A linter tuned to the specific mistakes agents repeat beats a generic scanner because the failures are not random, they cluster around the same three or four shapes every time. Are you tracking which rules fire most across your users? That list would basically write the next round of prompt guardrails.
That silent-failure pattern is the hard one — a bare catch that swallows the error, or a fallback that masks a failed auth. Worse than a missing check because it looks handled. We're not tracking rule fire rates across users yet but that's the right thing to build — aggregate signal from real codebases would basically write the v0.2 rule list. If you're seeing consistent shapes in agent diffs, opening an issue with examples would be genuinely useful: github.com/Asyncinnovator/hallint