eslint-plugin-vercel-ai-security 1.3.5 → 1.3.7

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/CHANGELOG.md CHANGED
@@ -1,3 +1,51 @@
1
+ ## 1.3.7
2
+
3
+ ### Patch Changes
4
+
5
+ - [#252](https://github.com/ofri-peretz/eslint/pull/252) [`d67e395`](https://github.com/ofri-peretz/eslint/commit/d67e3953c2748ad36e6aebe0f24b1d04e518b4d0) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - Fix Codecov badge showing "unknown" — switch from flag to component URL format
6
+
7
+ ## 1.3.6
8
+
9
+ ### Patch Changes
10
+
11
+ - [#144](https://github.com/ofri-peretz/eslint/pull/144) [`8843ce7`](https://github.com/ofri-peretz/eslint/commit/8843ce7fbb465caad161b97032467b3a37a49319) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - fix: ILB-Wild FP reduction + doc examples + doc-test-alignment scanner fixes
12
+
13
+ **`no-unlimited-resource-allocation` — FP reduction (430 Edge FPs)**
14
+ - Skip loop-allocation reporting when the first argument is a numeric literal (e.g. `Buffer.alloc(1024)` inside a loop is statically bounded, not a risk)
15
+ - Skip `Array.isArray`, `Array.from`, `Array.of` calls in the `alloc/Array` pattern check (these don't allocate unbounded memory)
16
+
17
+ **`no-hardcoded-credentials` — FP reduction (~280 Edge FPs)**
18
+ - Extended test-file skip to cover `.fixture.`, `.mock.`, `__mocks__/`, `/tests/`, `/fixtures/`, `/mocks/` paths
19
+ - Skip string literals that are fallback values in `process.env.X || 'fallback'` expressions — the secret lives in the environment, the string is only a dev-mode default
20
+
21
+ **Doc examples — 4 rules now have ❌ Incorrect examples**
22
+ - `lambda-security/no-missing-authorization-check`
23
+ - `lambda-security/no-overly-permissive-iam-policy`
24
+ - `node-security/prefer-native-crypto` (renamed non-standard `### ❌ Third-Party (Flagged)` to `### ❌ Incorrect`)
25
+ - `vercel-ai-security/require-tool-confirmation` (replaced placeholder with a real tested example)
26
+
27
+ **`ilb-doc-test-alignment` scanner fixes**
28
+ - Accept both `## ❌` and `### ❌` headings (docs use H3 under an H2 `## Examples` section; was only finding H2)
29
+ - Slice from end-of-line rather than end-of-regex-match (prevents `## ❌ Incorrect Code` from leaving a partial heading in the parsed section)
30
+
31
+ Result: `ilb:doc-test-alignment` → 206 ok, 0 doc has no ❌ examples (was 165 missing).
32
+
33
+ - [#143](https://github.com/ofri-peretz/eslint/pull/143) [`213cde1`](https://github.com/ofri-peretz/eslint/commit/213cde190ff2aea49ca7c1b533170940f879d9b4) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - fix(no-missing-null-checks): eliminate 53 false positives via three new narrowing patterns
34
+
35
+ Rules that were recognized as null guards are now correctly identified as safe:
36
+ 1. **Truthy if guard** — `if (obj) { obj.prop }` — direct truthy check proves non-null. Also covers chains: `if (response)` protects `response.data.items`.
37
+ 2. **Short-circuit AND** — `obj && obj.prop` — right side of `&&` only runs when left is truthy.
38
+ 3. **Ternary consequent** — `obj ? obj.prop : fallback` — truthy test guards the consequent.
39
+
40
+ Also: bumped `beforeAll` timeout to 30 seconds in 7 compatibility test files (`__compatibility__/*.spec.ts`). Native-addon packages routinely exceed the previous 10-second default on a cold ESM load.
41
+
42
+ - Updated dependencies [[`736a5fe`](https://github.com/ofri-peretz/eslint/commit/736a5fed47e673f6157ea900b29fe2a54e4bc7df)]:
43
+ - @interlace/eslint-devkit@1.4.1
44
+
45
+ ## 1.3.5 (2026-02-09)
46
+
47
+ This was a version bump only for eslint-plugin-vercel-ai-security to align it with other projects, there were no code changes.
48
+
1
49
  ## [1.3.3] - 2026-02-08
2
50
 
3
51
  ### Bug Fixes
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <a href="https://eslint.interlace.tools" target="blank"><img src="https://eslint.interlace.tools/eslint-interlace-logo-light.svg" alt="ESLint Interlace Logo" width="120" /></a>
2
+ <a href="https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security" target="blank"><img src="https://eslint.interlace.tools/eslint-interlace-logo-light.svg" alt="ESLint Interlace Logo" width="120" /></a>
3
3
  </p>
4
4
 
5
5
  <p align="center">
@@ -14,10 +14,11 @@
14
14
  <a href="https://github.com/ofri-peretz/eslint" target="_blank"><img src="https://img.shields.io/badge/Since-Dec_2025-blue?logo=rocket&logoColor=white" alt="Since Dec 2025" /></a>
15
15
  </p>
16
16
 
17
+ > ⭐ If this plugin caught a real bug for you, [**star the repo**](https://github.com/ofri-peretz/eslint) — it's the signal that keeps these rules maintained.
18
+
17
19
  ## Description
18
20
 
19
21
  This plugin provides Security rules for Vercel AI SDK usage (prompt injection, data handling).
20
- By using this plugin, you can proactively identify and mitigate security risks across your entire codebase.
21
22
 
22
23
  ## Philosophy
23
24
 
@@ -25,29 +26,17 @@ By using this plugin, you can proactively identify and mitigate security risks a
25
26
 
26
27
  ## Getting Started
27
28
 
28
- - To check out the [guide](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security), visit [eslint.interlace.tools](https://eslint.interlace.tools). 📚
29
- - 要查看中文 [指南](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security), 请访问 [eslint.interlace.tools](https://eslint.interlace.tools). 📚
30
- - [가이드](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security) 문서는 [eslint.interlace.tools](https://eslint.interlace.tools)에서 확인하실 수 있습니다. 📚
31
- - [ガイド](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security)は [eslint.interlace.tools](https://eslint.interlace.tools)でご確認ください。 📚
32
- - Para ver la [guía](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security), visita [eslint.interlace.tools](https://eslint.interlace.tools). 📚
33
- - للاطلاع على [الدليل](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security)، قم بزيارة [eslint.interlace.tools](https://eslint.interlace.tools). 📚
29
+ - To check out the [guide](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security), visit [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security). 📚
30
+ - 要查看中文 [指南](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security), 请访问 [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security). 📚
31
+ - [가이드](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) 문서는 [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security)에서 확인하실 수 있습니다. 📚
32
+ - [ガイド](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security)は [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security)でご確認ください。 📚
33
+ - Para ver la [guía](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security), visita [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security). 📚
34
+ - للاطلاع على [الدليل](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security)، قم بزيارة [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security). 📚
34
35
 
35
36
  ```bash
36
37
  npm install eslint-plugin-vercel-ai-security --save-dev
37
38
  ```
38
39
 
39
- ## 🔧 Supported AI SDK Functions
40
- | Function | Full Coverage |
41
- | ---------------------- | ------------------------------ |
42
- | `generateText` | ✅ All 19 rules |
43
- | `streamText` | ✅ All 19 rules + abort signal |
44
- | `generateObject` | ✅ All 19 rules |
45
- | `streamObject` | ✅ All 19 rules + abort signal |
46
- | `tool()` helper | ✅ Schema validation |
47
- | `embed()` / embeddings | ✅ Embedding validation |
48
-
49
- ---
50
-
51
40
  ## ⚙️ Configuration Presets
52
41
  | Preset | Description |
53
42
  | :------------ | :--------------------------------------- |
@@ -55,17 +44,10 @@ npm install eslint-plugin-vercel-ai-security --save-dev
55
44
  | `strict` | Maximum security (17 errors, 2 warnings) |
56
45
  | `minimal` | Minimal config |
57
46
 
58
- ---
59
-
60
- ## 📊 Test Coverage
61
- | Metric | Coverage |
62
- | ------------- | -------- |
63
- | **Rules** | 19 |
64
- | **Tests** | 200 |
65
- | **Lines** | 98%+ |
66
- | **Functions** | 100% |
67
-
68
- ---
47
+ ## 📚 Supported Libraries
48
+ | Library | npm | Downloads | Detection |
49
+ | -------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------ |
50
+ | `ai` (Vercel AI SDK) | [![npm](https://img.shields.io/npm/v/ai.svg?style=flat-square)](https://www.npmjs.com/package/ai) | [![downloads](https://img.shields.io/npm/dt/ai.svg?style=flat-square)](https://www.npmjs.com/package/ai) | Prompt Injection, Data Leakage |
69
51
 
70
52
  ## 🤖 AI-Agent Optimized Messages
71
53
  All rule messages follow a structured format optimized for AI coding assistants:
@@ -77,14 +59,23 @@ All rule messages follow a structured format optimized for AI coding assistants:
77
59
 
78
60
  By providing this structured context (CWE, OWASP, Fix), we enable AI tools to **reason** about the security flaw rather than hallucinating. This allows Copilot/Cursor to suggest the _exact_ correct fix immediately.
79
61
 
80
- ## 📦 Compatibility
81
- | Package | Version |
82
- | -------------------- | --------------------------------------------------------------------------------------------------------- |
83
- | `ai` (Vercel AI SDK) | [![npm](https://img.shields.io/npm/v/ai.svg?style=flat-square)](https://www.npmjs.com/package/ai) |
84
- | ESLint | [![npm](https://img.shields.io/npm/v/eslint.svg?style=flat-square)](https://www.npmjs.com/package/eslint) |
85
- | Node.js | [![node](https://img.shields.io/badge/node-%5E18.0.0-green?style=flat-square)](https://nodejs.org/) |
62
+ ## 🔧 Supported AI SDK Functions
63
+ | Function | Full Coverage |
64
+ | ---------------------- | ------------------------------ |
65
+ | `generateText` | All 19 rules |
66
+ | `streamText` | All 19 rules + abort signal |
67
+ | `generateObject` | ✅ All 19 rules |
68
+ | `streamObject` | ✅ All 19 rules + abort signal |
69
+ | `tool()` helper | ✅ Schema validation |
70
+ | `embed()` / embeddings | ✅ Embedding validation |
86
71
 
87
- ---
72
+ ## 📊 Test Coverage
73
+ | Metric | Coverage |
74
+ | ------------- | -------- |
75
+ | **Rules** | 19 |
76
+ | **Tests** | 200 |
77
+ | **Lines** | 98%+ |
78
+ | **Functions** | 100% |
88
79
 
89
80
  ## 🙋 FAQ
90
81
  ### What's the difference between this and generic AI security linters?
@@ -93,7 +84,7 @@ Generic linters guess at patterns. This plugin knows the **exact** Vercel AI SDK
93
84
 
94
85
  ### Does this work with ESLint 9 Flat Config?
95
86
 
96
- Yes! Designed specifically for ESLint Flat Config.
87
+ Yes! Designed specifically for ESLint Flat Config — works on ESLint 8 (with flat config), 9, and 10. See the [ESLint Version Support Policy](../../docs/ESLINT_VERSION_SUPPORT.md) for the full matrix.
97
88
 
98
89
  ### How do I suppress a rule for a specific line?
99
90
 
@@ -106,10 +97,14 @@ await generateText({ prompt: internalPrompt });
106
97
 
107
98
  TypeScript/JavaScript are memory-safe languages. Memory corruption vulnerabilities (buffer overflows, use-after-free, etc.) are not possible in these environments.
108
99
 
109
- ## 📚 Supported Libraries
110
- | Library | npm | Downloads | Detection |
111
- | -------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------ |
112
- | `ai` (Vercel AI SDK) | [![npm](https://img.shields.io/npm/v/ai.svg?style=flat-square)](https://www.npmjs.com/package/ai) | [![downloads](https://img.shields.io/npm/dt/ai.svg?style=flat-square)](https://www.npmjs.com/package/ai) | Prompt Injection, Data Leakage |
100
+ ## 📦 Compatibility
101
+ | Package | Version |
102
+ | -------------------- | --------------------------------------------------------------------------------------------------------- |
103
+ | `ai` (Vercel AI SDK) | [![npm](https://img.shields.io/npm/v/ai.svg?style=flat-square)](https://www.npmjs.com/package/ai) |
104
+ | ESLint | [![npm](https://img.shields.io/npm/v/eslint.svg?style=flat-square)](https://www.npmjs.com/package/eslint) |
105
+ | Node.js | [![node](https://img.shields.io/badge/node-%5E18.0.0-green?style=flat-square)](https://nodejs.org/) |
106
+
107
+ See the [ESLint Version Support Policy](../../docs/ESLINT_VERSION_SUPPORT.md) for the full matrix.
113
108
 
114
109
  ## Rules
115
110
 
@@ -118,33 +113,37 @@ TypeScript/JavaScript are memory-safe languages. Memory corruption vulnerabiliti
118
113
  | Icon | Description |
119
114
  | :---: | :--- |
120
115
  | 💼 | **Recommended**: Included in the recommended preset. |
121
- | ⚠️ | **Warns**: Set towarn in recommended preset. |
116
+ | ⚠️ | **Warns**: Set to warn in recommended preset. |
122
117
  | 🔧 | **Auto-fixable**: Automatically fixable by the `--fix` CLI option. |
123
118
  | 💡 | **Suggestions**: Providing code suggestions in IDE. |
124
119
  | 🚫 | **Deprecated**: This rule is deprecated. |
125
-
126
- | Rule | CWE | OWASP | CVSS | Description | 💼 | ⚠️ | 🔧 | 💡 | 🚫 |
127
- | :--- | :---: | :---: | :---: | :--- | :---: | :---: | :---: | :---: | :---: |
128
- | [no-dynamic-system-prompt](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/no-dynamic-system-prompt) | CWE-74 | | 8.0 | ESLint rule documentation for no-dynamic-system-prompt | 💼 | | | | |
129
- | [no-hardcoded-api-keys](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/no-hardcoded-api-keys) | CWE-798 | | 8.5 | ESLint rule documentation for no-hardcoded-api-keys | 💼 | | | | |
130
- | [no-sensitive-in-prompt](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/no-sensitive-in-prompt) | CWE-200 | | 8.0 | ESLint rule documentation for no-sensitive-in-prompt | 💼 | | | | |
131
- | [no-system-prompt-leak](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/no-system-prompt-leak) | CWE-200 | | 7.5 | ESLint rule documentation for no-system-prompt-leak | 💼 | | | | |
132
- | [no-training-data-exposure](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/no-training-data-exposure) | CWE-359 | | 7.0 | ESLint rule documentation for no-training-data-exposure | 💼 | ⚠️ | | | |
133
- | [no-unsafe-output-handling](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/no-unsafe-output-handling) | CWE-94 | | 9.8 | ESLint rule documentation for no-unsafe-output-handling | 💼 | | | | |
134
- | [require-abort-signal](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-abort-signal) | CWE-404 | | 4.0 | ESLint rule documentation for require-abort-signal | | | | 💡 | |
135
- | [require-audit-logging](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-audit-logging) | CWE-778 | | 4.0 | ESLint rule documentation for require-audit-logging | | | | 💡 | |
136
- | [require-embedding-validation](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-embedding-validation) | CWE-20 | | 5.5 | ESLint rule documentation for require-embedding-validation | | | | 💡 | |
137
- | [require-error-handling](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-error-handling) | CWE-755 | | 5.0 | ESLint rule documentation for require-error-handling | | | | 💡 | |
138
- | [require-max-steps](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-max-steps) | CWE-834 | | 6.5 | ESLint rule documentation for require-max-steps | 💼 | ⚠️ | | | |
139
- | [require-max-tokens](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-max-tokens) | CWE-770 | | 6.5 | ESLint rule documentation for require-max-tokens | 💼 | ⚠️ | | | |
140
- | [require-output-filtering](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-output-filtering) | CWE-200 | | 6.5 | ESLint rule documentation for require-output-filtering | 💼 | ⚠️ | | | |
141
- | [require-output-validation](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-output-validation) | CWE-707 | | 5.0 | ESLint rule documentation for require-output-validation | | | | 💡 | |
142
- | [require-rag-content-validation](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-rag-content-validation) | CWE-74 | | 6.0 | ESLint rule documentation for require-rag-content-validation | 💼 | ⚠️ | | | |
143
- | [require-request-timeout](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-request-timeout) | CWE-400 | | 5.0 | ESLint rule documentation for require-request-timeout | 💼 | ⚠️ | | | |
144
- | [require-tool-confirmation](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-tool-confirmation) | CWE-862 | | 7.0 | ESLint rule documentation for require-tool-confirmation | 💼 | | | | |
145
- | [require-tool-schema](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-tool-schema) | CWE-20 | | 7.5 | ESLint rule documentation for require-tool-schema | 💼 | ⚠️ | | | |
146
- | [require-validated-prompt](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-validated-prompt) | CWE-74 | | 9.0 | ESLint rule documentation for require-validated-prompt | 💼 | | | | |
147
-
120
+ | 🟢 | **Type-unaware**: AST-only, runs in oxlint JS-plugin tier. |
121
+ | 🟡 | **Type-aware (refining)**: pure-AST primary path; types refine precision. |
122
+ | 🟠 | **Type-aware (graceful)**: requires TS program; silent without it. |
123
+
124
+ <!-- AUTO-GENERATED:RULES_TABLE:START - Do not edit manually -->
125
+ | Rule | CWE | OWASP | CVSS | Description | 🧠 | 💼 | ⚠️ | 🔧 | 💡 | 🚫 |
126
+ | :--- | :---: | :---: | :---: | :--- | :---: | :---: | :---: | :---: | :---: | :---: |
127
+ | [no-dynamic-system-prompt](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/no-dynamic-system-prompt?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-74 | | | This rule identifies code patterns where system prompts contain dynamic or user-controlled content | 🟢 | | | | | |
128
+ | [no-hardcoded-api-keys](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/no-hardcoded-api-keys?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-798 | | | This rule identifies hardcoded API keys, tokens, and secrets in your codebase that are used with AI SDK pro… | 🟢 | | | | | |
129
+ | [no-sensitive-in-prompt](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/no-sensitive-in-prompt?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-200 | | | This rule identifies code patterns where sensitive data like passwords, API keys, tokens, or personally ide… | 🟢 | | | | | |
130
+ | [no-system-prompt-leak](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/no-system-prompt-leak?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-200 | | | This rule identifies code patterns where system prompts or AI instructions are returned in API responses, l… | 🟢 | | | | | |
131
+ | [no-training-data-exposure](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/no-training-data-exposure?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-359 | | | This rule identifies code patterns where user data might be sent to LLM training endpoints or when training… | 🟢 | | | | | |
132
+ | [no-unsafe-output-handling](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/no-unsafe-output-handling?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-94 | | | This rule identifies code patterns where AI-generated output is passed directly to dangerous functions that… | 🟢 | | | | | |
133
+ | [require-abort-signal](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-abort-signal?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-404 | | | This rule identifies streaming AI SDK calls (streamText, streamObject) that don't include an AbortSignal fo… | 🟢 | | | | | |
134
+ | [require-audit-logging](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-audit-logging?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-778 | | | This rule identifies AI SDK calls that aren't preceded by logging statements | 🟢 | | | | | |
135
+ | [require-embedding-validation](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-embedding-validation?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-20 | | | This rule identifies code patterns where embeddings are stored in vector databases without validation. | 🟢 | | | | | |
136
+ | [require-error-handling](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-error-handling?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-755 | | | This rule identifies AI SDK calls that aren't wrapped in try-catch blocks | 🟢 | | | | | |
137
+ | [require-max-steps](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-max-steps?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-834 | | | This rule identifies AI SDK calls that use tools but don't specify a maxSteps limit | 🟢 | | | | | |
138
+ | [require-max-tokens](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-max-tokens?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-770 | | | This rule identifies AI SDK calls that don't specify a maxTokens limit | 🟢 | | | | | |
139
+ | [require-output-filtering](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-output-filtering?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-200 | | | This rule identifies tool execute functions that return raw data from data sources (databases, APIs, file s… | 🟢 | | | | | |
140
+ | [require-output-validation](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-output-validation?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-707 | | | This rule identifies code patterns where AI-generated output is displayed to users without validation or fa… | 🟢 | | | | | |
141
+ | [require-rag-content-validation](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-rag-content-validation?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-74 | | | This rule identifies code patterns where content retrieved from vector stores or document retrieval systems… | 🟢 | | | | | |
142
+ | [require-request-timeout](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-request-timeout?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-400 | | | This rule identifies AI SDK calls that don't have timeout or abort signal configuration. | 🟢 | | | | | |
143
+ | [require-tool-confirmation](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-tool-confirmation?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-862 | | | This rule identifies destructive tools (delete, transfer, execute, etc.) that don't require human confirmat… | 🟢 | | | | | |
144
+ | [require-tool-schema](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-tool-schema?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-20 | | | Get weather | 🟢 | | | | | |
145
+ | [require-validated-prompt](https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security/rules/require-validated-prompt?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security) | CWE-74 | | | This rule identifies code patterns where user-controlled input is passed directly to AI prompts without val… | 🟢 | | | | | |
146
+ <!-- AUTO-GENERATED:RULES_TABLE:END -->
148
147
  ## 🔗 Related ESLint Plugins
149
148
 
150
149
  Part of the **Interlace ESLint Ecosystem** — AI-native security plugins with LLM-optimized error messages:
@@ -153,7 +152,7 @@ Part of the **Interlace ESLint Ecosystem** — AI-native security plugins with L
153
152
  | :--- | :---: | :--- |
154
153
  | [`eslint-plugin-secure-coding`](https://www.npmjs.com/package/eslint-plugin-secure-coding) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-secure-coding.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-secure-coding) | General security rules & OWASP guidelines. |
155
154
  | [`eslint-plugin-pg`](https://www.npmjs.com/package/eslint-plugin-pg) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-pg.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-pg) | PostgreSQL security & best practices. |
156
- | [`eslint-plugin-crypto`](https://www.npmjs.com/package/eslint-plugin-crypto) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-crypto.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-crypto) | NodeJS Cryptography security rules. |
155
+ | [`eslint-plugin-node-security`](https://www.npmjs.com/package/eslint-plugin-node-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-node-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-node-security) | Node.js core-module security (fs, child_process, vm, crypto, Buffer). |
157
156
  | [`eslint-plugin-jwt`](https://www.npmjs.com/package/eslint-plugin-jwt) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-jwt.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-jwt) | JWT security & best practices. |
158
157
  | [`eslint-plugin-browser-security`](https://www.npmjs.com/package/eslint-plugin-browser-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-browser-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-browser-security) | Browser-specific security & XSS prevention. |
159
158
  | [`eslint-plugin-express-security`](https://www.npmjs.com/package/eslint-plugin-express-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-express-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-express-security) | Express.js security hardening rules. |
@@ -163,10 +162,19 @@ Part of the **Interlace ESLint Ecosystem** — AI-native security plugins with L
163
162
  | [`eslint-plugin-vercel-ai-security`](https://www.npmjs.com/package/eslint-plugin-vercel-ai-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-vercel-ai-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-vercel-ai-security) | Vercel AI SDK security hardening. |
164
163
  | [`eslint-plugin-import-next`](https://www.npmjs.com/package/eslint-plugin-import-next) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-import-next.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-import-next) | Next-gen import sorting & architecture. |
165
164
 
165
+ <!-- INTERLACE:STAR_CTA:START -->
166
+ ## ⭐ Support & follow
167
+
168
+ If this plugin caught a real bug for you, **[star the repo](https://github.com/ofri-peretz/eslint)** — stars are the signal that keeps the Interlace ESLint ecosystem maintained — and **[follow the writeups on Dev.to](https://dev.to/ofri-peretz)** for the benchmarks and security research behind these rules.
169
+
170
+ [![GitHub stars](https://img.shields.io/github/stars/ofri-peretz/eslint?style=social)](https://github.com/ofri-peretz/eslint)
171
+
172
+ <!-- INTERLACE:STAR_CTA:END -->
173
+
166
174
  ## 📄 License
167
175
 
168
176
  MIT © [Ofri Peretz](https://github.com/ofri-peretz)
169
177
 
170
178
  <p align="center">
171
- <a href="https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security"><img src="https://eslint.interlace.tools/images/og-vercel-ai-security.png" alt="ESLint Interlace Plugin" width="100%" /></a>
179
+ <a href="https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security"><img src="https://eslint.interlace.tools/images/og-vercel-ai-security.png" alt="ESLint Interlace Plugin" width="100%" /></a>
172
180
  </p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-vercel-ai-security",
3
- "version": "1.3.5",
3
+ "version": "1.3.7",
4
4
  "description": "Security-focused ESLint plugin for Vercel AI SDK. SDK-aware rules for generateText, streamText, tools, and streaming patterns with full type knowledge.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -13,11 +13,15 @@
13
13
  "./types": {
14
14
  "types": "./src/types/index.d.ts",
15
15
  "default": "./src/types/index.js"
16
+ },
17
+ "./oxlint": {
18
+ "types": "./src/oxlint.d.ts",
19
+ "default": "./src/oxlint.js"
16
20
  }
17
21
  },
18
22
  "author": "Ofri Peretz <ofriperetzdev@gmail.com>",
19
23
  "license": "MIT",
20
- "homepage": "https://github.com/ofri-peretz/eslint/tree/main/packages/eslint-plugin-vercel-ai-security#readme",
24
+ "homepage": "https://eslint.interlace.tools/docs/security/plugin-vercel-ai-security?utm_source=npm&utm_medium=referral&utm_campaign=eslint-plugin-vercel-ai-security",
21
25
  "repository": {
22
26
  "type": "git",
23
27
  "url": "https://github.com/ofri-peretz/eslint",
@@ -64,7 +68,9 @@
64
68
  "engines": {
65
69
  "node": ">=18.0.0"
66
70
  },
67
- "peerDependencies": {},
71
+ "peerDependencies": {
72
+ "eslint": "^8.0.0 || ^9.0.0 || ^10.0.0"
73
+ },
68
74
  "peerDependenciesMeta": {
69
75
  "ai": {
70
76
  "optional": true
@@ -72,12 +78,22 @@
72
78
  },
73
79
  "dependencies": {
74
80
  "tslib": "^2.3.0",
75
- "@interlace/eslint-devkit": "^1.2.1"
81
+ "@interlace/eslint-devkit": "^1.4.1"
76
82
  },
77
83
  "devDependencies": {
78
- "@ai-sdk/openai": "^2.0.86",
84
+ "@ai-sdk/openai": "^4.0.16",
79
85
  "@typescript-eslint/parser": "^8.46.2",
80
86
  "@typescript-eslint/rule-tester": "^8.46.2",
81
- "ai": "^5.0.113"
87
+ "ai": "^7.0.31"
88
+ },
89
+ "scripts": {
90
+ "build": "tsx ../../scripts/build-package.ts",
91
+ "test": "vitest run",
92
+ "test:coverage": "vitest run --coverage",
93
+ "typecheck": "tsc -p tsconfig.lib.json --noEmit"
94
+ },
95
+ "funding": {
96
+ "type": "github",
97
+ "url": "https://github.com/ofri-peretz/eslint"
82
98
  }
83
- }
99
+ }
package/src/index.js CHANGED
@@ -152,7 +152,7 @@ exports.rules = {
152
152
  exports.plugin = {
153
153
  meta: {
154
154
  name: 'eslint-plugin-vercel-ai-security',
155
- version: '0.3.0',
155
+ version: '1.3.7',
156
156
  },
157
157
  rules: exports.rules,
158
158
  };
@@ -187,16 +187,16 @@ const recommendedConfig = {
187
187
  'vercel-ai-security/no-dynamic-system-prompt': 'error',
188
188
  'vercel-ai-security/require-tool-confirmation': 'error',
189
189
  // High - enabled as warnings
190
- 'vercel-ai-security/require-tool-schema': 'warn',
191
- 'vercel-ai-security/require-max-tokens': 'warn',
192
- 'vercel-ai-security/require-max-steps': 'warn',
190
+ 'vercel-ai-security/require-tool-schema': 'error',
191
+ 'vercel-ai-security/require-max-tokens': 'error',
192
+ 'vercel-ai-security/require-max-steps': 'error',
193
193
  'vercel-ai-security/require-output-filtering': 'warn',
194
194
  'vercel-ai-security/require-rag-content-validation': 'warn',
195
195
  'vercel-ai-security/no-training-data-exposure': 'warn',
196
196
  'vercel-ai-security/require-request-timeout': 'warn',
197
197
  // Medium - disabled by default
198
198
  'vercel-ai-security/require-error-handling': 'off',
199
- 'vercel-ai-security/require-abort-signal': 'off',
199
+ 'vercel-ai-security/require-abort-signal': 'error',
200
200
  'vercel-ai-security/require-audit-logging': 'off',
201
201
  'vercel-ai-security/require-embedding-validation': 'off',
202
202
  'vercel-ai-security/require-output-validation': 'off',
@@ -229,7 +229,7 @@ const strictConfig = {
229
229
  'vercel-ai-security/require-embedding-validation': 'error',
230
230
  'vercel-ai-security/require-output-validation': 'error',
231
231
  'vercel-ai-security/require-error-handling': 'error',
232
- 'vercel-ai-security/require-abort-signal': 'warn',
232
+ 'vercel-ai-security/require-abort-signal': 'error',
233
233
  'vercel-ai-security/require-audit-logging': 'warn',
234
234
  },
235
235
  };
@@ -237,6 +237,19 @@ const strictConfig = {
237
237
  * Available configurations (3 options)
238
238
  */
239
239
  exports.configs = {
240
+ /**
241
+ * Flagship preset — the rule from this plugin in the ecosystem-wide
242
+ * flagship list (`.agent/flagship-rules.md`). Anchors the OWASP LLM02
243
+ * niche — green-field, no competitor.
244
+ */
245
+ flagship: {
246
+ plugins: {
247
+ 'vercel-ai-security': exports.plugin,
248
+ },
249
+ rules: {
250
+ 'vercel-ai-security/no-unsafe-output-handling': 'error',
251
+ },
252
+ },
240
253
  /** Minimal: 2 critical rules for gradual adoption */
241
254
  minimal: minimalConfig,
242
255
  /** Recommended: Balanced security (7 errors, 7 warnings) */
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Copyright (c) 2025 Ofri Peretz
3
+ * Licensed under the MIT License. Use of this source code is governed by the
4
+ * MIT license that can be found in the LICENSE file.
5
+ */
6
+ /**
7
+ * oxlint sub-export.
8
+ *
9
+ * Consumers wire oxlint to this entry via:
10
+ * { "jsPlugins": ["eslint-plugin-vercel-ai-security/oxlint"] }
11
+ *
12
+ * oxlint's JS plugin loader does `require('eslint-plugin-vercel-ai-security/oxlint')` and reads
13
+ * `.rules` off the result. The compiled output of `export = plugin` is
14
+ * `module.exports = plugin`, which gives oxlint the plugin object directly
15
+ * (`{ meta, rules }`) — no wrapper, no `.default` indirection.
16
+ *
17
+ * Generated by scripts/generate-oxlint-shims.mjs. Do not edit by hand.
18
+ */
19
+ import { plugin } from './index';
20
+ export = plugin;
package/src/oxlint.js ADDED
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2025 Ofri Peretz
4
+ * Licensed under the MIT License. Use of this source code is governed by the
5
+ * MIT license that can be found in the LICENSE file.
6
+ */
7
+ /**
8
+ * oxlint sub-export.
9
+ *
10
+ * Consumers wire oxlint to this entry via:
11
+ * { "jsPlugins": ["eslint-plugin-vercel-ai-security/oxlint"] }
12
+ *
13
+ * oxlint's JS plugin loader does `require('eslint-plugin-vercel-ai-security/oxlint')` and reads
14
+ * `.rules` off the result. The compiled output of `export = plugin` is
15
+ * `module.exports = plugin`, which gives oxlint the plugin object directly
16
+ * (`{ meta, rules }`) — no wrapper, no `.default` indirection.
17
+ *
18
+ * Generated by scripts/generate-oxlint-shims.mjs. Do not edit by hand.
19
+ */
20
+ const index_1 = require("./index");
21
+ module.exports = index_1.plugin;
@@ -17,7 +17,10 @@ exports.noDynamicSystemPrompt = (0, eslint_devkit_1.createRule)({
17
17
  meta: {
18
18
  type: 'problem',
19
19
  docs: {
20
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/no-dynamic-system-prompt.md',
20
21
  description: 'Prevent dynamic content in system prompts to avoid agent confusion attacks',
22
+ cwe: 'CWE-74',
23
+ cvss: 8,
21
24
  },
22
25
  messages: {
23
26
  dynamicSystemPrompt: (0, eslint_devkit_1.formatLLMMessage)({
@@ -54,7 +57,7 @@ exports.noDynamicSystemPrompt = (0, eslint_devkit_1.createRule)({
54
57
  create(context) {
55
58
  const [options = {}] = context.options;
56
59
  const allowStaticTemplates = options.allowStaticTemplates ?? true;
57
- const sourceCode = context.sourceCode || context.getSourceCode();
60
+ const sourceCode = context.sourceCode;
58
61
  // Vercel AI SDK functions
59
62
  const aiSDKFunctions = ['generateText', 'streamText', 'generateObject', 'streamObject'];
60
63
  /**
@@ -17,7 +17,10 @@ exports.noHardcodedApiKeys = (0, eslint_devkit_1.createRule)({
17
17
  meta: {
18
18
  type: 'problem',
19
19
  docs: {
20
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/no-hardcoded-api-keys.md',
20
21
  description: 'Prevent hardcoded API keys in AI SDK model configuration',
22
+ cwe: 'CWE-798',
23
+ cvss: 8.5,
21
24
  },
22
25
  messages: {
23
26
  hardcodedApiKey: (0, eslint_devkit_1.formatLLMMessage)({
@@ -57,7 +60,7 @@ exports.noHardcodedApiKeys = (0, eslint_devkit_1.createRule)({
57
60
  const apiKeyPatterns = options.apiKeyPatterns ?? [
58
61
  'apiKey', 'api_key', 'token', 'secret', 'credentials',
59
62
  ];
60
- const sourceCode = context.sourceCode || context.getSourceCode();
63
+ const sourceCode = context.sourceCode;
61
64
  // Known model provider functions
62
65
  const providerFunctions = [
63
66
  'openai', 'anthropic', 'google', 'cohere', 'mistral',
@@ -66,6 +69,7 @@ exports.noHardcodedApiKeys = (0, eslint_devkit_1.createRule)({
66
69
  /**
67
70
  * Check if a string looks like an API key
68
71
  */
72
+ // oxlint-disable-next-line consistent-function-scoping
69
73
  function looksLikeApiKey(value) {
70
74
  // Common API key patterns
71
75
  const patterns = [
@@ -17,7 +17,10 @@ exports.noSensitiveInPrompt = (0, eslint_devkit_1.createRule)({
17
17
  meta: {
18
18
  type: 'problem',
19
19
  docs: {
20
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/no-sensitive-in-prompt.md',
20
21
  description: 'Prevent sensitive data (secrets, credentials, PII) from being passed to LLM prompts',
22
+ cwe: 'CWE-200',
23
+ cvss: 8,
21
24
  },
22
25
  messages: {
23
26
  sensitiveInPrompt: (0, eslint_devkit_1.formatLLMMessage)({
@@ -64,7 +67,7 @@ exports.noSensitiveInPrompt = (0, eslint_devkit_1.createRule)({
64
67
  'password', 'secret', 'apiKey', 'api_key', 'token', 'credential',
65
68
  'ssn', 'creditCard', 'privateKey', 'accessToken',
66
69
  ];
67
- const sourceCode = context.sourceCode || context.getSourceCode();
70
+ const sourceCode = context.sourceCode;
68
71
  // Vercel AI SDK functions
69
72
  const aiSDKFunctions = ['generateText', 'streamText', 'generateObject', 'streamObject'];
70
73
  /**
@@ -115,12 +118,12 @@ exports.noSensitiveInPrompt = (0, eslint_devkit_1.createRule)({
115
118
  if (!optionsArg || optionsArg.type !== 'ObjectExpression')
116
119
  return;
117
120
  // Check prompt, system, and messages properties
118
- const propsToCheck = ['prompt', 'system', 'messages'];
121
+ const propsToCheck = new Set(['prompt', 'system', 'messages']);
119
122
  for (const prop of optionsArg.properties) {
120
123
  if (prop.type !== 'Property')
121
124
  continue;
122
125
  const keyName = prop.key.type === 'Identifier' ? prop.key.name : null;
123
- if (!keyName || !propsToCheck.includes(keyName))
126
+ if (!keyName || !propsToCheck.has(keyName))
124
127
  continue;
125
128
  const sensitiveVar = findSensitiveData(prop.value);
126
129
  if (sensitiveVar) {
@@ -17,7 +17,10 @@ exports.noSystemPromptLeak = (0, eslint_devkit_1.createRule)({
17
17
  meta: {
18
18
  type: 'problem',
19
19
  docs: {
20
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/no-system-prompt-leak.md',
20
21
  description: 'Prevent system prompts from being exposed in API responses or client code',
22
+ cwe: 'CWE-200',
23
+ cvss: 7.5,
21
24
  },
22
25
  messages: {
23
26
  systemPromptLeak: (0, eslint_devkit_1.formatLLMMessage)({
@@ -63,7 +66,7 @@ exports.noSystemPromptLeak = (0, eslint_devkit_1.createRule)({
63
66
  'systemPrompt', 'system_prompt', 'SYSTEM_PROMPT',
64
67
  'systemMessage', 'instructions', 'agentPrompt',
65
68
  ];
66
- const sourceCode = context.sourceCode || context.getSourceCode();
69
+ const sourceCode = context.sourceCode;
67
70
  /**
68
71
  * Check if identifier matches system prompt pattern
69
72
  */
@@ -17,7 +17,10 @@ exports.noTrainingDataExposure = (0, eslint_devkit_1.createRule)({
17
17
  meta: {
18
18
  type: 'problem',
19
19
  docs: {
20
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/no-training-data-exposure.md',
20
21
  description: 'Prevent user data from being sent to LLM training endpoints',
22
+ cwe: 'CWE-359',
23
+ cvss: 7,
21
24
  },
22
25
  messages: {
23
26
  trainingDataExposure: (0, eslint_devkit_1.formatLLMMessage)({
@@ -18,7 +18,11 @@ exports.noUnsafeOutputHandling = (0, eslint_devkit_1.createRule)({
18
18
  meta: {
19
19
  type: 'problem',
20
20
  docs: {
21
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/no-unsafe-output-handling.md',
21
22
  description: 'Prevent using AI output directly in dangerous operations (eval, SQL, HTML)',
23
+ cwe: 'CWE-94',
24
+ cvss: 9.8,
25
+ confidence: 'medium',
22
26
  },
23
27
  messages: {
24
28
  unsafeOutputExecution: (0, eslint_devkit_1.formatLLMMessage)({
@@ -95,19 +99,70 @@ exports.noUnsafeOutputHandling = (0, eslint_devkit_1.createRule)({
95
99
  'result.text', 'response.text', 'completion', 'generated',
96
100
  'aiOutput', 'aiResponse', 'llmOutput', '.text',
97
101
  ];
98
- const sourceCode = context.sourceCode || context.getSourceCode();
102
+ const sourceCode = context.sourceCode;
99
103
  // Dangerous execution functions
100
104
  const dangerousFunctions = ['eval', 'Function', 'execSync', 'exec', 'spawn', 'execFile'];
101
105
  // SQL execution patterns
102
106
  const sqlPatterns = ['query', 'execute', 'run', 'raw'];
107
+ /**
108
+ * Names of variables locally bound to the result of a known AI SDK call.
109
+ * Tracks the idiomatic `const { text } = await generateText(...)` and
110
+ * `const result = await streamText(...)` patterns. Without this, the
111
+ * heuristic pattern match (`result.text`, `aiOutput`, …) missed every
112
+ * destructured-`text` case (real FN found by the OWASP-LLM02 corpus).
113
+ */
114
+ const aiBoundNames = new Set();
115
+ const AI_SDK_CALLS = new Set(['generateText', 'streamText', 'generateObject', 'streamObject']);
116
+ function isAISDKCall(node) {
117
+ // generateText(...) | ai.generateText(...) | sdk.generateText(...)
118
+ let target = node;
119
+ if (target.type === 'AwaitExpression')
120
+ target = target.argument;
121
+ if (target.type !== 'CallExpression')
122
+ return false;
123
+ const callee = target.callee;
124
+ if (callee.type === 'Identifier' && AI_SDK_CALLS.has(callee.name))
125
+ return true;
126
+ if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier' &&
127
+ AI_SDK_CALLS.has(callee.property.name))
128
+ return true;
129
+ return false;
130
+ }
103
131
  /**
104
132
  * Check if a node likely contains AI output
105
133
  */
106
134
  function isLikelyAIOutput(node) {
135
+ // Direct member access into a tracked variable: `result.text`, `out.text`
136
+ if (node.type === 'MemberExpression' && node.object.type === 'Identifier' &&
137
+ aiBoundNames.has(node.object.name)) {
138
+ return true;
139
+ }
140
+ // Bare reference to a tracked variable (covers destructured `text` from
141
+ // `const { text } = await generateText(...)`).
142
+ if (node.type === 'Identifier' && aiBoundNames.has(node.name)) {
143
+ return true;
144
+ }
145
+ // Original heuristic — still useful for `result.text`-shaped source even
146
+ // when scope tracking missed the binding.
107
147
  const text = sourceCode.getText(node);
108
148
  return aiOutputPatterns.some((pattern) => text.includes(pattern));
109
149
  }
110
150
  return {
151
+ // Track `const r = await generateText(...)` / `const { text } = ...` shapes
152
+ VariableDeclarator(node) {
153
+ if (!node.init || !isAISDKCall(node.init))
154
+ return;
155
+ if (node.id.type === 'Identifier') {
156
+ aiBoundNames.add(node.id.name);
157
+ }
158
+ else if (node.id.type === 'ObjectPattern') {
159
+ for (const prop of node.id.properties) {
160
+ if (prop.type === 'Property' && prop.value.type === 'Identifier') {
161
+ aiBoundNames.add(prop.value.name);
162
+ }
163
+ }
164
+ }
165
+ },
111
166
  // Check for eval() and similar with AI output
112
167
  CallExpression(node) {
113
168
  const callee = sourceCode.getText(node.callee);
@@ -16,9 +16,12 @@ const eslint_devkit_1 = require("@interlace/eslint-devkit");
16
16
  exports.requireAbortSignal = (0, eslint_devkit_1.createRule)({
17
17
  name: 'require-abort-signal',
18
18
  meta: {
19
- type: 'suggestion',
19
+ type: 'problem',
20
20
  docs: {
21
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/require-abort-signal.md',
21
22
  description: 'Require AbortSignal for streaming AI calls to enable proper cleanup',
23
+ cwe: 'CWE-404',
24
+ cvss: 4,
22
25
  },
23
26
  messages: {
24
27
  missingAbortSignal: (0, eslint_devkit_1.formatLLMMessage)({
@@ -55,7 +58,7 @@ exports.requireAbortSignal = (0, eslint_devkit_1.createRule)({
55
58
  create(context) {
56
59
  const [options = {}] = context.options;
57
60
  const targetFunctions = options.targetFunctions ?? ['streamText', 'streamObject'];
58
- const sourceCode = context.sourceCode || context.getSourceCode();
61
+ const sourceCode = context.sourceCode;
59
62
  return {
60
63
  CallExpression(node) {
61
64
  const callee = sourceCode.getText(node.callee);
@@ -17,7 +17,10 @@ exports.requireAuditLogging = (0, eslint_devkit_1.createRule)({
17
17
  meta: {
18
18
  type: 'suggestion',
19
19
  docs: {
20
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/require-audit-logging.md',
20
21
  description: 'Suggest audit logging for AI SDK operations',
22
+ cwe: 'CWE-778',
23
+ cvss: 4,
21
24
  },
22
25
  messages: {
23
26
  missingAuditLogging: (0, eslint_devkit_1.formatLLMMessage)({
@@ -54,8 +57,8 @@ exports.requireAuditLogging = (0, eslint_devkit_1.createRule)({
54
57
  create(context) {
55
58
  const [options = {}] = context.options;
56
59
  const allowInTests = options.allowInTests ?? true;
57
- const sourceCode = context.sourceCode || context.getSourceCode();
58
- const filename = context.filename || context.getFilename();
60
+ const sourceCode = context.sourceCode;
61
+ const filename = context.filename;
59
62
  // Skip test files if allowed
60
63
  if (allowInTests && /\.(test|spec)\.[jt]sx?$/.test(filename)) {
61
64
  return {};
@@ -17,7 +17,10 @@ exports.requireEmbeddingValidation = (0, eslint_devkit_1.createRule)({
17
17
  meta: {
18
18
  type: 'suggestion',
19
19
  docs: {
20
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/require-embedding-validation.md',
20
21
  description: 'Require validation of embeddings before storage or similarity search',
22
+ cwe: 'CWE-20',
23
+ cvss: 5.5,
21
24
  },
22
25
  messages: {
23
26
  unvalidatedEmbedding: (0, eslint_devkit_1.formatLLMMessage)({
@@ -72,15 +75,13 @@ exports.requireEmbeddingValidation = (0, eslint_devkit_1.createRule)({
72
75
  const validatorFunctions = options.validatorFunctions ?? [
73
76
  'validate', 'verify', 'check', 'sanitize',
74
77
  ];
75
- const sourceCode = context.sourceCode || context.getSourceCode();
78
+ const sourceCode = context.sourceCode;
76
79
  // Vector store operations
77
80
  const vectorStoreOps = ['upsert', 'insert', 'add', 'store', 'index', 'save'];
78
81
  /**
79
82
  * Check if expression is an embedding call
80
83
  */
81
84
  function isEmbeddingCall(node) {
82
- if (node.type !== 'CallExpression')
83
- return null;
84
85
  const callee = sourceCode.getText(node.callee);
85
86
  for (const pattern of embeddingPatterns) {
86
87
  if (callee.toLowerCase().includes(pattern.toLowerCase())) {
@@ -93,8 +94,6 @@ exports.requireEmbeddingValidation = (0, eslint_devkit_1.createRule)({
93
94
  * Check if expression is validated
94
95
  */
95
96
  function isValidated(node) {
96
- if (node.type !== 'CallExpression')
97
- return false;
98
97
  const callee = sourceCode.getText(node.callee);
99
98
  return validatorFunctions.some((fn) => callee.toLowerCase().includes(fn.toLowerCase()));
100
99
  }
@@ -123,13 +122,15 @@ exports.requireEmbeddingValidation = (0, eslint_devkit_1.createRule)({
123
122
  if (valueNode.type === 'AwaitExpression') {
124
123
  valueNode = valueNode.argument;
125
124
  }
126
- const embeddingSource = isEmbeddingCall(valueNode);
127
- if (embeddingSource && !isValidated(valueNode)) {
128
- context.report({
129
- node: prop.value,
130
- messageId: 'unvalidatedEmbedding',
131
- data: { source: embeddingSource },
132
- });
125
+ if (valueNode.type === 'CallExpression') {
126
+ const embeddingSource = isEmbeddingCall(valueNode);
127
+ if (embeddingSource && !isValidated(valueNode)) {
128
+ context.report({
129
+ node: prop.value,
130
+ messageId: 'unvalidatedEmbedding',
131
+ data: { source: embeddingSource },
132
+ });
133
+ }
133
134
  }
134
135
  }
135
136
  }
@@ -18,7 +18,10 @@ exports.requireErrorHandling = (0, eslint_devkit_1.createRule)({
18
18
  meta: {
19
19
  type: 'problem',
20
20
  docs: {
21
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/require-error-handling.md',
21
22
  description: 'Require error handling for AI SDK calls to prevent cascading failures',
23
+ cwe: 'CWE-755',
24
+ cvss: 5,
22
25
  },
23
26
  messages: {
24
27
  missingErrorHandling: (0, eslint_devkit_1.formatLLMMessage)({
@@ -55,8 +58,8 @@ exports.requireErrorHandling = (0, eslint_devkit_1.createRule)({
55
58
  create(context) {
56
59
  const [options = {}] = context.options;
57
60
  const allowInTests = options.allowInTests ?? true;
58
- const sourceCode = context.sourceCode || context.getSourceCode();
59
- const filename = context.filename || context.getFilename();
61
+ const sourceCode = context.sourceCode;
62
+ const filename = context.filename;
60
63
  // Skip test files if allowed
61
64
  if (allowInTests && /\.(test|spec)\.[jt]sx?$/.test(filename)) {
62
65
  return {};
@@ -66,6 +69,7 @@ exports.requireErrorHandling = (0, eslint_devkit_1.createRule)({
66
69
  /**
67
70
  * Check if node is inside a try block
68
71
  */
72
+ // oxlint-disable-next-line consistent-function-scoping
69
73
  function isInsideTryBlock(node) {
70
74
  let parent = node.parent;
71
75
  while (parent) {
@@ -18,7 +18,10 @@ exports.requireMaxSteps = (0, eslint_devkit_1.createRule)({
18
18
  meta: {
19
19
  type: 'problem',
20
20
  docs: {
21
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/require-max-steps.md',
21
22
  description: 'Require maxSteps limit for multi-step tool calling to prevent infinite loops',
23
+ cwe: 'CWE-834',
24
+ cvss: 6.5,
22
25
  },
23
26
  messages: {
24
27
  missingMaxSteps: (0, eslint_devkit_1.formatLLMMessage)({
@@ -54,7 +57,7 @@ exports.requireMaxSteps = (0, eslint_devkit_1.createRule)({
54
57
  },
55
58
  ],
56
59
  create(context) {
57
- const sourceCode = context.sourceCode || context.getSourceCode();
60
+ const sourceCode = context.sourceCode;
58
61
  // Vercel AI SDK functions that support tools
59
62
  const functionsWithTools = ['generateText', 'streamText'];
60
63
  return {
@@ -18,7 +18,10 @@ exports.requireMaxTokens = (0, eslint_devkit_1.createRule)({
18
18
  meta: {
19
19
  type: 'problem',
20
20
  docs: {
21
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/require-max-tokens.md',
21
22
  description: 'Require maxTokens limit in generateText and streamText calls',
23
+ cwe: 'CWE-770',
24
+ cvss: 6.5,
22
25
  },
23
26
  messages: {
24
27
  missingMaxTokens: (0, eslint_devkit_1.formatLLMMessage)({
@@ -64,7 +67,7 @@ exports.requireMaxTokens = (0, eslint_devkit_1.createRule)({
64
67
  const targetFunctions = options.targetFunctions ?? [
65
68
  'generateText', 'streamText', 'generateObject', 'streamObject',
66
69
  ];
67
- const sourceCode = context.sourceCode || context.getSourceCode();
70
+ const sourceCode = context.sourceCode;
68
71
  return {
69
72
  CallExpression(node) {
70
73
  const callee = sourceCode.getText(node.callee);
@@ -17,7 +17,10 @@ exports.requireOutputFiltering = (0, eslint_devkit_1.createRule)({
17
17
  meta: {
18
18
  type: 'suggestion',
19
19
  docs: {
20
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/require-output-filtering.md',
20
21
  description: 'Require filtering of sensitive data returned by AI tools',
22
+ cwe: 'CWE-200',
23
+ cvss: 6.5,
21
24
  },
22
25
  messages: {
23
26
  missingOutputFilter: (0, eslint_devkit_1.formatLLMMessage)({
@@ -72,13 +75,11 @@ exports.requireOutputFiltering = (0, eslint_devkit_1.createRule)({
72
75
  const filterFunctions = options.filterFunctions ?? [
73
76
  'filter', 'sanitize', 'redact', 'mask',
74
77
  ];
75
- const sourceCode = context.sourceCode || context.getSourceCode();
78
+ const sourceCode = context.sourceCode;
76
79
  /**
77
80
  * Check if expression is a data source call
78
81
  */
79
82
  function isDataSourceCall(node) {
80
- if (node.type !== 'CallExpression')
81
- return null;
82
83
  const callee = sourceCode.getText(node.callee);
83
84
  for (const pattern of dataSourcePatterns) {
84
85
  if (callee.toLowerCase().includes(pattern.toLowerCase())) {
@@ -91,14 +92,13 @@ exports.requireOutputFiltering = (0, eslint_devkit_1.createRule)({
91
92
  * Check if expression is wrapped in a filter function
92
93
  */
93
94
  function isFilteredCall(node) {
94
- if (node.type !== 'CallExpression')
95
- return false;
96
95
  const callee = sourceCode.getText(node.callee);
97
96
  return filterFunctions.some((filter) => callee.toLowerCase().includes(filter.toLowerCase()));
98
97
  }
99
98
  /**
100
99
  * Get tool name from context
101
100
  */
101
+ // oxlint-disable-next-line consistent-function-scoping
102
102
  function getToolName(node) {
103
103
  let current = node.parent;
104
104
  while (current) {
@@ -17,7 +17,10 @@ exports.requireOutputValidation = (0, eslint_devkit_1.createRule)({
17
17
  meta: {
18
18
  type: 'suggestion',
19
19
  docs: {
20
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/require-output-validation.md',
20
21
  description: 'Require validation of AI output before displaying to users',
22
+ cwe: 'CWE-707',
23
+ cvss: 5,
21
24
  },
22
25
  messages: {
23
26
  unvalidatedOutput: (0, eslint_devkit_1.formatLLMMessage)({
@@ -69,23 +72,11 @@ exports.requireOutputValidation = (0, eslint_devkit_1.createRule)({
69
72
  const displayPatterns = options.displayPatterns ?? [
70
73
  'render', 'display', 'show', 'send', 'respond',
71
74
  ];
72
- const validatorFunctions = options.validatorFunctions ?? [
73
- 'validate', 'verify', 'check', 'sanitize',
74
- ];
75
- const sourceCode = context.sourceCode || context.getSourceCode();
75
+ const sourceCode = context.sourceCode;
76
76
  // AI output property patterns
77
77
  const aiOutputPatterns = ['.text', '.content', '.message', '.response', '.output'];
78
78
  // Track variables that hold AI results
79
79
  const aiResultVariables = new Set();
80
- /**
81
- * Check if expression is validated
82
- */
83
- function isValidated(node) {
84
- if (node.type !== 'CallExpression')
85
- return false;
86
- const callee = sourceCode.getText(node.callee);
87
- return validatorFunctions.some((fn) => callee.toLowerCase().includes(fn.toLowerCase()));
88
- }
89
80
  /**
90
81
  * Check if expression accesses AI output
91
82
  */
@@ -122,7 +113,10 @@ exports.requireOutputValidation = (0, eslint_devkit_1.createRule)({
122
113
  return;
123
114
  // Check arguments for unvalidated AI output
124
115
  for (const arg of node.arguments) {
125
- if (isAIOutput(arg) && !isValidated(arg)) {
116
+ // NOTE: `isAIOutput` only matches MemberExpression/Identifier nodes,
117
+ // so a wrapped call like `render(validate(result.text))` never
118
+ // reaches here — validated output is inherently not flagged.
119
+ if (isAIOutput(arg)) {
126
120
  context.report({
127
121
  node: arg,
128
122
  messageId: 'unvalidatedOutput',
@@ -134,7 +128,7 @@ exports.requireOutputValidation = (0, eslint_devkit_1.createRule)({
134
128
  for (const prop of arg.properties) {
135
129
  if (prop.type !== 'Property')
136
130
  continue;
137
- if (isAIOutput(prop.value) && !isValidated(prop.value)) {
131
+ if (isAIOutput(prop.value)) {
138
132
  context.report({
139
133
  node: prop.value,
140
134
  messageId: 'unvalidatedOutput',
@@ -17,7 +17,10 @@ exports.requireRagContentValidation = (0, eslint_devkit_1.createRule)({
17
17
  meta: {
18
18
  type: 'suggestion',
19
19
  docs: {
20
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/require-rag-content-validation.md',
20
21
  description: 'Require validation of RAG content before including in AI prompts',
22
+ cwe: 'CWE-74',
23
+ cvss: 6,
21
24
  },
22
25
  messages: {
23
26
  unsanitizedRagContent: (0, eslint_devkit_1.formatLLMMessage)({
@@ -73,7 +76,7 @@ exports.requireRagContentValidation = (0, eslint_devkit_1.createRule)({
73
76
  const validatorFunctions = options.validatorFunctions ?? [
74
77
  'validate', 'sanitize', 'filter', 'clean',
75
78
  ];
76
- const sourceCode = context.sourceCode || context.getSourceCode();
79
+ const sourceCode = context.sourceCode;
77
80
  // Vercel AI SDK functions
78
81
  const aiSDKFunctions = ['generateText', 'streamText', 'generateObject', 'streamObject'];
79
82
  // Track variables that hold RAG content
@@ -96,8 +99,6 @@ exports.requireRagContentValidation = (0, eslint_devkit_1.createRule)({
96
99
  * Check if expression is wrapped in validation
97
100
  */
98
101
  function isValidated(node) {
99
- if (node.type !== 'CallExpression')
100
- return false;
101
102
  const callee = sourceCode.getText(node.callee);
102
103
  return validatorFunctions.some((fn) => callee.toLowerCase().includes(fn.toLowerCase()));
103
104
  }
@@ -17,7 +17,10 @@ exports.requireRequestTimeout = (0, eslint_devkit_1.createRule)({
17
17
  meta: {
18
18
  type: 'suggestion',
19
19
  docs: {
20
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/require-request-timeout.md',
20
21
  description: 'Require timeout configuration for AI SDK calls to prevent DoS',
22
+ cwe: 'CWE-400',
23
+ cvss: 5,
21
24
  },
22
25
  messages: {
23
26
  missingTimeout: (0, eslint_devkit_1.formatLLMMessage)({
@@ -54,8 +57,8 @@ exports.requireRequestTimeout = (0, eslint_devkit_1.createRule)({
54
57
  create(context) {
55
58
  const [options = {}] = context.options;
56
59
  const allowInTests = options.allowInTests ?? true;
57
- const sourceCode = context.sourceCode || context.getSourceCode();
58
- const filename = context.filename || context.getFilename();
60
+ const sourceCode = context.sourceCode;
61
+ const filename = context.filename;
59
62
  // Skip test files if allowed
60
63
  if (allowInTests && /\.(test|spec)\.[jt]sx?$/.test(filename)) {
61
64
  return {};
@@ -63,7 +66,7 @@ exports.requireRequestTimeout = (0, eslint_devkit_1.createRule)({
63
66
  // Vercel AI SDK functions
64
67
  const aiSDKFunctions = ['generateText', 'streamText', 'generateObject', 'streamObject'];
65
68
  // Timeout-related property names
66
- const timeoutProperties = ['timeout', 'abortSignal', 'signal', 'timeoutMs', 'requestTimeout'];
69
+ const timeoutProperties = new Set(['timeout', 'abortSignal', 'signal', 'timeoutMs', 'requestTimeout']);
67
70
  return {
68
71
  CallExpression(node) {
69
72
  const callee = sourceCode.getText(node.callee);
@@ -86,7 +89,7 @@ exports.requireRequestTimeout = (0, eslint_devkit_1.createRule)({
86
89
  if (prop.type !== 'Property')
87
90
  return false;
88
91
  const keyName = prop.key.type === 'Identifier' ? prop.key.name : null;
89
- return keyName && timeoutProperties.includes(keyName);
92
+ return keyName && timeoutProperties.has(keyName);
90
93
  });
91
94
  if (!hasTimeout) {
92
95
  context.report({
@@ -18,7 +18,10 @@ exports.requireToolConfirmation = (0, eslint_devkit_1.createRule)({
18
18
  meta: {
19
19
  type: 'suggestion',
20
20
  docs: {
21
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/require-tool-confirmation.md',
21
22
  description: 'Require human confirmation for destructive tool operations (delete, transfer, execute)',
23
+ cwe: 'CWE-862',
24
+ cvss: 7,
22
25
  },
23
26
  messages: {
24
27
  missingConfirmation: (0, eslint_devkit_1.formatLLMMessage)({
@@ -79,17 +82,18 @@ exports.requireToolConfirmation = (0, eslint_devkit_1.createRule)({
79
82
  /**
80
83
  * Check if tool has confirmation requirement
81
84
  */
85
+ // oxlint-disable-next-line consistent-function-scoping
82
86
  function hasConfirmationFlag(toolDef) {
83
- const confirmationProps = [
87
+ const confirmationProps = new Set([
84
88
  'requiresConfirmation', 'requireConfirmation', 'confirmation',
85
89
  'requiresApproval', 'requireApproval', 'approval',
86
90
  'dangerouslyAllowBrowser', // If explicitly acknowledged
87
- ];
91
+ ]);
88
92
  return toolDef.properties.some(prop => {
89
93
  if (prop.type !== 'Property')
90
94
  return false;
91
95
  const keyName = prop.key.type === 'Identifier' ? prop.key.name : null;
92
- return keyName && confirmationProps.includes(keyName);
96
+ return keyName && confirmationProps.has(keyName);
93
97
  });
94
98
  }
95
99
  return {
@@ -18,7 +18,10 @@ exports.requireToolSchema = (0, eslint_devkit_1.createRule)({
18
18
  meta: {
19
19
  type: 'problem',
20
20
  docs: {
21
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/require-tool-schema.md',
21
22
  description: 'Require inputSchema (Zod schema) for all AI SDK tools',
23
+ cwe: 'CWE-20',
24
+ cvss: 7.5,
22
25
  },
23
26
  messages: {
24
27
  missingInputSchema: (0, eslint_devkit_1.formatLLMMessage)({
@@ -65,8 +68,8 @@ exports.requireToolSchema = (0, eslint_devkit_1.createRule)({
65
68
  create(context) {
66
69
  const [options = {}] = context.options;
67
70
  const allowInTests = options.allowInTests ?? false;
68
- const sourceCode = context.sourceCode || context.getSourceCode();
69
- const filename = context.filename || context.getFilename();
71
+ const sourceCode = context.sourceCode;
72
+ const filename = context.filename;
70
73
  // Skip test files if allowed
71
74
  if (allowInTests && /\.(test|spec)\.[jt]sx?$/.test(filename)) {
72
75
  return {};
@@ -18,7 +18,10 @@ exports.requireValidatedPrompt = (0, eslint_devkit_1.createRule)({
18
18
  meta: {
19
19
  type: 'problem',
20
20
  docs: {
21
+ url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-vercel-ai-security/docs/rules/require-validated-prompt.md',
21
22
  description: 'Require validated/sanitized prompts in generateText and streamText calls',
23
+ cwe: 'CWE-74',
24
+ cvss: 9,
22
25
  },
23
26
  messages: {
24
27
  unsafePrompt: (0, eslint_devkit_1.formatLLMMessage)({
@@ -103,8 +106,8 @@ exports.requireValidatedPrompt = (0, eslint_devkit_1.createRule)({
103
106
  'input', 'query', 'message', 'req.body', 'request.body',
104
107
  ];
105
108
  const allowInTests = options.allowInTests ?? false;
106
- const sourceCode = context.sourceCode || context.getSourceCode();
107
- const filename = context.filename || context.getFilename();
109
+ const sourceCode = context.sourceCode;
110
+ const filename = context.filename;
108
111
  // Skip test files if allowed
109
112
  if (allowInTests && /\.(test|spec)\.[jt]sx?$/.test(filename)) {
110
113
  return {};