hallint Update: What We Fixed, What We Shipped, and What's Coming in v0.2 The hallint project shipped fixes for false positives in its security linter, removing the async-no-catch rule from the recommended set and adding inline suppression markers for intentionally public routes. Version 0.2 will include dual-pass detection for hardcoded secrets and improved scanner dispatch. This is a follow-up to I Built a Linter That Catches the Security Bugs AI Assistants Keep Writing. The comments on that post shaped most of what's in this update — thank you. The first post got a lot of honest feedback. Not just "cool project" — actual technical pushback on the things that weren't right yet. This post covers what we fixed based on that, what's shipped since, and what's coming in v0.2. Current versions: @asyncinnovator/hallint — @asyncinnovator/hallint-cli — Two issues came up repeatedly and they were both valid. 1. async-no-catch was polluting the signal. Nazar Boyko https://dev.to/nazar-boyko put it cleanly: the rule breaks the bar of "a human looking at the flagged line agrees it's wrong." Plenty of correct async code has no try/catch because the caller handles it, or because an Express error middleware catches it upstream. Running hallint on a real codebase and getting forty async-no-catch warnings next to one genuine hardcoded key destroys the signal you worked to build. Fix shipped: async-no-catch is now removed from the recommended ruleset. It still exists and still runs if you opt in with --rules all , but it no longer fires by default. The seven security rules that share the "this line is wrong" property remain in recommended . 2. missing-auth-check had a false positive problem on intentionally public routes. Dipankar Sarkar https://dev.to/dipankar sarkar and Adam Lewis https://dev.to/nark3d both flagged this. Health checks, webhooks, public endpoints — they're supposed to have no auth middleware. Flagging them is noise, and noise gets the rule disabled. Fix shipped: hallint now recognizes three inline suppression markers: // public // hallint-public / hallint-public / Add any of these to a route and hallint skips the auth check on that handler. Clean, explicit, no magic inference needed. js app.get '/health', req, res = { // hallint-public res.json { status: 'ok' } } hardcoded-secret now runs dual-pass detection. The original regex caught generic api key = "..." patterns. It missed token prefixes that are dead giveaways regardless of variable name. The rule now has a second pass targeting known prefixes: ghp , sk- , AKIA , xoxb- . A line containing any of these is flagged as critical even if the variable name looks innocent. Comments and env-var assignments are excluded. sqlInjection message copy was over-claiming. The original message said "user input flowing into query" but the detection was pattern-based, not data-flow-based. It couldn't actually prove the variable was user input — just that a template literal was used inside a query call. The message now reflects what the rule actually detects: template literal interpolation in a query call. Accurate scope, no false confidence. Scanner dispatch was refactored. Previously, the scanner used rule.layer to decide whether to run regex or AST matching. This meant rules had to set layer correctly or they'd be silently skipped. Now rule.match presence is the dispatch signal — if a rule has a match function it gets AST treatment, otherwise regex. layer is now metadata only, used for documentation and filtering. This makes rule authoring less error-prone. asyncNoCatch detection was fragile. The brace-counter approach used to find async function boundaries broke on nested objects and template literals with braces. Replaced with a stripStrings heuristic that removes string content before counting — significantly fewer false positives even before the rule was moved out of recommended . You can now gate CI on hallint in five lines: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npx @asyncinnovator/hallint-cli ./src hallint exits 1 on critical or high findings, so this blocks merges on real issues. The LLM layer — when it lands — will never trigger exit code 1 . Non-deterministic checks don't belong in CI gates. A few things were promised in the comments and are actively in progress. Cross-file router composition tracking. Right now missing-auth-check is same-file only. If your middleware is registered in app.ts and your routes live in routes/users.ts , hallint doesn't connect them. This is the honest limitation of regex-based analysis — you can't follow imports. v0.2 introduces tree-sitter for AST analysis, which makes cross-file tracking possible. This is the main structural change in v0.2. LLM layer — opt-in only. The optional LLM review pass will land in v0.2 as a strict opt-in: --llm ollama or --llm anthropic . It will never run by default and will never contribute to exit code 1 . As Kartik noted in the comments, non-determinism in a CI gate is a dealbreaker. The LLM layer surfaces things the regex and AST layers miss — semantic issues, logic flaws, context-blind patterns — but those findings go in a separate output section, not in the main results that block a merge. New rules under consideration for v0.2–v0.3: silent-error-swallow — bare catch blocks that swallow errors without logging or rethrowing. The pattern Edu Peralta described: looks handled, isn't. jwt-in-localstorage — storing tokens where XSS can reach them hallucinated-dependency — imports of packages that don't exist in package.json a real AI-specific failure mode Each rule is a single file, around 30 lines, with a bad.ts and good.ts fixture. If you've seen a pattern AI assistants keep producing that hallint doesn't catch yet, the path from "I noticed this" to "I shipped a fix" is short. Issues labeled good first issue are pre-scoped and ready to pick up. GitHub: github.com/Asyncinnovator/hallint https://github.com/Asyncinnovator/hallint npx @asyncinnovator/hallint-cli ./src MIT licensed. Free for personal and commercial use.