@xenos1996/usat 1.0.0

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 (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +335 -0
  3. package/USAT.md +596 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +438 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/config.d.ts +7 -0
  9. package/dist/config.d.ts.map +1 -0
  10. package/dist/config.js +72 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/detect/index.d.ts +58 -0
  13. package/dist/detect/index.d.ts.map +1 -0
  14. package/dist/detect/index.js +329 -0
  15. package/dist/detect/index.js.map +1 -0
  16. package/dist/engine/audit.d.ts +21 -0
  17. package/dist/engine/audit.d.ts.map +1 -0
  18. package/dist/engine/audit.js +295 -0
  19. package/dist/engine/audit.js.map +1 -0
  20. package/dist/engine/diff.d.ts +24 -0
  21. package/dist/engine/diff.d.ts.map +1 -0
  22. package/dist/engine/diff.js +136 -0
  23. package/dist/engine/diff.js.map +1 -0
  24. package/dist/engine/evaluate.d.ts +19 -0
  25. package/dist/engine/evaluate.d.ts.map +1 -0
  26. package/dist/engine/evaluate.js +377 -0
  27. package/dist/engine/evaluate.js.map +1 -0
  28. package/dist/engine/gate.d.ts +19 -0
  29. package/dist/engine/gate.d.ts.map +1 -0
  30. package/dist/engine/gate.js +47 -0
  31. package/dist/engine/gate.js.map +1 -0
  32. package/dist/engine/loader.d.ts +22 -0
  33. package/dist/engine/loader.d.ts.map +1 -0
  34. package/dist/engine/loader.js +360 -0
  35. package/dist/engine/loader.js.map +1 -0
  36. package/dist/engine/maturity.d.ts +20 -0
  37. package/dist/engine/maturity.d.ts.map +1 -0
  38. package/dist/engine/maturity.js +157 -0
  39. package/dist/engine/maturity.js.map +1 -0
  40. package/dist/engine/score.d.ts +19 -0
  41. package/dist/engine/score.d.ts.map +1 -0
  42. package/dist/engine/score.js +141 -0
  43. package/dist/engine/score.js.map +1 -0
  44. package/dist/engine/sections.d.ts +14 -0
  45. package/dist/engine/sections.d.ts.map +1 -0
  46. package/dist/engine/sections.js +50 -0
  47. package/dist/engine/sections.js.map +1 -0
  48. package/dist/index.d.ts +23 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +17 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/report/markdown.d.ts +16 -0
  53. package/dist/report/markdown.d.ts.map +1 -0
  54. package/dist/report/markdown.js +488 -0
  55. package/dist/report/markdown.js.map +1 -0
  56. package/dist/types.d.ts +282 -0
  57. package/dist/types.d.ts.map +1 -0
  58. package/dist/types.js +16 -0
  59. package/dist/types.js.map +1 -0
  60. package/dist/util/glob.d.ts +11 -0
  61. package/dist/util/glob.d.ts.map +1 -0
  62. package/dist/util/glob.js +86 -0
  63. package/dist/util/glob.js.map +1 -0
  64. package/dist/util/project.d.ts +72 -0
  65. package/dist/util/project.d.ts.map +1 -0
  66. package/dist/util/project.js +586 -0
  67. package/dist/util/project.js.map +1 -0
  68. package/dist/util/yaml.d.ts +28 -0
  69. package/dist/util/yaml.d.ts.map +1 -0
  70. package/dist/util/yaml.js +48 -0
  71. package/dist/util/yaml.js.map +1 -0
  72. package/docs/ARCHITECTURE.md +119 -0
  73. package/docs/README.md +27 -0
  74. package/docs/adr/0001-record-architecture-decisions.md +35 -0
  75. package/docs/adr/0002-typescript-engine-over-python.md +49 -0
  76. package/docs/adr/0003-rules-are-data-not-code.md +50 -0
  77. package/docs/adr/0004-markdown-only-output.md +41 -0
  78. package/docs/adr/0005-maturity-dampens-severity.md +56 -0
  79. package/docs/adr/0006-severity-status-two-axes.md +48 -0
  80. package/docs/adr/0007-suppressions-are-visible-and-expiring.md +46 -0
  81. package/docs/adr/0008-report-trailer-and-diff.md +48 -0
  82. package/docs/adr/0009-fail-closed-on-malformed-input.md +56 -0
  83. package/docs/adr/0010-detector-design.md +54 -0
  84. package/docs/adr/0011-coexist-with-deep-scanners.md +52 -0
  85. package/docs/adr/README.md +32 -0
  86. package/docs/agent-integration.md +129 -0
  87. package/docs/ci-integration.md +163 -0
  88. package/docs/concepts.md +196 -0
  89. package/docs/configuration.md +105 -0
  90. package/docs/detectors.md +186 -0
  91. package/docs/getting-started.md +99 -0
  92. package/docs/maturity-profiles.md +156 -0
  93. package/docs/reference/api.md +193 -0
  94. package/docs/reference/cli.md +412 -0
  95. package/docs/rule-packs.md +278 -0
  96. package/docs/standards-mapping.md +171 -0
  97. package/package.json +96 -0
  98. package/rules/core/architecture.yaml +190 -0
  99. package/rules/core/cicd.yaml +212 -0
  100. package/rules/core/code-quality.yaml +261 -0
  101. package/rules/core/dependencies.yaml +132 -0
  102. package/rules/core/documentation.yaml +119 -0
  103. package/rules/core/future-readiness.yaml +91 -0
  104. package/rules/core/release.yaml +131 -0
  105. package/rules/core/repo.yaml +303 -0
  106. package/rules/core/security.yaml +494 -0
  107. package/rules/core/supply-chain.yaml +357 -0
  108. package/rules/core/testing.yaml +260 -0
  109. package/rules/detectors.yaml +2255 -0
  110. package/rules/index.yaml +41 -0
  111. package/rules/profiles/maturity.yaml +135 -0
  112. package/rules/stacks/ai-era.yaml +193 -0
  113. package/rules/stacks/api-backend.yaml +121 -0
  114. package/rules/stacks/cli.yaml +105 -0
  115. package/rules/stacks/compliance.yaml +157 -0
  116. package/rules/stacks/containers.yaml +124 -0
  117. package/rules/stacks/data.yaml +165 -0
  118. package/rules/stacks/go.yaml +134 -0
  119. package/rules/stacks/iac.yaml +128 -0
  120. package/rules/stacks/jvm.yaml +150 -0
  121. package/rules/stacks/ml-ai.yaml +140 -0
  122. package/rules/stacks/mobile.yaml +121 -0
  123. package/rules/stacks/node-typescript.yaml +166 -0
  124. package/rules/stacks/python.yaml +175 -0
  125. package/rules/stacks/rust.yaml +129 -0
  126. package/rules/stacks/solidity.yaml +139 -0
  127. package/rules/stacks/web-frontend.yaml +159 -0
  128. package/templates/AGENTS.audit.md +135 -0
  129. package/templates/AUDIT_REPORT.md +155 -0
@@ -0,0 +1,166 @@
1
+ id: stacks/node-typescript
2
+ title: Node.js / TypeScript
3
+ section: S15
4
+ section_title: Platform-Specific
5
+ description: 'Activated when JavaScript or TypeScript is detected.'
6
+ version: '1.0'
7
+ skip_when:
8
+ all:
9
+ - { fact: 'lang:typescript', op: 'absent' }
10
+ - { fact: 'lang:javascript', op: 'absent' }
11
+ - { fact: 'pm:node', op: 'absent' }
12
+
13
+ rules:
14
+ - id: NODE-001
15
+ title: Node runtime version is pinned
16
+ section: S15
17
+ section_title: Platform-Specific
18
+ severity: MEDIUM
19
+ class: operations
20
+ check:
21
+ kind: grep_present
22
+ pattern: '(node-version-file|node-version:|"engines"|"volta"|"packageManager"|\.nvmrc)'
23
+ include:
24
+ [
25
+ 'package.json',
26
+ '.nvmrc',
27
+ '.node-version',
28
+ '.github/workflows/*.yml',
29
+ '.github/workflows/*.yaml',
30
+ '.gitlab-ci.yml',
31
+ ]
32
+ flags: i
33
+ why: '"Works on my Node" is a real bug class — native modules, syntax support, and crypto defaults all shift between majors.'
34
+ remediation: 'Add .nvmrc plus "engines": { "node": ">=20" } and node-version-file: .nvmrc in CI.'
35
+ references: ['Node-Release-Schedule']
36
+
37
+ - id: NODE-002
38
+ title: TypeScript strict mode is on
39
+ section: S15
40
+ section_title: Platform-Specific
41
+ severity: MEDIUM
42
+ class: correctness
43
+ applies_when: { fact: 'lang:typescript' }
44
+ check:
45
+ kind: grep_present
46
+ pattern: '"strict"\s*:\s*true|"extends"\s*:\s*(.*strict|@tsconfig/strict)'
47
+ include: ['**/tsconfig*.json']
48
+ flags: i
49
+ why: 'TypeScript without strict is TypeScript with the safety features switched off.'
50
+ remediation: 'Set "strict": true. If the migration is large, enable one flag at a time (strictNullChecks first).'
51
+ references: ['TS-Handbook']
52
+
53
+ - id: NODE-003
54
+ title: Unhandled promise rejections are handled
55
+ section: S15
56
+ section_title: Platform-Specific
57
+ severity: MEDIUM
58
+ class: correctness
59
+ applies_when: { fact: 'platform:server' }
60
+ check:
61
+ kind: grep_present
62
+ pattern: '(unhandledRejection|uncaughtException|process\.on\(\s*[''"]unhandled)'
63
+ include: ['**/*.{ts,js,mjs,cjs}']
64
+ exclude: ['**/*.test.*', '**/*.spec.*', '**/node_modules/**']
65
+ flags: i
66
+ why: 'An unhandled rejection takes the process down in Node 15+, silently dropping in-flight requests.'
67
+ remediation: 'Register process handlers for unhandledRejection/uncaughtException that log and exit non-zero so the orchestrator restarts cleanly.'
68
+ references: ['CWE-248', 'Node-Docs']
69
+
70
+ - id: NODE-004
71
+ title: No private registry credentials committed
72
+ section: S15
73
+ section_title: Platform-Specific
74
+ severity: CRITICAL
75
+ class: security
76
+ check:
77
+ kind: grep_absent
78
+ pattern: '(//[^/]*npm[^/]*/:[_-]?authToken|_auth\s*=|NPM_TOKEN\s*=\s*[A-Za-z0-9_-]{16,})'
79
+ include:
80
+ [
81
+ '.npmrc',
82
+ '**/.npmrc',
83
+ '.yarnrc*',
84
+ '.github/workflows/*.yml',
85
+ '.github/workflows/*.yaml',
86
+ 'Dockerfile*',
87
+ ]
88
+ flags: i
89
+ why: 'A committed registry token is publish access to your package (and everything it can reach).'
90
+ remediation: 'Use ${{ secrets.NPM_TOKEN }} in CI and `npm config set` at runtime. Rotate anything already exposed.'
91
+
92
+ - id: NODE-005
93
+ title: No deprecated Node APIs in use
94
+ section: S15
95
+ section_title: Platform-Specific
96
+ severity: MEDIUM
97
+ class: maintainability
98
+ check:
99
+ kind: grep_deprecated
100
+ pattern: '(new Buffer\s*\(|url\.parse\s*\(|crypto\.createCipher\s*\(|domain\.create|require\(\s*[''"]sys[''"]|new (?:punycode|querystring))'
101
+ include: ['**/*.{ts,tsx,js,jsx,mjs,cjs}']
102
+ exclude: ['**/*.test.*', '**/node_modules/**']
103
+ why: 'Deprecated APIs are usually deprecated because they are unsafe (createCipher) or broken (url.parse).'
104
+ remediation: 'Buffer.from/alloc, new URL(), crypto.createCipheriv, punycode/. Replace as you touch the file.'
105
+ references: ['Node-Deprecations']
106
+
107
+ - id: NODE-006
108
+ title: Environment variables are validated at startup
109
+ section: S15
110
+ section_title: Platform-Specific
111
+ severity: MEDIUM
112
+ class: correctness
113
+ applies_when: { fact: 'platform:server' }
114
+ check:
115
+ kind: grep_present
116
+ pattern: '(envalid|t3-oss/env|@t3-oss/env|zod.*\.parse\(\s*process\.env|\.parse\(\s*env\b|cleanEnv|convict|dotenv-safe|env\.safeParse)'
117
+ include: ['**/*.{ts,js,mjs,cjs}']
118
+ exclude: ['**/*.test.*', '**/node_modules/**']
119
+ flags: i
120
+ why: 'A missing env var discovered at request time is a 500 in production; at startup it is a failed deploy.'
121
+ remediation: 'Parse process.env against a schema at boot and exit with a clear message on failure.'
122
+ references: ['12-Factor:Config']
123
+
124
+ - id: NODE-007
125
+ title: Libraries declare sideEffects / exports for tree-shaking
126
+ section: S15
127
+ section_title: Platform-Specific
128
+ severity: LOW
129
+ class: performance
130
+ applies_when: { fact: 'project:library' }
131
+ check:
132
+ kind: grep_present
133
+ pattern: '"sideEffects"\s*:|"exports"\s*:'
134
+ include: ['package.json']
135
+ why: 'Without it, bundlers ship your whole library to consumers who use one function.'
136
+ remediation: 'Add "sideEffects": false and an "exports" map in package.json.'
137
+ references: ['Webpack-Tree-Shaking']
138
+
139
+ - id: NODE-008
140
+ title: Module system is consistent
141
+ section: S15
142
+ section_title: Platform-Specific
143
+ severity: LOW
144
+ class: maintainability
145
+ applies_when: { fact: 'lang:typescript' }
146
+ check: { kind: manual }
147
+ evidence: 'Either ESM throughout ("type": "module" + .js extensions in imports) or CJS throughout — not both.'
148
+ why: 'Mixed ESM/CJS is the source of the most confusing Node errors in existence.'
149
+ remediation: 'Pick one; add "type" to package.json; configure tsconfig module/moduleResolution to match.'
150
+ references: ['Node-ESM']
151
+
152
+ - id: NODE-009
153
+ title: A fast, isolated runtime is used for tests
154
+ section: S15
155
+ section_title: Platform-Specific
156
+ severity: LOW
157
+ class: operations
158
+ applies_when: { fact: 'has:tests' }
159
+ check:
160
+ kind: grep_present
161
+ pattern: '(vitest|jest|node --test|node:test|ava|tap)'
162
+ include: ['package.json']
163
+ flags: i
164
+ why: 'Slow tests do not get run. Unrun tests are decoration.'
165
+ remediation: 'Vitest or node:test; keep the unit suite under ~30 seconds so it runs on every save.'
166
+ references: ['DORA']
@@ -0,0 +1,175 @@
1
+ id: stacks/python
2
+ title: Python
3
+ section: S15
4
+ section_title: Platform-Specific
5
+ description: 'Activated when Python is detected.'
6
+ version: '1.0'
7
+ skip_when: { fact: 'lang:python', op: 'absent' }
8
+
9
+ rules:
10
+ - id: PY-001
11
+ title: Dependencies are declared in a modern manifest
12
+ section: S15
13
+ section_title: Platform-Specific
14
+ severity: MEDIUM
15
+ class: supply-chain
16
+ check:
17
+ kind: any_file
18
+ patterns:
19
+ [
20
+ 'pyproject.toml',
21
+ 'requirements.txt',
22
+ 'requirements/*.txt',
23
+ 'Pipfile',
24
+ 'environment.yml',
25
+ 'setup.py',
26
+ ]
27
+ why: 'Python projects with no manifest cannot be installed reproducibly by anyone, including CI.'
28
+ remediation: 'Use pyproject.toml with Poetry/uv, or pin requirements.txt with pip-compile and hashes.'
29
+ references: ['PEP-621', 'PyPA']
30
+
31
+ - id: PY-002
32
+ title: Python version is pinned
33
+ section: S15
34
+ section_title: Platform-Specific
35
+ severity: MEDIUM
36
+ class: operations
37
+ check:
38
+ kind: grep_present
39
+ pattern: '(python-version|python_version|requires-python|PYTHON_VERSION|\.python-version)'
40
+ include:
41
+ [
42
+ 'pyproject.toml',
43
+ 'setup.py',
44
+ 'setup.cfg',
45
+ '.python-version',
46
+ '.github/workflows/*.yml',
47
+ '.github/workflows/*.yaml',
48
+ '.gitlab-ci.yml',
49
+ 'Dockerfile*',
50
+ 'runtime.txt',
51
+ ]
52
+ flags: i
53
+ why: 'Python 2→3 style breakage is rare, but 3.9→3.12 removes stdlib modules you may rely on.'
54
+ remediation: 'Set requires-python in pyproject.toml and pin the same version in CI and the container.'
55
+ references: ['endoflife.date/python']
56
+
57
+ - id: PY-003
58
+ title: Dependencies are pinned to exact versions
59
+ section: S15
60
+ section_title: Platform-Specific
61
+ severity: MEDIUM
62
+ class: supply-chain
63
+ check:
64
+ kind: grep_present
65
+ pattern: '^[a-zA-Z0-9._-]+\s*==\s*[0-9]'
66
+ include: ['requirements*.txt', 'requirements/*.txt']
67
+ why: 'Unpinned requirements mean the build changes without a commit.'
68
+ remediation: 'pip-compile with hashes, or Poetry/uv lockfile. Pin exact versions for applications.'
69
+ references: ['OpenSSF-Scorecard:Pinned-Dependencies']
70
+
71
+ - id: PY-004
72
+ title: No bare except clauses
73
+ section: S15
74
+ section_title: Platform-Specific
75
+ severity: MEDIUM
76
+ class: correctness
77
+ check:
78
+ kind: grep_wrong
79
+ pattern: 'except\s*:\s*$|except\s+BaseException\s*:'
80
+ include: ['**/*.py']
81
+ exclude: ['**/tests/**', '**/test_*.py', '**/*_test.py']
82
+ why: 'A bare except swallows KeyboardInterrupt and SystemExit, and every real error with them.'
83
+ remediation: 'Catch the specific exceptions you can handle; let the rest propagate.'
84
+ references: ['PEP-8', 'CWE-396']
85
+
86
+ - id: PY-005
87
+ title: No mutable default arguments
88
+ section: S15
89
+ section_title: Platform-Specific
90
+ severity: MEDIUM
91
+ class: correctness
92
+ check:
93
+ kind: grep_wrong
94
+ pattern: 'def\s+\w+\s*\([^)]*=\s*(\[\]|\{\}|set\(\)|list\(\)|dict\(\))'
95
+ include: ['**/*.py']
96
+ exclude: ['**/tests/**']
97
+ why: 'The default is evaluated once at definition time, so every call shares the same list. This bug survives code review remarkably often.'
98
+ remediation: 'Default to None and assign inside the function: `def f(x=None): x = x or []`.'
99
+ references: ['Python-Gotchas']
100
+
101
+ - id: PY-006
102
+ title: No unsafe deserialisation
103
+ section: S15
104
+ section_title: Platform-Specific
105
+ severity: CRITICAL
106
+ class: security
107
+ check:
108
+ kind: grep_absent
109
+ pattern: '(pickle\.(loads?|load)|marshal\.load|yaml\.load\s*\((?!.*Safe)|torch\.load\s*\((?!.*weights_only)|eval\s*\(\s*(input|request))'
110
+ include: ['**/*.py']
111
+ exclude: ['**/tests/**']
112
+ why: 'pickle.load on untrusted input is remote code execution by design. yaml.load without SafeLoader is the same class of bug.'
113
+ remediation: 'Use json, yaml.safe_load, or torch.load(weights_only=True). Never deserialise untrusted pickle.'
114
+ references: ['CWE-502', 'ASVS-5.5.1']
115
+
116
+ - id: PY-007
117
+ title: Subprocess calls do not use shell=True
118
+ section: S15
119
+ section_title: Platform-Specific
120
+ severity: HIGH
121
+ class: security
122
+ check:
123
+ kind: grep_absent
124
+ pattern: 'shell\s*=\s*True'
125
+ include: ['**/*.py']
126
+ exclude: ['**/tests/**', '**/scripts/**']
127
+ why: 'shell=True with any interpolated input is command injection.'
128
+ remediation: 'Pass an argument list: subprocess.run(["ls", "-l"], check=True).'
129
+ references: ['CWE-78']
130
+
131
+ - id: PY-008
132
+ title: Debug mode is not enabled outside local development
133
+ section: S15
134
+ section_title: Platform-Specific
135
+ severity: CRITICAL
136
+ class: security
137
+ check:
138
+ kind: grep_absent
139
+ pattern: '(DEBUG\s*=\s*True|debug\s*=\s*True|app\.run\(\s*[^)]*debug\s*=\s*True|FLASK_DEBUG\s*=\s*1)'
140
+ include: ['**/*.py', '**/*.env*', 'docker-compose*.yml', 'Dockerfile*']
141
+ exclude: ['**/tests/**', '**/settings/*test*', '**/test_settings*']
142
+ flags: i
143
+ why: 'Django/Flask debug mode exposes a REPL-backed traceback page — full RCE to anyone who can reach it.'
144
+ remediation: 'Drive DEBUG from the environment and default it to False. Assert it is False in the production settings module.'
145
+ references: ['CWE-489', 'ASVS-14.3.3']
146
+
147
+ - id: PY-009
148
+ title: Type hints are used on public functions
149
+ section: S15
150
+ section_title: Platform-Specific
151
+ severity: LOW
152
+ class: maintainability
153
+ check:
154
+ kind: grep_present
155
+ pattern: 'def\s+\w+\s*\([^)]*:\s*\w+[^)]*\)\s*(->|:)'
156
+ include: ['**/*.py']
157
+ exclude: ['**/tests/**', '**/migrations/**']
158
+ why: 'Hints are documentation the type checker enforces, and they make an editor useful.'
159
+ remediation: 'Annotate public functions first; enable mypy/pyright on the modules that matter.'
160
+ references: ['PEP-484']
161
+
162
+ - id: PY-010
163
+ title: A linter/formatter is configured for Python
164
+ section: S15
165
+ section_title: Platform-Specific
166
+ severity: MEDIUM
167
+ class: maintainability
168
+ check:
169
+ kind: grep_present
170
+ pattern: '(\[tool\.ruff\]|\[tool\.black\]|\[tool\.pylint\]|\[tool\.flake8\]|ruff|flake8|pylint)'
171
+ include: ['pyproject.toml', 'setup.cfg', '.flake8', '.ruff.toml', '.pre-commit-config.yaml']
172
+ flags: i
173
+ why: 'Ruff runs in milliseconds and catches the errors reviewers consistently miss.'
174
+ remediation: 'Add ruff with a sensible rule set and wire it into pre-commit and CI.'
175
+ references: ['PyPA']
@@ -0,0 +1,129 @@
1
+ id: stacks/rust
2
+ title: Rust
3
+ section: S15
4
+ section_title: Platform-Specific
5
+ description: 'Activated when Rust is detected.'
6
+ version: '1.0'
7
+ skip_when: { fact: 'lang:rust', op: 'absent' }
8
+
9
+ rules:
10
+ - id: RS-001
11
+ title: Cargo.toml declares edition and metadata
12
+ section: S15
13
+ section_title: Platform-Specific
14
+ severity: MEDIUM
15
+ class: maintainability
16
+ check:
17
+ kind: grep_present
18
+ pattern: 'edition\s*=\s*"20(18|21|24)"'
19
+ include: ['Cargo.toml']
20
+ why: 'Editions change semantics (especially 2018 module paths and 2024 closure capture). Without one you get 2015.'
21
+ remediation: 'Set `edition = "2021"` (or newer) and make sure CI builds with it.'
22
+ references: ['Rust-Editions']
23
+
24
+ - id: RS-002
25
+ title: No unwrap/expect in non-test code
26
+ section: S15
27
+ section_title: Platform-Specific
28
+ severity: MEDIUM
29
+ class: correctness
30
+ check:
31
+ kind: grep_wrong
32
+ pattern: '\.(unwrap|expect)\s*\(\s*\)|\.unwrap\(\)'
33
+ include: ['**/*.rs']
34
+ exclude: ['**/tests/**', '**/benches/**', '**/examples/**']
35
+ why: 'unwrap() is a panic wearing a convenience costume. Fine in tests, a crash in production.'
36
+ remediation: 'Propagate with ? and a typed error, or handle the None case explicitly.'
37
+ references: ['Rust-Error-Handling']
38
+
39
+ - id: RS-003
40
+ title: unsafe blocks are justified and minimal
41
+ section: S15
42
+ section_title: Platform-Specific
43
+ severity: HIGH
44
+ class: security
45
+ check:
46
+ kind: grep_wrong
47
+ pattern: '(unsafe\s*\{|#!\[allow\(unsafe_code\)\]|#\[allow\(unsafe_code\)\])'
48
+ include: ['**/*.rs']
49
+ exclude: ['**/tests/**']
50
+ why: 'unsafe is where Rust stops protecting you. Every block needs a comment stating the invariant it upholds.'
51
+ remediation: 'Wrap in a safe abstraction with a documented invariant; add a // SAFETY: comment per block.'
52
+ references: ['Rust-Nomicon', 'CWE-119']
53
+
54
+ - id: RS-004
55
+ title: Clippy runs in CI
56
+ section: S15
57
+ section_title: Platform-Specific
58
+ severity: MEDIUM
59
+ class: maintainability
60
+ applies_when: { fact: 'has:ci' }
61
+ check:
62
+ kind: grep_present
63
+ pattern: '(cargo clippy|clippy)'
64
+ include:
65
+ [
66
+ '.github/workflows/*.yml',
67
+ '.github/workflows/*.yaml',
68
+ '.gitlab-ci.yml',
69
+ 'Makefile',
70
+ 'justfile',
71
+ 'Taskfile.yml',
72
+ ]
73
+ flags: i
74
+ why: 'Clippy catches the idiomatic mistakes a compiler cannot: needless clones, wrong locks, incorrect casts.'
75
+ remediation: 'Add `cargo clippy -- -D warnings` to CI.'
76
+ references: ['Clippy']
77
+
78
+ - id: RS-005
79
+ title: No todo!/unimplemented! in shipped paths
80
+ section: S15
81
+ section_title: Platform-Specific
82
+ severity: MEDIUM
83
+ class: correctness
84
+ check:
85
+ kind: grep_wrong
86
+ pattern: '(todo!\s*\(|unimplemented!\s*\(|unreachable!\s*\()'
87
+ include: ['src/**/*.rs']
88
+ exclude: ['**/tests/**']
89
+ why: 'unreachable! that turns out to be reachable is an abort at runtime.'
90
+ remediation: 'Return a Result for the unfinished paths, or finish them before release.'
91
+ references: ['Rust-API-Guidelines']
92
+
93
+ - id: RS-006
94
+ title: Dependency audit runs in CI (cargo-audit / deny)
95
+ section: S15
96
+ section_title: Platform-Specific
97
+ severity: MEDIUM
98
+ class: supply-chain
99
+ applies_when: { fact: 'has:ci' }
100
+ check:
101
+ kind: grep_present
102
+ pattern: '(cargo-audit|cargo audit|cargo-deny|cargo deny|rustsec)'
103
+ include:
104
+ [
105
+ '.github/workflows/*.yml',
106
+ '.github/workflows/*.yaml',
107
+ '.gitlab-ci.yml',
108
+ 'Makefile',
109
+ 'justfile',
110
+ ]
111
+ flags: i
112
+ why: 'The RustSec advisory DB is the Rust equivalent of npm audit, and it is one CI step away.'
113
+ remediation: 'Add cargo-audit (advisories) and cargo-deny (licenses/bans) to CI.'
114
+ references: ['RustSec', 'SLSA']
115
+
116
+ - id: RS-007
117
+ title: Release builds are optimised and stripped
118
+ section: S15
119
+ section_title: Platform-Specific
120
+ severity: LOW
121
+ class: performance
122
+ applies_when: { fact: 'project:cli' }
123
+ check:
124
+ kind: grep_present
125
+ pattern: '(strip\s*=\s*(true|"symbols"|"debuginfo")|opt-level\s*=\s*("?[23s]"?|"z")|lto\s*=\s*)'
126
+ include: ['Cargo.toml']
127
+ why: 'The default release profile leaves debug symbols in the binary — 5–10× larger for no benefit.'
128
+ remediation: 'Add a [profile.release] with lto = true, strip = true, and opt-level = "z" or 3.'
129
+ references: ['Cargo-Profiles']
@@ -0,0 +1,139 @@
1
+ id: stacks/solidity
2
+ title: Blockchain / Smart Contracts
3
+ section: S15
4
+ section_title: Platform-Specific
5
+ description: 'Activated when Solidity, Foundry, or Hardhat is detected. USAT is not a substitute for a third-party audit.'
6
+ version: '1.0'
7
+ skip_when: { fact: 'platform:evm', op: 'absent' }
8
+
9
+ rules:
10
+ - id: SOLID-001
11
+ title: Contracts are protected against reentrancy
12
+ section: S15
13
+ section_title: Platform-Specific
14
+ severity: CRITICAL
15
+ class: security
16
+ check: { kind: manual }
17
+ evidence: 'Every external call followed by a state change, or use of nonReentrant / checks-effects-interactions ordering.'
18
+ why: 'Reentrancy drained The DAO and has drained a great many contracts since. Funds are unrecoverable.'
19
+ remediation: 'Follow checks-effects-interactions; use OpenZeppelin ReentrancyGuard; never call external contracts before updating state.'
20
+ references: ['SWC-107', 'Consensys-Best-Practices']
21
+
22
+ - id: SOLID-002
23
+ title: Compiler version is 0.8+ (or SafeMath is used)
24
+ section: S15
25
+ section_title: Platform-Specific
26
+ severity: HIGH
27
+ class: security
28
+ check:
29
+ kind: grep_deprecated
30
+ pattern: 'pragma solidity\s+\^?0\.[0-7]\.'
31
+ include: ['**/*.sol']
32
+ why: 'Pre-0.8 Solidity wraps silently on overflow. That is how balances go negative.'
33
+ remediation: 'Use Solidity ^0.8.x, which reverts on overflow/underflow by default.'
34
+ references: ['SWC-101', 'Solidity-0.8-Breaking-Changes']
35
+
36
+ - id: SOLID-003
37
+ title: Access control is explicit on privileged functions
38
+ section: S15
39
+ section_title: Platform-Specific
40
+ severity: CRITICAL
41
+ class: security
42
+ check: { kind: manual }
43
+ evidence: 'For every state-changing external/public function: the modifier or require that restricts the caller.'
44
+ why: 'An unprotected initialise or setOwner function is a complete takeover, irrevocably.'
45
+ remediation: 'Use Ownable/AccessControl; require(msg.sender == owner) on every privileged entry point.'
46
+ references: ['SWC-105', 'SWC-112']
47
+
48
+ - id: SOLID-004
49
+ title: tx.origin is not used for authorisation
50
+ section: S15
51
+ section_title: Platform-Specific
52
+ severity: CRITICAL
53
+ class: security
54
+ check:
55
+ kind: grep_absent
56
+ pattern: '(require\s*\(\s*tx\.origin|if\s*\(\s*tx\.origin|msg\.sender\s*==\s*tx\.origin)'
57
+ include: ['**/*.sol']
58
+ why: 'tx.origin is the EOA that started the transaction — a phishing contract can impersonate it.'
59
+ remediation: 'Always authorise on msg.sender.'
60
+ references: ['SWC-115', 'Consensys-Best-Practices']
61
+
62
+ - id: SOLID-005
63
+ title: External calls are checked and treated as untrusted
64
+ section: S15
65
+ section_title: Platform-Specific
66
+ severity: HIGH
67
+ class: security
68
+ check:
69
+ kind: grep_wrong
70
+ pattern: '(\.call\s*\(\s*|\.send\s*\(\s*|\.transfer\s*\(\s*)(?![^\n;]*require)'
71
+ include: ['**/*.sol']
72
+ why: 'A failed low-level call returns false; it does not revert. Ignoring the return value means silent failure.'
73
+ remediation: 'Check the return value and revert, or use a safe wrapper (Address.sendValue).'
74
+ references: ['SWC-104', 'SWC-113']
75
+
76
+ - id: SOLID-006
77
+ title: No block.timestamp dependence for critical logic
78
+ section: S15
79
+ section_title: Platform-Specific
80
+ severity: MEDIUM
81
+ class: security
82
+ check:
83
+ kind: grep_wrong
84
+ pattern: '(block\.timestamp|block\.number|now)\s*(%|/|\*|>|<)\s*\d'
85
+ include: ['**/*.sol']
86
+ why: 'Miners can nudge timestamps by seconds, which is enough to swing a block-number-based lottery.'
87
+ remediation: 'Use an oracle or a commit-reveal scheme for randomness; allow tolerance in time comparisons.'
88
+ references: ['SWC-116', 'SWC-120']
89
+
90
+ - id: SOLID-007
91
+ title: Events are emitted for state changes
92
+ section: S15
93
+ section_title: Platform-Specific
94
+ severity: MEDIUM
95
+ class: maintainability
96
+ check: { kind: manual }
97
+ evidence: 'Every function that mutates meaningful state emits an event (transfer, ownership, config).'
98
+ why: 'Off-chain consumers (and incident responders) rely on events; without them, reconstructing history is guesswork.'
99
+ remediation: 'Emit an indexed event for every state transition, including parameters that identify the affected entity.'
100
+ references: ['Consensys-Best-Practices']
101
+
102
+ - id: SOLID-008
103
+ title: An emergency pause mechanism exists
104
+ section: S15
105
+ section_title: Platform-Specific
106
+ severity: MEDIUM
107
+ class: operations
108
+ check:
109
+ kind: grep_present
110
+ pattern: '(Pausable|whenNotPaused|_pause\s*\(|emergencyStop|circuitBreaker)'
111
+ include: ['**/*.sol']
112
+ why: 'When an exploit is live, the difference between losing everything and losing a little is a pause button.'
113
+ remediation: 'Add OpenZeppelin Pausable to value-moving functions, with a documented, time-bounded pausing authority.'
114
+ references: ['Consensys-Best-Practices']
115
+
116
+ - id: SOLID-009
117
+ title: A test suite with fuzzing/invariants exists
118
+ section: S15
119
+ section_title: Platform-Specific
120
+ severity: HIGH
121
+ class: correctness
122
+ check: { kind: manual }
123
+ evidence: 'Foundry invariant/fuzz tests, Echidna properties, or a Hardhat suite covering the economic edge cases.'
124
+ why: 'Unit tests with hand-picked inputs miss the one input that drains the pool.'
125
+ remediation: 'Add invariant tests for the core accounting identity (e.g. sum(balances) == totalSupply).'
126
+ references: ['Foundry-Invariant-Testing']
127
+
128
+ - id: SOLID-010
129
+ title: Third-party security audit has been performed
130
+ section: S15
131
+ section_title: Platform-Specific
132
+ severity: HIGH
133
+ class: security
134
+ applies_when: { fact: 'maturity:production' }
135
+ check: { kind: manual }
136
+ evidence: 'Audit report, the version it covered, and confirmation that every finding was fixed or explicitly accepted.'
137
+ why: 'Automated tooling catches patterns; it does not catch economic design flaws. Deployed contract code is immutable.'
138
+ remediation: 'Commission an audit before mainnet; publish the report and the remediation status.'
139
+ references: ['OpenSSF-Criticality', 'Consensys-Best-Practices']