@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,157 @@
1
+ id: stacks/compliance
2
+ title: Accessibility, i18n & Compliance
3
+ section: S13
4
+ section_title: Accessibility, i18n & Compliance
5
+ description: >
6
+ Accessibility, internationalisation, and regulatory obligations. Most of
7
+ these are judgement calls — USAT records what it can and asks for evidence
8
+ on the rest.
9
+ version: '1.0'
10
+ skip_when: { fact: 'maturity:prototype' }
11
+
12
+ rules:
13
+ - id: COMP-001
14
+ title: Interactive elements are keyboard accessible
15
+ section: S13
16
+ section_title: Accessibility, i18n & Compliance
17
+ severity: MEDIUM
18
+ class: compliance
19
+ applies_when:
20
+ any:
21
+ - { fact: 'platform:web' }
22
+ - { fact: 'platform:mobile' }
23
+ check: { kind: manual }
24
+ evidence: 'Tab through the main flow: can you reach and operate everything? Is focus always visible?'
25
+ why: 'Keyboard access is table stakes for screen readers, motor impairments, and power users alike.'
26
+ remediation: 'Use native controls; never remove focus outlines without a replacement; test with keyboard only.'
27
+ references: ['WCAG-2.1.1', 'WCAG-2.4.7', 'EN-301-549']
28
+
29
+ - id: COMP-002
30
+ title: Form inputs have associated labels
31
+ section: S13
32
+ section_title: Accessibility, i18n & Compliance
33
+ severity: MEDIUM
34
+ class: compliance
35
+ applies_when: { fact: 'platform:web' }
36
+ check: { kind: manual }
37
+ evidence: 'Every input has a <label for>, aria-label, or aria-labelledby — and placeholder alone does not count.'
38
+ why: 'Unlabelled inputs are unusable with a screen reader and are the most common WCAG failure.'
39
+ remediation: 'Associate label elements; add eslint-plugin-jsx-a11y to catch regressions.'
40
+ references: ['WCAG-3.3.2', 'WCAG-1.3.1']
41
+
42
+ - id: COMP-003
43
+ title: Colour contrast meets WCAG AA
44
+ section: S13
45
+ section_title: Accessibility, i18n & Compliance
46
+ severity: MEDIUM
47
+ class: compliance
48
+ applies_when: { fact: 'platform:web' }
49
+ check: { kind: manual }
50
+ evidence: 'Contrast ratio for body text (≥4.5:1) and large text (≥3:1), including placeholder and disabled states.'
51
+ why: 'Low contrast is the most common accessibility defect and the easiest to fix.'
52
+ remediation: 'Run axe/Lighthouse; fix the reported contrast failures; check dark mode too.'
53
+ references: ['WCAG-1.4.3']
54
+
55
+ - id: COMP-004
56
+ title: Content is not hardcoded to one locale
57
+ section: S13
58
+ section_title: Accessibility, i18n & Compliance
59
+ severity: MEDIUM
60
+ class: compliance
61
+ applies_when:
62
+ any:
63
+ - { fact: 'platform:web' }
64
+ - { fact: 'platform:mobile' }
65
+ check:
66
+ kind: grep_present
67
+ pattern: '(i18n|i18next|react-intl|vue-i18n|lingui|next-intl|Localizable|NSLocalizedString|intl\.|translations?/)'
68
+ include: ['**/*.{ts,tsx,js,jsx,vue,svelte,swift,kt,java,json,yaml,yml}']
69
+ exclude: ['**/node_modules/**', '**/Pods/**']
70
+ flags: i
71
+ why: 'Retrofitting i18n after launch means touching every string in the codebase.'
72
+ remediation: 'Extract user-facing strings now, even if you ship one language.'
73
+ references: ['WCAG-3.1.1', 'i18n-Best-Practices']
74
+
75
+ - id: COMP-005
76
+ title: Dates, numbers, and currencies are locale-aware
77
+ section: S13
78
+ section_title: Accessibility, i18n & Compliance
79
+ severity: LOW
80
+ class: compliance
81
+ applies_when: { fact: 'practice:i18n' }
82
+ check: { kind: manual }
83
+ evidence: 'Formatting via Intl/ICU rather than string concatenation or a hardcoded MM/DD/YYYY.'
84
+ why: '13/01/2026 is a valid date in most of the world and a bug report in the US.'
85
+ remediation: 'Use Intl.DateTimeFormat / Intl.NumberFormat everywhere; store timestamps in UTC.'
86
+ references: ['WCAG-3.1.2', 'ICU']
87
+
88
+ - id: COMP-006
89
+ title: A privacy policy and terms exist and match reality
90
+ section: S13
91
+ section_title: Accessibility, i18n & Compliance
92
+ severity: MEDIUM
93
+ class: compliance
94
+ applies_when:
95
+ any:
96
+ - { fact: 'has:pii' }
97
+ - { fact: 'maturity:production' }
98
+ check: { kind: manual }
99
+ evidence: 'Links to the live policy, and a check that it names the data you actually collect and the processors you actually use.'
100
+ why: 'A policy that does not match the code is worse than no policy: it is a documented misrepresentation.'
101
+ remediation: 'Generate the data inventory from your code, then write the policy to match it.'
102
+ references: ['GDPR-Art.13', 'CCPA']
103
+
104
+ - id: COMP-007
105
+ title: Data subject rights are implemented
106
+ section: S13
107
+ section_title: Accessibility, i18n & Compliance
108
+ severity: HIGH
109
+ class: compliance
110
+ applies_when: { fact: 'has:pii' }
111
+ check: { kind: manual }
112
+ evidence: 'Working flows for access (export), erasure, and correction — and the response time SLA.'
113
+ why: 'GDPR/CCPA give you 30 days. Without a built flow, each request is an ad-hoc database operation.'
114
+ remediation: 'Build export and delete endpoints behind authentication; log the request and the completion.'
115
+ references: ['GDPR-Art.15', 'GDPR-Art.17', 'CCPA']
116
+
117
+ - id: COMP-008
118
+ title: Card data is kept out of scope
119
+ section: S13
120
+ section_title: Accessibility, i18n & Compliance
121
+ severity: HIGH
122
+ class: compliance
123
+ applies_when: { fact: 'has:payments' }
124
+ check: { kind: manual }
125
+ evidence: 'Whether PAN/CVV ever touches your servers, or whether you use a hosted field / tokenisation (SAQ A/A-EP).'
126
+ why: 'Storing card data drags your entire infrastructure into PCI-DSS scope. Tokenisation removes it.'
127
+ remediation: 'Use provider-hosted fields or a tokenisation proxy; never log or store full card numbers.'
128
+ references: ['PCI-DSS-4.0', 'ASVS-8.3.1']
129
+
130
+ - id: COMP-009
131
+ title: Cookie / consent notice is compliant
132
+ section: S13
133
+ section_title: Accessibility, i18n & Compliance
134
+ severity: MEDIUM
135
+ class: compliance
136
+ applies_when: { fact: 'platform:web' }
137
+ check: { kind: manual }
138
+ evidence: 'Consent is opt-in for non-essential cookies, is as easy to refuse as to accept, and is recorded.'
139
+ why: '"By using this site you agree" has not been valid consent in the EU since 2018.'
140
+ remediation: 'Add a consent manager that defaults to rejected and stores the choice per user.'
141
+ references: ['ePrivacy', 'GDPR-Art.7']
142
+
143
+ - id: COMP-010
144
+ title: Source files carry an SPDX license identifier
145
+ section: S13
146
+ section_title: Accessibility, i18n & Compliance
147
+ severity: LOW
148
+ class: compliance
149
+ applies_when: { fact: 'project:library' }
150
+ check:
151
+ kind: grep_present
152
+ pattern: '(SPDX-License-Identifier|@license)'
153
+ include: ['**/*.{ts,js,py,go,rs,java,kt,c,h,css}']
154
+ exclude: ['**/node_modules/**']
155
+ why: 'Machine-readable licensing is what lets consumers automate compliance — and it costs one line per file.'
156
+ remediation: 'Add `// SPDX-License-Identifier: MIT` headers; enforce with a lint rule (REUSE).'
157
+ references: ['SPDX', 'REUSE-Spec']
@@ -0,0 +1,124 @@
1
+ id: stacks/containers
2
+ title: Containers & Runtime
3
+ section: S15
4
+ section_title: Platform-Specific
5
+ description: 'Activated when Docker, compose, or Kubernetes manifests are detected.'
6
+ version: '1.0'
7
+ skip_when: { fact: 'has:containers', op: 'absent' }
8
+
9
+ rules:
10
+ - id: CTNR-001
11
+ title: Container does not run as root
12
+ section: S15
13
+ section_title: Platform-Specific
14
+ severity: HIGH
15
+ class: security
16
+ check:
17
+ kind: grep_present
18
+ pattern: '(USER\s+(?!root\b)\w+|--chown=|runAsNonRoot\s*:\s*true|securityContext)'
19
+ include: ['Dockerfile*', '**/Dockerfile*', '**/*.{yaml,yml}', 'docker-compose*.yml']
20
+ exclude: ['**/node_modules/**']
21
+ why: 'A root process inside the container is one kernel exploit away from root on the host.'
22
+ remediation: 'Add `USER node` (or a dedicated non-root uid) and `securityContext.runAsNonRoot: true` in Kubernetes.'
23
+ references: ['CWE-250', 'CIS-Docker-4.1', 'ASVS-14.1.3']
24
+
25
+ - id: CTNR-002
26
+ title: Multi-stage builds are used
27
+ section: S15
28
+ section_title: Platform-Specific
29
+ severity: MEDIUM
30
+ class: performance
31
+ check:
32
+ kind: grep_present
33
+ pattern: 'FROM\s+\S+\s+AS\s+\w+'
34
+ include: ['Dockerfile*', '**/Dockerfile*']
35
+ flags: i
36
+ why: 'Without multi-stage, your production image ships the compiler, the test suite, and your .git directory.'
37
+ remediation: 'Build in one stage, copy only the artifact into a slim runtime stage.'
38
+ references: ['Docker-Multi-Stage']
39
+
40
+ - id: CTNR-003
41
+ title: Base image is minimal and pinned
42
+ section: S15
43
+ section_title: Platform-Specific
44
+ severity: MEDIUM
45
+ class: supply-chain
46
+ check:
47
+ kind: grep_absent
48
+ pattern: 'FROM\s+\S+:latest\s*$|FROM\s+\w+\s*$'
49
+ include: ['Dockerfile*', '**/Dockerfile*']
50
+ flags: i
51
+ why: ':latest means the image changes under you with no commit, and full distro images carry hundreds of CVEs.'
52
+ remediation: 'Pin a digest or an exact tag; prefer alpine, distroless, or chainguard images.'
53
+ references: ['OpenSSF-Scorecard:Pinned-Dependencies', 'CIS-Docker-4.2']
54
+
55
+ - id: CTNR-004
56
+ title: A .dockerignore exists
57
+ section: S15
58
+ section_title: Platform-Specific
59
+ severity: MEDIUM
60
+ class: supply-chain
61
+ check:
62
+ kind: any_file
63
+ patterns: ['.dockerignore']
64
+ why: 'Without it, the build context copies node_modules, .git, and — frequently — .env into the build.'
65
+ remediation: 'Add .dockerignore excluding .git, node_modules, .env, secrets, and local build output.'
66
+ references: ['CIS-Docker']
67
+
68
+ - id: CTNR-005
69
+ title: No secrets baked into the image
70
+ section: S15
71
+ section_title: Platform-Specific
72
+ severity: CRITICAL
73
+ class: security
74
+ check:
75
+ kind: grep_absent
76
+ pattern: '(ARG\s+(SECRET|TOKEN|PASSWORD|KEY)|ENV\s+(SECRET|TOKEN|PASSWORD|API_KEY|AWS_SECRET)\s*=|COPY\s+\.env)'
77
+ include: ['Dockerfile*', '**/Dockerfile*', 'docker-compose*.yml', 'compose.yaml']
78
+ flags: i
79
+ why: 'ENV and ARG values are visible in `docker history` to anyone who pulls the image.'
80
+ remediation: 'Inject secrets at runtime (env from the orchestrator, mounted files, or a secret manager).'
81
+ references: ['CWE-798', 'CIS-Docker-4.10']
82
+
83
+ - id: CTNR-006
84
+ title: HEALTHCHECK is defined
85
+ section: S15
86
+ section_title: Platform-Specific
87
+ severity: MEDIUM
88
+ class: operations
89
+ check:
90
+ kind: grep_present
91
+ pattern: '(HEALTHCHECK|livenessProbe|readinessProbe)'
92
+ include: ['Dockerfile*', '**/Dockerfile*', 'docker-compose*.yml', '**/*.{yaml,yml}']
93
+ why: 'The orchestrator cannot restart a container it does not know is sick.'
94
+ remediation: 'Add HEALTHCHECK (Docker) or liveness/readiness probes (Kubernetes) hitting your /health endpoint.'
95
+ references: ['K8s-Probes', 'CIS-Docker-4.6']
96
+
97
+ - id: CTNR-007
98
+ title: Kubernetes workloads declare resource limits
99
+ section: S15
100
+ section_title: Platform-Specific
101
+ severity: MEDIUM
102
+ class: performance
103
+ applies_when: { fact: 'has:kubernetes' }
104
+ check:
105
+ kind: grep_present
106
+ pattern: 'resources\s*:\s*(\s*$|[\s\S]{0,80}(limits|requests)\s*:)'
107
+ include: ['**/*.{yaml,yml}']
108
+ exclude: ['**/node_modules/**']
109
+ why: 'One pod with no limits can starve every other pod on the node.'
110
+ remediation: 'Set requests and limits on CPU and memory for every container; add a LimitRange as a backstop.'
111
+ references: ['K8s-Resource-Management', 'CIS-K8s']
112
+
113
+ - id: CTNR-008
114
+ title: Images are scanned and signed
115
+ section: S15
116
+ section_title: Platform-Specific
117
+ severity: MEDIUM
118
+ class: supply-chain
119
+ applies_when: { fact: 'maturity:production' }
120
+ check: { kind: manual }
121
+ evidence: 'Trivy/Grype in the pipeline and cosign signatures verified at admission (or a documented plan to add both).'
122
+ why: 'Unsigned images mean anything that can push to the registry can run in your cluster.'
123
+ remediation: 'Scan on push, sign with cosign, and enforce verification with a policy controller.'
124
+ references: ['SLSA-Build-L2', 'Sigstore-Cosign']
@@ -0,0 +1,165 @@
1
+ id: stacks/data
2
+ title: Data & Database
3
+ section: S6
4
+ section_title: Data & Database
5
+ description: 'Activated when a database, ORM, or migrations directory is detected.'
6
+ version: '1.0'
7
+ skip_when: { fact: 'has:database', op: 'absent' }
8
+
9
+ rules:
10
+ - id: DATA-001
11
+ title: A migration system is in place
12
+ section: S6
13
+ section_title: Data & Database
14
+ severity: HIGH
15
+ class: correctness
16
+ check:
17
+ kind: any_file
18
+ patterns:
19
+ [
20
+ 'migrations/**',
21
+ 'prisma/migrations/**',
22
+ 'alembic/**',
23
+ 'db/migrate/**',
24
+ '**/migrations/*.sql',
25
+ 'knexfile.*',
26
+ 'flyway/**',
27
+ 'liquibase/**',
28
+ 'atlas.hcl',
29
+ 'drizzle/**',
30
+ ]
31
+ why: 'Schema changes applied by hand cannot be reviewed, repeated, or rolled back.'
32
+ remediation: 'Adopt the migration tool for your ORM; never alter production schema manually.'
33
+ references: ['NIST-SSDF-PO.5']
34
+
35
+ - id: DATA-002
36
+ title: Foreign keys / relations are defined
37
+ section: S6
38
+ section_title: Data & Database
39
+ severity: MEDIUM
40
+ class: correctness
41
+ check:
42
+ kind: grep_present
43
+ pattern: '(references\s*\(|FOREIGN KEY|foreignKey|@ManyToOne|@Relation|belongs_to|has_many|ForeignKey\(|onDelete)'
44
+ include: ['**/*.{sql,prisma,py,ts,js,go,rs,java,kt,rb,php}']
45
+ exclude: ['**/node_modules/**', '**/tests/**']
46
+ flags: i
47
+ why: 'Relations enforced only in application code are relations that drift out of sync the first time someone writes a script.'
48
+ remediation: 'Declare constraints at the DB level; add a periodic orphan-row check if you cannot.'
49
+ references: ['ASVS-5.3.4']
50
+
51
+ - id: DATA-003
52
+ title: Indexes exist on queried columns
53
+ section: S6
54
+ section_title: Data & Database
55
+ severity: MEDIUM
56
+ class: performance
57
+ check: { kind: manual }
58
+ evidence: 'The five heaviest queries and their EXPLAIN plans. Any sequential scan on a large table needs a justification.'
59
+ why: 'Most "the database is slow" incidents are one missing index on one column.'
60
+ remediation: 'Index every foreign key and every column in a WHERE/ORDER BY on a large table; verify with EXPLAIN.'
61
+ references: ['Use-The-Index-Luke']
62
+
63
+ - id: DATA-004
64
+ title: No N+1 query patterns
65
+ section: S6
66
+ section_title: Data & Database
67
+ severity: HIGH
68
+ class: performance
69
+ check: { kind: manual }
70
+ evidence: 'Query logs for the main list endpoints: is there one query per row, or one query with a join/include?'
71
+ why: 'An endpoint that works with 10 rows falls over at 10,000 — usually in production, rarely in staging.'
72
+ remediation: 'Eager-load relations (include/join/prefetch_related); add query counting assertions to tests.'
73
+ references: ['ORM-N+1']
74
+
75
+ - id: DATA-005
76
+ title: List queries are paginated
77
+ section: S6
78
+ section_title: Data & Database
79
+ severity: HIGH
80
+ class: performance
81
+ check:
82
+ kind: grep_present
83
+ pattern: '(\.limit\s*\(|take\s*:\s*|LIMIT\s+\d|first\s*:\s*|pageSize|per_page|cursor\s*:\s*|page_size|Top\s+\d+)'
84
+ include: ['**/*.{ts,tsx,js,py,go,rs,java,kt,rb,php,graphql,sql}']
85
+ exclude: ['**/node_modules/**', '**/migrations/**', '**/*.test.*']
86
+ flags: i
87
+ why: 'An unbounded SELECT is a denial-of-service you wrote yourself, usually accidentally.'
88
+ remediation: 'Enforce a default and maximum page size on every list endpoint; prefer cursor pagination.'
89
+ references: ['CWE-770', 'ASVS-4.3.2']
90
+
91
+ - id: DATA-006
92
+ title: Connection pooling is configured
93
+ section: S6
94
+ section_title: Data & Database
95
+ severity: MEDIUM
96
+ class: performance
97
+ check:
98
+ kind: grep_present
99
+ pattern: '(pool|pool_size|max_?connections|connection_limit|MaximumPoolSize|poolSize|HikariCP|db_pool)'
100
+ include: ['**/*.{ts,js,py,go,rs,java,kt,rb,php,yml,yaml,toml,env,example,prisma}']
101
+ exclude: ['**/node_modules/**']
102
+ flags: i
103
+ why: 'Without a pool, every request opens a connection and the database runs out at the worst possible moment.'
104
+ remediation: 'Configure a pool sized to (cores × 2) + spindle count as a starting point; add a pgbouncer if needed.'
105
+ references: ['Postgres-Pooling']
106
+
107
+ - id: DATA-007
108
+ title: Multi-step writes are transactional
109
+ section: S6
110
+ section_title: Data & Database
111
+ severity: HIGH
112
+ class: correctness
113
+ check:
114
+ kind: grep_present
115
+ pattern: '(\$transaction|beginTransaction|BEGIN\s*;|withTransaction|atomic\s*\(|session\.with_transaction|db\.transaction)'
116
+ include: ['**/*.{ts,js,py,go,rs,java,kt,rb,php}']
117
+ exclude: ['**/node_modules/**', '**/migrations/**', '**/*.test.*']
118
+ flags: i
119
+ why: 'A half-applied write is worse than a failed one: the data looks fine until someone reconciles it.'
120
+ remediation: 'Wrap related writes in a transaction; make the operation idempotent so retries are safe.'
121
+ references: ['ASVS-5.3.4']
122
+
123
+ - id: DATA-008
124
+ title: No SELECT * in hot paths
125
+ section: S6
126
+ section_title: Data & Database
127
+ severity: LOW
128
+ class: performance
129
+ check:
130
+ kind: grep_wrong
131
+ pattern: 'SELECT\s+\*\s+FROM'
132
+ include: ['**/*.{sql,ts,js,py,go,rs,java,kt,rb,php}']
133
+ exclude: ['**/node_modules/**', '**/migrations/**', '**/tests/**']
134
+ why: 'SELECT * breaks when the schema changes, and pulls columns nobody needs across the network.'
135
+ remediation: 'Select the columns you use.'
136
+ references: ['Use-The-Index-Luke']
137
+
138
+ - id: DATA-009
139
+ title: Delete strategy is defined
140
+ section: S6
141
+ section_title: Data & Database
142
+ severity: LOW
143
+ class: compliance
144
+ check: { kind: manual }
145
+ evidence: 'Whether records are hard-deleted or soft-deleted, and how that interacts with retention and erasure requests.'
146
+ why: '"Delete the account" needs a defined answer before a regulator or a customer asks for it.'
147
+ remediation: 'Pick one per entity type; document it; make sure erasure requests honour the choice everywhere.'
148
+ references: ['GDPR-Art.17']
149
+
150
+ - id: DATA-010
151
+ title: Row-level access is enforced in the data layer
152
+ section: S6
153
+ section_title: Data & Database
154
+ severity: HIGH
155
+ class: security
156
+ applies_when:
157
+ any:
158
+ - { fact: 'db:postgres' }
159
+ - { fact: 'orm:prisma' }
160
+ - { fact: 'orm:sqlalchemy' }
161
+ check: { kind: manual }
162
+ evidence: 'Whether tenant/user scoping is applied by the query layer itself (RLS, a scoped repository) or by remembering to add a WHERE clause.'
163
+ why: 'Scoping that depends on remembering is scoping that will be forgotten exactly once, catastrophically.'
164
+ remediation: 'Use Postgres RLS or a repository pattern that requires the tenant scope; test cross-tenant reads explicitly.'
165
+ references: ['ASVS-4.2.1', 'OWASP-A01:2021']
@@ -0,0 +1,134 @@
1
+ id: stacks/go
2
+ title: Go
3
+ section: S15
4
+ section_title: Platform-Specific
5
+ description: 'Activated when Go is detected.'
6
+ version: '1.0'
7
+ skip_when: { fact: 'lang:go', op: 'absent' }
8
+
9
+ rules:
10
+ - id: GO-001
11
+ title: go.mod declares the Go version
12
+ section: S15
13
+ section_title: Platform-Specific
14
+ severity: MEDIUM
15
+ class: operations
16
+ check:
17
+ kind: grep_present
18
+ pattern: '^go\s+\d+\.\d+'
19
+ include: ['go.mod']
20
+ why: 'Without it the toolchain guesses, and the guess changes when the runner image updates.'
21
+ remediation: 'Add `go 1.22` to go.mod and pin the same version in CI.'
22
+ references: ['Go-Modules']
23
+
24
+ - id: GO-002
25
+ title: Errors are checked, not discarded
26
+ section: S15
27
+ section_title: Platform-Specific
28
+ severity: HIGH
29
+ class: correctness
30
+ check: { kind: manual }
31
+ evidence: 'errcheck or `go vet` output; grep for `_ =` assignments and bare `_` error receivers.'
32
+ why: 'A discarded error is a failure that reports success. This is the canonical Go bug.'
33
+ remediation: 'Run errcheck in CI; handle every returned error or explicitly document why it is ignored.'
34
+ references: ['Go-Proverbs', 'CWE-390']
35
+
36
+ - id: GO-003
37
+ title: Contexts are propagated
38
+ section: S15
39
+ section_title: Platform-Specific
40
+ severity: MEDIUM
41
+ class: correctness
42
+ check:
43
+ kind: grep_present
44
+ pattern: '(ctx\s+context\.Context|context\.WithTimeout|context\.WithCancel|context\.Background)'
45
+ include: ['**/*.go']
46
+ exclude: ['**/*_test.go']
47
+ why: 'Without context there is no cancellation, so a client disconnect leaves goroutines and DB connections hanging.'
48
+ remediation: 'Thread context.Context through every blocking call; derive timeouts at the boundary.'
49
+ references: ['Go-Context']
50
+
51
+ - id: GO-004
52
+ title: No panics in request paths
53
+ section: S15
54
+ section_title: Platform-Specific
55
+ severity: MEDIUM
56
+ class: correctness
57
+ check:
58
+ kind: grep_absent
59
+ pattern: '\bpanic\s*\('
60
+ include: ['**/*.go']
61
+ exclude: ['**/*_test.go', '**/cmd/**', 'main.go']
62
+ why: 'A panic in a handler takes down the whole process, not just the request.'
63
+ remediation: 'Return errors. Recover in middleware that logs and returns 500.'
64
+ references: ['Go-Error-Handling']
65
+
66
+ - id: GO-005
67
+ title: go vet / staticcheck / golangci-lint runs in CI
68
+ section: S15
69
+ section_title: Platform-Specific
70
+ severity: MEDIUM
71
+ class: maintainability
72
+ applies_when: { fact: 'has:ci' }
73
+ check:
74
+ kind: grep_present
75
+ pattern: '(go vet|golangci-lint|staticcheck|gofmt -l)'
76
+ include:
77
+ [
78
+ '.github/workflows/*.yml',
79
+ '.github/workflows/*.yaml',
80
+ '.gitlab-ci.yml',
81
+ 'Makefile',
82
+ 'justfile',
83
+ 'Taskfile.yml',
84
+ '.pre-commit-config.yaml',
85
+ ]
86
+ flags: i
87
+ why: 'go vet finds the bug class that compiles cleanly and fails at runtime.'
88
+ remediation: 'Add `go vet ./...` and golangci-lint to CI; start with the default linters.'
89
+ references: ['Go-Vet']
90
+
91
+ - id: GO-006
92
+ title: Goroutine lifecycle is bounded
93
+ section: S15
94
+ section_title: Platform-Specific
95
+ severity: MEDIUM
96
+ class: correctness
97
+ check: { kind: manual }
98
+ evidence: 'For every `go func`: what stops it? Is there a WaitGroup, a context, or an unbuffered channel that can block forever?'
99
+ why: 'Goroutine leaks are invisible in dev and fatal after three weeks in production.'
100
+ remediation: 'Tie every goroutine to a context; add a leak detector (goleak) to the test suite.'
101
+ references: ['Go-Concurrency']
102
+
103
+ - id: GO-007
104
+ title: SQL is parameterised
105
+ section: S15
106
+ section_title: Platform-Specific
107
+ severity: CRITICAL
108
+ class: security
109
+ applies_when: { fact: 'has:database' }
110
+ check:
111
+ kind: grep_absent
112
+ pattern: '(fmt\.Sprintf\(\s*[''"](SELECT|INSERT|UPDATE|DELETE)|db\.Query\(\s*[''"][^''"]*[''"]\s*\+|Exec\(\s*.*\+)'
113
+ include: ['**/*.go']
114
+ exclude: ['**/*_test.go']
115
+ flags: i
116
+ why: 'Sprintf-built SQL is textbook injection.'
117
+ remediation: 'Use placeholders: db.QueryContext(ctx, "SELECT ... WHERE id = $1", id).'
118
+ references: ['CWE-89', 'ASVS-5.3.4']
119
+
120
+ - id: GO-008
121
+ title: HTTP servers set timeouts
122
+ section: S15
123
+ section_title: Platform-Specific
124
+ severity: MEDIUM
125
+ class: performance
126
+ applies_when: { fact: 'platform:server' }
127
+ check:
128
+ kind: grep_present
129
+ pattern: '(ReadHeaderTimeout|ReadTimeout|WriteTimeout|IdleTimeout)'
130
+ include: ['**/*.go']
131
+ exclude: ['**/*_test.go']
132
+ why: 'The default net/http server has no timeouts, which is Slowloris as a service.'
133
+ remediation: 'Set ReadHeaderTimeout, ReadTimeout, WriteTimeout, and IdleTimeout on http.Server.'
134
+ references: ['CWE-400', 'Go-http-Server']