@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.
- package/LICENSE +21 -0
- package/README.md +335 -0
- package/USAT.md +596 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +438 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +72 -0
- package/dist/config.js.map +1 -0
- package/dist/detect/index.d.ts +58 -0
- package/dist/detect/index.d.ts.map +1 -0
- package/dist/detect/index.js +329 -0
- package/dist/detect/index.js.map +1 -0
- package/dist/engine/audit.d.ts +21 -0
- package/dist/engine/audit.d.ts.map +1 -0
- package/dist/engine/audit.js +295 -0
- package/dist/engine/audit.js.map +1 -0
- package/dist/engine/diff.d.ts +24 -0
- package/dist/engine/diff.d.ts.map +1 -0
- package/dist/engine/diff.js +136 -0
- package/dist/engine/diff.js.map +1 -0
- package/dist/engine/evaluate.d.ts +19 -0
- package/dist/engine/evaluate.d.ts.map +1 -0
- package/dist/engine/evaluate.js +377 -0
- package/dist/engine/evaluate.js.map +1 -0
- package/dist/engine/gate.d.ts +19 -0
- package/dist/engine/gate.d.ts.map +1 -0
- package/dist/engine/gate.js +47 -0
- package/dist/engine/gate.js.map +1 -0
- package/dist/engine/loader.d.ts +22 -0
- package/dist/engine/loader.d.ts.map +1 -0
- package/dist/engine/loader.js +360 -0
- package/dist/engine/loader.js.map +1 -0
- package/dist/engine/maturity.d.ts +20 -0
- package/dist/engine/maturity.d.ts.map +1 -0
- package/dist/engine/maturity.js +157 -0
- package/dist/engine/maturity.js.map +1 -0
- package/dist/engine/score.d.ts +19 -0
- package/dist/engine/score.d.ts.map +1 -0
- package/dist/engine/score.js +141 -0
- package/dist/engine/score.js.map +1 -0
- package/dist/engine/sections.d.ts +14 -0
- package/dist/engine/sections.d.ts.map +1 -0
- package/dist/engine/sections.js +50 -0
- package/dist/engine/sections.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/report/markdown.d.ts +16 -0
- package/dist/report/markdown.d.ts.map +1 -0
- package/dist/report/markdown.js +488 -0
- package/dist/report/markdown.js.map +1 -0
- package/dist/types.d.ts +282 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/dist/util/glob.d.ts +11 -0
- package/dist/util/glob.d.ts.map +1 -0
- package/dist/util/glob.js +86 -0
- package/dist/util/glob.js.map +1 -0
- package/dist/util/project.d.ts +72 -0
- package/dist/util/project.d.ts.map +1 -0
- package/dist/util/project.js +586 -0
- package/dist/util/project.js.map +1 -0
- package/dist/util/yaml.d.ts +28 -0
- package/dist/util/yaml.d.ts.map +1 -0
- package/dist/util/yaml.js +48 -0
- package/dist/util/yaml.js.map +1 -0
- package/docs/ARCHITECTURE.md +119 -0
- package/docs/README.md +27 -0
- package/docs/adr/0001-record-architecture-decisions.md +35 -0
- package/docs/adr/0002-typescript-engine-over-python.md +49 -0
- package/docs/adr/0003-rules-are-data-not-code.md +50 -0
- package/docs/adr/0004-markdown-only-output.md +41 -0
- package/docs/adr/0005-maturity-dampens-severity.md +56 -0
- package/docs/adr/0006-severity-status-two-axes.md +48 -0
- package/docs/adr/0007-suppressions-are-visible-and-expiring.md +46 -0
- package/docs/adr/0008-report-trailer-and-diff.md +48 -0
- package/docs/adr/0009-fail-closed-on-malformed-input.md +56 -0
- package/docs/adr/0010-detector-design.md +54 -0
- package/docs/adr/0011-coexist-with-deep-scanners.md +52 -0
- package/docs/adr/README.md +32 -0
- package/docs/agent-integration.md +129 -0
- package/docs/ci-integration.md +163 -0
- package/docs/concepts.md +196 -0
- package/docs/configuration.md +105 -0
- package/docs/detectors.md +186 -0
- package/docs/getting-started.md +99 -0
- package/docs/maturity-profiles.md +156 -0
- package/docs/reference/api.md +193 -0
- package/docs/reference/cli.md +412 -0
- package/docs/rule-packs.md +278 -0
- package/docs/standards-mapping.md +171 -0
- package/package.json +96 -0
- package/rules/core/architecture.yaml +190 -0
- package/rules/core/cicd.yaml +212 -0
- package/rules/core/code-quality.yaml +261 -0
- package/rules/core/dependencies.yaml +132 -0
- package/rules/core/documentation.yaml +119 -0
- package/rules/core/future-readiness.yaml +91 -0
- package/rules/core/release.yaml +131 -0
- package/rules/core/repo.yaml +303 -0
- package/rules/core/security.yaml +494 -0
- package/rules/core/supply-chain.yaml +357 -0
- package/rules/core/testing.yaml +260 -0
- package/rules/detectors.yaml +2255 -0
- package/rules/index.yaml +41 -0
- package/rules/profiles/maturity.yaml +135 -0
- package/rules/stacks/ai-era.yaml +193 -0
- package/rules/stacks/api-backend.yaml +121 -0
- package/rules/stacks/cli.yaml +105 -0
- package/rules/stacks/compliance.yaml +157 -0
- package/rules/stacks/containers.yaml +124 -0
- package/rules/stacks/data.yaml +165 -0
- package/rules/stacks/go.yaml +134 -0
- package/rules/stacks/iac.yaml +128 -0
- package/rules/stacks/jvm.yaml +150 -0
- package/rules/stacks/ml-ai.yaml +140 -0
- package/rules/stacks/mobile.yaml +121 -0
- package/rules/stacks/node-typescript.yaml +166 -0
- package/rules/stacks/python.yaml +175 -0
- package/rules/stacks/rust.yaml +129 -0
- package/rules/stacks/solidity.yaml +139 -0
- package/rules/stacks/web-frontend.yaml +159 -0
- package/templates/AGENTS.audit.md +135 -0
- package/templates/AUDIT_REPORT.md +155 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
id: core/architecture
|
|
2
|
+
title: Architecture & Design
|
|
3
|
+
section: S4
|
|
4
|
+
section_title: Architecture & Design
|
|
5
|
+
description: >
|
|
6
|
+
Structure, boundaries, and the decisions behind them. Mostly judgement —
|
|
7
|
+
USAT automates the parts that show up as files and flags the rest for
|
|
8
|
+
review rather than pretending a regex can measure coupling.
|
|
9
|
+
version: '1.0'
|
|
10
|
+
|
|
11
|
+
rules:
|
|
12
|
+
- id: ARCH-001
|
|
13
|
+
title: Architecture is documented
|
|
14
|
+
section: S4
|
|
15
|
+
section_title: Architecture & Design
|
|
16
|
+
severity: MEDIUM
|
|
17
|
+
class: documentation
|
|
18
|
+
applies_when:
|
|
19
|
+
not: { fact: 'maturity:prototype' }
|
|
20
|
+
check:
|
|
21
|
+
kind: any_file
|
|
22
|
+
patterns:
|
|
23
|
+
[
|
|
24
|
+
'ARCHITECTURE.md',
|
|
25
|
+
'docs/ARCHITECTURE.md',
|
|
26
|
+
'docs/architecture/**',
|
|
27
|
+
'docs/design/**',
|
|
28
|
+
'docs/overview.md',
|
|
29
|
+
]
|
|
30
|
+
why: 'Undocumented architecture is whatever the last person to touch it believed.'
|
|
31
|
+
remediation: 'One page: components, data flow, external dependencies, and the three decisions a newcomer would question.'
|
|
32
|
+
references: ['arc42', 'C4-Model']
|
|
33
|
+
|
|
34
|
+
- id: ARCH-002
|
|
35
|
+
title: Architecture decisions are recorded (ADRs)
|
|
36
|
+
section: S4
|
|
37
|
+
section_title: Architecture & Design
|
|
38
|
+
severity: LOW
|
|
39
|
+
class: documentation
|
|
40
|
+
applies_when:
|
|
41
|
+
not: { fact: 'maturity:prototype' }
|
|
42
|
+
check:
|
|
43
|
+
kind: any_file
|
|
44
|
+
patterns:
|
|
45
|
+
['**/adr-*.md', '**/ADR-*.md', 'docs/adrs/**', 'docs/adr/**', 'docs/decisions/**', 'adr/**']
|
|
46
|
+
why: 'Six months from now nobody will remember why Postgres won over Mongo, and someone will relitigate it.'
|
|
47
|
+
remediation: 'Keep short ADRs in docs/adrs: context, decision, consequences. One markdown file per decision.'
|
|
48
|
+
references: ['ADR-Nygard']
|
|
49
|
+
|
|
50
|
+
- id: ARCH-003
|
|
51
|
+
title: No god files
|
|
52
|
+
section: S4
|
|
53
|
+
section_title: Architecture & Design
|
|
54
|
+
severity: MEDIUM
|
|
55
|
+
class: maintainability
|
|
56
|
+
check:
|
|
57
|
+
kind: file_lines_max
|
|
58
|
+
patterns:
|
|
59
|
+
[
|
|
60
|
+
'src/**/*.{ts,tsx,js,jsx,py,go,rs,java,kt,rb,php,cs}',
|
|
61
|
+
'app/**/*.{ts,tsx,js,jsx,py,go,rs,java,kt}',
|
|
62
|
+
'lib/**/*.{ts,tsx,js,jsx,py,go,rs}',
|
|
63
|
+
'pkg/**/*.go',
|
|
64
|
+
'internal/**/*.go',
|
|
65
|
+
]
|
|
66
|
+
max_lines: 800
|
|
67
|
+
why: 'A file nobody can hold in their head is a file nobody changes safely.'
|
|
68
|
+
remediation: 'Split by responsibility: extract the data access, the policy, or the formatting into its own module.'
|
|
69
|
+
references: ['Anti-pattern:God-Object', 'ISO-5055']
|
|
70
|
+
|
|
71
|
+
- id: ARCH-004
|
|
72
|
+
title: Layers are separated (transport / domain / data)
|
|
73
|
+
section: S4
|
|
74
|
+
section_title: Architecture & Design
|
|
75
|
+
severity: MEDIUM
|
|
76
|
+
class: maintainability
|
|
77
|
+
applies_when:
|
|
78
|
+
any:
|
|
79
|
+
- { fact: 'platform:server' }
|
|
80
|
+
- { fact: 'project:api' }
|
|
81
|
+
- { fact: 'has:database' }
|
|
82
|
+
check: { kind: manual }
|
|
83
|
+
why: >-
|
|
84
|
+
Business logic mixed into HTTP handlers cannot be tested, reused, or moved — and every new transport duplicates it.
|
|
85
|
+
evidence: 'Directory layout or file:line showing HTTP handlers delegating to a domain/service layer rather than querying the DB inline.'
|
|
86
|
+
remediation: 'Handlers parse and authorise; services hold business rules; repositories own persistence. No SQL in a controller.'
|
|
87
|
+
references: ['Clean-Architecture', 'Hexagonal-Architecture']
|
|
88
|
+
|
|
89
|
+
- id: ARCH-005
|
|
90
|
+
title: No circular dependencies between modules
|
|
91
|
+
section: S4
|
|
92
|
+
section_title: Architecture & Design
|
|
93
|
+
severity: HIGH
|
|
94
|
+
class: maintainability
|
|
95
|
+
depths: [deep]
|
|
96
|
+
check:
|
|
97
|
+
kind: command
|
|
98
|
+
run: '! (command -v npx >/dev/null && npx --yes dpdm@latest --exit-code circular:1 "src/**/*.ts" 2>/dev/null | grep -qi circular)'
|
|
99
|
+
expect_exit: 0
|
|
100
|
+
why: 'Cycles make modules untestable in isolation and turn every build order into a guess.'
|
|
101
|
+
evidence: 'Output of dpdm / madge --circular / dependency-cruiser, or a manual import-graph review.'
|
|
102
|
+
remediation: 'Break the cycle with an interface, an event, or by extracting the shared type into a leaf module.'
|
|
103
|
+
references: ['Anti-pattern:Circular-Dependency']
|
|
104
|
+
|
|
105
|
+
- id: ARCH-006
|
|
106
|
+
title: Module boundaries are enforced by tooling
|
|
107
|
+
section: S4
|
|
108
|
+
section_title: Architecture & Design
|
|
109
|
+
severity: LOW
|
|
110
|
+
class: maintainability
|
|
111
|
+
applies_when: { fact: 'project:monorepo' }
|
|
112
|
+
check:
|
|
113
|
+
kind: grep_present
|
|
114
|
+
pattern: '(enforce-module-boundaries|no-restricted-imports|dependency-cruiser|import/no-cycle|@nx/enforce-module-boundaries|boundaries/)'
|
|
115
|
+
include:
|
|
116
|
+
[
|
|
117
|
+
'**/.eslintrc*',
|
|
118
|
+
'eslint.config.*',
|
|
119
|
+
'.dependency-cruiser.*',
|
|
120
|
+
'nx.json',
|
|
121
|
+
'project.json',
|
|
122
|
+
'**/tsconfig*.json',
|
|
123
|
+
]
|
|
124
|
+
flags: i
|
|
125
|
+
why: 'Boundaries that are not enforced decay within one sprint of the first deadline.'
|
|
126
|
+
remediation: 'Add nx enforce-module-boundaries or eslint no-restricted-imports between packages/layers.'
|
|
127
|
+
references: ['Nx', 'dependency-cruiser']
|
|
128
|
+
|
|
129
|
+
- id: ARCH-007
|
|
130
|
+
title: Configuration is externalised
|
|
131
|
+
section: S4
|
|
132
|
+
section_title: Architecture & Design
|
|
133
|
+
severity: MEDIUM
|
|
134
|
+
class: operations
|
|
135
|
+
check:
|
|
136
|
+
kind: grep_absent
|
|
137
|
+
pattern: '(process\.env\.[A-Z_]+\s*\|\|\s*[''"](?:prod|production|https?://)|\b(?:DATABASE_URL|API_URL|REDIS_URL)\s*[:=]\s*[''"](?:postgres|mysql|mongodb|redis|https?)://(?!localhost|127\.0\.0\.1))'
|
|
138
|
+
include: ['**/*.{ts,tsx,js,jsx,py,go,rs,java,kt,rb,php}']
|
|
139
|
+
exclude: ['**/*.test.*', '**/*.spec.*']
|
|
140
|
+
flags: i
|
|
141
|
+
why: 'A hardcoded production URL means the only way to run staging is to edit source.'
|
|
142
|
+
remediation: 'Read every environment-specific value from configuration; keep defaults that are safe for local dev.'
|
|
143
|
+
references: ['12-Factor:Config', 'ASVS-14.1.3']
|
|
144
|
+
|
|
145
|
+
- id: ARCH-008
|
|
146
|
+
title: Side effects are isolated and testable
|
|
147
|
+
section: S4
|
|
148
|
+
section_title: Architecture & Design
|
|
149
|
+
severity: LOW
|
|
150
|
+
class: maintainability
|
|
151
|
+
check: { kind: manual }
|
|
152
|
+
why: >-
|
|
153
|
+
Side effects called inline cannot be tested without the network, the clock, or the database. That is why tests get slow and flaky.
|
|
154
|
+
evidence: 'Examples of I/O (network, disk, clock, randomness) being injected or wrapped rather than called inline from business logic.'
|
|
155
|
+
remediation: 'Inject the clock, the HTTP client, and the repository; keep pure functions pure.'
|
|
156
|
+
references: ['Functional-Core-Imperative-Shell']
|
|
157
|
+
|
|
158
|
+
- id: ARCH-009
|
|
159
|
+
title: Single points of failure are identified
|
|
160
|
+
section: S4
|
|
161
|
+
section_title: Architecture & Design
|
|
162
|
+
severity: MEDIUM
|
|
163
|
+
class: operations
|
|
164
|
+
applies_when:
|
|
165
|
+
any:
|
|
166
|
+
- { fact: 'maturity:production' }
|
|
167
|
+
- { fact: 'maturity:beta' }
|
|
168
|
+
check: { kind: manual }
|
|
169
|
+
why: >-
|
|
170
|
+
Availability incidents are almost always an SPOF somebody already knew about. Writing it down is what turns it into a project.
|
|
171
|
+
evidence: 'A list of SPOFs (single DB, single region, single queue, single vendor) and the mitigation for each.'
|
|
172
|
+
remediation: 'For each SPOF: replica, failover, or an explicitly accepted risk with a documented recovery time.'
|
|
173
|
+
references: ['AWS-Well-Architected:Reliability']
|
|
174
|
+
|
|
175
|
+
- id: ARCH-010
|
|
176
|
+
title: Async work is offloaded to a queue
|
|
177
|
+
section: S4
|
|
178
|
+
section_title: Architecture & Design
|
|
179
|
+
severity: LOW
|
|
180
|
+
class: performance
|
|
181
|
+
applies_when: { fact: 'platform:server' }
|
|
182
|
+
check:
|
|
183
|
+
kind: grep_present
|
|
184
|
+
pattern: '(bullmq|bull|amqplib|kafkajs|sqs|nats|temporal|celery|rq|sidekiq|taskiq|dramatiq|asynq)'
|
|
185
|
+
include: ['**/*.{ts,tsx,js,jsx,py,go,rs,rb}']
|
|
186
|
+
exclude: ['**/*.test.*']
|
|
187
|
+
flags: i
|
|
188
|
+
why: 'Anything slow inside a request handler is a timeout waiting for a busy day.'
|
|
189
|
+
remediation: 'Move email, image processing, webhooks, and report generation onto a queue with retries and a DLQ.'
|
|
190
|
+
references: ['12-Factor:Processes']
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
id: core/cicd
|
|
2
|
+
title: CI/CD, Infrastructure & Observability
|
|
3
|
+
section: S8
|
|
4
|
+
section_title: CI/CD, Infrastructure & Observability
|
|
5
|
+
description: >
|
|
6
|
+
How code becomes running software, and how you find out when it breaks.
|
|
7
|
+
Heavily informed by the OpenSSF Scorecard and DORA's delivery metrics.
|
|
8
|
+
version: '1.0'
|
|
9
|
+
|
|
10
|
+
rules:
|
|
11
|
+
- id: CICD-001
|
|
12
|
+
title: A CI pipeline exists
|
|
13
|
+
section: S8
|
|
14
|
+
section_title: CI/CD, Infrastructure & Observability
|
|
15
|
+
severity: HIGH
|
|
16
|
+
class: operations
|
|
17
|
+
check:
|
|
18
|
+
kind: any_file
|
|
19
|
+
patterns:
|
|
20
|
+
- '.github/workflows/*.yml'
|
|
21
|
+
- '.github/workflows/*.yaml'
|
|
22
|
+
- '.gitlab-ci.yml'
|
|
23
|
+
- 'Jenkinsfile'
|
|
24
|
+
- '.circleci/config.yml'
|
|
25
|
+
- '.buildkite/**'
|
|
26
|
+
- 'azure-pipelines.yml'
|
|
27
|
+
- '.drone.yml'
|
|
28
|
+
- 'bitbucket-pipelines.yml'
|
|
29
|
+
why: 'Manual release steps work exactly until the person who knows them is on holiday.'
|
|
30
|
+
remediation: 'Add a minimal pipeline first: install → lint → test → build. Expand from there.'
|
|
31
|
+
references: ['OpenSSF-Scorecard:CI-Tests', 'DORA']
|
|
32
|
+
|
|
33
|
+
- id: CICD-002
|
|
34
|
+
title: CI runs lint, test, and build
|
|
35
|
+
section: S8
|
|
36
|
+
section_title: CI/CD, Infrastructure & Observability
|
|
37
|
+
severity: MEDIUM
|
|
38
|
+
class: operations
|
|
39
|
+
applies_when: { fact: 'has:ci' }
|
|
40
|
+
check: { kind: manual }
|
|
41
|
+
evidence: 'The workflow file: which of lint / typecheck / test / build run on pull_request?'
|
|
42
|
+
why: 'A pipeline that only builds is a compiler with a dashboard.'
|
|
43
|
+
remediation: 'Add steps in fail-fast order: lint (5s) → typecheck → unit → build → integration.'
|
|
44
|
+
references: ['OpenSSF-Scorecard:CI-Tests', 'DORA']
|
|
45
|
+
|
|
46
|
+
- id: CICD-003
|
|
47
|
+
title: Deployment is automated
|
|
48
|
+
section: S8
|
|
49
|
+
section_title: CI/CD, Infrastructure & Observability
|
|
50
|
+
severity: HIGH
|
|
51
|
+
class: operations
|
|
52
|
+
applies_when:
|
|
53
|
+
any:
|
|
54
|
+
- { fact: 'maturity:production' }
|
|
55
|
+
- { fact: 'maturity:beta' }
|
|
56
|
+
check: { kind: manual }
|
|
57
|
+
evidence: 'The path from merged commit to production: is there a human typing commands into a server?'
|
|
58
|
+
why: 'Manual deploys are unrepeatable, unauditable, and always happen at the worst possible time.'
|
|
59
|
+
remediation: 'Automate deploy from a tagged commit or main-branch push with an approval step if governance requires one.'
|
|
60
|
+
references: ['DORA:Deployment-Frequency', 'NIST-SSDF-PO.5']
|
|
61
|
+
|
|
62
|
+
- id: CICD-004
|
|
63
|
+
title: A rollback path exists and has been exercised
|
|
64
|
+
section: S8
|
|
65
|
+
section_title: CI/CD, Infrastructure & Observability
|
|
66
|
+
severity: HIGH
|
|
67
|
+
class: operations
|
|
68
|
+
applies_when:
|
|
69
|
+
any:
|
|
70
|
+
- { fact: 'maturity:production' }
|
|
71
|
+
- { fact: 'maturity:beta' }
|
|
72
|
+
check: { kind: manual }
|
|
73
|
+
evidence: 'Documented rollback command/action, and the date it was last actually used (successfully).'
|
|
74
|
+
why: 'An untested rollback is a second outage with extra panic.'
|
|
75
|
+
remediation: 'Make rollback a one-command operation; rehearse it; record the last successful rollback date.'
|
|
76
|
+
references: ['DORA:Time-to-Restore', 'NIST-SSDF-RV.3']
|
|
77
|
+
|
|
78
|
+
- id: CICD-005
|
|
79
|
+
title: Separate environments exist
|
|
80
|
+
section: S8
|
|
81
|
+
section_title: CI/CD, Infrastructure & Observability
|
|
82
|
+
severity: MEDIUM
|
|
83
|
+
class: operations
|
|
84
|
+
applies_when:
|
|
85
|
+
not: { fact: 'maturity:prototype' }
|
|
86
|
+
check: { kind: manual }
|
|
87
|
+
evidence: 'dev / staging / production configuration, and confirmation that dev cannot reach production data.'
|
|
88
|
+
why: 'Testing in production is a slogan, not a strategy.'
|
|
89
|
+
remediation: 'At minimum: local + staging + production, with separate credentials and datasets per environment.'
|
|
90
|
+
references: ['NIST-SSDF-PO.5', 'ASVS-14.1.3']
|
|
91
|
+
|
|
92
|
+
- id: CICD-006
|
|
93
|
+
title: Infrastructure is defined as code
|
|
94
|
+
section: S8
|
|
95
|
+
section_title: CI/CD, Infrastructure & Observability
|
|
96
|
+
severity: MEDIUM
|
|
97
|
+
class: operations
|
|
98
|
+
applies_when:
|
|
99
|
+
not: { fact: 'maturity:prototype' }
|
|
100
|
+
check:
|
|
101
|
+
kind: any_file
|
|
102
|
+
patterns:
|
|
103
|
+
[
|
|
104
|
+
'**/*.tf',
|
|
105
|
+
'**/*.tfvars',
|
|
106
|
+
'pulumi/**',
|
|
107
|
+
'**/Pulumi.*.yaml',
|
|
108
|
+
'cdk.json',
|
|
109
|
+
'**/*.cdk.ts',
|
|
110
|
+
'ansible/**',
|
|
111
|
+
'**/playbook*.yml',
|
|
112
|
+
'k8s/**/*.yaml',
|
|
113
|
+
'manifests/**/*.yaml',
|
|
114
|
+
'charts/**/Chart.yaml',
|
|
115
|
+
'docker-compose*.yml',
|
|
116
|
+
'compose.yaml',
|
|
117
|
+
'serverless.yml',
|
|
118
|
+
'Dockerfile*',
|
|
119
|
+
]
|
|
120
|
+
why: 'Infrastructure that only exists in a console cannot be reviewed, diffed, or rebuilt after an incident.'
|
|
121
|
+
remediation: 'Move environments into Terraform/Pulumi/CDK (or at least declarative compose + helm manifests).'
|
|
122
|
+
references: ['NIST-SSDF-PO.5']
|
|
123
|
+
|
|
124
|
+
- id: CICD-007
|
|
125
|
+
title: Health check endpoints exist
|
|
126
|
+
section: S8
|
|
127
|
+
section_title: CI/CD, Infrastructure & Observability
|
|
128
|
+
severity: MEDIUM
|
|
129
|
+
class: operations
|
|
130
|
+
applies_when: { fact: 'platform:server' }
|
|
131
|
+
check:
|
|
132
|
+
kind: grep_present
|
|
133
|
+
pattern: '(/(health|healthz|readyz|livez|ready|live|status|ping)[''"`\s]|healthCheck|health_check|app\.get\(\s*[''"]/health)'
|
|
134
|
+
include: ['**/*.{ts,tsx,js,jsx,py,go,rs,java,kt,rb,php,yml,yaml}']
|
|
135
|
+
exclude: ['**/*.test.*', '**/*.spec.*', '**/node_modules/**']
|
|
136
|
+
flags: i
|
|
137
|
+
why: 'Without /health and /ready, orchestrators cannot tell a booting service from a dead one.'
|
|
138
|
+
remediation: 'Expose /health (liveness) and /ready (dependencies reachable); wire them into the orchestrator and the LB.'
|
|
139
|
+
references: ['K8s-Probes', 'OpenSSF-Scorecard']
|
|
140
|
+
|
|
141
|
+
- id: CICD-008
|
|
142
|
+
title: Error tracking / APM is configured
|
|
143
|
+
section: S8
|
|
144
|
+
section_title: CI/CD, Infrastructure & Observability
|
|
145
|
+
severity: MEDIUM
|
|
146
|
+
class: operations
|
|
147
|
+
applies_when:
|
|
148
|
+
not: { fact: 'maturity:prototype' }
|
|
149
|
+
check:
|
|
150
|
+
kind: grep_present
|
|
151
|
+
pattern: '(sentry|opentelemetry|datadog|newrelic|new-relic|rollbar|bugsnag|honeycomb|grafana|prometheus|betterstack|axiom)'
|
|
152
|
+
include: ['**/*.{ts,tsx,js,jsx,py,go,rs,java,kt,rb,yml,yaml,json,toml}']
|
|
153
|
+
exclude: ['**/node_modules/**', '**/*.test.*']
|
|
154
|
+
flags: i
|
|
155
|
+
why: 'Your users will find the bugs. The only question is whether they tell you.'
|
|
156
|
+
remediation: 'Add Sentry (or equivalent) with source maps and release tracking; alert on new error types.'
|
|
157
|
+
references: ['DORA:Monitoring', 'OpenTelemetry']
|
|
158
|
+
|
|
159
|
+
- id: CICD-009
|
|
160
|
+
title: Alerting is configured for critical failures
|
|
161
|
+
section: S8
|
|
162
|
+
section_title: CI/CD, Infrastructure & Observability
|
|
163
|
+
severity: MEDIUM
|
|
164
|
+
class: operations
|
|
165
|
+
applies_when: { fact: 'maturity:production' }
|
|
166
|
+
check: { kind: manual }
|
|
167
|
+
evidence: 'Alert rules, their thresholds, and who gets paged. Bonus: the last time one fired and what happened.'
|
|
168
|
+
why: 'Monitoring without alerting is a dashboard nobody opens until after the outage.'
|
|
169
|
+
remediation: 'Alert on symptoms users feel (error rate, latency, saturation), not on every CPU blip.'
|
|
170
|
+
references: ['Google-SRE:Monitoring', 'DORA']
|
|
171
|
+
|
|
172
|
+
- id: CICD-010
|
|
173
|
+
title: Backups exist AND restores have been tested
|
|
174
|
+
section: S8
|
|
175
|
+
section_title: CI/CD, Infrastructure & Observability
|
|
176
|
+
severity: HIGH
|
|
177
|
+
class: operations
|
|
178
|
+
applies_when:
|
|
179
|
+
any:
|
|
180
|
+
- { fact: 'has:database' }
|
|
181
|
+
- { fact: 'maturity:production' }
|
|
182
|
+
check: { kind: manual }
|
|
183
|
+
evidence: 'Backup schedule, retention, encryption at rest, and — critically — the date of the last successful restore drill.'
|
|
184
|
+
why: 'Nobody has ever been fired for having backups. Plenty of teams have died from never testing the restore.'
|
|
185
|
+
remediation: 'Automate backups, encrypt them, store them off-site, and run a restore drill quarterly with a recorded RTO.'
|
|
186
|
+
references: ['NIST-SSDF-RV.3', 'CIS-Control-11']
|
|
187
|
+
|
|
188
|
+
- id: CICD-011
|
|
189
|
+
title: A disaster recovery plan exists
|
|
190
|
+
section: S8
|
|
191
|
+
section_title: CI/CD, Infrastructure & Observability
|
|
192
|
+
severity: MEDIUM
|
|
193
|
+
class: operations
|
|
194
|
+
applies_when: { fact: 'maturity:production' }
|
|
195
|
+
check: { kind: manual }
|
|
196
|
+
evidence: 'Documented RTO/RPO, the failover procedure, and who is authorised to declare a disaster.'
|
|
197
|
+
why: 'At 3am, the plan is the only thing between you and ad-hoc database surgery.'
|
|
198
|
+
remediation: 'Write a one-page DR runbook: trigger, failover steps, rollback, comms template, and owner.'
|
|
199
|
+
references: ['NIST-SSDF-RV.3', 'AWS-Well-Architected:Reliability']
|
|
200
|
+
|
|
201
|
+
- id: CICD-012
|
|
202
|
+
title: Observability covers the golden signals
|
|
203
|
+
section: S8
|
|
204
|
+
section_title: CI/CD, Infrastructure & Observability
|
|
205
|
+
severity: LOW
|
|
206
|
+
class: operations
|
|
207
|
+
applies_when: { fact: 'maturity:production' }
|
|
208
|
+
check: { kind: manual }
|
|
209
|
+
evidence: 'A dashboard or metric set covering latency, traffic, errors, and saturation for the primary service.'
|
|
210
|
+
why: 'Four signals answer "is it broken, how bad, and where" in under five minutes.'
|
|
211
|
+
remediation: 'Instrument RED/USE metrics; put them on one dashboard linked from the runbook.'
|
|
212
|
+
references: ['Google-SRE:Golden-Signals']
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
id: core/code-quality
|
|
2
|
+
title: Code Quality
|
|
3
|
+
section: S5
|
|
4
|
+
section_title: Code Quality
|
|
5
|
+
description: >
|
|
6
|
+
Linting, typing, error handling, and logging hygiene. These are the rules
|
|
7
|
+
an agent should be able to settle without asking a human — most of them are
|
|
8
|
+
answered by looking for a config file.
|
|
9
|
+
version: '1.0'
|
|
10
|
+
|
|
11
|
+
rules:
|
|
12
|
+
- id: CQ-001
|
|
13
|
+
title: A linter is configured
|
|
14
|
+
section: S5
|
|
15
|
+
section_title: Code Quality
|
|
16
|
+
severity: MEDIUM
|
|
17
|
+
class: maintainability
|
|
18
|
+
check:
|
|
19
|
+
kind: any_file
|
|
20
|
+
patterns:
|
|
21
|
+
- '.eslintrc*'
|
|
22
|
+
- 'eslint.config.*'
|
|
23
|
+
- 'biome.json*'
|
|
24
|
+
- '.ruff.toml'
|
|
25
|
+
- 'ruff.toml'
|
|
26
|
+
- '.flake8'
|
|
27
|
+
- 'setup.cfg'
|
|
28
|
+
- '.pylintrc'
|
|
29
|
+
- 'pyproject.toml'
|
|
30
|
+
- '.golangci*'
|
|
31
|
+
- 'clippy.toml'
|
|
32
|
+
- '.rustfmt.toml'
|
|
33
|
+
- 'rustfmt.toml'
|
|
34
|
+
- '.rubocop.yml'
|
|
35
|
+
- 'phpcs.xml*'
|
|
36
|
+
- 'detekt.yml'
|
|
37
|
+
- '.swiftlint.yml'
|
|
38
|
+
why: 'Style arguments in review are a tax on every pull request. Automate them and argue about design instead.'
|
|
39
|
+
remediation: 'Add the standard linter for your stack, start with its recommended config, fix what it finds.'
|
|
40
|
+
references: ['NIST-SSDF-PW.7']
|
|
41
|
+
|
|
42
|
+
- id: CQ-002
|
|
43
|
+
title: A formatter is configured
|
|
44
|
+
section: S5
|
|
45
|
+
section_title: Code Quality
|
|
46
|
+
severity: LOW
|
|
47
|
+
class: style
|
|
48
|
+
check:
|
|
49
|
+
kind: any_file
|
|
50
|
+
patterns:
|
|
51
|
+
[
|
|
52
|
+
'.prettierrc*',
|
|
53
|
+
'prettier.config.*',
|
|
54
|
+
'biome.json*',
|
|
55
|
+
'.editorconfig',
|
|
56
|
+
'.rustfmt.toml',
|
|
57
|
+
'rustfmt.toml',
|
|
58
|
+
'.clang-format',
|
|
59
|
+
'pyproject.toml',
|
|
60
|
+
'.gofmt*',
|
|
61
|
+
]
|
|
62
|
+
why: 'Formatting diffs hide real changes. Let a tool own it.'
|
|
63
|
+
remediation: 'Prettier/Black/gofmt/rustfmt — pick the default config and stop discussing it.'
|
|
64
|
+
references: ['NIST-SSDF-PW.7']
|
|
65
|
+
|
|
66
|
+
- id: CQ-003
|
|
67
|
+
title: Type checking is enabled and strict
|
|
68
|
+
section: S5
|
|
69
|
+
section_title: Code Quality
|
|
70
|
+
severity: MEDIUM
|
|
71
|
+
class: correctness
|
|
72
|
+
applies_when:
|
|
73
|
+
any:
|
|
74
|
+
- { fact: 'lang:typescript' }
|
|
75
|
+
- { fact: 'lang:python' }
|
|
76
|
+
check:
|
|
77
|
+
kind: grep_present
|
|
78
|
+
pattern: '("strict"\s*:\s*true|strict\s*=\s*true|\[tool\.mypy\]|\[tool\.pyright\]|noImplicitAny|disallow_untyped_defs)'
|
|
79
|
+
include:
|
|
80
|
+
[
|
|
81
|
+
'tsconfig.json',
|
|
82
|
+
'tsconfig.*.json',
|
|
83
|
+
'pyproject.toml',
|
|
84
|
+
'setup.cfg',
|
|
85
|
+
'mypy.ini',
|
|
86
|
+
'.mypy.ini',
|
|
87
|
+
'pyrightconfig.json',
|
|
88
|
+
]
|
|
89
|
+
flags: i
|
|
90
|
+
why: 'Types are the cheapest tests you will ever write. Lenient mode is types with the batteries removed.'
|
|
91
|
+
remediation: 'TypeScript: "strict": true. Python: mypy or pyright with strict settings on new modules.'
|
|
92
|
+
references: ['ISO-5055']
|
|
93
|
+
|
|
94
|
+
- id: CQ-004
|
|
95
|
+
title: Type escapes are not used as a default
|
|
96
|
+
section: S5
|
|
97
|
+
section_title: Code Quality
|
|
98
|
+
severity: LOW
|
|
99
|
+
class: maintainability
|
|
100
|
+
applies_when: { fact: 'lang:typescript' }
|
|
101
|
+
check:
|
|
102
|
+
kind: grep_absent
|
|
103
|
+
pattern: '(@ts-ignore|@ts-expect-error|:\s*any\b|\bas any\b|as unknown as)'
|
|
104
|
+
include: ['**/*.{ts,tsx}']
|
|
105
|
+
exclude: ['**/*.test.*', '**/*.spec.*', '**/*.d.ts']
|
|
106
|
+
why: 'Every `any` is a place the type system was told to stop helping. A few are fine; a pattern is a problem.'
|
|
107
|
+
remediation: 'Type the boundary properly, or narrow with a type guard. Delete stale @ts-ignore comments.'
|
|
108
|
+
references: ['ISO-5055']
|
|
109
|
+
|
|
110
|
+
- id: CQ-005
|
|
111
|
+
title: Errors are handled, not swallowed
|
|
112
|
+
section: S5
|
|
113
|
+
section_title: Code Quality
|
|
114
|
+
severity: HIGH
|
|
115
|
+
class: correctness
|
|
116
|
+
check:
|
|
117
|
+
kind: grep_absent
|
|
118
|
+
pattern: '(catch\s*\(\s*\w*\s*\)\s*\{\s*\}|except\s*:\s*(pass|\.\.\.)\s*$|catch\s*\{\s*\}|rescue\s*\n\s*end)'
|
|
119
|
+
include: ['**/*.{ts,tsx,js,jsx,mjs,cjs,py,go,rs,java,kt,rb,php,cs}']
|
|
120
|
+
exclude: ['**/*.test.*', '**/*.spec.*']
|
|
121
|
+
flags: i
|
|
122
|
+
why: 'An empty catch block converts a failure into a mystery. The bug report you get six weeks later will be useless.'
|
|
123
|
+
remediation: 'Log with context and either handle it or re-throw. If it is genuinely ignorable, say why in a comment.'
|
|
124
|
+
references: ['CWE-390', 'ASVS-7.4.1']
|
|
125
|
+
|
|
126
|
+
- id: CQ-006
|
|
127
|
+
title: There is a central error handler
|
|
128
|
+
section: S5
|
|
129
|
+
section_title: Code Quality
|
|
130
|
+
severity: MEDIUM
|
|
131
|
+
class: correctness
|
|
132
|
+
applies_when:
|
|
133
|
+
any:
|
|
134
|
+
- { fact: 'platform:server' }
|
|
135
|
+
- { fact: 'platform:web' }
|
|
136
|
+
check:
|
|
137
|
+
kind: grep_present
|
|
138
|
+
pattern: '(ErrorBoundary|errorHandler|error_handler|@ControllerAdvice|ExceptionHandler|app\.use\(\s*\(\s*err|onerror|useErrorBoundary|Sentry\.(captureException|init))'
|
|
139
|
+
include: ['**/*.{ts,tsx,js,jsx,py,go,rs,java,kt,rb,php}']
|
|
140
|
+
exclude: ['**/*.test.*', '**/*.spec.*']
|
|
141
|
+
why: 'Without a top-level handler, an unhandled rejection is a white screen or a dropped request.'
|
|
142
|
+
remediation: 'Add a framework-level error handler and an ErrorBoundary (React); report to your error tracker.'
|
|
143
|
+
references: ['ASVS-7.4.1']
|
|
144
|
+
|
|
145
|
+
- id: CQ-007
|
|
146
|
+
title: Logging is structured and levelled
|
|
147
|
+
section: S5
|
|
148
|
+
section_title: Code Quality
|
|
149
|
+
severity: LOW
|
|
150
|
+
class: operations
|
|
151
|
+
applies_when:
|
|
152
|
+
any:
|
|
153
|
+
- { fact: 'platform:server' }
|
|
154
|
+
- { fact: 'project:cli' }
|
|
155
|
+
check:
|
|
156
|
+
kind: manual
|
|
157
|
+
why: >-
|
|
158
|
+
Unstructured logs cannot be queried, alerted on, or correlated. At 3am, grep is not a strategy.
|
|
159
|
+
evidence: 'Sample log output: JSON or key=value, with DEBUG/INFO/WARN/ERROR used consistently.'
|
|
160
|
+
remediation: 'Emit JSON logs with a timestamp, level, message, and structured fields. Never log a bare concatenated string.'
|
|
161
|
+
references: ['OpenTelemetry-Logs', 'ASVS-7.1.1']
|
|
162
|
+
|
|
163
|
+
- id: CQ-008
|
|
164
|
+
title: Requests carry a correlation/trace ID
|
|
165
|
+
section: S5
|
|
166
|
+
section_title: Code Quality
|
|
167
|
+
severity: LOW
|
|
168
|
+
class: operations
|
|
169
|
+
applies_when: { fact: 'platform:server' }
|
|
170
|
+
check:
|
|
171
|
+
kind: grep_present
|
|
172
|
+
pattern: '(correlation[_-]?id|request[_-]?id|trace[_-]?id|x-request-id|traceId|requestId|traceContext)'
|
|
173
|
+
include: ['**/*.{ts,tsx,js,jsx,py,go,rs,java,kt}']
|
|
174
|
+
exclude: ['**/*.test.*', '**/*.spec.*']
|
|
175
|
+
flags: i
|
|
176
|
+
why: 'Without a request ID, debugging a production incident means grepping 40 million lines and praying.'
|
|
177
|
+
remediation: 'Generate an ID per request, put it in every log line and in the response header.'
|
|
178
|
+
references: ['OpenTelemetry', 'W3C-Trace-Context']
|
|
179
|
+
|
|
180
|
+
- id: CQ-009
|
|
181
|
+
title: Debug statements are not left in production paths
|
|
182
|
+
section: S5
|
|
183
|
+
section_title: Code Quality
|
|
184
|
+
severity: LOW
|
|
185
|
+
class: style
|
|
186
|
+
applies_when:
|
|
187
|
+
not: { fact: 'project:cli' }
|
|
188
|
+
check:
|
|
189
|
+
kind: grep_absent
|
|
190
|
+
pattern: '(console\.(log|debug|info)\s*\(|print\s*\(\s*[''"]DEBUG|pdb\.set_trace|debugger\s*;|var_dump\s*\()'
|
|
191
|
+
include: ['**/*.{ts,tsx,js,jsx,mjs,cjs,py,php,rb,go,rs,java,kt}']
|
|
192
|
+
exclude: ['**/*.test.*', '**/*.spec.*', '**/scripts/**', '**/tools/**', '**/debug/**']
|
|
193
|
+
why: 'Stray debug output is noise at best and a data leak at worst.'
|
|
194
|
+
remediation: 'Use the project logger, or delete the line. Add a lint rule to keep it out.'
|
|
195
|
+
references: ['CWE-532']
|
|
196
|
+
|
|
197
|
+
- id: CQ-010
|
|
198
|
+
title: TODO/FIXME debt is tracked, not just annotated
|
|
199
|
+
section: S5
|
|
200
|
+
section_title: Code Quality
|
|
201
|
+
severity: LOW
|
|
202
|
+
class: maintainability
|
|
203
|
+
check:
|
|
204
|
+
kind: grep_wrong
|
|
205
|
+
pattern: '(TODO|FIXME|XXX|HACK|WORKAROUND)\b'
|
|
206
|
+
include: ['**/*.{ts,tsx,js,jsx,py,go,rs,java,kt,rb,php,cs,yml,yaml}']
|
|
207
|
+
exclude: ['**/*.md', '**/docs/**', '**/node_modules/**']
|
|
208
|
+
why: 'A TODO with no owner and no ticket is a lie the codebase tells itself.'
|
|
209
|
+
remediation: 'Convert each one to a tracked issue with a link in the comment, or delete it if it no longer applies.'
|
|
210
|
+
|
|
211
|
+
- id: CQ-011
|
|
212
|
+
title: Functions do one thing
|
|
213
|
+
section: S5
|
|
214
|
+
section_title: Code Quality
|
|
215
|
+
severity: LOW
|
|
216
|
+
class: maintainability
|
|
217
|
+
depths: [deep]
|
|
218
|
+
check: { kind: manual }
|
|
219
|
+
why: >-
|
|
220
|
+
A function that needs "and" to describe is two functions. Long functions hide branches nobody tests.
|
|
221
|
+
evidence: 'Three representative functions over ~60 lines: can each be described in one sentence?'
|
|
222
|
+
remediation: 'Extract until each function reads as a single step at the current level of abstraction.'
|
|
223
|
+
references: ['Clean-Code', 'ISO-5055']
|
|
224
|
+
|
|
225
|
+
- id: CQ-012
|
|
226
|
+
title: Duplicated logic has been consolidated
|
|
227
|
+
section: S5
|
|
228
|
+
section_title: Code Quality
|
|
229
|
+
severity: MEDIUM
|
|
230
|
+
class: maintainability
|
|
231
|
+
depths: [deep]
|
|
232
|
+
check: { kind: manual }
|
|
233
|
+
evidence: 'Output of a duplication checker (jscpd, PMD CPD, sonar) — and the top three duplicates by size.'
|
|
234
|
+
why: 'Every copy is a place a bug fix will not reach.'
|
|
235
|
+
remediation: 'Extract the shared logic; accept duplication only when the two copies are expected to diverge.'
|
|
236
|
+
references: ['ISO-5055', 'Anti-pattern:Shotgun-Surgery']
|
|
237
|
+
|
|
238
|
+
- id: CQ-013
|
|
239
|
+
title: Complexity is measured
|
|
240
|
+
section: S5
|
|
241
|
+
section_title: Code Quality
|
|
242
|
+
severity: FUTURE
|
|
243
|
+
class: maintainability
|
|
244
|
+
depths: [deep]
|
|
245
|
+
check:
|
|
246
|
+
kind: grep_present
|
|
247
|
+
pattern: '(complexity|cyclomatic|radon|gocyclo|cognitive)'
|
|
248
|
+
include:
|
|
249
|
+
[
|
|
250
|
+
'**/.eslintrc*',
|
|
251
|
+
'eslint.config.*',
|
|
252
|
+
'pyproject.toml',
|
|
253
|
+
'.ruff.toml',
|
|
254
|
+
'setup.cfg',
|
|
255
|
+
'.golangci*',
|
|
256
|
+
'sonar-project.properties',
|
|
257
|
+
]
|
|
258
|
+
flags: i
|
|
259
|
+
why: 'You cannot argue about complexity without a number.'
|
|
260
|
+
remediation: 'Enable an eslint complexity rule (≤10) or radon/gocyclo; report rather than hard-fail at first.'
|
|
261
|
+
references: ['ISO-5055']
|