# Show HN: Hallint – lint AI-generated code for security issues

> Source: <https://github.com/Asyncinnovator/hallint>
> Published: 2026-07-10 12:43:06+00:00



```
██╗  ██╗ █████╗ ██╗     ██╗     ██╗███╗   ██╗████████╗
██║  ██║██╔══██╗██║     ██║     ██║████╗  ██║╚══██╔══╝
███████║███████║██║     ██║     ██║██╔██╗ ██║   ██║   
██╔══██║██╔══██║██║     ██║     ██║██║╚██╗██║   ██║   
██║  ██║██║  ██║███████╗███████╗██║██║ ╚████║   ██║   
╚═╝  ╚═╝╚═╝  ╚═╝╚══════╝╚══════╝╚═╝╚═╝  ╚═══╝   ╚═╝
```

**Static analysis for AI-generated code.**
Catch the security holes Artificial Intelligence leave behind — before they reach production.

AI coding assistants (Copilot, Cursor, etc.) are fast, but they consistently introduce the same classes of bugs: hardcoded secrets, SQL injection, missing auth, unsafe eval. hallint catches them.

```
npm install @asyncinnovator/hallint
```

Or run without installing:

```
npx @asyncinnovator/hallint-cli ./src
```

**Requirements:** Node.js >= 18

```
npx @asyncinnovator/hallint-cli ./src
npx @asyncinnovator/hallint-cli "./src/**/*.ts"
npx @asyncinnovator/hallint-cli ./src --min-severity high
npx @asyncinnovator/hallint-cli ./src --rules all
npx @asyncinnovator/hallint-cli ./src --no-color
```

| Option | Description | Default |
|---|---|---|
`--rules` |
Rule set: `recommended` or `all` |
`recommended` |
`--min-severity` |
Minimum severity: `critical` `high` `medium` `low` `info` |
`info` |
`--no-color` |
Disable colored output | off |
`--help` |
Show help | |
`--version` |
Show version |

| Code | Meaning |
|---|---|
`0` |
No issues found |
`1` |
One or more critical or high findings |
`2` |
Unexpected error |

``` js
import { scan } from '@asyncinnovator/hallint'

const result = await scan({
  files: ['./src/**/*.ts'],
})

result.findings.forEach(f => {
  console.log(`${f.severity} [${f.ruleId}] ${f.filePath}:${f.line}`)
  console.log(`  ${f.message}`)
  console.log(`  fix: ${f.fix}`)
})
js
const result = await scan({
  files: ['./src/**/*.ts'],
  rules: 'recommended',
  minSeverity: 'high',
  ignore: ['**/node_modules/**', '**/dist/**'],
})
js
import { scanSource } from '@asyncinnovator/hallint'

const source = `const apiKey = "sk-abc123def456ghi789jk"`

const findings = scanSource(source, 'example.ts')

findings.forEach(f => console.log(f.ruleId, f.message))
{
  findings: Finding[]       // all issues found
  scannedFiles: string[]    // list of files scanned
  durationMs: number        // time taken
  summary: {                // count per severity
    critical: number
    high: number
    medium: number
    low: number
    info: number
  }
}
{
  ruleId: string        // e.g. "hardcoded-secret"
  severity: string      // "critical" | "high" | "medium" | "low" | "info"
  message: string       // human-readable description
  fix: string           // suggested fix
  filePath: string      // absolute path to the file
  line: number          // line number
  snippet: string       // the offending line of code
}
```

| Rule | Severity | What it catches |
|---|---|---|
`hardcoded-secret` |
critical | API keys, tokens, passwords in source code |
`sql-injection` |
critical | User input interpolated into SQL queries |
`unsafe-eval` |
critical | `eval()` or `new Function()` with dynamic input |
`missing-auth-check` |
high | Route handlers with no auth middleware |
`xss-innerHTML` |
high | Unsanitized strings assigned to `innerHTML` |
`permissive-cors` |
high | `cors({ origin: '*' })` in route handlers |
`async-no-catch` |
medium | `async` functions with no `try/catch` or `.catch()` |
`http-not-https` |
medium | Hardcoded `http://` URLs in fetch or axios calls |

Create a `hallint.config.ts`

at your project root:

``` python
import type { ScanConfig } from '@asyncinnovator/hallint'

export default {
  rules: 'recommended',
  minSeverity: 'medium',
  ignore: [
    '**/node_modules/**',
    '**/dist/**',
    '**/*.test.ts',
  ],
} satisfies Omit<ScanConfig, 'files'>
```

Add hallint as a PR gate — it exits with code `1`

on any critical or high finding:

```
name: hallint

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  hallint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npx @asyncinnovator/hallint-cli ./src --min-severity high
```

Install [husky](https://github.com/typicode/husky):

```
npm install --save-dev husky
npx husky init
echo "npx @asyncinnovator/hallint-cli ./src --min-severity high" > .husky/pre-commit
```

Contributions are welcome. Each rule is a single file (~30 lines) with a `bad.ts`

/ `good.ts`

fixture — a new rule is a good first contribution.

- Fork the repo
- Create a branch:
`git checkout -b feat/rule-your-rule-name`

- Add your rule in
`packages/core/src/rules/index.ts`

- Add fixtures in
`packages/core/tests/fixtures/your-rule-name/`

- Run tests:
`npm test`

- Open a PR

Issues labeled **good first issue** are pre-scoped and ready to pick up.

MIT — free to use in personal and commercial projects.
