@fabriziosalmi/slopless 1.4.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/LICENSE +21 -0
- package/README.md +142 -0
- package/dist/engine/api.d.ts +16 -0
- package/dist/engine/api.js +543 -0
- package/dist/index.d.ts +122 -0
- package/dist/index.js +590 -0
- package/package.json +59 -0
- package/rules/VBC-001.yaml +33 -0
- package/rules/VBC-002.yaml +13 -0
- package/rules/VBC-003.yaml +36 -0
- package/rules/VBC-004.yaml +33 -0
- package/rules/VBC-005.yaml +24 -0
- package/rules/VBC-006.yaml +16 -0
- package/rules/VBC-007.yaml +28 -0
- package/rules/VBC-008.yaml +21 -0
- package/rules/VBC-009.yaml +24 -0
- package/rules/VBC-010.yaml +24 -0
- package/rules/VBC-011.yaml +21 -0
- package/rules/VBC-012.yaml +21 -0
- package/rules/VBC-013.yaml +17 -0
- package/rules/VBC-014.yaml +15 -0
- package/rules/VBC-015.yaml +40 -0
- package/rules/VBC-016.yaml +21 -0
- package/rules/VBC-017.yaml +23 -0
- package/rules/VBC-018.yaml +19 -0
- package/rules/VBC-019.yaml +42 -0
- package/rules/VBC-020.yaml +22 -0
- package/rules/VBC-021.yaml +22 -0
- package/rules/VBC-022.yaml +21 -0
- package/rules/VBC-023.yaml +22 -0
- package/rules/VBC-024.yaml +26 -0
- package/rules/VBC-025.yaml +13 -0
- package/rules/VBC-026.yaml +17 -0
- package/rules/VBC-027.yaml +27 -0
- package/rules/VBC-028.yaml +18 -0
- package/rules/VBC-029.yaml +22 -0
- package/rules/VBC-030.yaml +22 -0
- package/rules/VBC-032.yaml +20 -0
- package/rules/VBC-033.yaml +26 -0
- package/rules/VBC-034.yaml +41 -0
- package/rules/VBC-035.yaml +30 -0
- package/rules/VBC-039.yaml +18 -0
- package/rules/VBC-042.yaml +26 -0
- package/rules/VBC-045.yaml +13 -0
- package/rules/VBC-046.yaml +13 -0
- package/rules/VBC-048.yaml +23 -0
- package/rules/VBC-049.yaml +19 -0
- package/rules/VBC-051.yaml +23 -0
- package/rules/VBC-052.yaml +19 -0
- package/rules/VBC-053.yaml +19 -0
- package/rules/VBC-054.yaml +29 -0
- package/rules/VBC-056.yaml +28 -0
- package/rules/VBC-057.yaml +18 -0
- package/rules/VBC-058.yaml +18 -0
- package/rules/VBC-059.yaml +21 -0
- package/rules/VBC-063-B.yaml +15 -0
- package/rules/VBC-063.yaml +18 -0
- package/rules/VBC-065.yaml +33 -0
- package/rules/VBC-066.yaml +16 -0
- package/rules/VBC-068.yaml +21 -0
- package/rules/VBC-070.yaml +19 -0
- package/rules/VBC-077.yaml +35 -0
- package/rules/VBC-079.yaml +18 -0
- package/rules/VBC-080.yaml +18 -0
- package/rules/VBC-084.yaml +22 -0
- package/rules/VBC-086.yaml +22 -0
- package/rules/VBC-090.yaml +28 -0
- package/rules/VBC-096.yaml +19 -0
- package/rules/VBC-097.yaml +21 -0
- package/rules/VBC-098.yaml +18 -0
- package/rules/VBC-101.yaml +23 -0
- package/rules/VBC-102.yaml +20 -0
- package/rules/VBC-103.yaml +35 -0
- package/rules/VBC-104.yaml +24 -0
- package/rules/VBC-106.yaml +14 -0
- package/rules/VBC-112.yaml +20 -0
- package/rules/VBC-113.yaml +19 -0
- package/rules/VBC-117.yaml +21 -0
- package/rules/VBC-123.yaml +29 -0
- package/rules/VBC-131.yaml +20 -0
- package/rules/VBC-150.yaml +27 -0
- package/rules/VBC-153.yaml +21 -0
- package/rules/VBC-158.yaml +16 -0
- package/rules/VBC-161.yaml +27 -0
- package/rules/VBC-172.yaml +25 -0
- package/rules/VBC-197.yaml +22 -0
- package/rules/VBC-201.yaml +35 -0
- package/rules/VBC-206.yaml +19 -0
- package/rules/VBC-208.yaml +27 -0
- package/rules/VBC-212.yaml +19 -0
- package/rules/VBC-215.yaml +24 -0
- package/rules/VBC-305.yaml +20 -0
- package/rules/VBC-324.yaml +24 -0
- package/rules/VBC-334.yaml +23 -0
- package/rules/VBC-338.yaml +32 -0
- package/rules/VBC-340.yaml +22 -0
- package/rules/VBC-344.yaml +19 -0
- package/rules/VBC-347.yaml +21 -0
- package/rules/VBC-348.yaml +19 -0
- package/rules/VBC-385.yaml +21 -0
- package/rules/VBC-398.yaml +22 -0
- package/rules/VBC-401.yaml +12 -0
- package/rules/VBC-414.yaml +23 -0
- package/rules/VBC-421.yaml +20 -0
- package/rules/VBC-501.yaml +18 -0
- package/rules/VBC-502.yaml +23 -0
- package/rules/VBC-503.yaml +18 -0
- package/rules/VBC-504.yaml +20 -0
- package/rules/VBC-800.yaml +17 -0
- package/rules/VBC-901.yaml +48 -0
- package/rules/VBC-902.yaml +19 -0
- package/rules/VBC-903.yaml +17 -0
- package/rules/VBC-904.yaml +21 -0
- package/rules/VBC-905.yaml +17 -0
- package/rules/VBC-906.yaml +29 -0
- package/rules/VBC-907.yaml +24 -0
- package/rules/VBC-908.yaml +22 -0
- package/rules/VBC-909.yaml +21 -0
- package/rules/VBC-910.yaml +18 -0
- package/rules/VBC-911.yaml +21 -0
- package/rules/VBC-913.yaml +30 -0
- package/rules/VBC-914.yaml +31 -0
- package/rules/VBC-915.yaml +19 -0
- package/rules/VBC-916.yaml +24 -0
- package/rules/VBC-917.yaml +21 -0
- package/rules/VBC-918.yaml +22 -0
- package/rules/VBC-920.yaml +20 -0
- package/rules/VBC-921.yaml +45 -0
- package/rules/VBC-922.yaml +15 -0
- package/rules/VBC-923.yaml +15 -0
- package/rules/VBC-924.yaml +12 -0
- package/rules/VBC-925.yaml +14 -0
- package/rules/VBC-926.yaml +14 -0
- package/rules/VBC-927.yaml +24 -0
- package/rules/VBC-928.yaml +25 -0
- package/rules/VBC-929.yaml +21 -0
- package/rules/VBC-930.yaml +16 -0
- package/rules/VBC-931.yaml +16 -0
- package/rules/VBC-932.yaml +16 -0
- package/rules/VBC-933.yaml +28 -0
- package/rules/VBC-934.yaml +24 -0
- package/rules/VBC-935.yaml +26 -0
- package/rules/VBC-936.yaml +24 -0
- package/rules/VBC-937.yaml +24 -0
- package/rules/VBC-938.yaml +23 -0
- package/rules/VBC-939.yaml +23 -0
- package/rules/VBC-940.yaml +30 -0
- package/rules/VBC-941.yaml +28 -0
- package/rules/VBC-942.yaml +23 -0
- package/rules/VBC-943.yaml +22 -0
- package/rules/VBC-944.yaml +26 -0
- package/rules/VBC-945.yaml +33 -0
- package/rules/VBC-946.yaml +26 -0
- package/rules/VBC-947.yaml +22 -0
- package/rules/VBC-948.yaml +26 -0
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fabriziosalmi/slopless",
|
|
3
|
+
"version": "1.4.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"slopless": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"rules"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"test:watch": "vitest",
|
|
16
|
+
"test:coverage": "vitest run --coverage",
|
|
17
|
+
"build": "tsup",
|
|
18
|
+
"docs:dev": "npm run docs:gen && vitepress dev docs",
|
|
19
|
+
"docs:gen": "ts-node scripts/generate-rule-docs.ts",
|
|
20
|
+
"docs:build": "npm run docs:gen && vitepress build docs",
|
|
21
|
+
"docs:preview": "vitepress preview docs",
|
|
22
|
+
"verify:bundle": "node scripts/verify-bundle.js",
|
|
23
|
+
"verify:action": "bash scripts/run-action-locally.sh . 'src/**/*.ts'",
|
|
24
|
+
"release:prep": "npm run docs:gen && npm run build && npm test && npm run verify:bundle"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"linter",
|
|
28
|
+
"static-analysis",
|
|
29
|
+
"anti-vibecoding",
|
|
30
|
+
"sarif"
|
|
31
|
+
],
|
|
32
|
+
"author": "Fabrizio Salmi",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"type": "commonjs",
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/glob": "^8.1.0",
|
|
37
|
+
"@types/js-yaml": "^4.0.9",
|
|
38
|
+
"@types/node": "^25.3.0",
|
|
39
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
40
|
+
"commander": "^14.0.3",
|
|
41
|
+
"glob": "^13.0.6",
|
|
42
|
+
"ignore": "^7.0.5",
|
|
43
|
+
"js-yaml": "^4.1.1",
|
|
44
|
+
"minimatch": "^10.2.6",
|
|
45
|
+
"ts-node": "^10.9.2",
|
|
46
|
+
"tsup": "^8.5.1",
|
|
47
|
+
"typescript": "^5.9.3",
|
|
48
|
+
"vitepress": "^1.6.4",
|
|
49
|
+
"vitest": "^4.1.11",
|
|
50
|
+
"zod": "^4.3.6"
|
|
51
|
+
},
|
|
52
|
+
"allowScripts": {
|
|
53
|
+
"esbuild": true,
|
|
54
|
+
"fsevents": true
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
id: VBC-001
|
|
2
|
+
name: hardcoded-secret
|
|
3
|
+
severity: error
|
|
4
|
+
category: core
|
|
5
|
+
tags:
|
|
6
|
+
- security
|
|
7
|
+
- secrets
|
|
8
|
+
match:
|
|
9
|
+
regex: >-
|
|
10
|
+
\b(api_key|secret|password|token|bearer|auth[_-]?key|access[_-]?key)\b\s*[:=]\s*['\"`](?![^'\"`]*(?:secret|password|passwd|token|apikey|api[_-]?key|credential|dummy|example|sample|test|fake|changeme|placeholder|your[_-]|xxx|\.\.\.))([^'\"`]){8,}
|
|
11
|
+
flags: i
|
|
12
|
+
file_types:
|
|
13
|
+
- js
|
|
14
|
+
- ts
|
|
15
|
+
- py
|
|
16
|
+
- env
|
|
17
|
+
- yaml
|
|
18
|
+
- json
|
|
19
|
+
message: >-
|
|
20
|
+
Hardcoded credential at line {line}. Read it from the environment. Values that name what
|
|
21
|
+
they are, such as CHANGEME or a fixture spelling out "secret", are not reported: they describe
|
|
22
|
+
a field rather than hold a key.
|
|
23
|
+
tests:
|
|
24
|
+
fire:
|
|
25
|
+
- const password = "hunter2abc";
|
|
26
|
+
- 'api_key: "A9dK2mQ7xR4vL8nP1sT6wY3z"'
|
|
27
|
+
- token = "ghp_9fK2mQ7xR4vL8nP1sT6wY3zB5cE0"
|
|
28
|
+
quiet:
|
|
29
|
+
- const password = process.env.PASSWORD;
|
|
30
|
+
- const token = "";
|
|
31
|
+
- secret = "REAL-HMAC-SECRET"
|
|
32
|
+
- 'api_key: "your-api-key-here"'
|
|
33
|
+
- access_key = "AKIAIOSFODNN7EXAMPLE"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
id: VBC-002
|
|
2
|
+
name: committed-env
|
|
3
|
+
severity: error
|
|
4
|
+
category: core
|
|
5
|
+
tags:
|
|
6
|
+
- security
|
|
7
|
+
- git
|
|
8
|
+
match:
|
|
9
|
+
git_check: committed_env
|
|
10
|
+
message: >-
|
|
11
|
+
Environment file (.env) detected in staged files. This is a security risk.
|
|
12
|
+
tests:
|
|
13
|
+
external: 'git-checker.test.ts: needs a staged file list'
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
id: VBC-003
|
|
2
|
+
name: chmod-777
|
|
3
|
+
severity: error
|
|
4
|
+
category: core
|
|
5
|
+
tags:
|
|
6
|
+
- security
|
|
7
|
+
match:
|
|
8
|
+
regex: chmod\s+777
|
|
9
|
+
file_types:
|
|
10
|
+
- sh
|
|
11
|
+
- py
|
|
12
|
+
- js
|
|
13
|
+
exclude_files:
|
|
14
|
+
- '**/test/**'
|
|
15
|
+
- '**/tests/**'
|
|
16
|
+
- '**/__tests__/**'
|
|
17
|
+
- '**/testdata/**'
|
|
18
|
+
- '**/fixtures/**'
|
|
19
|
+
- '**/test_*.py'
|
|
20
|
+
- '**/*_test.py'
|
|
21
|
+
- '**/test.py'
|
|
22
|
+
- '**/conftest.py'
|
|
23
|
+
- '**/*_test.go'
|
|
24
|
+
- '**/*_bench.go'
|
|
25
|
+
- '**/*.test.*'
|
|
26
|
+
- '**/*.spec.*'
|
|
27
|
+
- '**/bench/**'
|
|
28
|
+
- '**/benchmark*'
|
|
29
|
+
- '**/*corpus*'
|
|
30
|
+
- '**/*payload*'
|
|
31
|
+
message: Insecure chmod 777 detected at line {line}
|
|
32
|
+
tests:
|
|
33
|
+
fire:
|
|
34
|
+
- chmod 777 /srv/app
|
|
35
|
+
quiet:
|
|
36
|
+
- chmod 640 /srv/app
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
id: VBC-004
|
|
2
|
+
name: sql-injection-concatenation
|
|
3
|
+
severity: error
|
|
4
|
+
category: security
|
|
5
|
+
tags:
|
|
6
|
+
- security
|
|
7
|
+
match:
|
|
8
|
+
regex: >-
|
|
9
|
+
(?:"[^"\n]*|'[^'\n]*|`[^`\n]*)(?:\bSELECT\b[^\n]{0,80}?\bFROM\b|\bINSERT\s+INTO\b|\bUPDATE\b[^\n]{0,80}?\bSET\b|\bDELETE\b[^\n]{0,80}?\bFROM\b|\bWHERE\b|\bVALUES\s*\()[^\n]{0,120}?["'`]\s*\+|\+=?\s*["'`][^\n]{0,80}?\b(?:WHERE\s|VALUES\s*\(|SET\s+\w+\s*=|ORDER\s+BY|GROUP\s+BY|LIMIT\s+\d|AND\s+\w+\s*=|OR\s+\w+\s*=)
|
|
10
|
+
flags: i
|
|
11
|
+
scan: all
|
|
12
|
+
file_types:
|
|
13
|
+
- js
|
|
14
|
+
- ts
|
|
15
|
+
- tsx
|
|
16
|
+
- jsx
|
|
17
|
+
- py
|
|
18
|
+
- go
|
|
19
|
+
- php
|
|
20
|
+
- rb
|
|
21
|
+
- java
|
|
22
|
+
- cs
|
|
23
|
+
message: >-
|
|
24
|
+
SQL built by string concatenation at line {line}. An attacker controlling the interpolated
|
|
25
|
+
value can rewrite the statement. Use parameterized queries or a query builder that binds
|
|
26
|
+
values separately.
|
|
27
|
+
tests:
|
|
28
|
+
fire:
|
|
29
|
+
- db.query("SELECT * FROM users WHERE id = " + id);
|
|
30
|
+
- sql += " AND status = " + status;
|
|
31
|
+
quiet:
|
|
32
|
+
- db.query("SELECT * FROM users WHERE id = $1", [id]);
|
|
33
|
+
- const label = 'Update ' + count + ' items';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
id: VBC-005
|
|
2
|
+
name: use-var
|
|
3
|
+
severity: error
|
|
4
|
+
category: core
|
|
5
|
+
tags:
|
|
6
|
+
- best-practice
|
|
7
|
+
match:
|
|
8
|
+
regex: (?<!\bdeclare\s{1,4})\bvar\b
|
|
9
|
+
file_types:
|
|
10
|
+
- js
|
|
11
|
+
- ts
|
|
12
|
+
fix:
|
|
13
|
+
regex_replace:
|
|
14
|
+
pattern: \bvar\b
|
|
15
|
+
replacement: let
|
|
16
|
+
message: Use of 'var' detected at line {line}. Use 'let' or 'const' instead.
|
|
17
|
+
tests:
|
|
18
|
+
fire:
|
|
19
|
+
- var total = 0;
|
|
20
|
+
quiet:
|
|
21
|
+
- const total = 0;
|
|
22
|
+
- let total = 0;
|
|
23
|
+
- file: global.d.ts
|
|
24
|
+
code: 'declare var sampleRate: number;'
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
id: VBC-006
|
|
2
|
+
name: committed-node-modules
|
|
3
|
+
severity: error
|
|
4
|
+
category: git
|
|
5
|
+
tags:
|
|
6
|
+
- git
|
|
7
|
+
- security
|
|
8
|
+
- performance
|
|
9
|
+
match:
|
|
10
|
+
git_check: node_modules
|
|
11
|
+
message: >-
|
|
12
|
+
node_modules directory detected in staged files. Add node_modules/ to .gitignore and use
|
|
13
|
+
package.json for dependency management. This bloats the repository and causes cross-platform
|
|
14
|
+
dependency hell.
|
|
15
|
+
tests:
|
|
16
|
+
external: 'git-checker.test.ts: needs a staged file list'
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
id: VBC-007
|
|
2
|
+
name: float-for-currency
|
|
3
|
+
severity: error
|
|
4
|
+
category: security
|
|
5
|
+
tags:
|
|
6
|
+
- security
|
|
7
|
+
- clean-code
|
|
8
|
+
match:
|
|
9
|
+
regex: >-
|
|
10
|
+
\b(?:(?:price|cost|fee|fees|tax|subtotal|payment|invoice|salary|refund|charge)(?![a-z])|[a-z_$]+(?:Price|Cost|Fee|Fees|Tax|Subtotal|Payment|Invoice|Salary|Refund|Charge)(?![a-z]))[A-Za-z_$]*\s*[:=]\s*[0-9]+\.[0-9]+
|
|
11
|
+
file_types:
|
|
12
|
+
- js
|
|
13
|
+
- ts
|
|
14
|
+
- py
|
|
15
|
+
message: >-
|
|
16
|
+
Monetary value declared as a floating point literal at line {line}. Binary floats cannot
|
|
17
|
+
represent decimal cents exactly. Store money as integer minor units or use a decimal type.
|
|
18
|
+
tests:
|
|
19
|
+
fire:
|
|
20
|
+
- const unitPrice = 19.99;
|
|
21
|
+
- let taxRate = 0.22;
|
|
22
|
+
- 'totalCost: 4.50'
|
|
23
|
+
quiet:
|
|
24
|
+
- const unitPriceCents = 1999;
|
|
25
|
+
- const ratio = 0.22;
|
|
26
|
+
- const feedback = 0.5;
|
|
27
|
+
- const amount = 0.5;
|
|
28
|
+
- const coffee = 1.5;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
id: VBC-008
|
|
2
|
+
name: sleep-race-condition
|
|
3
|
+
severity: warning
|
|
4
|
+
category: clean-code
|
|
5
|
+
tags:
|
|
6
|
+
- clean-code
|
|
7
|
+
- robustness
|
|
8
|
+
match:
|
|
9
|
+
regex: await\s+new\s+Promise\s*\([^)]*setTimeout
|
|
10
|
+
file_types:
|
|
11
|
+
- js
|
|
12
|
+
- ts
|
|
13
|
+
message: >-
|
|
14
|
+
Using setTimeout as a sleep to paper over a race condition at line {line}. This is a flaky
|
|
15
|
+
band-aid that slows the app down. Fix the underlying async coordination with proper event
|
|
16
|
+
handling or promises instead.
|
|
17
|
+
tests:
|
|
18
|
+
fire:
|
|
19
|
+
- await new Promise(resolve => setTimeout(resolve, 500));
|
|
20
|
+
quiet:
|
|
21
|
+
- await waitForReady();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
id: VBC-009
|
|
2
|
+
name: jquery-modern-project
|
|
3
|
+
severity: warning
|
|
4
|
+
category: clean-code
|
|
5
|
+
tags:
|
|
6
|
+
- clean-code
|
|
7
|
+
- performance
|
|
8
|
+
match:
|
|
9
|
+
regex: (require\(['"]jquery['"]\)|from\s+['"]jquery['"])
|
|
10
|
+
file_types:
|
|
11
|
+
- js
|
|
12
|
+
- ts
|
|
13
|
+
- tsx
|
|
14
|
+
- jsx
|
|
15
|
+
message: >-
|
|
16
|
+
jQuery dependency detected at line {line}. In 2026, native DOM APIs (querySelector, fetch,
|
|
17
|
+
classList, addEventListener) are sufficient and far lighter. Dropping jQuery reduces bundle
|
|
18
|
+
size and removes an unnecessary abstraction layer.
|
|
19
|
+
tests:
|
|
20
|
+
fire:
|
|
21
|
+
- import $ from 'jquery';
|
|
22
|
+
- const $ = require('jquery');
|
|
23
|
+
quiet:
|
|
24
|
+
- import { render } from 'react-dom';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
id: VBC-010
|
|
2
|
+
name: div-onclick-non-semantic
|
|
3
|
+
severity: error
|
|
4
|
+
category: ux-dx
|
|
5
|
+
tags:
|
|
6
|
+
- accessibility
|
|
7
|
+
- html
|
|
8
|
+
match:
|
|
9
|
+
regex: <div[^>]*\s(onclick|onClick)=
|
|
10
|
+
file_types:
|
|
11
|
+
- html
|
|
12
|
+
- js
|
|
13
|
+
- ts
|
|
14
|
+
- tsx
|
|
15
|
+
- jsx
|
|
16
|
+
message: >-
|
|
17
|
+
Clickable <div> with onclick handler at line {line}. Use <button> or <a> for interactive
|
|
18
|
+
elements. Non-semantic clickable divs are invisible to keyboard navigation and screen readers,
|
|
19
|
+
excluding users with disabilities.
|
|
20
|
+
tests:
|
|
21
|
+
fire:
|
|
22
|
+
- <div onClick={handleClick}>Save</div>
|
|
23
|
+
quiet:
|
|
24
|
+
- <button onClick={handleClick}>Save</button>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
id: VBC-011
|
|
2
|
+
name: document-cookie-direct
|
|
3
|
+
severity: warning
|
|
4
|
+
category: security
|
|
5
|
+
tags:
|
|
6
|
+
- security
|
|
7
|
+
- clean-code
|
|
8
|
+
match:
|
|
9
|
+
regex: document\.cookie\s*=(?!=)
|
|
10
|
+
file_types:
|
|
11
|
+
- js
|
|
12
|
+
- ts
|
|
13
|
+
message: >-
|
|
14
|
+
Direct document.cookie write at line {line}. Manual cookie manipulation is error-prone and
|
|
15
|
+
easy to misconfigure. Use a cookie library that enforces proper security flags (HttpOnly,
|
|
16
|
+
Secure, SameSite) by default.
|
|
17
|
+
tests:
|
|
18
|
+
fire:
|
|
19
|
+
- document.cookie = "session=abc";
|
|
20
|
+
quiet:
|
|
21
|
+
- if (document.cookie === "") { init(); }
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
id: VBC-012
|
|
2
|
+
name: redundant-promise-constructor
|
|
3
|
+
severity: warning
|
|
4
|
+
category: clean-code
|
|
5
|
+
tags:
|
|
6
|
+
- clean-code
|
|
7
|
+
- performance
|
|
8
|
+
match:
|
|
9
|
+
regex: new Promise\s*\(\s*(resolve|res)\s*=>\s*(resolve|res)\s*\(
|
|
10
|
+
file_types:
|
|
11
|
+
- js
|
|
12
|
+
- ts
|
|
13
|
+
message: >-
|
|
14
|
+
Redundant Promise constructor wrapping at line {line}. This is an anti-pattern called "Promise
|
|
15
|
+
constructor anti-pattern". Use Promise.resolve() directly or return the value/promise without
|
|
16
|
+
wrapping it.
|
|
17
|
+
tests:
|
|
18
|
+
fire:
|
|
19
|
+
- const p = new Promise(resolve => resolve(value));
|
|
20
|
+
quiet:
|
|
21
|
+
- const p = Promise.resolve(value);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
id: VBC-013
|
|
2
|
+
name: empty-catch
|
|
3
|
+
severity: error
|
|
4
|
+
category: core
|
|
5
|
+
tags:
|
|
6
|
+
- robustness
|
|
7
|
+
match:
|
|
8
|
+
ast_check:
|
|
9
|
+
type: empty-catch
|
|
10
|
+
message: >-
|
|
11
|
+
Empty catch block detected at line {line}. Swallowing errors makes debugging impossible.
|
|
12
|
+
tests:
|
|
13
|
+
fire:
|
|
14
|
+
- try { risky(); } catch (error) {}
|
|
15
|
+
quiet:
|
|
16
|
+
- try { risky(); } catch (error) { report(error); }
|
|
17
|
+
- try { risky(); } catch (error) { /* nothing to do here */ }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
id: VBC-014
|
|
2
|
+
name: committed-ide-settings
|
|
3
|
+
severity: warning
|
|
4
|
+
category: git
|
|
5
|
+
tags:
|
|
6
|
+
- git
|
|
7
|
+
- dx
|
|
8
|
+
match:
|
|
9
|
+
git_check: committed_ide_settings
|
|
10
|
+
message: >-
|
|
11
|
+
IDE settings file '{file}' detected in staged files. Add .vscode/ and .idea/ to .gitignore.
|
|
12
|
+
Committing personal IDE settings enforces your preferences on the entire team and creates
|
|
13
|
+
unnecessary merge conflicts.
|
|
14
|
+
tests:
|
|
15
|
+
external: 'git-checker.test.ts: needs a staged file list'
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
id: VBC-015
|
|
2
|
+
name: magic-numbers
|
|
3
|
+
severity: warning
|
|
4
|
+
category: clean-code
|
|
5
|
+
tags:
|
|
6
|
+
- clean-code
|
|
7
|
+
match:
|
|
8
|
+
regex: >-
|
|
9
|
+
(?<!\w)(?<!(?:const|let|var|static|final)\s{1,4}[A-Za-z_$][\w$]{0,60}\s{0,4}=\s{0,4})(?:86400|3600|1000|60)(?![\w.])
|
|
10
|
+
file_types:
|
|
11
|
+
- js
|
|
12
|
+
- ts
|
|
13
|
+
- py
|
|
14
|
+
exclude_files:
|
|
15
|
+
- '**/test/**'
|
|
16
|
+
- '**/tests/**'
|
|
17
|
+
- '**/__tests__/**'
|
|
18
|
+
- '**/testdata/**'
|
|
19
|
+
- '**/fixtures/**'
|
|
20
|
+
- '**/test_*.py'
|
|
21
|
+
- '**/*_test.py'
|
|
22
|
+
- '**/test.py'
|
|
23
|
+
- '**/conftest.py'
|
|
24
|
+
- '**/*_test.go'
|
|
25
|
+
- '**/*_bench.go'
|
|
26
|
+
- '**/*.test.*'
|
|
27
|
+
- '**/*.spec.*'
|
|
28
|
+
- '**/bench/**'
|
|
29
|
+
- '**/benchmark*'
|
|
30
|
+
- '**/*corpus*'
|
|
31
|
+
- '**/*payload*'
|
|
32
|
+
message: >-
|
|
33
|
+
Magic number '{match}' detected at line {line}. Consider using a named constant (e.g., SECONDS_IN_DAY).
|
|
34
|
+
tests:
|
|
35
|
+
fire:
|
|
36
|
+
- setTimeout(retry, 1000);
|
|
37
|
+
- const expiry = now + 86400;
|
|
38
|
+
quiet:
|
|
39
|
+
- const ONE_SECOND_MS = 1000;
|
|
40
|
+
- const port = 10001;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
id: VBC-016
|
|
2
|
+
name: commented-out-code
|
|
3
|
+
severity: warning
|
|
4
|
+
category: clean-code
|
|
5
|
+
tags:
|
|
6
|
+
- clean-code
|
|
7
|
+
match:
|
|
8
|
+
regex: >-
|
|
9
|
+
^\s*//\s*(function|const|let|var|if|for|while|return|class|export|import)\b
|
|
10
|
+
scan: comments
|
|
11
|
+
file_types:
|
|
12
|
+
- js
|
|
13
|
+
- ts
|
|
14
|
+
message: >-
|
|
15
|
+
Potential commented-out code detected at line {line}. If the code is no longer needed, delete
|
|
16
|
+
it. Use Git for history.
|
|
17
|
+
tests:
|
|
18
|
+
fire:
|
|
19
|
+
- // const legacy = compute();
|
|
20
|
+
quiet:
|
|
21
|
+
- // computes the running total
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
id: VBC-017
|
|
2
|
+
name: fake-test-assertion
|
|
3
|
+
severity: error
|
|
4
|
+
category: core
|
|
5
|
+
tags:
|
|
6
|
+
- testing
|
|
7
|
+
- clean-code
|
|
8
|
+
match:
|
|
9
|
+
regex: >-
|
|
10
|
+
(expect\(true\)\.(toBe|toEqual|toStrictEqual)\(true\)|expect\(1\)\.(toBe|toEqual)\(1\)|assert\.equal\(true,\s*true\)|assert\.ok\(true\)|assert\.isTrue\(true\))
|
|
11
|
+
file_types:
|
|
12
|
+
- js
|
|
13
|
+
- ts
|
|
14
|
+
message: >-
|
|
15
|
+
Trivially true test assertion at line {line}. This test always passes regardless of the
|
|
16
|
+
code under test and provides zero confidence. Write a real assertion that can actually fail
|
|
17
|
+
when the implementation is broken.
|
|
18
|
+
tests:
|
|
19
|
+
fire:
|
|
20
|
+
- expect(true).toBe(true);
|
|
21
|
+
- assert.ok(true);
|
|
22
|
+
quiet:
|
|
23
|
+
- expect(sum(1, 2)).toBe(3);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
id: VBC-018
|
|
2
|
+
name: console-logs-detected
|
|
3
|
+
severity: warning
|
|
4
|
+
category: clean-code
|
|
5
|
+
tags:
|
|
6
|
+
- clean-code
|
|
7
|
+
match:
|
|
8
|
+
regex: console\.(log|info|debug|dir)\(
|
|
9
|
+
file_types:
|
|
10
|
+
- js
|
|
11
|
+
- ts
|
|
12
|
+
message: >-
|
|
13
|
+
Production code with console usage '{match}' found at line {line}. Consider a professional
|
|
14
|
+
logging library.
|
|
15
|
+
tests:
|
|
16
|
+
fire:
|
|
17
|
+
- console.log("debug me");
|
|
18
|
+
quiet:
|
|
19
|
+
- logger.info("started");
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
id: VBC-019
|
|
2
|
+
name: absolute-paths
|
|
3
|
+
severity: error
|
|
4
|
+
category: core
|
|
5
|
+
tags:
|
|
6
|
+
- security
|
|
7
|
+
- portability
|
|
8
|
+
match:
|
|
9
|
+
regex: '[''"]/Users/|[''"]/home/|[''"]C:\\'
|
|
10
|
+
scan: strings
|
|
11
|
+
file_types:
|
|
12
|
+
- js
|
|
13
|
+
- ts
|
|
14
|
+
- py
|
|
15
|
+
- sh
|
|
16
|
+
exclude_files:
|
|
17
|
+
- '**/test/**'
|
|
18
|
+
- '**/tests/**'
|
|
19
|
+
- '**/__tests__/**'
|
|
20
|
+
- '**/testdata/**'
|
|
21
|
+
- '**/fixtures/**'
|
|
22
|
+
- '**/test_*.py'
|
|
23
|
+
- '**/*_test.py'
|
|
24
|
+
- '**/test.py'
|
|
25
|
+
- '**/conftest.py'
|
|
26
|
+
- '**/*_test.go'
|
|
27
|
+
- '**/*_bench.go'
|
|
28
|
+
- '**/*.test.*'
|
|
29
|
+
- '**/*.spec.*'
|
|
30
|
+
- '**/bench/**'
|
|
31
|
+
- '**/benchmark*'
|
|
32
|
+
- '**/*corpus*'
|
|
33
|
+
- '**/*payload*'
|
|
34
|
+
message: >-
|
|
35
|
+
Hardcoded absolute path '{match}' detected at line {line}. This breaks system portability
|
|
36
|
+
and leaks local structure.
|
|
37
|
+
tests:
|
|
38
|
+
fire:
|
|
39
|
+
- const home = '/Users/fab/project';
|
|
40
|
+
- const win = 'C:\\Users\\fab';
|
|
41
|
+
quiet:
|
|
42
|
+
- const home = path.join(os.homedir(), 'project');
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
id: VBC-020
|
|
2
|
+
name: justify-text-web
|
|
3
|
+
severity: warning
|
|
4
|
+
category: ux-dx
|
|
5
|
+
tags:
|
|
6
|
+
- ux
|
|
7
|
+
- accessibility
|
|
8
|
+
match:
|
|
9
|
+
regex: text-align:\s*justify
|
|
10
|
+
file_types:
|
|
11
|
+
- css
|
|
12
|
+
- scss
|
|
13
|
+
- less
|
|
14
|
+
message: >-
|
|
15
|
+
text-align:justify detected at line {line}. Justified text creates uneven "rivers of white
|
|
16
|
+
space" between words that make text significantly harder to read, particularly for users
|
|
17
|
+
with dyslexia. Use left-aligned text for body copy.
|
|
18
|
+
tests:
|
|
19
|
+
fire:
|
|
20
|
+
- 'p { text-align: justify; }'
|
|
21
|
+
quiet:
|
|
22
|
+
- 'p { text-align: left; }'
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
id: VBC-021
|
|
2
|
+
name: thin-font-weight
|
|
3
|
+
severity: warning
|
|
4
|
+
category: ux-dx
|
|
5
|
+
tags:
|
|
6
|
+
- ux
|
|
7
|
+
- accessibility
|
|
8
|
+
match:
|
|
9
|
+
regex: font-weight:\s*(?:100|200)\b
|
|
10
|
+
file_types:
|
|
11
|
+
- css
|
|
12
|
+
- scss
|
|
13
|
+
- less
|
|
14
|
+
message: >-
|
|
15
|
+
Thin font weight (100-199) detected at line {line}. Ultra-thin typefaces look elegant on
|
|
16
|
+
high-DPI Retina screens but become nearly invisible on standard monitors. Use a minimum
|
|
17
|
+
of 300 (Light) for body text and 400 (Regular) for critical UI copy.
|
|
18
|
+
tests:
|
|
19
|
+
fire:
|
|
20
|
+
- 'body { font-weight: 100; }'
|
|
21
|
+
quiet:
|
|
22
|
+
- 'body { font-weight: 400; }'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
id: VBC-022
|
|
2
|
+
name: custom-cursor-css
|
|
3
|
+
severity: warning
|
|
4
|
+
category: ux-dx
|
|
5
|
+
tags:
|
|
6
|
+
- ux
|
|
7
|
+
match:
|
|
8
|
+
regex: cursor:\s*url\s*\(
|
|
9
|
+
file_types:
|
|
10
|
+
- css
|
|
11
|
+
- scss
|
|
12
|
+
- less
|
|
13
|
+
message: >-
|
|
14
|
+
Custom cursor via CSS url() at line {line}. Custom cursors are often laggy, pixel-inaccurate,
|
|
15
|
+
and confusing for users who rely on their cursor to interact with native OS elements. This
|
|
16
|
+
is a hallmark of style-over-function design.
|
|
17
|
+
tests:
|
|
18
|
+
fire:
|
|
19
|
+
- 'body { cursor: url(cat.png), auto; }'
|
|
20
|
+
quiet:
|
|
21
|
+
- 'body { cursor: default; }'
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
id: VBC-023
|
|
2
|
+
name: notification-permission-eager
|
|
3
|
+
severity: error
|
|
4
|
+
category: ux-dx
|
|
5
|
+
tags:
|
|
6
|
+
- ux
|
|
7
|
+
- security
|
|
8
|
+
match:
|
|
9
|
+
regex: Notification\.requestPermission\(
|
|
10
|
+
file_types:
|
|
11
|
+
- js
|
|
12
|
+
- ts
|
|
13
|
+
message: >-
|
|
14
|
+
Notification.requestPermission() detected at line {line}. Requesting browser notification
|
|
15
|
+
permissions immediately on page load is hostile UX that causes most users to click "Block"
|
|
16
|
+
by default. Only request permissions in direct response to an explicit user action that
|
|
17
|
+
requires notifications.
|
|
18
|
+
tests:
|
|
19
|
+
fire:
|
|
20
|
+
- Notification.requestPermission();
|
|
21
|
+
quiet:
|
|
22
|
+
- button.addEventListener("click", askForNotifications);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
id: VBC-024
|
|
2
|
+
name: autoplay-video-unmuted
|
|
3
|
+
severity: error
|
|
4
|
+
category: ux-dx
|
|
5
|
+
tags:
|
|
6
|
+
- ux
|
|
7
|
+
- accessibility
|
|
8
|
+
match:
|
|
9
|
+
regex: <video[^>]*\bautoplay(?![^>]*\bmuted\b)[^>]*>
|
|
10
|
+
multiline: true
|
|
11
|
+
file_types:
|
|
12
|
+
- html
|
|
13
|
+
- js
|
|
14
|
+
- ts
|
|
15
|
+
- tsx
|
|
16
|
+
- jsx
|
|
17
|
+
message: >-
|
|
18
|
+
Autoplay video without the muted attribute at line {line}. Autoplaying audio is one of the
|
|
19
|
+
most hostile UX patterns: it embarrasses users in quiet environments, eats mobile data,
|
|
20
|
+
and is blocked by most browsers anyway. Add muted or remove autoplay.
|
|
21
|
+
tests:
|
|
22
|
+
fire:
|
|
23
|
+
- <video autoplay src="clip.mp4"></video>
|
|
24
|
+
quiet:
|
|
25
|
+
- <video autoplay muted src="clip.mp4"></video>
|
|
26
|
+
- <video controls></video>
|