@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.
- package/LICENSE +21 -0
- package/README.md +335 -0
- package/USA.md +597 -0
- package/dist/bootstrap/catalog.d.ts +46 -0
- package/dist/bootstrap/catalog.d.ts.map +1 -0
- package/dist/bootstrap/catalog.js +312 -0
- package/dist/bootstrap/catalog.js.map +1 -0
- package/dist/bootstrap/index.d.ts +36 -0
- package/dist/bootstrap/index.d.ts.map +1 -0
- package/dist/bootstrap/index.js +177 -0
- package/dist/bootstrap/index.js.map +1 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +706 -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 +88 -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 +32 -0
- package/dist/engine/audit.d.ts.map +1 -0
- package/dist/engine/audit.js +366 -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 +37 -0
- package/dist/engine/loader.d.ts.map +1 -0
- package/dist/engine/loader.js +388 -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/evolution/benchmark.d.ts +12 -0
- package/dist/evolution/benchmark.d.ts.map +1 -0
- package/dist/evolution/benchmark.js +107 -0
- package/dist/evolution/benchmark.js.map +1 -0
- package/dist/evolution/capability.d.ts +22 -0
- package/dist/evolution/capability.d.ts.map +1 -0
- package/dist/evolution/capability.js +69 -0
- package/dist/evolution/capability.js.map +1 -0
- package/dist/evolution/coverage.d.ts +15 -0
- package/dist/evolution/coverage.d.ts.map +1 -0
- package/dist/evolution/coverage.js +65 -0
- package/dist/evolution/coverage.js.map +1 -0
- package/dist/evolution/gap.d.ts +10 -0
- package/dist/evolution/gap.d.ts.map +1 -0
- package/dist/evolution/gap.js +52 -0
- package/dist/evolution/gap.js.map +1 -0
- package/dist/evolution/propose.d.ts +50 -0
- package/dist/evolution/propose.d.ts.map +1 -0
- package/dist/evolution/propose.js +188 -0
- package/dist/evolution/propose.js.map +1 -0
- package/dist/evolution/queue.d.ts +64 -0
- package/dist/evolution/queue.d.ts.map +1 -0
- package/dist/evolution/queue.js +192 -0
- package/dist/evolution/queue.js.map +1 -0
- package/dist/evolution/release.d.ts +11 -0
- package/dist/evolution/release.d.ts.map +1 -0
- package/dist/evolution/release.js +55 -0
- package/dist/evolution/release.js.map +1 -0
- package/dist/evolution/run.d.ts +88 -0
- package/dist/evolution/run.d.ts.map +1 -0
- package/dist/evolution/run.js +258 -0
- package/dist/evolution/run.js.map +1 -0
- package/dist/evolution/schedule.d.ts +59 -0
- package/dist/evolution/schedule.d.ts.map +1 -0
- package/dist/evolution/schedule.js +77 -0
- package/dist/evolution/schedule.js.map +1 -0
- package/dist/evolution/types.d.ts +174 -0
- package/dist/evolution/types.d.ts.map +1 -0
- package/dist/evolution/types.js +8 -0
- package/dist/evolution/types.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/learn/index.d.ts +27 -0
- package/dist/learn/index.d.ts.map +1 -0
- package/dist/learn/index.js +196 -0
- package/dist/learn/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/snapshot/index.d.ts +55 -0
- package/dist/snapshot/index.d.ts.map +1 -0
- package/dist/snapshot/index.js +76 -0
- package/dist/snapshot/index.js.map +1 -0
- package/dist/store/index.d.ts +31 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +69 -0
- package/dist/store/index.js.map +1 -0
- package/dist/types.d.ts +287 -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 +90 -0
- package/dist/util/project.d.ts.map +1 -0
- package/dist/util/project.js +636 -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/package.json +94 -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 +313 -0
- package/rules/core/security.yaml +496 -0
- package/rules/core/supply-chain.yaml +357 -0
- package/rules/core/testing.yaml +266 -0
- package/rules/detectors.yaml +2028 -0
- package/rules/index.yaml +42 -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/swift.yaml +100 -0
- 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']
|