@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.
Files changed (155) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +142 -0
  3. package/dist/engine/api.d.ts +16 -0
  4. package/dist/engine/api.js +543 -0
  5. package/dist/index.d.ts +122 -0
  6. package/dist/index.js +590 -0
  7. package/package.json +59 -0
  8. package/rules/VBC-001.yaml +33 -0
  9. package/rules/VBC-002.yaml +13 -0
  10. package/rules/VBC-003.yaml +36 -0
  11. package/rules/VBC-004.yaml +33 -0
  12. package/rules/VBC-005.yaml +24 -0
  13. package/rules/VBC-006.yaml +16 -0
  14. package/rules/VBC-007.yaml +28 -0
  15. package/rules/VBC-008.yaml +21 -0
  16. package/rules/VBC-009.yaml +24 -0
  17. package/rules/VBC-010.yaml +24 -0
  18. package/rules/VBC-011.yaml +21 -0
  19. package/rules/VBC-012.yaml +21 -0
  20. package/rules/VBC-013.yaml +17 -0
  21. package/rules/VBC-014.yaml +15 -0
  22. package/rules/VBC-015.yaml +40 -0
  23. package/rules/VBC-016.yaml +21 -0
  24. package/rules/VBC-017.yaml +23 -0
  25. package/rules/VBC-018.yaml +19 -0
  26. package/rules/VBC-019.yaml +42 -0
  27. package/rules/VBC-020.yaml +22 -0
  28. package/rules/VBC-021.yaml +22 -0
  29. package/rules/VBC-022.yaml +21 -0
  30. package/rules/VBC-023.yaml +22 -0
  31. package/rules/VBC-024.yaml +26 -0
  32. package/rules/VBC-025.yaml +13 -0
  33. package/rules/VBC-026.yaml +17 -0
  34. package/rules/VBC-027.yaml +27 -0
  35. package/rules/VBC-028.yaml +18 -0
  36. package/rules/VBC-029.yaml +22 -0
  37. package/rules/VBC-030.yaml +22 -0
  38. package/rules/VBC-032.yaml +20 -0
  39. package/rules/VBC-033.yaml +26 -0
  40. package/rules/VBC-034.yaml +41 -0
  41. package/rules/VBC-035.yaml +30 -0
  42. package/rules/VBC-039.yaml +18 -0
  43. package/rules/VBC-042.yaml +26 -0
  44. package/rules/VBC-045.yaml +13 -0
  45. package/rules/VBC-046.yaml +13 -0
  46. package/rules/VBC-048.yaml +23 -0
  47. package/rules/VBC-049.yaml +19 -0
  48. package/rules/VBC-051.yaml +23 -0
  49. package/rules/VBC-052.yaml +19 -0
  50. package/rules/VBC-053.yaml +19 -0
  51. package/rules/VBC-054.yaml +29 -0
  52. package/rules/VBC-056.yaml +28 -0
  53. package/rules/VBC-057.yaml +18 -0
  54. package/rules/VBC-058.yaml +18 -0
  55. package/rules/VBC-059.yaml +21 -0
  56. package/rules/VBC-063-B.yaml +15 -0
  57. package/rules/VBC-063.yaml +18 -0
  58. package/rules/VBC-065.yaml +33 -0
  59. package/rules/VBC-066.yaml +16 -0
  60. package/rules/VBC-068.yaml +21 -0
  61. package/rules/VBC-070.yaml +19 -0
  62. package/rules/VBC-077.yaml +35 -0
  63. package/rules/VBC-079.yaml +18 -0
  64. package/rules/VBC-080.yaml +18 -0
  65. package/rules/VBC-084.yaml +22 -0
  66. package/rules/VBC-086.yaml +22 -0
  67. package/rules/VBC-090.yaml +28 -0
  68. package/rules/VBC-096.yaml +19 -0
  69. package/rules/VBC-097.yaml +21 -0
  70. package/rules/VBC-098.yaml +18 -0
  71. package/rules/VBC-101.yaml +23 -0
  72. package/rules/VBC-102.yaml +20 -0
  73. package/rules/VBC-103.yaml +35 -0
  74. package/rules/VBC-104.yaml +24 -0
  75. package/rules/VBC-106.yaml +14 -0
  76. package/rules/VBC-112.yaml +20 -0
  77. package/rules/VBC-113.yaml +19 -0
  78. package/rules/VBC-117.yaml +21 -0
  79. package/rules/VBC-123.yaml +29 -0
  80. package/rules/VBC-131.yaml +20 -0
  81. package/rules/VBC-150.yaml +27 -0
  82. package/rules/VBC-153.yaml +21 -0
  83. package/rules/VBC-158.yaml +16 -0
  84. package/rules/VBC-161.yaml +27 -0
  85. package/rules/VBC-172.yaml +25 -0
  86. package/rules/VBC-197.yaml +22 -0
  87. package/rules/VBC-201.yaml +35 -0
  88. package/rules/VBC-206.yaml +19 -0
  89. package/rules/VBC-208.yaml +27 -0
  90. package/rules/VBC-212.yaml +19 -0
  91. package/rules/VBC-215.yaml +24 -0
  92. package/rules/VBC-305.yaml +20 -0
  93. package/rules/VBC-324.yaml +24 -0
  94. package/rules/VBC-334.yaml +23 -0
  95. package/rules/VBC-338.yaml +32 -0
  96. package/rules/VBC-340.yaml +22 -0
  97. package/rules/VBC-344.yaml +19 -0
  98. package/rules/VBC-347.yaml +21 -0
  99. package/rules/VBC-348.yaml +19 -0
  100. package/rules/VBC-385.yaml +21 -0
  101. package/rules/VBC-398.yaml +22 -0
  102. package/rules/VBC-401.yaml +12 -0
  103. package/rules/VBC-414.yaml +23 -0
  104. package/rules/VBC-421.yaml +20 -0
  105. package/rules/VBC-501.yaml +18 -0
  106. package/rules/VBC-502.yaml +23 -0
  107. package/rules/VBC-503.yaml +18 -0
  108. package/rules/VBC-504.yaml +20 -0
  109. package/rules/VBC-800.yaml +17 -0
  110. package/rules/VBC-901.yaml +48 -0
  111. package/rules/VBC-902.yaml +19 -0
  112. package/rules/VBC-903.yaml +17 -0
  113. package/rules/VBC-904.yaml +21 -0
  114. package/rules/VBC-905.yaml +17 -0
  115. package/rules/VBC-906.yaml +29 -0
  116. package/rules/VBC-907.yaml +24 -0
  117. package/rules/VBC-908.yaml +22 -0
  118. package/rules/VBC-909.yaml +21 -0
  119. package/rules/VBC-910.yaml +18 -0
  120. package/rules/VBC-911.yaml +21 -0
  121. package/rules/VBC-913.yaml +30 -0
  122. package/rules/VBC-914.yaml +31 -0
  123. package/rules/VBC-915.yaml +19 -0
  124. package/rules/VBC-916.yaml +24 -0
  125. package/rules/VBC-917.yaml +21 -0
  126. package/rules/VBC-918.yaml +22 -0
  127. package/rules/VBC-920.yaml +20 -0
  128. package/rules/VBC-921.yaml +45 -0
  129. package/rules/VBC-922.yaml +15 -0
  130. package/rules/VBC-923.yaml +15 -0
  131. package/rules/VBC-924.yaml +12 -0
  132. package/rules/VBC-925.yaml +14 -0
  133. package/rules/VBC-926.yaml +14 -0
  134. package/rules/VBC-927.yaml +24 -0
  135. package/rules/VBC-928.yaml +25 -0
  136. package/rules/VBC-929.yaml +21 -0
  137. package/rules/VBC-930.yaml +16 -0
  138. package/rules/VBC-931.yaml +16 -0
  139. package/rules/VBC-932.yaml +16 -0
  140. package/rules/VBC-933.yaml +28 -0
  141. package/rules/VBC-934.yaml +24 -0
  142. package/rules/VBC-935.yaml +26 -0
  143. package/rules/VBC-936.yaml +24 -0
  144. package/rules/VBC-937.yaml +24 -0
  145. package/rules/VBC-938.yaml +23 -0
  146. package/rules/VBC-939.yaml +23 -0
  147. package/rules/VBC-940.yaml +30 -0
  148. package/rules/VBC-941.yaml +28 -0
  149. package/rules/VBC-942.yaml +23 -0
  150. package/rules/VBC-943.yaml +22 -0
  151. package/rules/VBC-944.yaml +26 -0
  152. package/rules/VBC-945.yaml +33 -0
  153. package/rules/VBC-946.yaml +26 -0
  154. package/rules/VBC-947.yaml +22 -0
  155. 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>