eslint-plugin-ai-guard 1.4.3 → 1.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,715 +1,766 @@
1
- <p align="center">
2
- <a href="https://getaiguard.dev">
3
- <img src="./assets/logo/ai-guard-logo.png" alt="AI Guard Logo" width="260" />
4
- </a>
5
- </p>
6
-
7
- <h1 align="center">AI Guard</h1>
8
-
9
- <p align="center">
10
- <strong>Deterministic AST safety layer and CI guardrails for AI-assisted JavaScript and TypeScript code.</strong>
11
- </p>
12
-
13
- <p align="center">
14
- <a href="https://getaiguard.dev"><strong>Website</strong></a> &nbsp;•&nbsp;
15
- <a href="https://www.npmjs.com/package/eslint-plugin-ai-guard"><strong>npm Package</strong></a> &nbsp;•&nbsp;
16
- <a href="https://github.com/ai-guard-dev/eslint-plugin-ai-guard"><strong>GitHub Repository</strong></a> &nbsp;•&nbsp;
17
- <a href="./docs/rules/"><strong>Rules Catalog</strong></a> &nbsp;•&nbsp;
18
- <a href="./docs/benchmarks.md"><strong>Benchmarks</strong></a> &nbsp;•&nbsp;
19
- <a href="./docs/integrations/claude-code.md"><strong>Claude Code</strong></a> &nbsp;•&nbsp;
20
- <a href="./docs/getting-started.md"><strong>Documentation</strong></a>
21
- </p>
22
-
23
- <p align="center">
24
- <a href="https://www.npmjs.com/package/eslint-plugin-ai-guard"><img src="https://img.shields.io/npm/v/eslint-plugin-ai-guard.svg?style=flat-square&color=0284c7" alt="npm version"></a>
25
- <a href="https://github.com/ai-guard-dev/eslint-plugin-ai-guard/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/ai-guard-dev/eslint-plugin-ai-guard/ci.yml?style=flat-square&label=CI&color=10b981" alt="CI Status"></a>
26
- <a href="https://www.npmjs.com/package/eslint-plugin-ai-guard"><img src="https://img.shields.io/npm/dm/eslint-plugin-ai-guard.svg?style=flat-square&color=3b82f6" alt="npm downloads"></a>
27
- <a href="https://github.com/ai-guard-dev/eslint-plugin-ai-guard/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-64748b?style=flat-square" alt="MIT License"></a>
28
- <a href="https://github.com/ai-guard-dev/eslint-plugin-ai-guard/blob/main/SECURITY.md"><img src="https://img.shields.io/badge/security-policy-orange?style=flat-square" alt="Security Policy"></a>
29
- <a href="https://getaiguard.dev"><img src="https://img.shields.io/badge/website-getaiguard.dev-0ea5e9?style=flat-square" alt="Website"></a>
30
- </p>
31
-
32
- ---
33
-
34
- ## What is AI Guard?
35
-
36
- **AI Guard** (`eslint-plugin-ai-guard`) provides:
37
- - **ESLint plugin** with 18 deterministic rules and 4 presets (`recommended`, `strict`, `security`, `agent`)
38
- - **CLI** (`ai-guard`) for terminal, changed-file, and CI scanning
39
- - **GitHub Action** with native SARIF 2.1.0 output for GitHub Code Scanning
40
- - **Check integrity** (`ai-guard integrity`) — detects changes that weaken checks: new suppressions, skipped or removed tests, lowered ESLint/TypeScript/coverage settings, removed CI steps
41
- - **Claude Code integration** with hooks that send findings back to Claude and block finishing once when checks were weakened
42
- - **MCP server** (`ai-guard-mcp`) for local AI-agent workflows over STDIO
43
-
44
- It is engineered to detect reliability bugs, async hazards, security vulnerabilities, and code-scaffolding defects frequently introduced during AI-assisted development (GitHub Copilot, Cursor, Claude Code, Gemini Code Assist, etc.).
45
-
46
- AI Guard analyzes Abstract Syntax Trees (AST) using ESLint's native engine. It runs locally in your editor, in your terminal via the zero-config CLI, and in your CI/CD pipelines via native SARIF 2.1.0 integration with GitHub Code Scanning.
47
-
48
- ### What AI Guard is NOT
49
-
50
- > [!IMPORTANT]
51
- > - **AI Guard is NOT an AI detector.** It does not attempt to predict whether code was authored by an LLM or a human.
52
- > - **AI Guard detects dangerous or fragile code patterns** that LLMs repeatedly introduce due to incomplete context, hallucinated patterns, or probabilistic generation.
53
- > - **AI Guard does NOT replace ESLint.** It extends ESLint with 18 specialized, high-impact rules that core ESLint and standard configurations omit.
54
-
55
- ---
56
-
57
- ## Why AI Guard?
58
-
59
- AI coding assistants write code at remarkable velocity, but generated code repeatedly suffers from predictable reliability and security anti-patterns that conventional linters miss:
60
-
61
- | Pattern | Why AI Assistants Generate It | Real-World Impact |
62
- | :--- | :--- | :--- |
63
- | **Floating Promises** | Omits `await`, `return`, or `.catch()` on async calls | Unhandled promise rejections, silent failures in background jobs |
64
- | **Async Array Iteration** | Passes async callbacks into `array.map()` or `.filter()` | Returns unawaited `Promise[]` instead of resolved values |
65
- | **Sequential Awaits in Loops** | Loops over items with sequential `await` | Significant latency bottlenecks; blocks event loop execution |
66
- | **Empty Catch Blocks** | Inserts generic `try { ... } catch (e) {}` blocks | Swallows production exceptions silently without telemetry |
67
- | **Hardcoded Secrets** | Injects placeholder or real API keys/tokens | Credential leakage in version control and deployment bundles |
68
- | **Dynamic `eval()`** | Generates dynamic function compilation | Arbitrary code execution and code injection |
69
- | **Raw SQL Concatenation** | Concatenates query strings with variables | Severe SQL injection vulnerabilities |
70
- | **Unsafe Deserialization** | Calls `JSON.parse(req.body)` directly without schema checks | Denial of service and unhandled runtime crashes |
71
- | **Missing Route Auth & Authz** | Emits boilerplate endpoints without auth middleware | Unprotected API endpoints and IDOR privilege escalations |
72
- | **Dead Branches & Scaffolding** | Leaves `if (true)` or conflicting conditions from prompt iterations | Bloated bundles and dead code paths |
73
-
74
- AI Guard provides an instantaneous, deterministic feedback loop that catches these issues before they reach pull requests or production.
75
-
76
- ---
77
-
78
- ## Architecture & Workflow
79
-
80
- ```mermaid
81
- flowchart TD
82
- subgraph Dev["1. Development & Prompt Phase"]
83
- A["Developer + AI Coding Assistant\n(Copilot, Cursor, Claude Code)"] --> B["JavaScript / TypeScript Code"]
84
- end
85
-
86
- subgraph ShiftLeft["Shift Left — Context Injection"]
87
- SL["npx ai-guard init-context"] -.-> CTX["CLAUDE.md\n.cursorrules\ncopilot-instructions.md"]
88
- CTX -.-> A
89
- end
90
-
91
- subgraph Analysis["2. Deterministic AST Analysis"]
92
- B --> C["ESLint Parser\n(espree / @typescript-eslint/parser)"]
93
- C --> D["AST Representation"]
94
- D --> E["AI Guard Rules Engine\n(18 Deterministic Rules)"]
95
- end
96
-
97
- subgraph Tiers["3. Classification & Presets"]
98
- E --> F{"Active Preset\n(recommended | strict | security | agent)"}
99
- F --> G["Confidence Tiering & AST Filtering"]
100
- end
101
-
102
- subgraph Outputs["4. Output & Remediation"]
103
- G --> H["Local CLI Scanning\n(ai-guard run / changed)"]
104
- G --> I["Autofix Remediation\n(eslint --fix)"]
105
- G --> J["HTML Dashboard\n(ai-guard report)"]
106
- G --> K["SARIF 2.1.0 Artifact\n(ai-guard --sarif)"]
107
- end
108
-
109
- subgraph CI["5. GitHub Pull Request & CI/CD"]
110
- K --> L["GitHub Action\nai-guard-dev/eslint-plugin-ai-guard@v1"]
111
- L --> M["GitHub Code Scanning Alerts"]
112
- L --> N["Inline PR Code Annotations"]
113
- L --> O["PR Status Check (Blocks Merge)"]
114
- end
115
- ```
116
-
117
- ---
118
-
119
- ## Rules Catalog
120
-
121
- AI Guard includes **18 deterministic rules** divided into four specialized categories. Every rule is engineered with low false-positive heuristics and validated against real-world production codebases:
122
-
123
- ### 🔴 Security (6 Rules)
124
-
125
- | Rule | Recommended | What It Catches | Fixable? |
126
- | :--- | :---: | :--- | :---: |
127
- | [`no-hardcoded-secret`](./docs/rules/no-hardcoded-secret.md) | **`error`** | API keys, bearer tokens, passwords, and private keys committed directly in source code. | **Yes** (`process.env.*`) |
128
- | [`no-eval-dynamic`](./docs/rules/no-eval-dynamic.md) | **`error`** | `eval()`, `new Function()`, and `setTimeout`/`setInterval` with dynamic/non-literal string expressions. | No |
129
- | [`no-sql-string-concat`](./docs/rules/no-sql-string-concat.md) | `warn` | SQL queries constructed by string concatenation or raw template literals — SQL injection risks. | No |
130
- | [`no-unsafe-deserialize`](./docs/rules/no-unsafe-deserialize.md) | `warn` | Unchecked `JSON.parse()` called directly on HTTP request inputs (`req.body`, `req.query`, `req.params`). | No |
131
- | [`require-auth-middleware`](./docs/rules/require-auth-middleware.md) | `warn` | Express and Fastify route definitions exposed without authentication middleware. | No |
132
- | [`require-authz-check`](./docs/rules/require-authz-check.md) | `warn` | Endpoints accessing sensitive resources or user IDs without tenant/ownership authorization checks. | No |
133
-
134
- ### 🟠 Reliability (4 Rules)
135
-
136
- | Rule | Recommended | What It Catches | Fixable? |
137
- | :--- | :---: | :--- | :---: |
138
- | [`no-empty-catch`](./docs/rules/no-empty-catch.md) | **`error`** | Empty `catch (e) {}` blocks that silently swallow exceptions without logging or rethrowing. | **Yes** (inserts `/* TODO: handle error */`) |
139
- | [`no-broad-exception`](./docs/rules/no-broad-exception.md) | `warn` | Catching broad exception types like `catch (e: any)` that mask system faults and typing. | No |
140
- | [`no-catch-log-rethrow`](./docs/rules/no-catch-log-rethrow.md) | `off`* | Catch blocks that only log to `console` and rethrow without adding context or diagnostic info. | No |
141
- | [`no-catch-without-use`](./docs/rules/no-catch-without-use.md) | `off`* | Caught error variables that are declared in catch parameters but never referenced. | No |
142
-
143
- ### 🟡 Async Stability (5 Rules)
144
-
145
- | Rule | Recommended | What It Catches | Fixable? |
146
- | :--- | :---: | :--- | :---: |
147
- | [`no-floating-promise`](./docs/rules/no-floating-promise.md) | **`error`** | Async function invocations without `await`, `.catch()`, or `return` — leading to silent dropped errors. | **Yes** (marks with `void`) |
148
- | [`no-async-array-callback`](./docs/rules/no-async-array-callback.md) | `warn` | Async callbacks passed to `map()`, `filter()`, `forEach()`, or `reduce()` returning `Promise[]`. | No |
149
- | [`no-await-in-loop`](./docs/rules/no-await-in-loop.md) | `warn` | Sequential `await` in loops where iterations can be safely executed concurrently with `Promise.all`. | **Yes** (rewrites to `Promise.all`) |
150
- | [`no-async-without-await`](./docs/rules/no-async-without-await.md) | `warn` | Functions declared `async` that never execute an `await` expression, adding unnecessary Promise overhead. | No |
151
- | [`no-redundant-await`](./docs/rules/no-redundant-await.md) | `off`* | Redundant `return await` statements outside of `try...catch` blocks. | No |
152
-
153
- ### 🔵 AI Patterns (3 Rules)
154
-
155
- | Rule | Recommended | What It Catches | Fixable? |
156
- | :--- | :---: | :--- | :---: |
157
- | [`no-dead-branch`](./docs/rules/no-dead-branch.md) | `warn` | Unreachable or tautological branches (`if (true)`, `if (false)`, `x && !x`) left behind from LLM code synthesis. | No |
158
- | [`no-duplicate-logic-block`](./docs/rules/no-duplicate-logic-block.md) | `off`* | Consecutive duplicate code blocks or repeated conditional branches duplicated during AI edits. | No |
159
- | [`no-console-in-handler`](./docs/rules/no-console-in-handler.md) | `off`* | Unstructured `console.log` statements left in HTTP route handlers instead of production loggers. | No |
160
-
161
- *\* Enabled at `error` level in the `strict` preset.*
162
-
163
- ---
164
-
165
- ## Presets
166
-
167
- AI Guard exports four official configurations ready for flat config or legacy setups:
168
-
169
- | Preset | Description | Configuration Focus |
170
- | :--- | :--- | :--- |
171
- | **`recommended`** | **Default.** Balanced adoption preset. Enables 4 high-confidence critical rules at `error`, 9 context-sensitive rules at `warn`, and disables 5 noisy rules. Zero noise on day one. | Production codebases, new teams |
172
- | **`strict`** | Enforces **all 18 rules at `error`**. Designed for zero-tolerance CI gates, high-assurance software, and mature teams. | Strict CI/CD quality gates |
173
- | **`security`** | Focuses exclusively on the **6 security rules** (`no-hardcoded-secret`, `no-eval-dynamic`, `no-sql-string-concat` at `error`; remainder at `warn`). | AppSec auditing & security scans |
174
- | **`agent`** | **5 high-signal rules at `error`**. Optimized for AI-agent editing workflows where lint feedback runs immediately after each file edit (e.g., PostToolUse hooks). | Claude Code, Cursor, real-time agent loops |
175
-
176
- ---
177
-
178
- ## Quick Start & Installation
179
-
180
- Install the package as a development dependency using your package manager:
181
-
182
- ```bash
183
- # npm
184
- npm install --save-dev eslint-plugin-ai-guard
185
-
186
- # pnpm
187
- pnpm add -D eslint-plugin-ai-guard
188
-
189
- # yarn
190
- yarn add -D eslint-plugin-ai-guard
191
-
192
- # bun
193
- bun add -d eslint-plugin-ai-guard
194
- ```
195
-
196
- ### Requirements
197
-
198
- - **Node.js:** `>= 20.0.0`
199
- - **ESLint:** `>= 8.0.0` (Supports both Flat Config and legacy configs)
200
- - **TypeScript (optional):** `@typescript-eslint/parser >= 6.0.0` for TypeScript AST parsing
201
-
202
- ---
203
-
204
- ## ESLint Configuration
205
-
206
- ### 1. Modern Flat Config (`eslint.config.mjs` / `eslint.config.js`)
207
-
208
- AI Guard exports full native support for modern ESLint Flat Config:
209
-
210
- ```javascript
211
- // eslint.config.mjs
212
- import aiGuard from 'eslint-plugin-ai-guard';
213
-
214
- export default [
215
- {
216
- plugins: {
217
- 'ai-guard': aiGuard,
218
- },
219
- rules: {
220
- ...aiGuard.configs.recommended.rules,
221
- // Custom overrides if desired:
222
- 'ai-guard/no-floating-promise': 'error',
223
- },
224
- },
225
- ];
226
- ```
227
-
228
- To use the `strict` or `security` preset in flat config:
229
-
230
- ```javascript
231
- // Strict preset — all 18 rules at error
232
- rules: {
233
- ...aiGuard.configs.strict.rules,
234
- }
235
-
236
- // Security preset — security rules only
237
- rules: {
238
- ...aiGuard.configs.security.rules,
239
- }
240
- ```
241
-
242
- ### 2. Legacy Config (`.eslintrc.js` / `.eslintrc.json`)
243
-
244
- ```javascript
245
- // .eslintrc.js
246
- module.exports = {
247
- plugins: ['ai-guard'],
248
- extends: ['plugin:ai-guard/recommended'],
249
- };
250
- ```
251
-
252
- ---
253
-
254
- ## CLI Reference
255
-
256
- AI Guard includes a full-featured CLI binary (`ai-guard`) that runs out of the box with zero ESLint configuration files required:
257
-
258
- ```bash
259
- npx ai-guard <command> [options]
260
- ```
261
-
262
- ### Core Commands
263
-
264
- | Command | Purpose | Common Options |
265
- | :--- | :--- | :--- |
266
- | `run` | Scan your workspace using AI Guard AST rules | `--path <dir>`, `--strict`, `--security`, `--json`, `--sarif`, `--fail-on <level>`, `--max-warnings <n>`, `--inline-suppressions <mode>` |
267
- | `changed` | Fast CI scan — only scans modified files in git | `--pr`, `--staged`, `--base <branch>`, `--baseline [file]`, `--integrity <off\|report\|enforce>`, `--inline-suppressions <mode>`, `--strict`, `--sarif`, `--sarif-output <file>`, `--fail-on <level>` |
268
- | `integrity` | Detect changes that weaken checks vs a base revision ([docs](./docs/integrity.md)) | `--base <ref>`, `--pr`, `--staged`, `--enforce`, `--json`, `--sarif`, `--accept <fingerprints\|all>` |
269
- | `init` | Automatically detect environment & configure ESLint | `--preset <name>`, `--flat`, `--dry-run`, `-y, --yes` |
270
- | `init-context` | Generate prompt instruction files for AI coding agents | `-a, --all`, `--force`, `--dry-run`, `--rules <categories>` |
271
- | `doctor` | Diagnose your ESLint, parser, and plugin environment | (No options needed — prints actionable diagnostic report) |
272
- | `baseline` | Snapshot current issues to track only new regressions | `--save`, `--check`, `--mode <strict\|stable>`, `--preset <name>` |
273
- | `report` | Generate an interactive standalone HTML audit report | `--path <dir>`, `--preset <name>`, `--output <file>`, `--no-open`, `--json` |
274
- | `preset` | Interactively select and switch active preset in config | (Interactive prompt with automatic config patch & backup) |
275
- | `ignore` | Add standard ignore paths (`.next`, `dist`, `build`) to config | (Patches flat config or legacy ignores safely) |
276
-
277
- **`run` exit codes:** `0` no errors · `1` errors found (or `--max-warnings` exceeded) · `2` scan did not run — the file passed to `--path` matches a built-in ignore pattern (`node_modules/`, `dist/`, `build/`, `generated/`, `vendor/`, …), so nothing was analyzed. Directory scans skip ignored files silently and never exit `2`.
278
-
279
- ### CLI Usage Examples
280
-
281
- ```bash
282
- # 1. Immediate scan of current directory
283
- npx ai-guard run
284
-
285
- # 2. Strict CI scan failing only on high-confidence issues
286
- npx ai-guard run --strict --fail-on high
287
-
288
- # 3. Pull Request scan (diffs against PR target branch)
289
- npx ai-guard changed --pr --sarif --sarif-output results.sarif
290
-
291
- # 4. Generate AI agent guardrails for Cursor, Claude Code, and Copilot
292
- npx ai-guard init-context --all
293
-
294
- # 5. Generate interactive HTML diagnostic report
295
- npx ai-guard report --output ai-guard-report.html
296
-
297
- # 6. Save existing issues as baseline and only fail on new regressions
298
- npx ai-guard baseline --save
299
- npx ai-guard baseline --check
300
-
301
- # 7. Fail a PR that skips tests, adds eslint-disable, or lowers coverage thresholds
302
- npx ai-guard changed --pr --integrity enforce
303
- ```
304
-
305
- ### Inline suppressions never hide findings silently
306
-
307
- AI Guard reads `eslint-disable` directives itself; ESLint's own inline config is disabled in AI Guard's private ESLint instance, so a comment cannot make a finding vanish without a trace. `--inline-suppressions` selects the policy:
308
-
309
- | Mode | Effect |
310
- |---|---|
311
- | `allow` (default) | A covering directive hides the finding from totals and exit codes; it is listed under "Hidden by inline directives" (JSON `suppressedIssues`, step summary, SARIF `suppressedFindingsCount`) |
312
- | `require-reason` | Only directives with a ` -- reason` hide findings (used by the Claude Code hook) |
313
- | `ignore` | Directives never hide AI Guard findings (always used by the MCP server) |
314
-
315
- This applies to `ai-guard run`, `changed`, the GitHub Action, the Claude Code hook and MCP. It does not change how your own `eslint .` treats directives. TypeScript comments (`@ts-ignore`, `@ts-nocheck`) never affect AI Guard findings; `ai-guard integrity` reports newly added ones.
316
-
317
- ---
318
-
319
- ## AI Agent Integration (`init-context`)
320
-
321
- Standard linters only run **after** code has already been written. The `init-context` command shifts your guardrails left by embedding AI Guard's rules directly into the instruction files loaded by your AI coding tools:
322
-
323
- ```bash
324
- npx ai-guard init-context --all
325
- ```
326
-
327
- This generates three targeted context files:
328
- 1. **`CLAUDE.md`** — Automatically loaded by **Claude Code**
329
- 2. **`.cursorrules`** — Automatically loaded by **Cursor**
330
- 3. **`.github/copilot-instructions.md`** — Automatically loaded by **GitHub Copilot**
331
-
332
- ### How Shift-Left Works
333
-
334
- ```
335
- AI Guard (init-context)
336
- ↓
337
- Generates project guardrail files (CLAUDE.md, .cursorrules, copilot-instructions.md)
338
- ↓
339
- AI coding assistant reads safety rules before generating code
340
- ↓
341
- Model avoids floating promises, empty catches, and hardcoded secrets at prompt time
342
- ↓
343
- AI Guard CLI & GitHub Action deterministically verifies the output in CI
344
- ```
345
-
346
- This dual-layer defense minimizes review friction and ensures generated code meets your security standard on the first pass.
347
-
348
- ---
349
-
350
- ## Claude Code Integration
351
-
352
- AI Guard integrates directly with [Claude Code](https://code.claude.com/docs/en/hooks) through hooks:
353
-
354
- ```bash
355
- # One-command setup
356
- npx ai-guard init-claude
357
- ```
358
-
359
- - **PostToolUse** — after each edit, findings from the `agent` preset and newly weakened checks in the edited file are sent back to Claude (`decision: "block"` + `reason`, per the hooks protocol). Earlier versions printed plain stdout, which Claude Code does not show to Claude.
360
- - **SessionStart + Stop** — before Claude finishes, [check integrity](./docs/integrity.md) runs on everything changed in the session. High-confidence weakenings (a new `.skip`, `eslint-disable`, lowered threshold…) block stopping once per finding set; advisories are shown to you, never block.
361
- - **PreToolUse (Bash)** — `git commit --no-verify` / `HUSKY=0` ask for confirmation.
362
-
363
- ```bash
364
- # Preview before applying
365
- npx ai-guard init-claude --dry-run
366
-
367
- # Use per-machine settings (gitignored)
368
- npx ai-guard init-claude --local
369
- ```
370
-
371
- **Agent preset rules:** `no-hardcoded-secret`, `no-eval-dynamic`, `no-empty-catch`, `no-sql-string-concat`, `no-floating-promise`
372
-
373
- → [Full documentation](./docs/integrations/claude-code.md)
374
-
375
- ---
376
-
377
- ## GitHub Action
378
-
379
- The official AI Guard GitHub Action runs on PRs, detects changed files, provides step summaries, outputs SARIF 2.1.0, and posts inline PR annotations directly on GitHub:
380
-
381
- ```yaml
382
- # .github/workflows/ai-guard.yml
383
- name: AI Guard
384
-
385
- on:
386
- pull_request:
387
- branches: [main, develop]
388
- push:
389
- branches: [main]
390
-
391
- jobs:
392
- ai-guard-scan:
393
- name: AI Guard Code Review
394
- runs-on: ubuntu-latest
395
-
396
- permissions:
397
- contents: read
398
- security-events: write
399
- actions: read
400
-
401
- steps:
402
- - name: Checkout Code
403
- uses: actions/checkout@v4
404
- with:
405
- fetch-depth: 0 # Required for git diff comparison
406
-
407
- - name: Setup Node.js
408
- uses: actions/setup-node@v4
409
- with:
410
- node-version: 20
411
- cache: 'npm'
412
-
413
- - name: Run AI Guard
414
- uses: ai-guard-dev/eslint-plugin-ai-guard@v1
415
- with:
416
- preset: 'recommended'
417
- fail-on: 'high'
418
- changed-only: 'true'
419
- upload-sarif: 'true'
420
- ```
421
-
422
- ### Action Inputs (`action.yml`)
423
-
424
- | Input | Description | Default |
425
- | :--- | :--- | :---: |
426
- | `preset` | Rule preset: `recommended` \| `strict` \| `security` | `'recommended'` |
427
- | `fail-on` | Severity threshold to fail CI: `high` \| `medium` \| `any` \| `none` | `'high'` |
428
- | `changed-only` | Scan only files changed in this PR / commit | `'true'` |
429
- | `path` | Target file or directory to scan | `'.'` |
430
- | `upload-sarif` | Upload results to GitHub Code Scanning | `'true'` |
431
- | `github-summary` | Write an execution breakdown to the GitHub Actions Job Summary | `'true'` |
432
- | `working-directory`| Working directory for scanning (ideal for monorepos) | `'.'` |
433
- | `package-manager` | Package manager: `auto` \| `npm` \| `pnpm` \| `yarn` | `'auto'` |
434
- | `sarif-output` | Output filepath for generated SARIF report | `'ai-guard-results.sarif'` |
435
- | `install-deps` | Install project dependencies prior to scanning | `'true'` |
436
- | `baseline` | Committed baseline file; with `changed-only`, report only findings not in it | `''` |
437
- | `integrity` | Check integrity: `off` \| `report` \| `enforce` (needs `changed-only` and `fetch-depth: 0`) | `'off'` |
438
- | `inline-suppressions` | `allow` \| `require-reason` \| `ignore` for eslint-disable directives covering AI Guard findings | `''` (allow) |
439
-
440
- ### Action Outputs
441
-
442
- | Output | Description |
443
- | :--- | :--- |
444
- | `issues-found` | Total number of issues found across scanned files |
445
- | `high-confidence-count` | Number of high-confidence issues flagged |
446
- | `medium-confidence-count` | Number of medium-confidence issues flagged |
447
- | `files-scanned` | Count of files analyzed during the execution |
448
- | `integrity-high-count` | High-confidence check-integrity findings (when `integrity` is enabled) |
449
- | `integrity-advisory-count` | Advisory check-integrity findings (when `integrity` is enabled) |
450
- | `sarif-file` | Absolute path to the generated SARIF 2.1.0 artifact |
451
- | `duration-ms` | Total scan execution duration in milliseconds |
452
-
453
- ---
454
-
455
- ## SARIF & GitHub Code Scanning
456
-
457
- AI Guard natively outputs **SARIF 2.1.0** (`Static Analysis Results Interchange Format`). When uploaded via the GitHub Action or `github/codeql-action/upload-sarif@v3`, findings integrate directly with GitHub Advanced Security:
458
-
459
- - **Inline PR Annotations:** Direct comments on the exact source lines where flaws exist.
460
- - **Security Dashboard:** Persistent alerts under your repository's `Security → Code scanning` tab.
461
- - **Merge Protection:** Block merges automatically when high-confidence security or async bugs are detected.
462
-
463
- ### Visual Previews
464
-
465
- #### Inline Pull Request Annotations
466
- <img src="./assets/ss9.jpg" alt="AI Guard PR Inline Annotations" width="1000" />
467
-
468
- #### GitHub Advanced Security Summary
469
- <img src="./assets/ss10.jpg" alt="GitHub Advanced Security Summary for AI Guard" width="1000" />
470
-
471
- #### Persistent Code Scanning Dashboard
472
- <img src="./assets/ss11.jpg" alt="GitHub Code Scanning Alerts List" width="1000" />
473
-
474
- ---
475
-
476
- ## Rule Examples (Before & After)
477
-
478
- ### 1. `no-floating-promise` (Unhandled Promises)
479
-
480
- ```typescript
481
- // ❌ BAD: Floating promise. Errors are dropped silently.
482
- async function syncUserProfile(user: User) {
483
- sendTelemetryEvent('user_sync', user.id);
484
- database.save(user);
485
- }
486
-
487
- // ✅ GOOD: Awaited, explicitly handled, or marked with void
488
- async function syncUserProfile(user: User) {
489
- await database.save(user);
490
- void sendTelemetryEvent('user_sync', user.id); // Explicitly unhandled
491
- }
492
- ```
493
-
494
- ### 2. `no-hardcoded-secret` (Committed Credentials)
495
-
496
- ```typescript
497
- // ❌ BAD: Secret committed inline
498
- const client = new PaymentGateway({
499
- apiKey: 'sk-prod-983427598273498273948273',
500
- });
501
-
502
- // ✅ GOOD: Read from environment variable (Autofixable!)
503
- const client = new PaymentGateway({
504
- apiKey: process.env.API_KEY,
505
- });
506
- ```
507
-
508
- ### 3. `no-await-in-loop` (Sequential Latency Trap)
509
-
510
- ```typescript
511
- // ❌ BAD: Consecutive awaits block each iteration sequentially
512
- async function fetchAllUsers(ids: string[]) {
513
- const users = [];
514
- for (const id of ids) {
515
- users.push(await fetchUser(id));
516
- }
517
- return users;
518
- }
519
-
520
- // ✅ GOOD: Concurrently fetched with Promise.all (Autofixable!)
521
- async function fetchAllUsers(ids: string[]) {
522
- return await Promise.all(ids.map((id) => fetchUser(id)));
523
- }
524
- ```
525
-
526
- ### 4. `no-empty-catch` (Swallowed Errors)
527
-
528
- ```typescript
529
- // ❌ BAD: Exception swallowed without trace
530
- try {
531
- parseConfiguration(rawConfig);
532
- } catch (e) {}
533
-
534
- // ✅ GOOD: Logged, rethrown, or documented (Autofixable!)
535
- try {
536
- parseConfiguration(rawConfig);
537
- } catch (e) {
538
- logger.error('Configuration parsing failed', { error: e });
539
- throw e;
540
- }
541
- ```
542
-
543
- ### 5. `no-sql-string-concat` (SQL Injection)
544
-
545
- ```typescript
546
- // ❌ BAD: Dynamic string interpolation in SQL
547
- const query = `SELECT * FROM users WHERE organization_id = '${orgId}' AND role = '${role}'`;
548
- await db.query(query);
549
-
550
- // ✅ GOOD: Parameterized query binding
551
- const query = 'SELECT * FROM users WHERE organization_id = $1 AND role = $2';
552
- await db.query(query, [orgId, role]);
553
- ```
554
-
555
- ---
556
-
557
- ## Automatic Remediation (Autofix)
558
-
559
- Rules that have deterministic solutions provide automatic autofix handlers. Run ESLint's native `--fix` flag to automatically resolve them:
560
-
561
- ```bash
562
- npx eslint . --fix
563
- ```
564
-
565
- | Rule | Automatic Fix Behavior |
566
- | :--- | :--- |
567
- | `no-hardcoded-secret` | Replaces hardcoded string literal with `process.env.VARIABLE_NAME` |
568
- | `no-empty-catch` | Inserts `/* TODO: handle error */` comment to prevent silent swallowing |
569
- | `no-floating-promise` | Prepends `void ` expression to intentionally unawaited calls |
570
- | `no-await-in-loop` | Rewrites straightforward sequential loops to `await Promise.all(...)` |
571
-
572
- ---
573
-
574
- ## Benchmarks & Empirical Evaluation
575
-
576
- AI Guard has been empirically evaluated across multiple benchmarks comparing runtime scan performance, coverage gaps vs. standard tooling, and detection accuracy across real-world codebases.
577
-
578
- ### 1. What AI Guard Catches vs. Conventional Linters
579
-
580
- Conventional linters either omit AI-specific hazards entirely or require heavyweight TypeScript type-checking (`parserOptions.project`) that significantly slows down CI:
581
-
582
- | Pattern | AI Guard Rule | ESLint Core | `@typescript-eslint` |
583
- | :--- | :--- | :---: | :--- |
584
- | **Floating Promises** (unawaited async call) | [`no-floating-promise`](./docs/rules/no-floating-promise.md) | ❌ None | `@typescript-eslint/no-floating-promises` *(requires type info)* |
585
- | **Async Array Callbacks** (`.map(async ...)`) | [`no-async-array-callback`](./docs/rules/no-async-array-callback.md) | ❌ None | Partial: `no-misused-promises` *(requires type info)* |
586
- | **Empty Catch Blocks** (swallowed errors) | [`no-empty-catch`](./docs/rules/no-empty-catch.md) | `no-empty` *(weaker)* | ❌ None |
587
- | **Hardcoded Secrets / API Tokens** | [`no-hardcoded-secret`](./docs/rules/no-hardcoded-secret.md) | ❌ None | ❌ None |
588
- | **Raw SQL String Concatenation** | [`no-sql-string-concat`](./docs/rules/no-sql-string-concat.md) | ❌ None | ❌ None |
589
- | **Missing Route Auth Middleware** | [`require-auth-middleware`](./docs/rules/require-auth-middleware.md) | ❌ None | ❌ None |
590
- | **Missing Route Authorization Checks** | [`require-authz-check`](./docs/rules/require-authz-check.md) | ❌ None | ❌ None |
591
- | **Dynamic `eval()` / `new Function()`** | [`no-eval-dynamic`](./docs/rules/no-eval-dynamic.md) | `no-eval` *(blanket ban)* | ❌ None |
592
- | **Unsafe `JSON.parse(req.body)`** | [`no-unsafe-deserialize`](./docs/rules/no-unsafe-deserialize.md) | ❌ None | ❌ None |
593
- | **Async Without Await** | [`no-async-without-await`](./docs/rules/no-async-without-await.md) | ❌ None | `require-await` |
594
- | **Sequential Await in Loop** | [`no-await-in-loop`](./docs/rules/no-await-in-loop.md) | `no-await-in-loop` *(no fix)* | ❌ None |
595
- | **Dead Code Branches (`if (true)`)** | [`no-dead-branch`](./docs/rules/no-dead-branch.md) | ❌ None | ❌ None |
596
-
597
- > [!TIP]
598
- > **Minimal Type-Information Dependency:** 17 of 18 AI Guard rules operate in pure syntax/scope-analysis mode without `projectService` or `tsconfig.json`. The `no-floating-promise` rule optionally uses TypeScript parser services for higher recall on cross-module Promise calls, with syntax/scope-based heuristics as a zero-config fallback.
599
-
600
- ---
601
-
602
- ### 2. Runtime Performance
603
-
604
- Benchmark: scanning 196 TypeScript / JavaScript files (`algorithm-automata-simulator`, Windows 11, Node.js 20, median of 3 runs):
605
-
606
- | Tool / Mode | Scan Time | Configuration Overhead |
607
- | :--- | :---: | :--- |
608
- | **`ai-guard run --strict`** | **~1.8s** | **Zero config** (18 AST heuristic rules, no `tsconfig.json` needed) |
609
- | `eslint .` (recommended) | ~2.5s | Core syntax rules only (misses floating promises & secrets) |
610
- | `eslint .` (type-aware `@typescript-eslint`) | **~8–12s** | Requires full TS compiler graph binding (**4x–6x slower**) |
611
-
612
- AI Guard executes **4x–6x faster** than type-aware linting suites because it leverages deterministic AST heuristics rather than reconstructing the full TypeScript symbol graph.
613
-
614
- ---
615
-
616
- ### 3. Empirical Bug Detection & Precision Study
617
-
618
- Two comprehensive empirical benchmark evaluations were conducted to measure real-world precision and detection yields:
619
-
620
- #### A. Accuracy & False Positive Audit (4 Real-World Repositories, 378 Files)
621
-
622
- | Category | Findings | True Positives | False Positive Rate |
623
- | :--- | :---: | :---: | :---: |
624
- | **Security** (`no-hardcoded-secret`, `no-eval-dynamic`, etc.) | 15 | 15 | **0%** |
625
- | **Reliability** (`no-empty-catch`, broad exceptions) | 53 | 53 | **0%** |
626
- | **Async Stability** (`no-floating-promise`, async callbacks) | 43 | ~40 | **~7%** |
627
- | **AI Patterns** (duplicate logic, dead branches) | 58 | 43 | **26%** *(reduced in v1.2.8+)* |
628
-
629
- #### B. Dual-Mode Detection Study (48 Call Sites & Real Production Target)
630
-
631
- Audited across a controlled multi-file test corpus (13 files, 48 call sites) and a real-world Express + MongoDB production application (`Truvita New`):
632
-
633
- - **High Precision:** **100% precision** on type-aware exclusive findings (26 of 26 verified True Positives; 0 false alarms on synchronous controls).
634
- - **Critical Detection:** Detected **1 critical database startup race condition** in production (`server/index.ts connectDB()`) where an unawaited connection call allowed requests to hit the database before initialization.
635
- - **Single-File Actionability:** **100%** of findings were resolvable locally at the call site (`await`, `.catch()`, or `void`).
636
-
637
- For the complete methodology, raw findings, and benchmark harness, see [**`docs/benchmarks.md`**](./docs/benchmarks.md).
638
-
639
- ---
640
-
641
- ## Performance & Philosophy
642
-
643
- - **Zero LLM Overhead:** AI Guard does not call external APIs, does not incur token costs, and does not add LLM latency. A scan of 100+ files executes in milliseconds.
644
- - **100% Deterministic:** Every finding is derived strictly from Abstract Syntax Tree analysis. No probabilistic drift, no non-deterministic hallucinated findings.
645
- - **Low False Positives:** Built with precision-first design. Context-sensitive rules are configured at `warn` or `off` in the recommended preset so developers are never blocked by noise.
646
- - **Self-Scanning:** AI Guard enforces its own rules on its own codebase in CI using the `strict` preset.
647
-
648
- ---
649
-
650
- ## MCP Server (Model Context Protocol)
651
-
652
- AI Guard includes a local MCP server that exposes its deterministic analysis engine to MCP-capable AI clients (Claude Code, Claude Desktop, and other MCP-compatible integrations).
653
-
654
- ```bash
655
- npx --yes --package eslint-plugin-ai-guard ai-guard-mcp
656
- ```
657
-
658
- **Available tools:**
659
-
660
- | Tool | Description |
661
- | :--- | :--- |
662
- | `ai_guard_scan_file` | Scan a single JS/TS file with structured findings |
663
- | `ai_guard_scan_diff` | Scan changed files in git diff (including newly created untracked JS/TS files) |
664
- | `ai_guard_scan_project` | Scan a bounded workspace directory (current source; not yet published) |
665
- | `ai_guard_rules` | List rules and presets, or look up specific rules (`rules: ["no-floating-promise"]`) with remediation and docs link |
666
- | `ai_guard_check_integrity` | Report checks weakened by current Git changes (new suppressions, skipped/removed tests, lowered configs, removed CI steps), with `tier` and `hasBlockingFindings` |
667
-
668
- All tools declare an `outputSchema`; the text content is the same result as compact JSON. Scan tools accept `maxIssues` (default 50, errors first, exact `totalIssues` and `truncated`). The `verify_changes` prompt runs the scan → fix → rescan loop.
669
-
670
- **Claude Code configuration** (project-root `.mcp.json`):
671
-
672
- ```json
673
- {
674
- "mcpServers": {
675
- "ai-guard": {
676
- "type": "stdio",
677
- "command": "node",
678
- "args": ["/absolute/path/to/ai-guard/dist/mcp/server.js"],
679
- "env": { "AI_GUARD_WORKSPACE": "/absolute/path/to/project" }
680
- }
681
- }
682
- }
683
- ```
684
-
685
- The SDK in-memory and built STDIO integrations are tested, plus a headless Claude Code CLI smoke test (see the MCP guide for scope). Use the built checkout to test unreleased changes. Scan, fix, then rescan; a partial or failed scan is never clean. For setup, result status, limits, and client verification scope, see [**`docs/mcp-server.md`**](./docs/mcp-server.md).
686
-
687
- ---
688
-
689
- ## Learn More & Ecosystem
690
-
691
- Visit [getaiguard.dev](https://getaiguard.dev) to explore interactive documentation, rule catalogs, benchmarks, and deep-dive engineering articles.
692
-
693
- ### Official Links
694
-
695
- - **Website:** [https://getaiguard.dev](https://getaiguard.dev)
696
- - **GitHub Organization:** [https://github.com/ai-guard-dev](https://github.com/ai-guard-dev)
697
- - **Repository:** [https://github.com/ai-guard-dev/eslint-plugin-ai-guard](https://github.com/ai-guard-dev/eslint-plugin-ai-guard)
698
- - **npm Registry:** [https://www.npmjs.com/package/eslint-plugin-ai-guard](https://www.npmjs.com/package/eslint-plugin-ai-guard)
699
- - **GitHub Action:** [ai-guard-dev/eslint-plugin-ai-guard@v1](https://github.com/ai-guard-dev/eslint-plugin-ai-guard)
700
-
701
- ---
702
-
703
- ## Contributing
704
-
705
- We welcome contributions, new rule ideas, bug reports, and false-positive reports!
706
-
707
- 1. Check out our [Contributing Guide](CONTRIBUTING.md) for local setup and testing standards.
708
- 2. Review our [Security Policy](SECURITY.md) to report vulnerabilities responsibly.
709
- 3. Check open issues or submit new ones on our [Issue Tracker](https://github.com/ai-guard-dev/eslint-plugin-ai-guard/issues).
710
-
711
- ---
712
-
713
- ## License
714
-
715
- [MIT](LICENSE) © AI Guard Authors. Free and open source forever.
1
+ <p align="center">
2
+ <a href="https://getaiguard.dev">
3
+ <img src="./assets/logo/ai-guard-logo.png" alt="AI Guard Logo" width="260" />
4
+ </a>
5
+ </p>
6
+
7
+ <h1 align="center">AI Guard</h1>
8
+
9
+ <p align="center">
10
+ <strong>Deterministic AST safety layer and CI guardrails for AI-assisted JavaScript and TypeScript code.</strong>
11
+ </p>
12
+
13
+ <p align="center">
14
+ <a href="https://getaiguard.dev"><strong>Website</strong></a> &nbsp;•&nbsp;
15
+ <a href="https://www.npmjs.com/package/eslint-plugin-ai-guard"><strong>npm Package</strong></a> &nbsp;•&nbsp;
16
+ <a href="https://github.com/ai-guard-dev/eslint-plugin-ai-guard"><strong>GitHub Repository</strong></a> &nbsp;•&nbsp;
17
+ <a href="./docs/rules/"><strong>Rules Catalog</strong></a> &nbsp;•&nbsp;
18
+ <a href="./docs/benchmarks.md"><strong>Benchmarks</strong></a> &nbsp;•&nbsp;
19
+ <a href="./docs/integrations/claude-code.md"><strong>Claude Code</strong></a> &nbsp;•&nbsp;
20
+ <a href="./docs/getting-started.md"><strong>Documentation</strong></a>
21
+ </p>
22
+
23
+ <p align="center">
24
+ <a href="https://www.npmjs.com/package/eslint-plugin-ai-guard"><img src="https://img.shields.io/npm/v/eslint-plugin-ai-guard.svg?style=flat-square&color=0284c7" alt="npm version"></a>
25
+ <a href="https://github.com/ai-guard-dev/eslint-plugin-ai-guard/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/ai-guard-dev/eslint-plugin-ai-guard/ci.yml?style=flat-square&label=CI&color=10b981" alt="CI Status"></a>
26
+ <a href="https://www.npmjs.com/package/eslint-plugin-ai-guard"><img src="https://img.shields.io/npm/dm/eslint-plugin-ai-guard.svg?style=flat-square&color=3b82f6" alt="npm downloads"></a>
27
+ <a href="https://github.com/ai-guard-dev/eslint-plugin-ai-guard/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-64748b?style=flat-square" alt="MIT License"></a>
28
+ <a href="https://github.com/ai-guard-dev/eslint-plugin-ai-guard/blob/main/SECURITY.md"><img src="https://img.shields.io/badge/security-policy-orange?style=flat-square" alt="Security Policy"></a>
29
+ <a href="https://getaiguard.dev"><img src="https://img.shields.io/badge/website-getaiguard.dev-0ea5e9?style=flat-square" alt="Website"></a>
30
+ </p>
31
+
32
+ ---
33
+
34
+ ## What is AI Guard?
35
+
36
+ **AI Guard** (`eslint-plugin-ai-guard`) provides:
37
+ - **ESLint plugin** with 27 deterministic rules and 4 presets (`recommended`, `strict`, `security`, `agent`)
38
+ - **CLI** (`ai-guard`) for terminal, changed-file, and CI scanning
39
+ - **GitHub Action** with native SARIF 2.1.0 output for GitHub Code Scanning
40
+ - **Check integrity** (`ai-guard integrity`) — detects changes that weaken checks: new suppressions, skipped or removed tests, lowered ESLint/TypeScript/coverage settings, removed CI steps
41
+ - **Agent safety**: `ai-guard verify` (one verdict for a change), `ai-guard scan-agent-config` (hidden text and prompt injection in `CLAUDE.md`, skills and MCP config), and `changed --test-integrity` (weakened tests)
42
+ - **Hooks for Claude Code, Cursor and Codex** (`ai-guard init-hooks`): findings go back to the agent as it works, and it is stopped once before finishing on weakened checks or problems it introduced
43
+ - **A pre-commit gate** (`ai-guard init-git-hook`) that checks every commit, whichever agent or person wrote it
44
+ - **Works with Oxlint** as a JS plugin, with identical results checked in CI (`ai-guard init-oxlint`)
45
+ - **MCP server** (`ai-guard-mcp`, 9 tools) for local AI-agent workflows over STDIO
46
+
47
+ It is engineered to detect reliability bugs, async hazards, security vulnerabilities, and code-scaffolding defects frequently introduced during AI-assisted development (GitHub Copilot, Cursor, Claude Code, Gemini Code Assist, etc.).
48
+
49
+ AI Guard analyzes Abstract Syntax Trees (AST) using ESLint's native engine. It runs locally in your editor, in your terminal via the zero-config CLI, and in your CI/CD pipelines via native SARIF 2.1.0 integration with GitHub Code Scanning.
50
+
51
+ ### What AI Guard is NOT
52
+
53
+ > [!IMPORTANT]
54
+ > - **AI Guard is NOT an AI detector.** It does not attempt to predict whether code was authored by an LLM or a human.
55
+ > - **AI Guard detects dangerous or fragile code patterns** that LLMs repeatedly introduce due to incomplete context, hallucinated patterns, or probabilistic generation.
56
+ > - **AI Guard does NOT replace ESLint.** It extends ESLint with 27 specialized, high-impact rules that core ESLint and standard configurations omit.
57
+
58
+ ---
59
+
60
+ ## Why AI Guard?
61
+
62
+ AI coding assistants write code at remarkable velocity, but generated code repeatedly suffers from predictable reliability and security anti-patterns that conventional linters miss:
63
+
64
+ | Pattern | Why AI Assistants Generate It | Real-World Impact |
65
+ | :--- | :--- | :--- |
66
+ | **Floating Promises** | Omits `await`, `return`, or `.catch()` on async calls | Unhandled promise rejections, silent failures in background jobs |
67
+ | **Async Array Iteration** | Passes async callbacks into `array.map()` or `.filter()` | Returns unawaited `Promise[]` instead of resolved values |
68
+ | **Sequential Awaits in Loops** | Loops over items with sequential `await` | Significant latency bottlenecks; blocks event loop execution |
69
+ | **Empty Catch Blocks** | Inserts generic `try { ... } catch (e) {}` blocks | Swallows production exceptions silently without telemetry |
70
+ | **Hardcoded Secrets** | Injects placeholder or real API keys/tokens | Credential leakage in version control and deployment bundles |
71
+ | **Dynamic `eval()`** | Generates dynamic function compilation | Arbitrary code execution and code injection |
72
+ | **Raw SQL Concatenation** | Concatenates query strings with variables | Severe SQL injection vulnerabilities |
73
+ | **Unsafe Deserialization** | Calls `JSON.parse(req.body)` directly without schema checks | Denial of service and unhandled runtime crashes |
74
+ | **Missing Route Auth & Authz** | Emits boilerplate endpoints without auth middleware | Unprotected API endpoints and IDOR privilege escalations |
75
+ | **Dead Branches & Scaffolding** | Leaves `if (true)` or conflicting conditions from prompt iterations | Bloated bundles and dead code paths |
76
+
77
+ AI Guard provides an instantaneous, deterministic feedback loop that catches these issues before they reach pull requests or production.
78
+
79
+ ---
80
+
81
+ ## Architecture & Workflow
82
+
83
+ ```mermaid
84
+ flowchart TD
85
+ subgraph Dev["1. Development & Prompt Phase"]
86
+ A["Developer + AI Coding Assistant\n(Copilot, Cursor, Claude Code)"] --> B["JavaScript / TypeScript Code"]
87
+ end
88
+
89
+ subgraph ShiftLeft["Shift Left — Context Injection"]
90
+ SL["npx ai-guard init-context"] -.-> CTX["CLAUDE.md\n.cursorrules\ncopilot-instructions.md"]
91
+ CTX -.-> A
92
+ end
93
+
94
+ subgraph Analysis["2. Deterministic AST Analysis"]
95
+ B --> C["ESLint Parser\n(espree / @typescript-eslint/parser)"]
96
+ C --> D["AST Representation"]
97
+ D --> E["AI Guard Rules Engine\n(27 Deterministic Rules)"]
98
+ end
99
+
100
+ subgraph Tiers["3. Classification & Presets"]
101
+ E --> F{"Active Preset\n(recommended | strict | security | agent)"}
102
+ F --> G["Confidence Tiering & AST Filtering"]
103
+ end
104
+
105
+ subgraph Outputs["4. Output & Remediation"]
106
+ G --> H["Local CLI Scanning\n(ai-guard run / changed)"]
107
+ G --> I["Autofix Remediation\n(eslint --fix)"]
108
+ G --> J["HTML Dashboard\n(ai-guard report)"]
109
+ G --> K["SARIF 2.1.0 Artifact\n(ai-guard --sarif)"]
110
+ end
111
+
112
+ subgraph CI["5. GitHub Pull Request & CI/CD"]
113
+ K --> L["GitHub Action\nai-guard-dev/eslint-plugin-ai-guard@v1"]
114
+ L --> M["GitHub Code Scanning Alerts"]
115
+ L --> N["Inline PR Code Annotations"]
116
+ L --> O["PR Status Check (Blocks Merge)"]
117
+ end
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Rules Catalog
123
+
124
+ AI Guard includes **27 deterministic rules** divided into four specialized categories. Every rule is engineered with low false-positive heuristics and validated against real-world production codebases:
125
+
126
+ ### 🔴 Security (10 Rules)
127
+
128
+ | Rule | Recommended | What It Catches | Fixable? |
129
+ | :--- | :---: | :--- | :---: |
130
+ | [`no-hardcoded-secret`](./docs/rules/no-hardcoded-secret.md) | **`error`** | API keys, bearer tokens, passwords, and private keys committed directly in source code. | **Yes** (`process.env.*`) |
131
+ | [`no-eval-dynamic`](./docs/rules/no-eval-dynamic.md) | **`error`** | `eval()`, `new Function()`, and `setTimeout`/`setInterval` with dynamic/non-literal string expressions. | No |
132
+ | [`no-sql-string-concat`](./docs/rules/no-sql-string-concat.md) | `warn` | SQL queries constructed by string concatenation or raw template literals — SQL injection risks. | No |
133
+ | [`no-unsafe-deserialize`](./docs/rules/no-unsafe-deserialize.md) | `warn` | Unchecked `JSON.parse()` called directly on HTTP request inputs (`req.body`, `req.query`, `req.params`). | No |
134
+ | [`require-auth-middleware`](./docs/rules/require-auth-middleware.md) | `warn` | Express and Fastify route definitions exposed without authentication middleware. | No |
135
+ | [`require-authz-check`](./docs/rules/require-authz-check.md) | `warn` | Endpoints accessing sensitive resources or user IDs without tenant/ownership authorization checks. | No |
136
+ | [`no-client-exposed-secret`](./docs/rules/no-client-exposed-secret.md) | **`error`** | Server secrets that reach the browser: `NEXT_PUBLIC_`/`VITE_`/`EXPO_PUBLIC_` prefixes on secret keys, server secrets in `"use client"` files, `dangerouslyAllowBrowser` with the app's own key. | No |
137
+ | [`no-invisible-unicode`](./docs/rules/no-invisible-unicode.md) | **`error`** | Trojan Source bidi overrides, zero-width and Hangul-filler characters, and Unicode tag characters that hide instructions for AI tools (decoded in the message). | **Yes** (escapes in strings, removal in comments) |
138
+ | [`no-unsanitized-html`](./docs/rules/no-unsanitized-html.md) | `warn` | Dynamic values in `innerHTML`, `insertAdjacentHTML`, `document.write` and `dangerouslySetInnerHTML`, with sharper messages for URL input, LLM output, markdown and JSON-LD. | No |
139
+ | [`no-undeclared-import`](./docs/rules/no-undeclared-import.md) | `warn` | Imports of packages missing from package.json: hallucinated names (slopsquatting) and phantom dependencies. Offline and monorepo-aware. | No |
140
+
141
+ ### 🟠 Reliability (5 Rules)
142
+
143
+ | Rule | Recommended | What It Catches | Fixable? |
144
+ | :--- | :---: | :--- | :---: |
145
+ | [`no-empty-catch`](./docs/rules/no-empty-catch.md) | **`error`** | Empty `catch (e) {}` blocks that silently swallow exceptions without logging or rethrowing. | **Yes** (inserts `/* TODO: handle error */`) |
146
+ | [`no-broad-exception`](./docs/rules/no-broad-exception.md) | `warn` | Catching broad exception types like `catch (e: any)` that mask system faults and typing. | No |
147
+ | [`no-catch-log-rethrow`](./docs/rules/no-catch-log-rethrow.md) | `off`* | Catch blocks that only log to `console` and rethrow without adding context or diagnostic info. | No |
148
+ | [`no-catch-without-use`](./docs/rules/no-catch-without-use.md) | `off`* | Caught error variables that are declared in catch parameters but never referenced. | No |
149
+ | [`no-silent-fallback`](./docs/rules/no-silent-fallback.md) | `warn` | Secret env vars with hardcoded fallbacks (`process.env.JWT_SECRET \|\| 'secret'`) and `catch { return [] }` around awaited I/O. | No |
150
+
151
+ ### 🟡 Async Stability (5 Rules)
152
+
153
+ | Rule | Recommended | What It Catches | Fixable? |
154
+ | :--- | :---: | :--- | :---: |
155
+ | [`no-floating-promise`](./docs/rules/no-floating-promise.md) | **`error`** | Async function invocations without `await`, `.catch()`, or `return` — leading to silent dropped errors. | **Yes** (marks with `void`) |
156
+ | [`no-async-array-callback`](./docs/rules/no-async-array-callback.md) | `warn` | Async callbacks passed to `map()`, `filter()`, `forEach()`, or `reduce()` returning `Promise[]`. | No |
157
+ | [`no-await-in-loop`](./docs/rules/no-await-in-loop.md) | `warn` | Sequential `await` in loops where iterations can be safely executed concurrently with `Promise.all`. | **Yes** (rewrites to `Promise.all`) |
158
+ | [`no-async-without-await`](./docs/rules/no-async-without-await.md) | `warn` | Functions declared `async` that never execute an `await` expression, adding unnecessary Promise overhead. | No |
159
+ | [`no-redundant-await`](./docs/rules/no-redundant-await.md) | `off`* | Redundant `return await` statements outside of `try...catch` blocks. | No |
160
+
161
+ ### 🔵 AI Patterns (7 Rules)
162
+
163
+ | Rule | Recommended | What It Catches | Fixable? |
164
+ | :--- | :---: | :--- | :---: |
165
+ | [`no-dead-branch`](./docs/rules/no-dead-branch.md) | `warn` | Unreachable or tautological branches (`if (true)`, `if (false)`, `x && !x`) left behind from LLM code synthesis. | No |
166
+ | [`no-duplicate-logic-block`](./docs/rules/no-duplicate-logic-block.md) | `off`* | Consecutive duplicate code blocks or repeated conditional branches duplicated during AI edits. | No |
167
+ | [`no-console-in-handler`](./docs/rules/no-console-in-handler.md) | `off`* | Unstructured `console.log` statements left in HTTP route handlers instead of production loggers. | No |
168
+ | [`no-placeholder-implementation`](./docs/rules/no-placeholder-implementation.md) | `warn` | Code an agent left unfinished: `// ... rest of the code` elisions that delete code, TODO-only stubs, `throw new Error('Not implemented')` bodies, and mock data served from production code. | No |
169
+ | [`no-tautological-assertion`](./docs/rules/no-tautological-assertion.md) | `warn` | Assertions that always pass: `expect(true).toBe(true)`, `expect(x).toEqual(x)`, `assert.ok(1)`. | No |
170
+ | [`no-assertionless-test`](./docs/rules/no-assertionless-test.md) | `warn` | Test cases with no assertion, which pass whatever the code does. | No |
171
+ | [`no-test-env-branch`](./docs/rules/no-test-env-branch.md) | `warn` | Production code that returns a canned result under a test runner (`if (NODE_ENV === 'test') return true`). | No |
172
+
173
+ *\* Enabled at `error` level in the `strict` preset.*
174
+
175
+ ---
176
+
177
+ ## Agent Safety
178
+
179
+ Linting checks the code. These check the agent:
180
+
181
+ | Feature | What it does |
182
+ | :--- | :--- |
183
+ | [`ai-guard verify`](./docs/cli/verify.md) | One verdict for the current change: lint findings it introduced, weakened tests, and agent-config issues. Pre-existing problems never block. |
184
+ | [`ai-guard integrity`](./docs/integrity.md) | Everything that weakens checks vs a base revision: new suppressions, skipped or removed tests, lowered coverage and TypeScript settings, removed CI steps. |
185
+ | [`ai-guard changed --test-integrity`](./docs/cli/test-integrity.md) | A test-focused comparison: focused, skipped, removed or weakened tests, rewritten snapshots, and test commands that can no longer fail. Overlaps with `integrity`; use `integrity` for the full set. |
186
+ | [`ai-guard verify-deps`](./docs/cli/verify-deps.md) | Checks npm packages before they are installed: names that don't exist (invented by assistants), look-alikes of popular packages, new or rarely used packages, install scripts, deprecations. Also as a pre-install hook for Claude Code, Cursor and Codex (`init-hooks --check-installs`) and the `ai_guard_check_package` MCP tool. Uses the npm registry; private registries are skipped. |
187
+ | [`ai-guard scan-agent-config`](./docs/cli/scan-agent-config.md) | Scans `CLAUDE.md`, `AGENTS.md`, `.cursorrules`, skills and MCP configs for hidden text, prompt injection, unpinned MCP servers and inline secrets. |
188
+ | [Agent hooks: Claude Code, Cursor, Codex](./docs/integrations/agents.md) | `ai-guard init-hooks` sets up every agent a project uses. Findings reach the agent after edits; before it finishes, the whole change is verified once; `git commit --no-verify` and risky installs are caught before they run. |
189
+ | [Pre-commit gate](./docs/integrations/git-hook.md) | `ai-guard init-git-hook` runs `verify --staged` on every commit through husky, lefthook, simple-git-hooks or pre-commit. Only problems the commit introduces block it. |
190
+ | [MCP tools](./docs/mcp-server.md) | `ai_guard_scan_file`, `ai_guard_scan_diff`, `ai_guard_scan_project`, `ai_guard_rules`, `ai_guard_check_integrity`, `ai_guard_verify_change`, `ai_guard_scan_agent_config`, `ai_guard_check_package`, and `ai_guard_fix` (a diff, unless `apply` is set). |
191
+
192
+ ---
193
+
194
+ ## Presets
195
+
196
+ AI Guard exports four official configurations ready for flat config or legacy setups:
197
+
198
+ | Preset | Description | Configuration Focus |
199
+ | :--- | :--- | :--- |
200
+ | **`recommended`** | **Default.** Balanced adoption preset. Enables 6 high-confidence critical rules at `error`, 16 context-sensitive rules at `warn`, and disables 5 noisy rules. Zero noise on day one. | Production codebases, new teams |
201
+ | **`strict`** | Enforces **all 27 rules at `error`**. Designed for zero-tolerance CI gates, high-assurance software, and mature teams. | Strict CI/CD quality gates |
202
+ | **`security`** | Focuses on the **11 security-relevant rules** (secrets, eval, SQL, client-exposed secrets, invisible Unicode and HTML sinks at `error`; the rest at `warn`). | AppSec auditing & security scans |
203
+ | **`agent`** | **8 high-signal rules at `error`**. Optimized for AI-agent editing workflows where lint feedback runs immediately after each file edit (e.g., PostToolUse hooks). | Claude Code, Cursor, real-time agent loops |
204
+
205
+ ---
206
+
207
+ ## Quick Start & Installation
208
+
209
+ Install the package as a development dependency using your package manager:
210
+
211
+ ```bash
212
+ # npm
213
+ npm install --save-dev eslint-plugin-ai-guard
214
+
215
+ # pnpm
216
+ pnpm add -D eslint-plugin-ai-guard
217
+
218
+ # yarn
219
+ yarn add -D eslint-plugin-ai-guard
220
+
221
+ # bun
222
+ bun add -d eslint-plugin-ai-guard
223
+ ```
224
+
225
+ ### Requirements
226
+
227
+ - **Node.js:** `>= 20.0.0`
228
+ - **ESLint:** `>= 8.0.0` (Supports both Flat Config and legacy configs)
229
+ - **TypeScript (optional):** `@typescript-eslint/parser >= 6.0.0` for TypeScript AST parsing
230
+
231
+ ---
232
+
233
+ ## ESLint Configuration
234
+
235
+ ### 1. Modern Flat Config (`eslint.config.mjs` / `eslint.config.js`)
236
+
237
+ AI Guard exports full native support for modern ESLint Flat Config:
238
+
239
+ ```javascript
240
+ // eslint.config.mjs
241
+ import aiGuard from 'eslint-plugin-ai-guard';
242
+
243
+ export default [
244
+ {
245
+ plugins: {
246
+ 'ai-guard': aiGuard,
247
+ },
248
+ rules: {
249
+ ...aiGuard.configs.recommended.rules,
250
+ // Custom overrides if desired:
251
+ 'ai-guard/no-floating-promise': 'error',
252
+ },
253
+ },
254
+ ];
255
+ ```
256
+
257
+ To use the `strict` or `security` preset in flat config:
258
+
259
+ ```javascript
260
+ // Strict preset — all 27 rules at error
261
+ rules: {
262
+ ...aiGuard.configs.strict.rules,
263
+ }
264
+
265
+ // Security preset — security rules only
266
+ rules: {
267
+ ...aiGuard.configs.security.rules,
268
+ }
269
+ ```
270
+
271
+ ### 2. Legacy Config (`.eslintrc.js` / `.eslintrc.json`)
272
+
273
+ ```javascript
274
+ // .eslintrc.js
275
+ module.exports = {
276
+ plugins: ['ai-guard'],
277
+ extends: ['plugin:ai-guard/recommended'],
278
+ };
279
+ ```
280
+
281
+ ---
282
+
283
+ ## CLI Reference
284
+
285
+ AI Guard includes a full-featured CLI binary (`ai-guard`) that runs out of the box with zero ESLint configuration files required:
286
+
287
+ ```bash
288
+ npx ai-guard <command> [options]
289
+ ```
290
+
291
+ ### Core Commands
292
+
293
+ | Command | Purpose | Common Options |
294
+ | :--- | :--- | :--- |
295
+ | `run` | Scan your workspace using AI Guard AST rules | `--path <dir>`, `--strict`, `--security`, `--json`, `--sarif`, `--fail-on <level>`, `--max-warnings <n>`, `--inline-suppressions <mode>` |
296
+ | `changed` | Fast CI scan — only scans modified files in git | `--pr`, `--staged`, `--base <branch>`, `--baseline [file]`, `--integrity <off\|report\|enforce>`, `--test-integrity`, `--inline-suppressions <mode>`, `--strict`, `--sarif`, `--sarif-output <file>`, `--fail-on <level>` |
297
+ | `verify` | One verdict for the current change ([docs](./docs/cli/verify.md)) | `--base <ref>`, `--staged`, `--preset <name>`, `--strict`, `--json` |
298
+ | `verify-deps` | Check npm packages before they are installed ([docs](./docs/cli/verify-deps.md)) | `[packages...]`, `--base <ref>`, `--staged`, `--all`, `--json`, `--sarif`, `--fail-on <level>` |
299
+ | `scan-agent-config` | Scan agent instructions and MCP config ([docs](./docs/cli/scan-agent-config.md)) | `--path <dir>`, `--json`, `--sarif`, `--fail-on <level>`, `--update-lock` |
300
+ | `integrity` | Detect changes that weaken checks vs a base revision ([docs](./docs/integrity.md)) | `--base <ref>`, `--pr`, `--staged`, `--enforce`, `--json`, `--sarif`, `--accept <fingerprints\|all>` |
301
+ | `init` | Automatically detect environment & configure ESLint | `--preset <name>`, `--flat`, `--dry-run`, `-y, --yes` |
302
+ | `init-hooks` | Hooks for Claude Code, Cursor and Codex ([docs](./docs/integrations/agents.md)) | `--agent <list>`, `--check-installs`, `--no-stop-hook`, `--no-bash-guard`, `--dry-run` |
303
+ | `init-git-hook` | Pre-commit gate running `verify --staged` ([docs](./docs/integrations/git-hook.md)) | `--manager <name>`, `--check-deps`, `--dry-run` |
304
+ | `init-oxlint` | Add AI Guard to `.oxlintrc.json` ([docs](./docs/integrations/oxlint.md)) | `--preset <name>`, `--dry-run` |
305
+ | `init-context` | Generate prompt instruction files for AI coding agents | `-a, --all`, `--force`, `--dry-run`, `--rules <categories>` |
306
+ | `doctor` | Diagnose your ESLint, parser, and plugin environment | (No options needed — prints actionable diagnostic report) |
307
+ | `baseline` | Record existing findings, then report only new ones | `create`, `check`, `--fail-on <level>`, `--json`, `--mode <strict\|stable>`, `--preset <name>` |
308
+ | `report` | Generate an interactive standalone HTML audit report | `--path <dir>`, `--preset <name>`, `--output <file>`, `--no-open`, `--json` |
309
+ | `preset` | Interactively select and switch active preset in config | (Interactive prompt with automatic config patch & backup) |
310
+ | `ignore` | Add standard ignore paths (`.next`, `dist`, `build`) to config | (Patches flat config or legacy ignores safely) |
311
+
312
+ **`run` exit codes:** `0` no errors · `1` errors found (or `--max-warnings` exceeded) · `2` scan did not run — the file passed to `--path` matches a built-in ignore pattern (`node_modules/`, `dist/`, `build/`, `generated/`, `vendor/`, …), so nothing was analyzed. Directory scans skip ignored files silently and never exit `2`.
313
+
314
+ ### CLI Usage Examples
315
+
316
+ ```bash
317
+ # 1. Immediate scan of current directory
318
+ npx ai-guard run
319
+
320
+ # 2. Strict CI scan failing only on high-confidence issues
321
+ npx ai-guard run --strict --fail-on high
322
+
323
+ # 3. Pull Request scan (diffs against PR target branch)
324
+ npx ai-guard changed --pr --sarif --sarif-output results.sarif
325
+
326
+ # 4. Generate AI agent guardrails for Cursor, Claude Code, and Copilot
327
+ npx ai-guard init-context --all
328
+
329
+ # 5. Generate interactive HTML diagnostic report
330
+ npx ai-guard report --output ai-guard-report.html
331
+
332
+ # 6. Save existing issues as baseline and only fail on new regressions
333
+ npx ai-guard baseline create # writes .ai-guard-baseline.json (commit it)
334
+ npx ai-guard baseline check # exit 1 if any new finding
335
+ npx ai-guard baseline check --fail-on high # fail only on new high-confidence findings
336
+ npx ai-guard baseline check --json # machine-readable new findings
337
+
338
+ # 7. Fail a PR that skips tests, adds eslint-disable, or lowers coverage thresholds
339
+ npx ai-guard changed --pr --integrity enforce
340
+ ```
341
+
342
+ ### Inline suppressions never hide findings silently
343
+
344
+ AI Guard reads `eslint-disable` directives itself; ESLint's own inline config is disabled in AI Guard's private ESLint instance, so a comment cannot make a finding vanish without a trace. `--inline-suppressions` selects the policy:
345
+
346
+ | Mode | Effect |
347
+ |---|---|
348
+ | `allow` (default) | A covering directive hides the finding from totals and exit codes; it is listed under "Hidden by inline directives" (JSON `suppressedIssues`, step summary, SARIF `suppressedFindingsCount`) |
349
+ | `require-reason` | Only directives with a ` -- reason` hide findings (used by the Claude Code hook) |
350
+ | `ignore` | Directives never hide AI Guard findings (always used by the MCP server) |
351
+
352
+ This applies to `ai-guard run`, `changed`, the GitHub Action, the Claude Code hook and MCP. It does not change how your own `eslint .` treats directives. TypeScript comments (`@ts-ignore`, `@ts-nocheck`) never affect AI Guard findings; `ai-guard integrity` reports newly added ones.
353
+
354
+ ---
355
+
356
+ ## AI Agent Integration (`init-context`)
357
+
358
+ Standard linters only run **after** code has already been written. The `init-context` command shifts your guardrails left by embedding AI Guard's rules directly into the instruction files loaded by your AI coding tools:
359
+
360
+ ```bash
361
+ npx ai-guard init-context --all
362
+ ```
363
+
364
+ This generates three targeted context files:
365
+ 1. **`CLAUDE.md`** — Automatically loaded by **Claude Code**
366
+ 2. **`.cursorrules`** — Automatically loaded by **Cursor**
367
+ 3. **`.github/copilot-instructions.md`** — Automatically loaded by **GitHub Copilot**
368
+
369
+ ### How Shift-Left Works
370
+
371
+ ```
372
+ AI Guard (init-context)
373
+ ↓
374
+ Generates project guardrail files (CLAUDE.md, .cursorrules, copilot-instructions.md)
375
+ ↓
376
+ AI coding assistant reads safety rules before generating code
377
+ ↓
378
+ Model avoids floating promises, empty catches, and hardcoded secrets at prompt time
379
+ ↓
380
+ AI Guard CLI & GitHub Action deterministically verifies the output in CI
381
+ ```
382
+
383
+ This dual-layer defense minimizes review friction and ensures generated code meets your security standard on the first pass.
384
+
385
+ ---
386
+
387
+ ## Claude Code Integration
388
+
389
+ Using Cursor or Codex too? `npx ai-guard init-hooks` sets up every agent the project uses; see [Agent hooks](./docs/integrations/agents.md).
390
+
391
+ Two ways to use it with Claude Code. Pick one:
392
+
393
+ - **In a project (recommended):** `npm install --save-dev eslint-plugin-ai-guard`, then `npx ai-guard init-claude`. This writes the hooks into the project's `.claude/settings.json`, so everyone on the project gets them.
394
+ - **As a Claude Code plugin** (adds the MCP tools and the `/ai-guard:verify-changes` skill, and works in projects without AI Guard installed): `claude plugin marketplace add ai-guard-dev/eslint-plugin-ai-guard`, then `claude plugin install ai-guard@ai-guard-plugins`.
395
+
396
+ AI Guard integrates directly with [Claude Code](https://code.claude.com/docs/en/hooks) through hooks:
397
+
398
+ ```bash
399
+ # One-command setup
400
+ npx ai-guard init-claude
401
+ ```
402
+
403
+ - **PostToolUse** — after each edit, findings from the `agent` preset and newly weakened checks in the edited file are sent back to Claude (`decision: "block"` + `reason`, per the hooks protocol). Earlier versions printed plain stdout, which Claude Code does not show to Claude.
404
+ - **SessionStart + Stop** — before Claude finishes, [check integrity](./docs/integrity.md) runs on everything changed in the session. High-confidence weakenings (a new `.skip`, `eslint-disable`, lowered threshold…) block stopping once per finding set; advisories are shown to you, never block. The same hook then [verifies](./docs/cli/verify.md) lint findings and agent-config issues the session introduced (placeholder code, a canned return under a test runner, a hidden instruction in `CLAUDE.md`), which also block once. Skip both with `--no-stop-hook`.
405
+ - **PreToolUse (Bash)** — `git commit --no-verify` / `HUSKY=0` ask for confirmation. With `--check-installs` (opt-in, uses the npm registry), packages in install commands are checked first: names that don't exist are blocked, look-alikes of popular packages ask you.
406
+
407
+ ```bash
408
+ # Preview before applying
409
+ npx ai-guard init-claude --dry-run
410
+
411
+ # Use per-machine settings (gitignored)
412
+ npx ai-guard init-claude --local
413
+ ```
414
+
415
+ **Agent preset rules (8):** `no-hardcoded-secret`, `no-eval-dynamic`, `no-empty-catch`, `no-sql-string-concat`, `no-floating-promise`, `no-client-exposed-secret`, `no-invisible-unicode`, `no-undeclared-import`
416
+
417
+ → [Full documentation](./docs/integrations/claude-code.md)
418
+
419
+ ---
420
+
421
+ ## GitHub Action
422
+
423
+ The official AI Guard GitHub Action runs on PRs, detects changed files, provides step summaries, outputs SARIF 2.1.0, and posts inline PR annotations directly on GitHub:
424
+
425
+ ```yaml
426
+ # .github/workflows/ai-guard.yml
427
+ name: AI Guard
428
+
429
+ on:
430
+ pull_request:
431
+ branches: [main, develop]
432
+ push:
433
+ branches: [main]
434
+
435
+ jobs:
436
+ ai-guard-scan:
437
+ name: AI Guard Code Review
438
+ runs-on: ubuntu-latest
439
+
440
+ permissions:
441
+ contents: read
442
+ security-events: write
443
+ actions: read
444
+
445
+ steps:
446
+ - name: Checkout Code
447
+ uses: actions/checkout@v4
448
+ with:
449
+ fetch-depth: 0 # Required for git diff comparison
450
+
451
+ - name: Setup Node.js
452
+ uses: actions/setup-node@v4
453
+ with:
454
+ node-version: 20
455
+ cache: 'npm'
456
+
457
+ - name: Run AI Guard
458
+ uses: ai-guard-dev/eslint-plugin-ai-guard@v1
459
+ with:
460
+ preset: 'recommended'
461
+ fail-on: 'high'
462
+ changed-only: 'true'
463
+ upload-sarif: 'true'
464
+ ```
465
+
466
+ ### Action Inputs (`action.yml`)
467
+
468
+ | Input | Description | Default |
469
+ | :--- | :--- | :---: |
470
+ | `preset` | Rule preset: `recommended` \| `strict` \| `security` | `'recommended'` |
471
+ | `fail-on` | Severity threshold to fail CI: `high` \| `medium` \| `any` \| `none` | `'high'` |
472
+ | `changed-only` | Scan only files changed in this PR / commit | `'true'` |
473
+ | `path` | Target file or directory to scan | `'.'` |
474
+ | `upload-sarif` | Upload results to GitHub Code Scanning | `'true'` |
475
+ | `github-summary` | Write an execution breakdown to the GitHub Actions Job Summary | `'true'` |
476
+ | `working-directory`| Working directory for scanning (ideal for monorepos) | `'.'` |
477
+ | `package-manager` | Package manager: `auto` \| `npm` \| `pnpm` \| `yarn` | `'auto'` |
478
+ | `sarif-output` | Output filepath for generated SARIF report | `'ai-guard-results.sarif'` |
479
+ | `install-deps` | Install project dependencies prior to scanning | `'true'` |
480
+ | `baseline` | Committed baseline file; with `changed-only`, report only findings not in it | `''` |
481
+ | `integrity` | Check integrity: `off` \| `report` \| `enforce` (needs `changed-only` and `fetch-depth: 0`) | `'off'` |
482
+ | `test-integrity` | With `changed-only`, also flag tests the PR deleted, skipped, focused or weakened (needs `fetch-depth: 0`) | `'false'` |
483
+ | `inline-suppressions` | `allow` \| `require-reason` \| `ignore` for eslint-disable directives covering AI Guard findings | `''` (allow) |
484
+
485
+ ### Action Outputs
486
+
487
+ | Output | Description |
488
+ | :--- | :--- |
489
+ | `issues-found` | Total number of issues found across scanned files |
490
+ | `high-confidence-count` | Number of high-confidence issues flagged |
491
+ | `medium-confidence-count` | Number of medium-confidence issues flagged |
492
+ | `files-scanned` | Count of files analyzed during the execution |
493
+ | `integrity-high-count` | High-confidence check-integrity findings (when `integrity` is enabled) |
494
+ | `integrity-advisory-count` | Advisory check-integrity findings (when `integrity` is enabled) |
495
+ | `sarif-file` | Absolute path to the generated SARIF 2.1.0 artifact |
496
+ | `duration-ms` | Total scan execution duration in milliseconds |
497
+
498
+ ---
499
+
500
+ ## SARIF & GitHub Code Scanning
501
+
502
+ AI Guard natively outputs **SARIF 2.1.0** (`Static Analysis Results Interchange Format`). When uploaded via the GitHub Action or `github/codeql-action/upload-sarif@v3`, findings integrate directly with GitHub Advanced Security:
503
+
504
+ - **Inline PR Annotations:** Direct comments on the exact source lines where flaws exist.
505
+ - **Security Dashboard:** Persistent alerts under your repository's `Security → Code scanning` tab.
506
+ - **Merge Protection:** Block merges automatically when high-confidence security or async bugs are detected.
507
+
508
+ ### Visual Previews
509
+
510
+ #### Inline Pull Request Annotations
511
+ <img src="./assets/ss9.jpg" alt="AI Guard PR Inline Annotations" width="1000" />
512
+
513
+ #### GitHub Advanced Security Summary
514
+ <img src="./assets/ss10.jpg" alt="GitHub Advanced Security Summary for AI Guard" width="1000" />
515
+
516
+ #### Persistent Code Scanning Dashboard
517
+ <img src="./assets/ss11.jpg" alt="GitHub Code Scanning Alerts List" width="1000" />
518
+
519
+ ---
520
+
521
+ ## Rule Examples (Before & After)
522
+
523
+ ### 1. `no-floating-promise` (Unhandled Promises)
524
+
525
+ ```typescript
526
+ // ❌ BAD: Floating promise. Errors are dropped silently.
527
+ async function syncUserProfile(user: User) {
528
+ sendTelemetryEvent('user_sync', user.id);
529
+ database.save(user);
530
+ }
531
+
532
+ // ✅ GOOD: Awaited, explicitly handled, or marked with void
533
+ async function syncUserProfile(user: User) {
534
+ await database.save(user);
535
+ void sendTelemetryEvent('user_sync', user.id); // Explicitly unhandled
536
+ }
537
+ ```
538
+
539
+ ### 2. `no-hardcoded-secret` (Committed Credentials)
540
+
541
+ ```typescript
542
+ // ❌ BAD: Secret committed inline
543
+ const client = new PaymentGateway({
544
+ apiKey: 'sk-prod-983427598273498273948273',
545
+ });
546
+
547
+ // ✅ GOOD: Read from environment variable (Autofixable!)
548
+ const client = new PaymentGateway({
549
+ apiKey: process.env.API_KEY,
550
+ });
551
+ ```
552
+
553
+ ### 3. `no-await-in-loop` (Sequential Latency Trap)
554
+
555
+ ```typescript
556
+ // ❌ BAD: Consecutive awaits block each iteration sequentially
557
+ async function fetchAllUsers(ids: string[]) {
558
+ const users = [];
559
+ for (const id of ids) {
560
+ users.push(await fetchUser(id));
561
+ }
562
+ return users;
563
+ }
564
+
565
+ // ✅ GOOD: Concurrently fetched with Promise.all (Autofixable!)
566
+ async function fetchAllUsers(ids: string[]) {
567
+ return await Promise.all(ids.map((id) => fetchUser(id)));
568
+ }
569
+ ```
570
+
571
+ ### 4. `no-empty-catch` (Swallowed Errors)
572
+
573
+ ```typescript
574
+ // ❌ BAD: Exception swallowed without trace
575
+ try {
576
+ parseConfiguration(rawConfig);
577
+ } catch (e) {}
578
+
579
+ // ✅ GOOD: Logged, rethrown, or documented (Autofixable!)
580
+ try {
581
+ parseConfiguration(rawConfig);
582
+ } catch (e) {
583
+ logger.error('Configuration parsing failed', { error: e });
584
+ throw e;
585
+ }
586
+ ```
587
+
588
+ ### 5. `no-sql-string-concat` (SQL Injection)
589
+
590
+ ```typescript
591
+ // ❌ BAD: Dynamic string interpolation in SQL
592
+ const query = `SELECT * FROM users WHERE organization_id = '${orgId}' AND role = '${role}'`;
593
+ await db.query(query);
594
+
595
+ // ✅ GOOD: Parameterized query binding
596
+ const query = 'SELECT * FROM users WHERE organization_id = $1 AND role = $2';
597
+ await db.query(query, [orgId, role]);
598
+ ```
599
+
600
+ ---
601
+
602
+ ## Automatic Remediation (Autofix)
603
+
604
+ Rules that have deterministic solutions provide automatic autofix handlers. Run ESLint's native `--fix` flag to automatically resolve them:
605
+
606
+ ```bash
607
+ npx eslint . --fix
608
+ ```
609
+
610
+ | Rule | Automatic Fix Behavior |
611
+ | :--- | :--- |
612
+ | `no-hardcoded-secret` | Replaces hardcoded string literal with `process.env.VARIABLE_NAME` |
613
+ | `no-empty-catch` | Inserts `/* TODO: handle error */` comment to prevent silent swallowing |
614
+ | `no-floating-promise` | Prepends `void ` expression to intentionally unawaited calls |
615
+ | `no-await-in-loop` | Rewrites straightforward sequential loops to `await Promise.all(...)` |
616
+
617
+ ---
618
+
619
+ ## Benchmarks & Empirical Evaluation
620
+
621
+ AI Guard has been empirically evaluated across multiple benchmarks comparing runtime scan performance, coverage gaps vs. standard tooling, and detection accuracy across real-world codebases.
622
+
623
+ ### 1. What AI Guard Catches vs. Conventional Linters
624
+
625
+ Conventional linters either omit AI-specific hazards entirely or require heavyweight TypeScript type-checking (`parserOptions.project`) that significantly slows down CI:
626
+
627
+ | Pattern | AI Guard Rule | ESLint Core | `@typescript-eslint` |
628
+ | :--- | :--- | :---: | :--- |
629
+ | **Floating Promises** (unawaited async call) | [`no-floating-promise`](./docs/rules/no-floating-promise.md) | ❌ None | `@typescript-eslint/no-floating-promises` *(requires type info)* |
630
+ | **Async Array Callbacks** (`.map(async ...)`) | [`no-async-array-callback`](./docs/rules/no-async-array-callback.md) | ❌ None | Partial: `no-misused-promises` *(requires type info)* |
631
+ | **Empty Catch Blocks** (swallowed errors) | [`no-empty-catch`](./docs/rules/no-empty-catch.md) | `no-empty` *(weaker)* | ❌ None |
632
+ | **Hardcoded Secrets / API Tokens** | [`no-hardcoded-secret`](./docs/rules/no-hardcoded-secret.md) | ❌ None | ❌ None |
633
+ | **Raw SQL String Concatenation** | [`no-sql-string-concat`](./docs/rules/no-sql-string-concat.md) | ❌ None | ❌ None |
634
+ | **Missing Route Auth Middleware** | [`require-auth-middleware`](./docs/rules/require-auth-middleware.md) | ❌ None | ❌ None |
635
+ | **Missing Route Authorization Checks** | [`require-authz-check`](./docs/rules/require-authz-check.md) | ❌ None | ❌ None |
636
+ | **Dynamic `eval()` / `new Function()`** | [`no-eval-dynamic`](./docs/rules/no-eval-dynamic.md) | `no-eval` *(blanket ban)* | ❌ None |
637
+ | **Unsafe `JSON.parse(req.body)`** | [`no-unsafe-deserialize`](./docs/rules/no-unsafe-deserialize.md) | ❌ None | ❌ None |
638
+ | **Async Without Await** | [`no-async-without-await`](./docs/rules/no-async-without-await.md) | ❌ None | `require-await` |
639
+ | **Sequential Await in Loop** | [`no-await-in-loop`](./docs/rules/no-await-in-loop.md) | `no-await-in-loop` *(no fix)* | ❌ None |
640
+ | **Dead Code Branches (`if (true)`)** | [`no-dead-branch`](./docs/rules/no-dead-branch.md) | ❌ None | ❌ None |
641
+ | **Secrets exposed to the browser** (`NEXT_PUBLIC_*_SECRET`) | [`no-client-exposed-secret`](./docs/rules/no-client-exposed-secret.md) | ❌ None | ❌ None |
642
+ | **Trojan Source / hidden Unicode instructions** | [`no-invisible-unicode`](./docs/rules/no-invisible-unicode.md) | Partial: `no-irregular-whitespace` *(whitespace only)* | ❌ None |
643
+ | **Hallucinated / undeclared packages** | [`no-undeclared-import`](./docs/rules/no-undeclared-import.md) | ❌ None | ❌ None |
644
+ | **Unsanitized HTML sinks (XSS)** | [`no-unsanitized-html`](./docs/rules/no-unsanitized-html.md) | ❌ None | ❌ None |
645
+ | **Hardcoded secret fallbacks** | [`no-silent-fallback`](./docs/rules/no-silent-fallback.md) | ❌ None | ❌ None |
646
+ | **Elided / stubbed code left by agents** | [`no-placeholder-implementation`](./docs/rules/no-placeholder-implementation.md) | Partial: `no-warning-comments` *(TODO text only)* | ❌ None |
647
+
648
+ > [!TIP]
649
+ > **Minimal Type-Information Dependency:** 26 of 27 AI Guard rules operate in pure syntax/scope-analysis mode without `projectService` or `tsconfig.json`. The `no-floating-promise` rule optionally uses TypeScript parser services for higher recall on cross-module Promise calls, with syntax/scope-based heuristics as a zero-config fallback.
650
+
651
+ ---
652
+
653
+ ### 2. Runtime Performance
654
+
655
+ Benchmark: scanning 196 TypeScript / JavaScript files (`algorithm-automata-simulator`, Windows 11, Node.js 20, median of 3 runs):
656
+
657
+ | Tool / Mode | Scan Time | Configuration Overhead |
658
+ | :--- | :---: | :--- |
659
+ | **`ai-guard run --strict`** | **~1.8s** | **Zero config** (18 AST heuristic rules, no `tsconfig.json` needed) |
660
+ | `eslint .` (recommended) | ~2.5s | Core syntax rules only (misses floating promises & secrets) |
661
+ | `eslint .` (type-aware `@typescript-eslint`) | **~8–12s** | Requires full TS compiler graph binding (**4x–6x slower**) |
662
+
663
+ AI Guard executes **4x–6x faster** than type-aware linting suites because it leverages deterministic AST heuristics rather than reconstructing the full TypeScript symbol graph.
664
+
665
+ ---
666
+
667
+ ### 3. Empirical Bug Detection & Precision Study
668
+
669
+ Two comprehensive empirical benchmark evaluations were conducted to measure real-world precision and detection yields:
670
+
671
+ #### A. Accuracy & False Positive Audit (4 Real-World Repositories, 378 Files)
672
+
673
+ | Category | Findings | True Positives | False Positive Rate |
674
+ | :--- | :---: | :---: | :---: |
675
+ | **Security** (`no-hardcoded-secret`, `no-eval-dynamic`, etc.) | 15 | 15 | **0%** |
676
+ | **Reliability** (`no-empty-catch`, broad exceptions) | 53 | 53 | **0%** |
677
+ | **Async Stability** (`no-floating-promise`, async callbacks) | 43 | ~40 | **~7%** |
678
+ | **AI Patterns** (duplicate logic, dead branches) | 58 | 43 | **26%** *(reduced in v1.2.8+)* |
679
+
680
+ #### B. Dual-Mode Detection Study (48 Call Sites & Real Production Target)
681
+
682
+ Audited across a controlled multi-file test corpus (13 files, 48 call sites) and a real-world Express + MongoDB production application (`Truvita New`):
683
+
684
+ - **High Precision:** **100% precision** on type-aware exclusive findings (26 of 26 verified True Positives; 0 false alarms on synchronous controls).
685
+ - **Critical Detection:** Detected **1 critical database startup race condition** in production (`server/index.ts connectDB()`) where an unawaited connection call allowed requests to hit the database before initialization.
686
+ - **Single-File Actionability:** **100%** of findings were resolvable locally at the call site (`await`, `.catch()`, or `void`).
687
+
688
+ For the complete methodology, raw findings, and benchmark harness, see [**`docs/benchmarks.md`**](./docs/benchmarks.md).
689
+
690
+ ---
691
+
692
+ ## Performance & Philosophy
693
+
694
+ - **Zero LLM Overhead:** AI Guard does not call external APIs, does not incur token costs, and does not add LLM latency. A scan of 100+ files executes in milliseconds.
695
+ - **100% Deterministic:** Every finding is derived strictly from Abstract Syntax Tree analysis. No probabilistic drift, no non-deterministic hallucinated findings.
696
+ - **Low False Positives:** Built with precision-first design. Context-sensitive rules are configured at `warn` or `off` in the recommended preset so developers are never blocked by noise.
697
+ - **Self-Scanning:** AI Guard enforces its own rules on its own codebase in CI using the `strict` preset.
698
+
699
+ ---
700
+
701
+ ## MCP Server (Model Context Protocol)
702
+
703
+ AI Guard includes a local MCP server that exposes its deterministic analysis engine to MCP-capable AI clients (Claude Code, Claude Desktop, and other MCP-compatible integrations).
704
+
705
+ ```bash
706
+ npx --yes --package eslint-plugin-ai-guard ai-guard-mcp
707
+ ```
708
+
709
+ **Available tools:**
710
+
711
+ | Tool | Description |
712
+ | :--- | :--- |
713
+ | `ai_guard_scan_file` | Scan a single JS/TS file with structured findings |
714
+ | `ai_guard_scan_diff` | Scan changed files in git diff (including newly created untracked JS/TS files) |
715
+ | `ai_guard_scan_project` | Scan a bounded workspace directory (current source; not yet published) |
716
+ | `ai_guard_rules` | List rules and presets, or look up specific rules (`rules: ["no-floating-promise"]`) with remediation and docs link |
717
+ | `ai_guard_check_integrity` | Report checks weakened by current Git changes (new suppressions, skipped/removed tests, lowered configs, removed CI steps), with `tier` and `hasBlockingFindings` |
718
+
719
+ All tools declare an `outputSchema`; the text content is the same result as compact JSON. Scan tools accept `maxIssues` (default 50, errors first, exact `totalIssues` and `truncated`). The `verify_changes` prompt runs the scan → fix → rescan loop.
720
+
721
+ **Claude Code configuration** (project-root `.mcp.json`):
722
+
723
+ ```json
724
+ {
725
+ "mcpServers": {
726
+ "ai-guard": {
727
+ "type": "stdio",
728
+ "command": "node",
729
+ "args": ["/absolute/path/to/ai-guard/dist/mcp/server.js"],
730
+ "env": { "AI_GUARD_WORKSPACE": "/absolute/path/to/project" }
731
+ }
732
+ }
733
+ }
734
+ ```
735
+
736
+ The SDK in-memory and built STDIO integrations are tested, plus a headless Claude Code CLI smoke test (see the MCP guide for scope). Use the built checkout to test unreleased changes. Scan, fix, then rescan; a partial or failed scan is never clean. For setup, result status, limits, and client verification scope, see [**`docs/mcp-server.md`**](./docs/mcp-server.md).
737
+
738
+ ---
739
+
740
+ ## Learn More & Ecosystem
741
+
742
+ Visit [getaiguard.dev](https://getaiguard.dev) to explore interactive documentation, rule catalogs, benchmarks, and deep-dive engineering articles.
743
+
744
+ ### Official Links
745
+
746
+ - **Website:** [https://getaiguard.dev](https://getaiguard.dev)
747
+ - **GitHub Organization:** [https://github.com/ai-guard-dev](https://github.com/ai-guard-dev)
748
+ - **Repository:** [https://github.com/ai-guard-dev/eslint-plugin-ai-guard](https://github.com/ai-guard-dev/eslint-plugin-ai-guard)
749
+ - **npm Registry:** [https://www.npmjs.com/package/eslint-plugin-ai-guard](https://www.npmjs.com/package/eslint-plugin-ai-guard)
750
+ - **GitHub Action:** [ai-guard-dev/eslint-plugin-ai-guard@v1](https://github.com/ai-guard-dev/eslint-plugin-ai-guard)
751
+
752
+ ---
753
+
754
+ ## Contributing
755
+
756
+ We welcome contributions, new rule ideas, bug reports, and false-positive reports!
757
+
758
+ 1. Check out our [Contributing Guide](CONTRIBUTING.md) for local setup and testing standards.
759
+ 2. Review our [Security Policy](SECURITY.md) to report vulnerabilities responsibly.
760
+ 3. Check open issues or submit new ones on our [Issue Tracker](https://github.com/ai-guard-dev/eslint-plugin-ai-guard/issues).
761
+
762
+ ---
763
+
764
+ ## License
765
+
766
+ [MIT](LICENSE) © AI Guard Authors. Free and open source forever.