@xenos1996/usa 2.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 (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +335 -0
  3. package/USA.md +597 -0
  4. package/dist/bootstrap/catalog.d.ts +46 -0
  5. package/dist/bootstrap/catalog.d.ts.map +1 -0
  6. package/dist/bootstrap/catalog.js +312 -0
  7. package/dist/bootstrap/catalog.js.map +1 -0
  8. package/dist/bootstrap/index.d.ts +36 -0
  9. package/dist/bootstrap/index.d.ts.map +1 -0
  10. package/dist/bootstrap/index.js +177 -0
  11. package/dist/bootstrap/index.js.map +1 -0
  12. package/dist/cli.d.ts +5 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +706 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/config.d.ts +7 -0
  17. package/dist/config.d.ts.map +1 -0
  18. package/dist/config.js +88 -0
  19. package/dist/config.js.map +1 -0
  20. package/dist/detect/index.d.ts +58 -0
  21. package/dist/detect/index.d.ts.map +1 -0
  22. package/dist/detect/index.js +329 -0
  23. package/dist/detect/index.js.map +1 -0
  24. package/dist/engine/audit.d.ts +32 -0
  25. package/dist/engine/audit.d.ts.map +1 -0
  26. package/dist/engine/audit.js +366 -0
  27. package/dist/engine/audit.js.map +1 -0
  28. package/dist/engine/diff.d.ts +24 -0
  29. package/dist/engine/diff.d.ts.map +1 -0
  30. package/dist/engine/diff.js +136 -0
  31. package/dist/engine/diff.js.map +1 -0
  32. package/dist/engine/evaluate.d.ts +19 -0
  33. package/dist/engine/evaluate.d.ts.map +1 -0
  34. package/dist/engine/evaluate.js +377 -0
  35. package/dist/engine/evaluate.js.map +1 -0
  36. package/dist/engine/gate.d.ts +19 -0
  37. package/dist/engine/gate.d.ts.map +1 -0
  38. package/dist/engine/gate.js +47 -0
  39. package/dist/engine/gate.js.map +1 -0
  40. package/dist/engine/loader.d.ts +37 -0
  41. package/dist/engine/loader.d.ts.map +1 -0
  42. package/dist/engine/loader.js +388 -0
  43. package/dist/engine/loader.js.map +1 -0
  44. package/dist/engine/maturity.d.ts +20 -0
  45. package/dist/engine/maturity.d.ts.map +1 -0
  46. package/dist/engine/maturity.js +157 -0
  47. package/dist/engine/maturity.js.map +1 -0
  48. package/dist/engine/score.d.ts +19 -0
  49. package/dist/engine/score.d.ts.map +1 -0
  50. package/dist/engine/score.js +141 -0
  51. package/dist/engine/score.js.map +1 -0
  52. package/dist/engine/sections.d.ts +14 -0
  53. package/dist/engine/sections.d.ts.map +1 -0
  54. package/dist/engine/sections.js +50 -0
  55. package/dist/engine/sections.js.map +1 -0
  56. package/dist/evolution/benchmark.d.ts +12 -0
  57. package/dist/evolution/benchmark.d.ts.map +1 -0
  58. package/dist/evolution/benchmark.js +107 -0
  59. package/dist/evolution/benchmark.js.map +1 -0
  60. package/dist/evolution/capability.d.ts +22 -0
  61. package/dist/evolution/capability.d.ts.map +1 -0
  62. package/dist/evolution/capability.js +69 -0
  63. package/dist/evolution/capability.js.map +1 -0
  64. package/dist/evolution/coverage.d.ts +15 -0
  65. package/dist/evolution/coverage.d.ts.map +1 -0
  66. package/dist/evolution/coverage.js +65 -0
  67. package/dist/evolution/coverage.js.map +1 -0
  68. package/dist/evolution/gap.d.ts +10 -0
  69. package/dist/evolution/gap.d.ts.map +1 -0
  70. package/dist/evolution/gap.js +52 -0
  71. package/dist/evolution/gap.js.map +1 -0
  72. package/dist/evolution/propose.d.ts +50 -0
  73. package/dist/evolution/propose.d.ts.map +1 -0
  74. package/dist/evolution/propose.js +188 -0
  75. package/dist/evolution/propose.js.map +1 -0
  76. package/dist/evolution/queue.d.ts +64 -0
  77. package/dist/evolution/queue.d.ts.map +1 -0
  78. package/dist/evolution/queue.js +192 -0
  79. package/dist/evolution/queue.js.map +1 -0
  80. package/dist/evolution/release.d.ts +11 -0
  81. package/dist/evolution/release.d.ts.map +1 -0
  82. package/dist/evolution/release.js +55 -0
  83. package/dist/evolution/release.js.map +1 -0
  84. package/dist/evolution/run.d.ts +88 -0
  85. package/dist/evolution/run.d.ts.map +1 -0
  86. package/dist/evolution/run.js +258 -0
  87. package/dist/evolution/run.js.map +1 -0
  88. package/dist/evolution/schedule.d.ts +59 -0
  89. package/dist/evolution/schedule.d.ts.map +1 -0
  90. package/dist/evolution/schedule.js +77 -0
  91. package/dist/evolution/schedule.js.map +1 -0
  92. package/dist/evolution/types.d.ts +174 -0
  93. package/dist/evolution/types.d.ts.map +1 -0
  94. package/dist/evolution/types.js +8 -0
  95. package/dist/evolution/types.js.map +1 -0
  96. package/dist/index.d.ts +36 -0
  97. package/dist/index.d.ts.map +1 -0
  98. package/dist/index.js +30 -0
  99. package/dist/index.js.map +1 -0
  100. package/dist/learn/index.d.ts +27 -0
  101. package/dist/learn/index.d.ts.map +1 -0
  102. package/dist/learn/index.js +196 -0
  103. package/dist/learn/index.js.map +1 -0
  104. package/dist/report/markdown.d.ts +16 -0
  105. package/dist/report/markdown.d.ts.map +1 -0
  106. package/dist/report/markdown.js +488 -0
  107. package/dist/report/markdown.js.map +1 -0
  108. package/dist/snapshot/index.d.ts +55 -0
  109. package/dist/snapshot/index.d.ts.map +1 -0
  110. package/dist/snapshot/index.js +76 -0
  111. package/dist/snapshot/index.js.map +1 -0
  112. package/dist/store/index.d.ts +31 -0
  113. package/dist/store/index.d.ts.map +1 -0
  114. package/dist/store/index.js +69 -0
  115. package/dist/store/index.js.map +1 -0
  116. package/dist/types.d.ts +287 -0
  117. package/dist/types.d.ts.map +1 -0
  118. package/dist/types.js +16 -0
  119. package/dist/types.js.map +1 -0
  120. package/dist/util/glob.d.ts +11 -0
  121. package/dist/util/glob.d.ts.map +1 -0
  122. package/dist/util/glob.js +86 -0
  123. package/dist/util/glob.js.map +1 -0
  124. package/dist/util/project.d.ts +90 -0
  125. package/dist/util/project.d.ts.map +1 -0
  126. package/dist/util/project.js +636 -0
  127. package/dist/util/project.js.map +1 -0
  128. package/dist/util/yaml.d.ts +28 -0
  129. package/dist/util/yaml.d.ts.map +1 -0
  130. package/dist/util/yaml.js +48 -0
  131. package/dist/util/yaml.js.map +1 -0
  132. package/package.json +94 -0
  133. package/rules/core/architecture.yaml +190 -0
  134. package/rules/core/cicd.yaml +212 -0
  135. package/rules/core/code-quality.yaml +261 -0
  136. package/rules/core/dependencies.yaml +132 -0
  137. package/rules/core/documentation.yaml +119 -0
  138. package/rules/core/future-readiness.yaml +91 -0
  139. package/rules/core/release.yaml +131 -0
  140. package/rules/core/repo.yaml +313 -0
  141. package/rules/core/security.yaml +496 -0
  142. package/rules/core/supply-chain.yaml +357 -0
  143. package/rules/core/testing.yaml +266 -0
  144. package/rules/detectors.yaml +2028 -0
  145. package/rules/index.yaml +42 -0
  146. package/rules/profiles/maturity.yaml +135 -0
  147. package/rules/stacks/ai-era.yaml +193 -0
  148. package/rules/stacks/api-backend.yaml +121 -0
  149. package/rules/stacks/cli.yaml +105 -0
  150. package/rules/stacks/compliance.yaml +157 -0
  151. package/rules/stacks/containers.yaml +124 -0
  152. package/rules/stacks/data.yaml +165 -0
  153. package/rules/stacks/go.yaml +134 -0
  154. package/rules/stacks/iac.yaml +128 -0
  155. package/rules/stacks/jvm.yaml +150 -0
  156. package/rules/stacks/ml-ai.yaml +140 -0
  157. package/rules/stacks/mobile.yaml +121 -0
  158. package/rules/stacks/node-typescript.yaml +166 -0
  159. package/rules/stacks/python.yaml +175 -0
  160. package/rules/stacks/rust.yaml +129 -0
  161. package/rules/stacks/solidity.yaml +139 -0
  162. package/rules/stacks/swift.yaml +100 -0
  163. package/rules/stacks/web-frontend.yaml +159 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../../src/util/yaml.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,sFAAsF;AACtF,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE9C,wBAAgB,KAAK,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,OAAO,CAE9C;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAEzC;AAED,4BAA4B;AAC5B,wBAAgB,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAElD;AAED,4BAA4B;AAC5B,wBAAgB,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAElD;AAED,6BAA6B;AAC7B,wBAAgB,IAAI,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CAEpD;AAED,4BAA4B;AAC5B,wBAAgB,IAAI,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,EAAE,GAAG,SAAS,CAEtD;AAED,wDAAwD;AACxD,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,SAAS,CAExD;AAED,4DAA4D;AAC5D,wBAAgB,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAI/E;AAED,2CAA2C;AAC3C,wBAAgB,IAAI,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,EAAE,CAE1C"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Shape helpers for decoded YAML.
3
+ *
4
+ * `yaml.parse` hands back plain JS objects, which is exactly the *wrong* thing
5
+ * to sprinkle through the engine: every downstream field access would be `any`
6
+ * and a typo in a rule pack would surface as `undefined` three modules later.
7
+ * These helpers force the parse boundary to state up front what shape it
8
+ * expects, and make the "absent" case explicit rather than implicit.
9
+ */
10
+ export function isMap(v) {
11
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
12
+ }
13
+ export function asMap(v) {
14
+ return isMap(v) ? v : {};
15
+ }
16
+ /** Present and a string. */
17
+ export function str(v) {
18
+ return typeof v === 'string' ? v : undefined;
19
+ }
20
+ /** Present and a number. */
21
+ export function num(v) {
22
+ return typeof v === 'number' && Number.isFinite(v) ? v : undefined;
23
+ }
24
+ /** Present and a boolean. */
25
+ export function bool(v) {
26
+ return typeof v === 'boolean' ? v : undefined;
27
+ }
28
+ /** Present and an array. */
29
+ export function list(v) {
30
+ return Array.isArray(v) ? v : undefined;
31
+ }
32
+ /** Present and an array — every element stringified. */
33
+ export function strList(v) {
34
+ return Array.isArray(v) ? v.map((x) => String(x)) : undefined;
35
+ }
36
+ /** A scalar suitable for `json_path.equals` comparisons. */
37
+ export function scalar(v) {
38
+ if (v === null)
39
+ return null;
40
+ if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean')
41
+ return v;
42
+ return undefined;
43
+ }
44
+ /** One entry of an array, as a mapping. */
45
+ export function maps(v) {
46
+ return Array.isArray(v) ? v.filter(isMap) : [];
47
+ }
48
+ //# sourceMappingURL=yaml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml.js","sourceRoot":"","sources":["../../src/util/yaml.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,MAAM,UAAU,KAAK,CAAC,CAAU;IAC9B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,CAAU;IAC9B,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC3B,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,GAAG,CAAC,CAAU;IAC5B,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/C,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,GAAG,CAAC,CAAU;IAC5B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACrE,CAAC;AAED,6BAA6B;AAC7B,MAAM,UAAU,IAAI,CAAC,CAAU;IAC7B,OAAO,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAChD,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,IAAI,CAAC,CAAU;IAC7B,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAAe,CAAC,CAAC,CAAC,SAAS,CAAC;AACzD,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,OAAO,CAAC,CAAU;IAChC,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAChE,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,MAAM,CAAC,CAAU;IAC/B,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC5B,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC;IACvF,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,IAAI,CAAC,CAAU;IAC7B,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACjD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "@xenos1996/usa",
3
+ "version": "2.0.0",
4
+ "description": "Universal Software Auditor — an open-source, self-adapting audit framework for any project, any stack, any stage",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "usa": "dist/cli.js"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./rules": "./rules/index.yaml"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "rules",
22
+ "USA.md",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
26
+ "keywords": [
27
+ "audit",
28
+ "software-audit",
29
+ "security-audit",
30
+ "code-audit",
31
+ "code-review",
32
+ "openssf",
33
+ "owasp-asvs",
34
+ "slsa",
35
+ "sast",
36
+ "agent",
37
+ "llm",
38
+ "template",
39
+ "tech-debt",
40
+ "quality-gate"
41
+ ],
42
+ "author": "",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/Er-Sajan-PLG/universal-software-auditor.git"
46
+ },
47
+ "bugs": {
48
+ "url": "https://github.com/Er-Sajan-PLG/universal-software-auditor/issues"
49
+ },
50
+ "homepage": "https://github.com/Er-Sajan-PLG/universal-software-auditor#readme",
51
+ "engines": {
52
+ "node": ">=20.0.0"
53
+ },
54
+ "scripts": {
55
+ "build": "tsc -p tsconfig.json",
56
+ "usa": "node --experimental-strip-types src/cli.ts",
57
+ "lint": "eslint .",
58
+ "lint:fix": "eslint . --fix",
59
+ "format": "prettier --write .",
60
+ "format:check": "prettier --check .",
61
+ "typecheck": "tsc -p tsconfig.json --noEmit",
62
+ "test": "vitest run",
63
+ "test:watch": "vitest",
64
+ "test:cov": "vitest run --coverage",
65
+ "self-audit": "npm run build && node dist/cli.js audit . --out AUDIT.md",
66
+ "prepublishOnly": "npm run build"
67
+ },
68
+ "lint-staged": {
69
+ "*.{js,ts,mjs,cjs}": [
70
+ "eslint --fix",
71
+ "prettier --write"
72
+ ],
73
+ "*.{json,md,yaml,yml}": [
74
+ "prettier --write"
75
+ ]
76
+ },
77
+ "dependencies": {
78
+ "yaml": "^2.6.1"
79
+ },
80
+ "devDependencies": {
81
+ "@commitlint/cli": "^21.2.2",
82
+ "@commitlint/config-conventional": "^21.2.2",
83
+ "@eslint/js": "^10.0.1",
84
+ "@types/node": "^26.4.1",
85
+ "@vitest/coverage-v8": "^5.0.0",
86
+ "eslint": "^10.10.0",
87
+ "husky": "^9.1.7",
88
+ "lint-staged": "^17.5.0",
89
+ "prettier": "^3.4.2",
90
+ "typescript": "^5.7.3",
91
+ "typescript-eslint": "^8.20.0",
92
+ "vitest": "^5.0.0"
93
+ }
94
+ }
@@ -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
+ USA 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']