tryassay 0.28.2 → 0.30.1
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/dist/cli.js +10 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/assess.js +4 -2
- package/dist/commands/assess.js.map +1 -1
- package/dist/commands/generate.js +1 -0
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/harvest.d.ts +9 -0
- package/dist/commands/harvest.js +76 -0
- package/dist/commands/harvest.js.map +1 -0
- package/dist/lib/__tests__/learned-rules.test.d.ts +1 -0
- package/dist/lib/__tests__/learned-rules.test.js +260 -0
- package/dist/lib/__tests__/learned-rules.test.js.map +1 -0
- package/dist/lib/__tests__/pr-harvester-types.test.d.ts +1 -0
- package/dist/lib/__tests__/pr-harvester-types.test.js +43 -0
- package/dist/lib/__tests__/pr-harvester-types.test.js.map +1 -0
- package/dist/lib/__tests__/pr-harvester.test.d.ts +1 -0
- package/dist/lib/__tests__/pr-harvester.test.js +341 -0
- package/dist/lib/__tests__/pr-harvester.test.js.map +1 -0
- package/dist/lib/__tests__/rule-harvester.test.d.ts +1 -0
- package/dist/lib/__tests__/rule-harvester.test.js +526 -0
- package/dist/lib/__tests__/rule-harvester.test.js.map +1 -0
- package/dist/lib/claim-extractor.d.ts +1 -0
- package/dist/lib/claim-extractor.js +116 -15
- package/dist/lib/claim-extractor.js.map +1 -1
- package/dist/lib/code-verifier.d.ts +12 -1
- package/dist/lib/code-verifier.js +155 -12
- package/dist/lib/code-verifier.js.map +1 -1
- package/dist/lib/learned-rules/category-map.d.ts +28 -0
- package/dist/lib/learned-rules/category-map.js +110 -0
- package/dist/lib/learned-rules/category-map.js.map +1 -0
- package/dist/lib/learned-rules/index.d.ts +105 -0
- package/dist/lib/learned-rules/index.js +198 -0
- package/dist/lib/learned-rules/index.js.map +1 -0
- package/dist/lib/learned-rules/learned-catalog.d.ts +62 -0
- package/dist/lib/learned-rules/learned-catalog.js +161 -0
- package/dist/lib/learned-rules/learned-catalog.js.map +1 -0
- package/dist/lib/learned-rules/pattern-extractor.d.ts +25 -0
- package/dist/lib/learned-rules/pattern-extractor.js +351 -0
- package/dist/lib/learned-rules/pattern-extractor.js.map +1 -0
- package/dist/lib/learned-rules/rule-codifier.d.ts +41 -0
- package/dist/lib/learned-rules/rule-codifier.js +138 -0
- package/dist/lib/learned-rules/rule-codifier.js.map +1 -0
- package/dist/lib/learned-rules/starter-catalog.d.ts +16 -0
- package/dist/lib/learned-rules/starter-catalog.js +402 -0
- package/dist/lib/learned-rules/starter-catalog.js.map +1 -0
- package/dist/lib/learned-rules/types.d.ts +196 -0
- package/dist/lib/learned-rules/types.js +9 -0
- package/dist/lib/learned-rules/types.js.map +1 -0
- package/dist/lib/learned-rules/validation-harness.d.ts +26 -0
- package/dist/lib/learned-rules/validation-harness.js +260 -0
- package/dist/lib/learned-rules/validation-harness.js.map +1 -0
- package/dist/lib/llm-provider.d.ts +7 -0
- package/dist/lib/llm-provider.js +99 -6
- package/dist/lib/llm-provider.js.map +1 -1
- package/dist/lib/rule-harvester/diff-parser.d.ts +9 -0
- package/dist/lib/rule-harvester/diff-parser.js +77 -0
- package/dist/lib/rule-harvester/diff-parser.js.map +1 -0
- package/dist/lib/rule-harvester/file-selector.d.ts +10 -0
- package/dist/lib/rule-harvester/file-selector.js +59 -0
- package/dist/lib/rule-harvester/file-selector.js.map +1 -0
- package/dist/lib/rule-harvester/ground-truth.d.ts +19 -0
- package/dist/lib/rule-harvester/ground-truth.js +156 -0
- package/dist/lib/rule-harvester/ground-truth.js.map +1 -0
- package/dist/lib/rule-harvester/harvest.d.ts +26 -0
- package/dist/lib/rule-harvester/harvest.js +307 -0
- package/dist/lib/rule-harvester/harvest.js.map +1 -0
- package/dist/lib/rule-harvester/pr-discovery.d.ts +49 -0
- package/dist/lib/rule-harvester/pr-discovery.js +168 -0
- package/dist/lib/rule-harvester/pr-discovery.js.map +1 -0
- package/dist/lib/rule-harvester/pr-harvest.d.ts +53 -0
- package/dist/lib/rule-harvester/pr-harvest.js +326 -0
- package/dist/lib/rule-harvester/pr-harvest.js.map +1 -0
- package/dist/lib/rule-harvester/progress.d.ts +13 -0
- package/dist/lib/rule-harvester/progress.js +50 -0
- package/dist/lib/rule-harvester/progress.js.map +1 -0
- package/dist/lib/rule-harvester/reporter.d.ts +35 -0
- package/dist/lib/rule-harvester/reporter.js +46 -0
- package/dist/lib/rule-harvester/reporter.js.map +1 -0
- package/dist/lib/rule-harvester/rule-generalizer.d.ts +25 -0
- package/dist/lib/rule-harvester/rule-generalizer.js +135 -0
- package/dist/lib/rule-harvester/rule-generalizer.js.map +1 -0
- package/dist/lib/rule-harvester/scanner.d.ts +20 -0
- package/dist/lib/rule-harvester/scanner.js +37 -0
- package/dist/lib/rule-harvester/scanner.js.map +1 -0
- package/dist/runtime/types.d.ts +1 -1
- package/dist/sdk/forward-verify.d.ts +3 -1
- package/dist/sdk/forward-verify.js +68 -5
- package/dist/sdk/forward-verify.js.map +1 -1
- package/dist/sdk/index.d.ts +1 -1
- package/dist/sdk/index.js +7 -5
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/types.d.ts +21 -0
- package/package.json +1 -1
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Starter Catalog — ~15 high-signal learned rules that ship with the npm package.
|
|
3
|
+
*
|
|
4
|
+
* These rules were curated from 214 rules harvested from real PR diffs in
|
|
5
|
+
* popular open-source TypeScript projects. Selection criteria:
|
|
6
|
+
* - Valid, compilable regex
|
|
7
|
+
* - High or critical severity preferred
|
|
8
|
+
* - General-purpose (not tied to a specific project)
|
|
9
|
+
* - Good coverage across categories (security, error handling, etc.)
|
|
10
|
+
* - Useful fixDescription
|
|
11
|
+
*
|
|
12
|
+
* All starter rules have `source: "bundled"` to distinguish them from
|
|
13
|
+
* user-harvested rules. Local rules with the same ID take precedence.
|
|
14
|
+
*/
|
|
15
|
+
const NOW = '2026-03-14T00:00:00.000Z';
|
|
16
|
+
export const STARTER_RULES = [
|
|
17
|
+
// ─── SECURITY ───────────────────────────────────────────────
|
|
18
|
+
{
|
|
19
|
+
id: 'starter_security_token_logging',
|
|
20
|
+
pattern: {
|
|
21
|
+
id: 'starter_security_token_logging',
|
|
22
|
+
description: 'Avoid logging sensitive data (like JWT tokens, access tokens, or API keys) directly to console. They can be intercepted by browser extensions, leaked in log aggregators, or exposed in CI output.',
|
|
23
|
+
kind: 'regex',
|
|
24
|
+
languages: ['typescript', 'javascript'],
|
|
25
|
+
regexPattern: '(?i)console\\.log.*\\{token|\\[access_token\\]|\\[JWT\\]',
|
|
26
|
+
fileGlob: '**/*.{ts,tsx,js,jsx}',
|
|
27
|
+
matchBehavior: 'presence_is_bad',
|
|
28
|
+
claimCategory: 'security',
|
|
29
|
+
severity: 'critical',
|
|
30
|
+
evidenceTemplate: '{file}:{line} logs sensitive token data: \'{match}\'. Remove or redact before shipping.',
|
|
31
|
+
},
|
|
32
|
+
status: 'promoted',
|
|
33
|
+
createdAt: NOW,
|
|
34
|
+
updatedAt: NOW,
|
|
35
|
+
fireCount: 0,
|
|
36
|
+
truePositiveCount: 0,
|
|
37
|
+
falsePositiveCount: 0,
|
|
38
|
+
sourceFindings: [],
|
|
39
|
+
source: 'bundled',
|
|
40
|
+
fixDescription: 'Remove console.log statements that print raw token values, or replace sensitive values with a placeholder like "[REDACTED]".',
|
|
41
|
+
confirmationCount: 4,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'starter_security_webhook_ssrf',
|
|
45
|
+
pattern: {
|
|
46
|
+
id: 'starter_security_webhook_ssrf',
|
|
47
|
+
description: 'Webhook URLs should be validated against private/internal IP ranges before processing to prevent SSRF and data leakage through internal network access.',
|
|
48
|
+
kind: 'regex',
|
|
49
|
+
languages: ['typescript', 'javascript'],
|
|
50
|
+
regexPattern: '(?:await\\s+)?(?:\\w+\\.)*(?:create|update|send).*webhook',
|
|
51
|
+
fileGlob: '**/*.{ts,tsx,js,jsx}',
|
|
52
|
+
matchBehavior: 'presence_is_bad',
|
|
53
|
+
claimCategory: 'security',
|
|
54
|
+
severity: 'high',
|
|
55
|
+
evidenceTemplate: '{file}:{line} performs webhook operation without visible private-URL validation: {match}',
|
|
56
|
+
},
|
|
57
|
+
status: 'promoted',
|
|
58
|
+
createdAt: NOW,
|
|
59
|
+
updatedAt: NOW,
|
|
60
|
+
fireCount: 0,
|
|
61
|
+
truePositiveCount: 0,
|
|
62
|
+
falsePositiveCount: 0,
|
|
63
|
+
sourceFindings: [],
|
|
64
|
+
source: 'bundled',
|
|
65
|
+
fixDescription: 'Add a validation check after URL extraction to ensure the target is not a private/loopback/link-local IP before performing webhook operations.',
|
|
66
|
+
confirmationCount: 1,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: 'starter_security_hardcoded_credentials',
|
|
70
|
+
pattern: {
|
|
71
|
+
id: 'starter_security_hardcoded_credentials',
|
|
72
|
+
description: 'Hardcoded service account emails or credentials in source code should be replaced by environment variables to support multi-tenant deployments and prevent credential leakage.',
|
|
73
|
+
kind: 'regex',
|
|
74
|
+
languages: ['typescript', 'javascript'],
|
|
75
|
+
regexPattern: "^const [A-Z][A-Z_]*(EMAIL|PASSWORD|SECRET|TOKEN|KEY)\\s*=\\s*['\"][^'\"]+['\"];?$",
|
|
76
|
+
fileGlob: '**/*.{ts,tsx,js,jsx}',
|
|
77
|
+
matchBehavior: 'presence_is_bad',
|
|
78
|
+
claimCategory: 'security',
|
|
79
|
+
severity: 'high',
|
|
80
|
+
evidenceTemplate: '{file}:{line} contains hardcoded credential constant: {match}. Use environment variables instead.',
|
|
81
|
+
},
|
|
82
|
+
status: 'promoted',
|
|
83
|
+
createdAt: NOW,
|
|
84
|
+
updatedAt: NOW,
|
|
85
|
+
fireCount: 0,
|
|
86
|
+
truePositiveCount: 0,
|
|
87
|
+
falsePositiveCount: 0,
|
|
88
|
+
sourceFindings: [],
|
|
89
|
+
source: 'bundled',
|
|
90
|
+
fixDescription: 'Replace hardcoded credential constants with process.env lookups (e.g., process.env.SERVICE_ACCOUNT_EMAIL) with appropriate fallback handling.',
|
|
91
|
+
confirmationCount: 2,
|
|
92
|
+
},
|
|
93
|
+
// ─── CONCURRENCY ────────────────────────────────────────────
|
|
94
|
+
{
|
|
95
|
+
id: 'starter_concurrency_version_increment',
|
|
96
|
+
pattern: {
|
|
97
|
+
id: 'starter_concurrency_version_increment',
|
|
98
|
+
description: 'Application-side version increments (read-modify-write) introduce TOCTOU race conditions. Version counters should be managed atomically within the database query.',
|
|
99
|
+
kind: 'regex',
|
|
100
|
+
languages: ['typescript', 'javascript'],
|
|
101
|
+
regexPattern: 'version:\\s+.*\\.version\\s*\\+\\s*1',
|
|
102
|
+
fileGlob: '**/*.{ts,tsx,js,jsx}',
|
|
103
|
+
matchBehavior: 'presence_is_bad',
|
|
104
|
+
claimCategory: 'concurrency',
|
|
105
|
+
severity: 'high',
|
|
106
|
+
evidenceTemplate: '{file}:{line} has non-atomic version increment: {match}. Use db.raw() or .increment() inside the SQL query instead.',
|
|
107
|
+
},
|
|
108
|
+
status: 'promoted',
|
|
109
|
+
createdAt: NOW,
|
|
110
|
+
updatedAt: NOW,
|
|
111
|
+
fireCount: 0,
|
|
112
|
+
truePositiveCount: 0,
|
|
113
|
+
falsePositiveCount: 0,
|
|
114
|
+
sourceFindings: [],
|
|
115
|
+
source: 'bundled',
|
|
116
|
+
fixDescription: 'Replace application-side version arithmetic with an atomic database operation (e.g., db.raw("?? + 1", [...]) or .increment()) inside the SQL query.',
|
|
117
|
+
confirmationCount: 1,
|
|
118
|
+
},
|
|
119
|
+
// ─── ERROR HANDLING ─────────────────────────────────────────
|
|
120
|
+
{
|
|
121
|
+
id: 'starter_error_dynamic_import_unhandled',
|
|
122
|
+
pattern: {
|
|
123
|
+
id: 'starter_error_dynamic_import_unhandled',
|
|
124
|
+
description: 'Dynamic imports with .then() but no .catch() will silently fail, leaving the component in a null/undefined state that causes missing UI elements or runtime errors.',
|
|
125
|
+
kind: 'regex',
|
|
126
|
+
languages: ['typescript', 'javascript'],
|
|
127
|
+
regexPattern: '(import\\(.*\\)\\.then\\()\\s*\\([^)]+\\)',
|
|
128
|
+
fileGlob: '**/*.{ts,tsx,js,jsx}',
|
|
129
|
+
matchBehavior: 'presence_is_bad',
|
|
130
|
+
claimCategory: 'error-handling',
|
|
131
|
+
severity: 'high',
|
|
132
|
+
evidenceTemplate: '{file}:{line} has dynamic import without error handling: {match}. Add .catch() to handle module load failures.',
|
|
133
|
+
},
|
|
134
|
+
status: 'promoted',
|
|
135
|
+
createdAt: NOW,
|
|
136
|
+
updatedAt: NOW,
|
|
137
|
+
fireCount: 0,
|
|
138
|
+
truePositiveCount: 0,
|
|
139
|
+
falsePositiveCount: 0,
|
|
140
|
+
sourceFindings: [],
|
|
141
|
+
source: 'bundled',
|
|
142
|
+
fixDescription: 'Add a .catch() handler to the dynamic import that sets an error state and logs the failure. Ensure dependent logic handles the error state gracefully.',
|
|
143
|
+
confirmationCount: 1,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: 'starter_error_void_import_no_catch',
|
|
147
|
+
pattern: {
|
|
148
|
+
id: 'starter_error_void_import_no_catch',
|
|
149
|
+
description: 'Fire-and-forget dynamic imports (void import("...").then()) silently swallow rejections, corrupting component state when the module fails to load.',
|
|
150
|
+
kind: 'regex',
|
|
151
|
+
languages: ['typescript', 'javascript'],
|
|
152
|
+
regexPattern: 'void import\\("([^"]+)"\\).then\\([^)]+\\);',
|
|
153
|
+
fileGlob: '**/*.{ts,tsx,js,jsx}',
|
|
154
|
+
matchBehavior: 'presence_is_bad',
|
|
155
|
+
claimCategory: 'error-handling',
|
|
156
|
+
severity: 'high',
|
|
157
|
+
evidenceTemplate: '{file}:{line} uses fire-and-forget dynamic import without error handling: {match}. If this rejects, the app enters a silent error state.',
|
|
158
|
+
},
|
|
159
|
+
status: 'promoted',
|
|
160
|
+
createdAt: NOW,
|
|
161
|
+
updatedAt: NOW,
|
|
162
|
+
fireCount: 0,
|
|
163
|
+
truePositiveCount: 0,
|
|
164
|
+
falsePositiveCount: 0,
|
|
165
|
+
sourceFindings: [],
|
|
166
|
+
source: 'bundled',
|
|
167
|
+
fixDescription: 'Wrap the dynamic import to catch errors, set a failure state flag, and ensure dependent logic falls back to raw data or a sensible default.',
|
|
168
|
+
confirmationCount: 1,
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: 'starter_error_fragile_nth_child_selector',
|
|
172
|
+
pattern: {
|
|
173
|
+
id: 'starter_error_fragile_nth_child_selector',
|
|
174
|
+
description: 'CSS selectors relying on DOM structure (like nth-child) in E2E tests break when UI layout changes. Use data-testid attributes for stable element targeting.',
|
|
175
|
+
kind: 'regex',
|
|
176
|
+
languages: ['typescript', 'javascript'],
|
|
177
|
+
regexPattern: 'page\\.locator\\([^)]*nth-child[^)]*\\)',
|
|
178
|
+
fileGlob: '**/*.{ts,tsx,js,jsx}',
|
|
179
|
+
matchBehavior: 'presence_is_bad',
|
|
180
|
+
claimCategory: 'test-robustness',
|
|
181
|
+
severity: 'high',
|
|
182
|
+
evidenceTemplate: '{file}:{line} uses fragile nth-child selector: {match}. Replace with getByTestId() for stable targeting.',
|
|
183
|
+
},
|
|
184
|
+
status: 'promoted',
|
|
185
|
+
createdAt: NOW,
|
|
186
|
+
updatedAt: NOW,
|
|
187
|
+
fireCount: 0,
|
|
188
|
+
truePositiveCount: 0,
|
|
189
|
+
falsePositiveCount: 0,
|
|
190
|
+
sourceFindings: [],
|
|
191
|
+
source: 'bundled',
|
|
192
|
+
fixDescription: 'Replace nth-child or text-based locators with getByTestId() targeting explicit data-testid attributes on the DOM elements.',
|
|
193
|
+
confirmationCount: 2,
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
id: 'starter_error_nested_transactions',
|
|
197
|
+
pattern: {
|
|
198
|
+
id: 'starter_error_nested_transactions',
|
|
199
|
+
description: 'Nested database transactions waste resources and can cause deadlocks. When an external transaction is already provided, use it directly instead of opening a new one.',
|
|
200
|
+
kind: 'regex',
|
|
201
|
+
languages: ['typescript', 'javascript'],
|
|
202
|
+
regexPattern: '(await\\s+[a-zA-Z_]+\\.transaction\\(\\)|\\.transaction\\(\\))\\s*;?\\s*(?!;)',
|
|
203
|
+
fileGlob: '**/*.{ts,tsx,js,jsx}',
|
|
204
|
+
matchBehavior: 'presence_is_bad',
|
|
205
|
+
claimCategory: 'error-handling',
|
|
206
|
+
severity: 'medium',
|
|
207
|
+
evidenceTemplate: '{file}:{line} opens a transaction that may be nested: {match}. Check if an external transaction is already in scope.',
|
|
208
|
+
},
|
|
209
|
+
status: 'promoted',
|
|
210
|
+
createdAt: NOW,
|
|
211
|
+
updatedAt: NOW,
|
|
212
|
+
fireCount: 0,
|
|
213
|
+
truePositiveCount: 0,
|
|
214
|
+
falsePositiveCount: 0,
|
|
215
|
+
sourceFindings: [],
|
|
216
|
+
source: 'bundled',
|
|
217
|
+
fixDescription: 'Check whether an external transaction (trx) is already available before opening a new one. Pass the existing transaction through instead of nesting.',
|
|
218
|
+
confirmationCount: 2,
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
id: 'starter_error_usestate_true_default',
|
|
222
|
+
pattern: {
|
|
223
|
+
id: 'starter_error_usestate_true_default',
|
|
224
|
+
description: 'Initializing a boolean useState to true without checking if dependent data exists causes unintended side effects (e.g., enabling a feature before its prerequisites are loaded).',
|
|
225
|
+
kind: 'regex',
|
|
226
|
+
languages: ['typescript', 'javascript'],
|
|
227
|
+
regexPattern: '^\\s*const \\[([^,]+,\\s*[^\\]]+)\\]\\s*=\\s*useState\\((?:(?!&&|&&&).)*true\\);',
|
|
228
|
+
fileGlob: '**/*.{ts,tsx,js,jsx}',
|
|
229
|
+
matchBehavior: 'presence_is_bad',
|
|
230
|
+
claimCategory: 'error-handling',
|
|
231
|
+
severity: 'high',
|
|
232
|
+
evidenceTemplate: '{file}:{line} initializes state to true without verifying dependent data: {match}. Consider defaulting to false and setting true only when prerequisites are confirmed.',
|
|
233
|
+
},
|
|
234
|
+
status: 'promoted',
|
|
235
|
+
createdAt: NOW,
|
|
236
|
+
updatedAt: NOW,
|
|
237
|
+
fireCount: 0,
|
|
238
|
+
truePositiveCount: 0,
|
|
239
|
+
falsePositiveCount: 0,
|
|
240
|
+
sourceFindings: [],
|
|
241
|
+
source: 'bundled',
|
|
242
|
+
fixDescription: 'Default the useState flag to false and set it to true only after confirming the dependent value (e.g., email, config) exists. Use a useEffect or derive the initial value from props.',
|
|
243
|
+
confirmationCount: 1,
|
|
244
|
+
},
|
|
245
|
+
// ─── TYPE SAFETY ────────────────────────────────────────────
|
|
246
|
+
{
|
|
247
|
+
id: 'starter_typesafety_truthy_array_guard',
|
|
248
|
+
pattern: {
|
|
249
|
+
id: 'starter_typesafety_truthy_array_guard',
|
|
250
|
+
description: 'Using `|| []` to guard against non-array inputs fails for empty objects `{}` (which are truthy). Use Array.isArray() for correct type narrowing before calling .map().',
|
|
251
|
+
kind: 'regex',
|
|
252
|
+
languages: ['typescript', 'javascript'],
|
|
253
|
+
regexPattern: '(\\w+)\\.map\\(.*\\)\\s*=>\\s*\\((\\w+)\\.map.*|\\1||\\[\\]\\)',
|
|
254
|
+
fileGlob: '**/*.{ts,tsx,js,jsx}',
|
|
255
|
+
matchBehavior: 'presence_is_bad',
|
|
256
|
+
claimCategory: 'type-safety',
|
|
257
|
+
severity: 'high',
|
|
258
|
+
evidenceTemplate: '{file}:{line} uses truthy guard instead of Array.isArray(): {match}. Empty objects {} will pass the truthy check and throw on .map().',
|
|
259
|
+
},
|
|
260
|
+
status: 'promoted',
|
|
261
|
+
createdAt: NOW,
|
|
262
|
+
updatedAt: NOW,
|
|
263
|
+
fireCount: 0,
|
|
264
|
+
truePositiveCount: 0,
|
|
265
|
+
falsePositiveCount: 0,
|
|
266
|
+
sourceFindings: [],
|
|
267
|
+
source: 'bundled',
|
|
268
|
+
fixDescription: 'Replace `value || []` with `Array.isArray(value) ? value : []` before calling .map() to correctly handle non-array truthy values like empty objects.',
|
|
269
|
+
confirmationCount: 1,
|
|
270
|
+
},
|
|
271
|
+
// ─── REACT PATTERNS ────────────────────────────────────────
|
|
272
|
+
{
|
|
273
|
+
id: 'starter_react_ref_mutation_during_render',
|
|
274
|
+
pattern: {
|
|
275
|
+
id: 'starter_react_ref_mutation_during_render',
|
|
276
|
+
description: 'Mutating Refs (.current.set(), .current.push()) directly during render violates React rules and causes inconsistent UI. Move mutations to useEffect hooks.',
|
|
277
|
+
kind: 'regex',
|
|
278
|
+
languages: ['typescript', 'javascript'],
|
|
279
|
+
regexPattern: '(\\s*const\\s+\\w+\\s*=\\s*[\\[\\(]\\w+\\.forEach.*?\\{[^}]*Ref\\.|\\.current\\.set\\(|\\.current\\.push\\()',
|
|
280
|
+
fileGlob: '**/*.{ts,tsx,js,jsx}',
|
|
281
|
+
matchBehavior: 'presence_is_bad',
|
|
282
|
+
claimCategory: 'react-best-practices',
|
|
283
|
+
severity: 'high',
|
|
284
|
+
evidenceTemplate: '{file}:{line} mutates a Ref during render: {match}. Wrap in useEffect to avoid inconsistent state.',
|
|
285
|
+
},
|
|
286
|
+
status: 'promoted',
|
|
287
|
+
createdAt: NOW,
|
|
288
|
+
updatedAt: NOW,
|
|
289
|
+
fireCount: 0,
|
|
290
|
+
truePositiveCount: 0,
|
|
291
|
+
falsePositiveCount: 0,
|
|
292
|
+
sourceFindings: [],
|
|
293
|
+
source: 'bundled',
|
|
294
|
+
fixDescription: 'Move Ref mutations (.current.set(), .current.push()) into a useEffect hook with appropriate dependency arrays.',
|
|
295
|
+
confirmationCount: 1,
|
|
296
|
+
},
|
|
297
|
+
// ─── VALIDATION ─────────────────────────────────────────────
|
|
298
|
+
{
|
|
299
|
+
id: 'starter_validation_form_submit_no_check',
|
|
300
|
+
pattern: {
|
|
301
|
+
id: 'starter_validation_form_submit_no_check',
|
|
302
|
+
description: 'Calling formRef.current.submit() or .reset() without checking form validity bypasses HTML5 validation constraints, allowing invalid data to be submitted.',
|
|
303
|
+
kind: 'regex',
|
|
304
|
+
languages: ['typescript', 'javascript'],
|
|
305
|
+
regexPattern: '\\s+formRef\\.current\\?\\.(submit|reset)\\(',
|
|
306
|
+
fileGlob: '**/*.{ts,tsx,js,jsx}',
|
|
307
|
+
matchBehavior: 'presence_is_bad',
|
|
308
|
+
claimCategory: 'validation',
|
|
309
|
+
severity: 'medium',
|
|
310
|
+
evidenceTemplate: '{file}:{line} submits form without validity check: {match}. Call formRef.current.checkValidity() first.',
|
|
311
|
+
},
|
|
312
|
+
status: 'promoted',
|
|
313
|
+
createdAt: NOW,
|
|
314
|
+
updatedAt: NOW,
|
|
315
|
+
fireCount: 0,
|
|
316
|
+
truePositiveCount: 0,
|
|
317
|
+
falsePositiveCount: 0,
|
|
318
|
+
sourceFindings: [],
|
|
319
|
+
source: 'bundled',
|
|
320
|
+
fixDescription: 'Guard manual form submission with a validity check: if (formRef.current?.checkValidity()) { formRef.current.submit(); }',
|
|
321
|
+
confirmationCount: 1,
|
|
322
|
+
},
|
|
323
|
+
// ─── LOGGING ────────────────────────────────────────────────
|
|
324
|
+
{
|
|
325
|
+
id: 'starter_logging_console_with_objects',
|
|
326
|
+
pattern: {
|
|
327
|
+
id: 'starter_logging_console_with_objects',
|
|
328
|
+
description: 'Direct console.error/log/warn calls with non-string arguments (objects, errors) bypass centralized logging infrastructure, causing inconsistent log formatting and missing observability.',
|
|
329
|
+
kind: 'regex',
|
|
330
|
+
languages: ['typescript', 'javascript'],
|
|
331
|
+
regexPattern: "(\\s)(?:console\\.error|console\\.log|console\\.info|console\\.warn)\\((?![\"'`])",
|
|
332
|
+
fileGlob: '**/*.{ts,tsx,js,jsx}',
|
|
333
|
+
matchBehavior: 'presence_is_bad',
|
|
334
|
+
claimCategory: 'logging',
|
|
335
|
+
severity: 'medium',
|
|
336
|
+
evidenceTemplate: '{file}:{line} uses direct console call with non-string arg: {match}. Use a structured logger instead.',
|
|
337
|
+
},
|
|
338
|
+
status: 'promoted',
|
|
339
|
+
createdAt: NOW,
|
|
340
|
+
updatedAt: NOW,
|
|
341
|
+
fireCount: 0,
|
|
342
|
+
truePositiveCount: 0,
|
|
343
|
+
falsePositiveCount: 0,
|
|
344
|
+
sourceFindings: [],
|
|
345
|
+
source: 'bundled',
|
|
346
|
+
fixDescription: 'Replace direct console.error/log/warn calls with your application\'s structured logger (e.g., logger.error(), winston, pino) for consistent formatting and observability.',
|
|
347
|
+
confirmationCount: 1,
|
|
348
|
+
},
|
|
349
|
+
// ─── NULL SAFETY ────────────────────────────────────────────
|
|
350
|
+
{
|
|
351
|
+
id: 'starter_null_promise_all_conditional',
|
|
352
|
+
pattern: {
|
|
353
|
+
id: 'starter_null_promise_all_conditional',
|
|
354
|
+
description: 'Promise.all() with conditional expressions that may resolve to null can silently pass null values to subsequent function calls. Assign results to intermediate variables and check for null before use.',
|
|
355
|
+
kind: 'regex',
|
|
356
|
+
languages: ['typescript', 'javascript'],
|
|
357
|
+
regexPattern: 'await Promise\\.all\\(\\[([\\w\\s:<>]+)\\?.*?:.*?null,',
|
|
358
|
+
fileGlob: '**/*.{ts,tsx,js,jsx}',
|
|
359
|
+
matchBehavior: 'presence_is_bad',
|
|
360
|
+
claimCategory: 'null-safety',
|
|
361
|
+
severity: 'high',
|
|
362
|
+
evidenceTemplate: '{file}:{line} has Promise.all with conditional null: {match}. Destructure results and null-check before use.',
|
|
363
|
+
},
|
|
364
|
+
status: 'promoted',
|
|
365
|
+
createdAt: NOW,
|
|
366
|
+
updatedAt: NOW,
|
|
367
|
+
fireCount: 0,
|
|
368
|
+
truePositiveCount: 0,
|
|
369
|
+
falsePositiveCount: 0,
|
|
370
|
+
sourceFindings: [],
|
|
371
|
+
source: 'bundled',
|
|
372
|
+
fixDescription: 'Destructure Promise.all results into named variables and explicitly check for null before passing to subsequent function calls.',
|
|
373
|
+
confirmationCount: 1,
|
|
374
|
+
},
|
|
375
|
+
// ─── INTERNATIONALIZATION ───────────────────────────────────
|
|
376
|
+
{
|
|
377
|
+
id: 'starter_i18n_suspense_hardcoded_text',
|
|
378
|
+
pattern: {
|
|
379
|
+
id: 'starter_i18n_suspense_hardcoded_text',
|
|
380
|
+
description: 'String literals inside Suspense fallback components or other UI elements should be wrapped with i18n translation functions (Trans, t()) to support internationalization.',
|
|
381
|
+
kind: 'regex',
|
|
382
|
+
languages: ['typescript', 'javascript'],
|
|
383
|
+
regexPattern: '<Suspense[^>]*fallback=[^>]*><div[^>]*>([^<]+)</div>',
|
|
384
|
+
fileGlob: '**/*.{ts,tsx,js,jsx}',
|
|
385
|
+
matchBehavior: 'presence_is_bad',
|
|
386
|
+
claimCategory: 'i18n',
|
|
387
|
+
severity: 'medium',
|
|
388
|
+
evidenceTemplate: '{file}:{line} has hardcoded text in Suspense fallback: {match}. Wrap with <Trans> or t() for i18n support.',
|
|
389
|
+
},
|
|
390
|
+
status: 'promoted',
|
|
391
|
+
createdAt: NOW,
|
|
392
|
+
updatedAt: NOW,
|
|
393
|
+
fireCount: 0,
|
|
394
|
+
truePositiveCount: 0,
|
|
395
|
+
falsePositiveCount: 0,
|
|
396
|
+
sourceFindings: [],
|
|
397
|
+
source: 'bundled',
|
|
398
|
+
fixDescription: 'Wrap hardcoded string literals in Suspense fallbacks and other UI elements with the i18n translation component (e.g., <Trans>) or function (e.g., t()).',
|
|
399
|
+
confirmationCount: 1,
|
|
400
|
+
},
|
|
401
|
+
];
|
|
402
|
+
//# sourceMappingURL=starter-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"starter-catalog.js","sourceRoot":"","sources":["../../../src/lib/learned-rules/starter-catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the self-expanding formal verification system (Phase 1).
|
|
3
|
+
*
|
|
4
|
+
* When the LLM verifier finds a bug and it's confirmed correct,
|
|
5
|
+
* the system extracts the pattern and codifies it as a deterministic rule.
|
|
6
|
+
* Future scans use the formal rule instead of the LLM.
|
|
7
|
+
*/
|
|
8
|
+
/** The kind of detection pattern extracted from a confirmed finding. */
|
|
9
|
+
export type PatternKind = 'regex' | 'ast_pattern' | 'type_constraint';
|
|
10
|
+
/** A generalizable pattern extracted from a confirmed LLM finding. */
|
|
11
|
+
export interface ExtractedPattern {
|
|
12
|
+
/** Unique pattern ID (e.g., "lp_001"). */
|
|
13
|
+
readonly id: string;
|
|
14
|
+
/** Human-readable description of what this pattern catches. */
|
|
15
|
+
readonly description: string;
|
|
16
|
+
/** The kind of detection used. */
|
|
17
|
+
readonly kind: PatternKind;
|
|
18
|
+
/** Languages this pattern applies to. */
|
|
19
|
+
readonly languages: readonly string[];
|
|
20
|
+
/** The regex pattern string for detection (kind='regex'). */
|
|
21
|
+
readonly regexPattern?: string;
|
|
22
|
+
/** Glob for which files to scan. */
|
|
23
|
+
readonly fileGlob: string;
|
|
24
|
+
/** Whether the pattern should be PRESENT (and it's bad) or ABSENT (and it's bad if present). */
|
|
25
|
+
readonly matchBehavior: 'presence_is_bad' | 'absence_is_bad';
|
|
26
|
+
/** The original claim category that triggered this pattern. */
|
|
27
|
+
readonly claimCategory: string;
|
|
28
|
+
/** Severity when this pattern fires. */
|
|
29
|
+
readonly severity: 'critical' | 'high' | 'medium';
|
|
30
|
+
/** Evidence template using {match}, {file}, etc. */
|
|
31
|
+
readonly evidenceTemplate: string;
|
|
32
|
+
}
|
|
33
|
+
/** Status lifecycle of a learned rule. */
|
|
34
|
+
export type LearnedRuleStatus = 'candidate' | 'validated' | 'promoted' | 'rejected' | 'deprecated';
|
|
35
|
+
/** A rule generated from a confirmed LLM finding. */
|
|
36
|
+
export interface LearnedRule {
|
|
37
|
+
/** Unique rule ID (e.g., "lr_001"). */
|
|
38
|
+
readonly id: string;
|
|
39
|
+
/** The pattern this rule detects. */
|
|
40
|
+
readonly pattern: ExtractedPattern;
|
|
41
|
+
/** Current lifecycle status. */
|
|
42
|
+
readonly status: LearnedRuleStatus;
|
|
43
|
+
/** When the rule was first created. */
|
|
44
|
+
readonly createdAt: string;
|
|
45
|
+
/** When the rule was last updated. */
|
|
46
|
+
readonly updatedAt: string;
|
|
47
|
+
/** How many times this rule has fired across all scans. */
|
|
48
|
+
readonly fireCount: number;
|
|
49
|
+
/** How many times a human confirmed a fire was correct. */
|
|
50
|
+
readonly truePositiveCount: number;
|
|
51
|
+
/** How many times a human marked a fire as false positive. */
|
|
52
|
+
readonly falsePositiveCount: number;
|
|
53
|
+
/** The original finding that spawned this rule. */
|
|
54
|
+
readonly sourceFindings: readonly SourceFinding[];
|
|
55
|
+
/** Validation results from the harness. */
|
|
56
|
+
readonly validationResults?: ValidationResult;
|
|
57
|
+
/** Whether this rule was discovered via code scanning, PR review mining, or ships bundled. */
|
|
58
|
+
readonly source?: 'scan' | 'pr' | 'bundled';
|
|
59
|
+
/** Human-readable description of how to fix the detected issue. */
|
|
60
|
+
readonly fixDescription?: string;
|
|
61
|
+
/** Regex replacement pattern for auto-fix suggestions. */
|
|
62
|
+
readonly fixPattern?: string;
|
|
63
|
+
/** Number of distinct PRs/repos where this pattern was independently confirmed. */
|
|
64
|
+
readonly confirmationCount?: number;
|
|
65
|
+
}
|
|
66
|
+
/** Reference to the original LLM finding that spawned a learned rule. */
|
|
67
|
+
export interface SourceFinding {
|
|
68
|
+
/** The claim ID from the original verification. */
|
|
69
|
+
readonly claimId: string;
|
|
70
|
+
/** Description of the claim. */
|
|
71
|
+
readonly claimDescription: string;
|
|
72
|
+
/** The code snippet that contained the bug. */
|
|
73
|
+
readonly codeSnippet: string;
|
|
74
|
+
/** The file path where the bug was found. */
|
|
75
|
+
readonly filePath: string;
|
|
76
|
+
/** The language of the code. */
|
|
77
|
+
readonly language: string;
|
|
78
|
+
/** When this finding was recorded. */
|
|
79
|
+
readonly timestamp: string;
|
|
80
|
+
}
|
|
81
|
+
/** Results from running a rule through the validation harness. */
|
|
82
|
+
export interface ValidationResult {
|
|
83
|
+
/** Whether the rule passed validation. */
|
|
84
|
+
readonly passed: boolean;
|
|
85
|
+
/** True positives: correctly flagged known-bad code. */
|
|
86
|
+
readonly truePositives: number;
|
|
87
|
+
/** False positives: incorrectly flagged known-good code. */
|
|
88
|
+
readonly falsePositives: number;
|
|
89
|
+
/** True negatives: correctly passed known-good code. */
|
|
90
|
+
readonly trueNegatives: number;
|
|
91
|
+
/** False negatives: missed known-bad code. */
|
|
92
|
+
readonly falseNegatives: number;
|
|
93
|
+
/** Precision = TP / (TP + FP). */
|
|
94
|
+
readonly precision: number;
|
|
95
|
+
/** Recall = TP / (TP + FN). */
|
|
96
|
+
readonly recall: number;
|
|
97
|
+
/** Validation timestamp. */
|
|
98
|
+
readonly validatedAt: string;
|
|
99
|
+
/** Synthetic test cases used. */
|
|
100
|
+
readonly testCases: readonly TestCase[];
|
|
101
|
+
}
|
|
102
|
+
/** A test case for validating a learned rule. */
|
|
103
|
+
export interface TestCase {
|
|
104
|
+
/** Description of what this test case checks. */
|
|
105
|
+
readonly description: string;
|
|
106
|
+
/** The code to test. */
|
|
107
|
+
readonly code: string;
|
|
108
|
+
/** Whether this code SHOULD trigger the rule. */
|
|
109
|
+
readonly shouldMatch: boolean;
|
|
110
|
+
/** Whether the rule DID match. */
|
|
111
|
+
readonly didMatch: boolean;
|
|
112
|
+
}
|
|
113
|
+
/** Input to the pattern extractor. */
|
|
114
|
+
export interface PatternExtractionInput {
|
|
115
|
+
/** The verified claim (LLM found a real bug). */
|
|
116
|
+
readonly claim: {
|
|
117
|
+
readonly id: string;
|
|
118
|
+
readonly category: string;
|
|
119
|
+
readonly severity: 'critical' | 'high' | 'medium' | 'low';
|
|
120
|
+
readonly description: string;
|
|
121
|
+
readonly assertion: string;
|
|
122
|
+
};
|
|
123
|
+
/** The verification result (confirmed FAIL). */
|
|
124
|
+
readonly verification: {
|
|
125
|
+
readonly verdict: 'FAIL';
|
|
126
|
+
readonly reasoning: string;
|
|
127
|
+
readonly evidence?: string;
|
|
128
|
+
};
|
|
129
|
+
/** The code that was verified. */
|
|
130
|
+
readonly code: string;
|
|
131
|
+
/** The language of the code. */
|
|
132
|
+
readonly language: string;
|
|
133
|
+
/** The file path of the code. */
|
|
134
|
+
readonly filePath: string;
|
|
135
|
+
}
|
|
136
|
+
/** Output of the pattern extractor. */
|
|
137
|
+
export interface PatternExtractionResult {
|
|
138
|
+
/** Whether extraction succeeded. */
|
|
139
|
+
readonly success: boolean;
|
|
140
|
+
/** The extracted pattern, if successful. */
|
|
141
|
+
readonly pattern?: ExtractedPattern;
|
|
142
|
+
/** Why extraction failed, if it did. */
|
|
143
|
+
readonly failureReason?: string;
|
|
144
|
+
}
|
|
145
|
+
/** A review comment from a PR code review. */
|
|
146
|
+
export interface PRReviewComment {
|
|
147
|
+
readonly body: string;
|
|
148
|
+
readonly path: string;
|
|
149
|
+
readonly line: number | null;
|
|
150
|
+
}
|
|
151
|
+
/** A merged PR discovered for rule mining. */
|
|
152
|
+
export interface DiscoveredPR {
|
|
153
|
+
readonly repo: string;
|
|
154
|
+
readonly prNumber: number;
|
|
155
|
+
readonly title: string;
|
|
156
|
+
readonly labels: string[];
|
|
157
|
+
readonly mergeCommit: string;
|
|
158
|
+
readonly reviewComments: PRReviewComment[];
|
|
159
|
+
readonly files: string[];
|
|
160
|
+
}
|
|
161
|
+
/** A single hunk from a parsed PR diff. */
|
|
162
|
+
export interface DiffHunk {
|
|
163
|
+
readonly file: string;
|
|
164
|
+
readonly removedLines: string[];
|
|
165
|
+
readonly addedLines: string[];
|
|
166
|
+
readonly context: string[];
|
|
167
|
+
readonly startLine: number;
|
|
168
|
+
}
|
|
169
|
+
/** A PR with its parsed diff hunks. */
|
|
170
|
+
export interface ParsedPRDiff {
|
|
171
|
+
readonly pr: DiscoveredPR;
|
|
172
|
+
readonly hunks: DiffHunk[];
|
|
173
|
+
}
|
|
174
|
+
/** A generalized rule extracted from PR review patterns. */
|
|
175
|
+
export interface GeneralizedRule {
|
|
176
|
+
readonly category: string;
|
|
177
|
+
readonly severity: 'low' | 'medium' | 'high' | 'critical';
|
|
178
|
+
readonly description: string;
|
|
179
|
+
readonly detection: {
|
|
180
|
+
readonly pattern: string;
|
|
181
|
+
readonly language: string;
|
|
182
|
+
};
|
|
183
|
+
readonly fix: {
|
|
184
|
+
readonly description: string;
|
|
185
|
+
readonly pattern: string;
|
|
186
|
+
};
|
|
187
|
+
readonly fileGlob: string;
|
|
188
|
+
readonly matchBehavior: 'presence_is_bad' | 'absence_is_bad';
|
|
189
|
+
readonly evidenceTemplate: string;
|
|
190
|
+
readonly provenance: {
|
|
191
|
+
readonly repo: string;
|
|
192
|
+
readonly pr: number;
|
|
193
|
+
readonly file: string;
|
|
194
|
+
readonly reviewComment?: string;
|
|
195
|
+
};
|
|
196
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the self-expanding formal verification system (Phase 1).
|
|
3
|
+
*
|
|
4
|
+
* When the LLM verifier finds a bug and it's confirmed correct,
|
|
5
|
+
* the system extracts the pattern and codifies it as a deterministic rule.
|
|
6
|
+
* Future scans use the formal rule instead of the LLM.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/lib/learned-rules/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Harness — tests learned rules before promotion to the formal catalog.
|
|
3
|
+
*
|
|
4
|
+
* Every auto-generated rule must pass validation:
|
|
5
|
+
* 1. Fires correctly on the ORIGINAL code that triggered it
|
|
6
|
+
* 2. Fires correctly on synthetic "known-bad" variations
|
|
7
|
+
* 3. Does NOT fire on synthetic "known-good" variations
|
|
8
|
+
*
|
|
9
|
+
* Only rules with precision >= 0.8 and recall >= 0.5 are promoted.
|
|
10
|
+
*/
|
|
11
|
+
import type { LearnedRule, ValidationResult } from './types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Validate a learned rule against synthetic test cases.
|
|
14
|
+
*
|
|
15
|
+
* @param rule - The candidate rule to validate.
|
|
16
|
+
* @returns The validation result with precision/recall metrics.
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateRule(rule: LearnedRule): ValidationResult;
|
|
19
|
+
/**
|
|
20
|
+
* Get the validation thresholds.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getValidationThresholds(): {
|
|
23
|
+
minPrecision: number;
|
|
24
|
+
minRecall: number;
|
|
25
|
+
minTestCases: number;
|
|
26
|
+
};
|