@xenos1996/usat 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +335 -0
- package/USAT.md +596 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +438 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +72 -0
- package/dist/config.js.map +1 -0
- package/dist/detect/index.d.ts +58 -0
- package/dist/detect/index.d.ts.map +1 -0
- package/dist/detect/index.js +329 -0
- package/dist/detect/index.js.map +1 -0
- package/dist/engine/audit.d.ts +21 -0
- package/dist/engine/audit.d.ts.map +1 -0
- package/dist/engine/audit.js +295 -0
- package/dist/engine/audit.js.map +1 -0
- package/dist/engine/diff.d.ts +24 -0
- package/dist/engine/diff.d.ts.map +1 -0
- package/dist/engine/diff.js +136 -0
- package/dist/engine/diff.js.map +1 -0
- package/dist/engine/evaluate.d.ts +19 -0
- package/dist/engine/evaluate.d.ts.map +1 -0
- package/dist/engine/evaluate.js +377 -0
- package/dist/engine/evaluate.js.map +1 -0
- package/dist/engine/gate.d.ts +19 -0
- package/dist/engine/gate.d.ts.map +1 -0
- package/dist/engine/gate.js +47 -0
- package/dist/engine/gate.js.map +1 -0
- package/dist/engine/loader.d.ts +22 -0
- package/dist/engine/loader.d.ts.map +1 -0
- package/dist/engine/loader.js +360 -0
- package/dist/engine/loader.js.map +1 -0
- package/dist/engine/maturity.d.ts +20 -0
- package/dist/engine/maturity.d.ts.map +1 -0
- package/dist/engine/maturity.js +157 -0
- package/dist/engine/maturity.js.map +1 -0
- package/dist/engine/score.d.ts +19 -0
- package/dist/engine/score.d.ts.map +1 -0
- package/dist/engine/score.js +141 -0
- package/dist/engine/score.js.map +1 -0
- package/dist/engine/sections.d.ts +14 -0
- package/dist/engine/sections.d.ts.map +1 -0
- package/dist/engine/sections.js +50 -0
- package/dist/engine/sections.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/report/markdown.d.ts +16 -0
- package/dist/report/markdown.d.ts.map +1 -0
- package/dist/report/markdown.js +488 -0
- package/dist/report/markdown.js.map +1 -0
- package/dist/types.d.ts +282 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/dist/util/glob.d.ts +11 -0
- package/dist/util/glob.d.ts.map +1 -0
- package/dist/util/glob.js +86 -0
- package/dist/util/glob.js.map +1 -0
- package/dist/util/project.d.ts +72 -0
- package/dist/util/project.d.ts.map +1 -0
- package/dist/util/project.js +586 -0
- package/dist/util/project.js.map +1 -0
- package/dist/util/yaml.d.ts +28 -0
- package/dist/util/yaml.d.ts.map +1 -0
- package/dist/util/yaml.js +48 -0
- package/dist/util/yaml.js.map +1 -0
- package/docs/ARCHITECTURE.md +119 -0
- package/docs/README.md +27 -0
- package/docs/adr/0001-record-architecture-decisions.md +35 -0
- package/docs/adr/0002-typescript-engine-over-python.md +49 -0
- package/docs/adr/0003-rules-are-data-not-code.md +50 -0
- package/docs/adr/0004-markdown-only-output.md +41 -0
- package/docs/adr/0005-maturity-dampens-severity.md +56 -0
- package/docs/adr/0006-severity-status-two-axes.md +48 -0
- package/docs/adr/0007-suppressions-are-visible-and-expiring.md +46 -0
- package/docs/adr/0008-report-trailer-and-diff.md +48 -0
- package/docs/adr/0009-fail-closed-on-malformed-input.md +56 -0
- package/docs/adr/0010-detector-design.md +54 -0
- package/docs/adr/0011-coexist-with-deep-scanners.md +52 -0
- package/docs/adr/README.md +32 -0
- package/docs/agent-integration.md +129 -0
- package/docs/ci-integration.md +163 -0
- package/docs/concepts.md +196 -0
- package/docs/configuration.md +105 -0
- package/docs/detectors.md +186 -0
- package/docs/getting-started.md +99 -0
- package/docs/maturity-profiles.md +156 -0
- package/docs/reference/api.md +193 -0
- package/docs/reference/cli.md +412 -0
- package/docs/rule-packs.md +278 -0
- package/docs/standards-mapping.md +171 -0
- package/package.json +96 -0
- package/rules/core/architecture.yaml +190 -0
- package/rules/core/cicd.yaml +212 -0
- package/rules/core/code-quality.yaml +261 -0
- package/rules/core/dependencies.yaml +132 -0
- package/rules/core/documentation.yaml +119 -0
- package/rules/core/future-readiness.yaml +91 -0
- package/rules/core/release.yaml +131 -0
- package/rules/core/repo.yaml +303 -0
- package/rules/core/security.yaml +494 -0
- package/rules/core/supply-chain.yaml +357 -0
- package/rules/core/testing.yaml +260 -0
- package/rules/detectors.yaml +2255 -0
- package/rules/index.yaml +41 -0
- package/rules/profiles/maturity.yaml +135 -0
- package/rules/stacks/ai-era.yaml +193 -0
- package/rules/stacks/api-backend.yaml +121 -0
- package/rules/stacks/cli.yaml +105 -0
- package/rules/stacks/compliance.yaml +157 -0
- package/rules/stacks/containers.yaml +124 -0
- package/rules/stacks/data.yaml +165 -0
- package/rules/stacks/go.yaml +134 -0
- package/rules/stacks/iac.yaml +128 -0
- package/rules/stacks/jvm.yaml +150 -0
- package/rules/stacks/ml-ai.yaml +140 -0
- package/rules/stacks/mobile.yaml +121 -0
- package/rules/stacks/node-typescript.yaml +166 -0
- package/rules/stacks/python.yaml +175 -0
- package/rules/stacks/rust.yaml +129 -0
- package/rules/stacks/solidity.yaml +139 -0
- package/rules/stacks/web-frontend.yaml +159 -0
- package/templates/AGENTS.audit.md +135 -0
- package/templates/AUDIT_REPORT.md +155 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/engine/gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGlE;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,GAAG,OAAO,EAAE,CAUpF;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAuBxF"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { SEVERITY_LADDER } from './loader.js';
|
|
2
|
+
/**
|
|
3
|
+
* Findings that should block a build: anything at or above `threshold` that is
|
|
4
|
+
* neither passing, unverified, nor explicitly accepted by project policy.
|
|
5
|
+
*
|
|
6
|
+
* Suppressed findings never block — that is the entire point of recording an
|
|
7
|
+
* accepted risk in `.usat.yaml`. UNKNOWN never blocks either: "needs a human"
|
|
8
|
+
* is not "failed", and gating on it would make every manual rule a CI failure.
|
|
9
|
+
*/
|
|
10
|
+
export function blockingFindings(report, threshold) {
|
|
11
|
+
const open = report.findings.filter((f) => f.status !== 'PASS' &&
|
|
12
|
+
f.status !== 'NOT_APPLICABLE' &&
|
|
13
|
+
f.status !== 'UNKNOWN' &&
|
|
14
|
+
!f.suppressedReason);
|
|
15
|
+
const min = SEVERITY_LADDER.indexOf(threshold);
|
|
16
|
+
return open.filter((f) => SEVERITY_LADDER.indexOf(f.severity) >= min);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* `--fail-on` gate. Returns a process exit code.
|
|
20
|
+
*
|
|
21
|
+
* The ladder is ascending (`FUTURE` … `CRITICAL`), so "at or above" means
|
|
22
|
+
* *higher* index. Getting this backwards makes `--fail-on critical` fail on
|
|
23
|
+
* every finding in the report, which is a memorable way to lose a CI pipeline.
|
|
24
|
+
*/
|
|
25
|
+
export function evaluateGate(report, failOn, quiet) {
|
|
26
|
+
if (failOn === 'none')
|
|
27
|
+
return 0;
|
|
28
|
+
const threshold = failOn.toUpperCase();
|
|
29
|
+
if (!SEVERITY_LADDER.includes(threshold)) {
|
|
30
|
+
console.error(`--fail-on must be none or one of ${[...SEVERITY_LADDER].reverse().join('|').toLowerCase()}`);
|
|
31
|
+
return 2;
|
|
32
|
+
}
|
|
33
|
+
const blocking = blockingFindings(report, threshold);
|
|
34
|
+
if (blocking.length === 0)
|
|
35
|
+
return 0;
|
|
36
|
+
if (!quiet) {
|
|
37
|
+
console.error(`\nGate failed: ${blocking.length} finding(s) at or above ${threshold}.`);
|
|
38
|
+
for (const f of blocking.slice(0, 10)) {
|
|
39
|
+
const where = f.locations[0] ? ` (${f.locations[0].file}:${f.locations[0].line})` : '';
|
|
40
|
+
console.error(` ${f.severity.padEnd(8)} ${f.ruleId} ${f.title}${where}`);
|
|
41
|
+
}
|
|
42
|
+
if (blocking.length > 10)
|
|
43
|
+
console.error(` … and ${blocking.length - 10} more`);
|
|
44
|
+
}
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/engine/gate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAmB,EAAE,SAAmB;IACvE,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CACjC,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,MAAM,KAAK,MAAM;QACnB,CAAC,CAAC,MAAM,KAAK,gBAAgB;QAC7B,CAAC,CAAC,MAAM,KAAK,SAAS;QACtB,CAAC,CAAC,CAAC,gBAAgB,CACtB,CAAC;IACF,MAAM,GAAG,GAAG,eAAe,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC/C,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC;AACxE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,MAAmB,EAAE,MAAc,EAAE,KAAc;IAC9E,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,CAAC,CAAC;IAEhC,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,EAAc,CAAC;IACnD,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACzC,OAAO,CAAC,KAAK,CACX,oCAAoC,CAAC,GAAG,eAAe,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAC7F,CAAC;QACF,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,QAAQ,GAAG,gBAAgB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACrD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAEpC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,kBAAkB,QAAQ,CAAC,MAAM,2BAA2B,SAAS,GAAG,CAAC,CAAC;QACxF,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YACtC,MAAM,KAAK,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACxF,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC,CAAC;QAC7E,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,GAAG,EAAE;YAAE,OAAO,CAAC,KAAK,CAAC,WAAW,QAAQ,CAAC,MAAM,GAAG,EAAE,OAAO,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { RuleClass, RulePack, Severity } from '../types.js';
|
|
2
|
+
export declare const SEVERITY_LADDER: Severity[];
|
|
3
|
+
export declare const RULE_CLASSES: RuleClass[];
|
|
4
|
+
export declare const DEFAULT_WEIGHT: Record<Severity, number>;
|
|
5
|
+
export interface LoaderResult {
|
|
6
|
+
packs: RulePack[];
|
|
7
|
+
warnings: string[];
|
|
8
|
+
}
|
|
9
|
+
/** Read `rules/index.yaml` and every pack it references. */
|
|
10
|
+
export declare function loadRulePacks(rulesDir: string): LoaderResult;
|
|
11
|
+
/**
|
|
12
|
+
* `applies_when` is author-controlled input to the audit hot loop. An unknown
|
|
13
|
+
* key, a non-list `all`/`any`, or an uncompilable `matches` regex must warn at
|
|
14
|
+
* load time, because evaluation fails closed (rule skipped) on all of them.
|
|
15
|
+
*/
|
|
16
|
+
export declare function validatePredicate(p: unknown, where: string, warnings: string[]): void;
|
|
17
|
+
/** Merge config-driven rule overrides onto loaded packs. */
|
|
18
|
+
export declare function applyRuleOverrides(packs: RulePack[], overrides: Record<string, {
|
|
19
|
+
severity?: Severity;
|
|
20
|
+
weight?: number;
|
|
21
|
+
}>): void;
|
|
22
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/engine/loader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAsB,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAErF,eAAO,MAAM,eAAe,EAAE,QAAQ,EAAoD,CAAC;AAC3F,eAAO,MAAM,YAAY,EAAE,SAAS,EAUnC,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAMnD,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CA2B5D;AAkPD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAWrF;AAgFD,4DAA4D;AAC5D,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,QAAQ,EAAE,EACjB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAClE,IAAI,CASN"}
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { parse as parseYaml } from 'yaml';
|
|
2
|
+
import { asMap, isMap, list, num, scalar, str, strList } from '../util/yaml.js';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
export const SEVERITY_LADDER = ['FUTURE', 'LOW', 'MEDIUM', 'HIGH', 'CRITICAL'];
|
|
6
|
+
export const RULE_CLASSES = [
|
|
7
|
+
'security',
|
|
8
|
+
'supply-chain',
|
|
9
|
+
'correctness',
|
|
10
|
+
'maintainability',
|
|
11
|
+
'operations',
|
|
12
|
+
'performance',
|
|
13
|
+
'compliance',
|
|
14
|
+
'documentation',
|
|
15
|
+
'style',
|
|
16
|
+
];
|
|
17
|
+
export const DEFAULT_WEIGHT = {
|
|
18
|
+
CRITICAL: 10,
|
|
19
|
+
HIGH: 6,
|
|
20
|
+
MEDIUM: 3,
|
|
21
|
+
LOW: 1.5,
|
|
22
|
+
FUTURE: 0.5,
|
|
23
|
+
};
|
|
24
|
+
/** Read `rules/index.yaml` and every pack it references. */
|
|
25
|
+
export function loadRulePacks(rulesDir) {
|
|
26
|
+
const warnings = [];
|
|
27
|
+
const indexPath = path.join(rulesDir, 'index.yaml');
|
|
28
|
+
if (!fs.existsSync(indexPath)) {
|
|
29
|
+
throw new Error(`Rule index not found at ${indexPath}. Pass --rules-dir or run \`usat init\`.`);
|
|
30
|
+
}
|
|
31
|
+
let indexDoc;
|
|
32
|
+
try {
|
|
33
|
+
indexDoc = parseYaml(fs.readFileSync(indexPath, 'utf8'));
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
// A corrupt index must not abort the audit with a stack trace: warn and
|
|
37
|
+
// continue with zero packs rather than zero findings with no explanation.
|
|
38
|
+
warnings.push(`rule index ${indexPath} is not valid YAML (${err.message}) — no packs loaded`);
|
|
39
|
+
return { packs: [], warnings };
|
|
40
|
+
}
|
|
41
|
+
const entries = indexDoc?.packs ?? [];
|
|
42
|
+
const packs = [];
|
|
43
|
+
const seenIds = new Set();
|
|
44
|
+
const seenRuleIds = new Map();
|
|
45
|
+
for (const entry of entries) {
|
|
46
|
+
processIndexEntry(entry, rulesDir, packs, seenIds, seenRuleIds, warnings);
|
|
47
|
+
}
|
|
48
|
+
return { packs, warnings };
|
|
49
|
+
}
|
|
50
|
+
function processIndexEntry(entry, rulesDir, packs, seenIds, seenRuleIds, warnings) {
|
|
51
|
+
const file = typeof entry === 'string' ? entry : entry.file;
|
|
52
|
+
const enabled = typeof entry === 'string' ? true : entry.enabled !== false;
|
|
53
|
+
if (!enabled)
|
|
54
|
+
return;
|
|
55
|
+
const abs = path.resolve(rulesDir, file);
|
|
56
|
+
if (!fs.existsSync(abs)) {
|
|
57
|
+
warnings.push(`pack listed in index but missing on disk: ${file}`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const pack = parsePack(abs, seenRuleIds, warnings);
|
|
61
|
+
if (!pack)
|
|
62
|
+
return;
|
|
63
|
+
if (seenIds.has(pack.id)) {
|
|
64
|
+
warnings.push(`duplicate pack id "${pack.id}" (${file}) — later pack ignored`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
seenIds.add(pack.id);
|
|
68
|
+
pack.source = path.relative(path.dirname(rulesDir), abs) || file;
|
|
69
|
+
packs.push(pack);
|
|
70
|
+
}
|
|
71
|
+
function parsePack(file, seenRuleIds, warnings) {
|
|
72
|
+
let raw;
|
|
73
|
+
try {
|
|
74
|
+
raw = parseYaml(fs.readFileSync(file, 'utf8'));
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
// One corrupt pack must not abort the whole audit: skip it loudly.
|
|
78
|
+
warnings.push(`pack ${file} is not valid YAML (${err.message}) — pack skipped`);
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
if (!isMap(raw)) {
|
|
82
|
+
warnings.push(`${file}: not a YAML mapping`);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const id = str(raw.id) ?? path.basename(file, path.extname(file));
|
|
86
|
+
const rules = [];
|
|
87
|
+
(list(raw.rules) ?? []).forEach((r, i) => {
|
|
88
|
+
const parsed = parseRule(r, id, i, warnings);
|
|
89
|
+
if (!parsed)
|
|
90
|
+
return;
|
|
91
|
+
const firstSeen = seenRuleIds.get(parsed.id);
|
|
92
|
+
if (firstSeen !== undefined) {
|
|
93
|
+
// Duplicate rule IDs double-count weight in scoring and collide as
|
|
94
|
+
// duplicate YAML keys in the report trailer (second silently wins in
|
|
95
|
+
// `usat diff`). First definition wins; the later one is dropped loudly.
|
|
96
|
+
warnings.push(`duplicate rule id "${parsed.id}" in pack "${id}" (first defined in pack "${firstSeen}") — later rule ignored`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
seenRuleIds.set(parsed.id, id);
|
|
100
|
+
rules.push(parsed);
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
id,
|
|
104
|
+
title: str(raw.title) ?? id,
|
|
105
|
+
description: str(raw.description),
|
|
106
|
+
version: str(raw.version),
|
|
107
|
+
section: str(raw.section),
|
|
108
|
+
sectionTitle: str(raw.section_title),
|
|
109
|
+
skipWhen: isMap(raw.skip_when) ? raw.skip_when : undefined,
|
|
110
|
+
provides: strList(raw.provides),
|
|
111
|
+
rules,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function parseRule(raw, packId, index, warnings) {
|
|
115
|
+
const where = `${packId}[${index}]`;
|
|
116
|
+
const head = readRuleHead(raw, where, warnings);
|
|
117
|
+
if (!head)
|
|
118
|
+
return null;
|
|
119
|
+
const { map: r, id, severity } = head;
|
|
120
|
+
const ruleClass = resolveRuleClass(r, where, id, warnings);
|
|
121
|
+
const check = parseCheck(r.check, `${where} ${id}`, warnings);
|
|
122
|
+
if (!check)
|
|
123
|
+
return null;
|
|
124
|
+
validatePredicate(r.applies_when, `${where} ${id}`, warnings);
|
|
125
|
+
return assembleRule(r, id, severity, ruleClass, check);
|
|
126
|
+
}
|
|
127
|
+
function assembleRule(r, id, severity, ruleClass, check) {
|
|
128
|
+
const depths = Array.isArray(r.depths) ? r.depths.filter(isDepth) : undefined;
|
|
129
|
+
return {
|
|
130
|
+
id,
|
|
131
|
+
title: String(r.title ?? id),
|
|
132
|
+
section: String(r.section ?? 'S0'),
|
|
133
|
+
sectionTitle: optText(r.section_title),
|
|
134
|
+
severity,
|
|
135
|
+
weight: optNumber(r.weight),
|
|
136
|
+
ruleClass,
|
|
137
|
+
appliesWhen: r.applies_when,
|
|
138
|
+
depths,
|
|
139
|
+
check,
|
|
140
|
+
why: optText(r.why),
|
|
141
|
+
evidence: optText(r.evidence),
|
|
142
|
+
remediation: optText(r.remediation),
|
|
143
|
+
references: optStrArray(r.references),
|
|
144
|
+
tags: optStrArray(r.tags),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function optText(v) {
|
|
148
|
+
return v ? String(v) : undefined;
|
|
149
|
+
}
|
|
150
|
+
function optNumber(v) {
|
|
151
|
+
return typeof v === 'number' ? v : undefined;
|
|
152
|
+
}
|
|
153
|
+
function optStrArray(v) {
|
|
154
|
+
return Array.isArray(v) ? v.map(String) : undefined;
|
|
155
|
+
}
|
|
156
|
+
function readRuleHead(raw, where, warnings) {
|
|
157
|
+
if (!isMap(raw)) {
|
|
158
|
+
warnings.push(`${where}: rule is not a mapping`);
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
const id = raw.id ? String(raw.id) : null;
|
|
162
|
+
if (!id) {
|
|
163
|
+
warnings.push(`${where}: rule missing "id"`);
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
const severity = normalizeSeverity(raw.severity);
|
|
167
|
+
if (!severity) {
|
|
168
|
+
warnings.push(`${where} ${id}: invalid severity "${String(raw.severity)}"`);
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
return { map: raw, id, severity };
|
|
172
|
+
}
|
|
173
|
+
function resolveRuleClass(r, where, id, warnings) {
|
|
174
|
+
const declaredClass = str(r.class);
|
|
175
|
+
if (declaredClass && !RULE_CLASSES.includes(declaredClass)) {
|
|
176
|
+
warnings.push(`${where} ${id}: unknown class "${declaredClass}" → maintainability`);
|
|
177
|
+
}
|
|
178
|
+
return declaredClass && RULE_CLASSES.includes(declaredClass)
|
|
179
|
+
? declaredClass
|
|
180
|
+
: 'maintainability';
|
|
181
|
+
}
|
|
182
|
+
/** One check kind per entry — adding a kind means adding a line, not a branch. */
|
|
183
|
+
const CHECK_BUILDERS = {
|
|
184
|
+
manual: () => ({ kind: 'manual' }),
|
|
185
|
+
info: () => ({ kind: 'info' }),
|
|
186
|
+
file_exists: (c) => ({ kind: 'file_exists', files: toStringArray(c.files) }),
|
|
187
|
+
file_absent: (c) => ({ kind: 'file_absent', files: toStringArray(c.files) }),
|
|
188
|
+
any_file: (c) => ({ kind: 'any_file', patterns: toStringArray(c.patterns ?? c.files) }),
|
|
189
|
+
grep_present: (c) => grepCheck('grep_present', c),
|
|
190
|
+
grep_absent: (c) => grepCheck('grep_absent', c),
|
|
191
|
+
grep_wrong: (c) => grepCheck('grep_wrong', c),
|
|
192
|
+
grep_deprecated: (c) => grepCheck('grep_deprecated', c),
|
|
193
|
+
json_path: (c) => ({
|
|
194
|
+
kind: 'json_path',
|
|
195
|
+
file: String(c.file ?? ''),
|
|
196
|
+
path: String(c.path ?? ''),
|
|
197
|
+
equals: scalar(c.equals) ?? undefined,
|
|
198
|
+
}),
|
|
199
|
+
tracked_present: (c) => ({
|
|
200
|
+
kind: 'tracked_present',
|
|
201
|
+
patterns: toStringArray(c.patterns ?? c.files),
|
|
202
|
+
}),
|
|
203
|
+
tracked_absent: (c) => ({
|
|
204
|
+
kind: 'tracked_absent',
|
|
205
|
+
patterns: toStringArray(c.patterns ?? c.files),
|
|
206
|
+
}),
|
|
207
|
+
file_lines_max: (c) => ({
|
|
208
|
+
kind: 'file_lines_max',
|
|
209
|
+
patterns: toStringArray(c.patterns ?? c.files),
|
|
210
|
+
max_lines: num(c.max_lines) ?? 800,
|
|
211
|
+
}),
|
|
212
|
+
count_min: (c) => ({
|
|
213
|
+
kind: 'count_min',
|
|
214
|
+
patterns: toStringArray(c.patterns ?? c.files),
|
|
215
|
+
min: num(c.min) ?? 1,
|
|
216
|
+
}),
|
|
217
|
+
command: (c) => ({
|
|
218
|
+
kind: 'command',
|
|
219
|
+
run: str(c.run) ?? '',
|
|
220
|
+
expect_exit: num(c.expect_exit) ?? 0,
|
|
221
|
+
}),
|
|
222
|
+
};
|
|
223
|
+
function parseCheck(raw, where, warnings) {
|
|
224
|
+
const c = asMap(raw);
|
|
225
|
+
const kind = str(c.kind);
|
|
226
|
+
if (!kind) {
|
|
227
|
+
warnings.push(`${where}: check missing "kind"`);
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
const build = CHECK_BUILDERS[kind];
|
|
231
|
+
if (!build) {
|
|
232
|
+
warnings.push(`${where}: unsupported check kind "${String(c.kind)}"`);
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
const built = build(c);
|
|
236
|
+
if (built.kind.startsWith('grep_') && 'pattern' in built && built.pattern === '') {
|
|
237
|
+
// `new RegExp('')` matches every line: a grep_absent rule with a missing
|
|
238
|
+
// pattern FAILs the whole repo, a grep_present one awards free credit.
|
|
239
|
+
// A malformed rule must never become a score weapon — drop it loudly.
|
|
240
|
+
warnings.push(`${where}: ${built.kind} has an empty pattern (matches every line) — rule ignored`);
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
return built;
|
|
244
|
+
}
|
|
245
|
+
/** Predicate operators the evaluator understands. Anything else fails closed. */
|
|
246
|
+
const PREDICATE_OPS = new Set([
|
|
247
|
+
'exists',
|
|
248
|
+
'absent',
|
|
249
|
+
'eq',
|
|
250
|
+
'neq',
|
|
251
|
+
'in',
|
|
252
|
+
'includes',
|
|
253
|
+
'gt',
|
|
254
|
+
'lt',
|
|
255
|
+
'matches',
|
|
256
|
+
]);
|
|
257
|
+
/**
|
|
258
|
+
* `applies_when` is author-controlled input to the audit hot loop. An unknown
|
|
259
|
+
* key, a non-list `all`/`any`, or an uncompilable `matches` regex must warn at
|
|
260
|
+
* load time, because evaluation fails closed (rule skipped) on all of them.
|
|
261
|
+
*/
|
|
262
|
+
export function validatePredicate(p, where, warnings) {
|
|
263
|
+
if (p === undefined || p === null)
|
|
264
|
+
return;
|
|
265
|
+
if (!isMap(p)) {
|
|
266
|
+
warnings.push(`${where}: applies_when must be a mapping — constraint ignored, rule skipped`);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const keys = Object.keys(p);
|
|
270
|
+
if (keys.length === 0)
|
|
271
|
+
return;
|
|
272
|
+
for (const k of keys) {
|
|
273
|
+
validatePredicateKey(p, k, where, warnings);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function validatePredicateKey(p, k, where, warnings) {
|
|
277
|
+
if (k === 'all' || k === 'any') {
|
|
278
|
+
validatePredicateList(p[k], `${where}: applies_when.${k}`, warnings);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (k === 'not') {
|
|
282
|
+
validatePredicate(p.not, where, warnings);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (k === 'fact') {
|
|
286
|
+
validateFactPredicate(p, where, warnings);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (k === 'op' || k === 'value') {
|
|
290
|
+
// `op`/`value` are operands of a sibling `fact` — meaningful only there.
|
|
291
|
+
if (typeof p.fact !== 'string') {
|
|
292
|
+
warnings.push(`${where}: "${k}" without a "fact" — ignoring`);
|
|
293
|
+
}
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
warnings.push(`${where}: unknown applies_when key "${k}" — constraint ignored, rule skipped`);
|
|
297
|
+
}
|
|
298
|
+
function validatePredicateList(v, where, warnings) {
|
|
299
|
+
const items = list(v);
|
|
300
|
+
if (!items) {
|
|
301
|
+
warnings.push(`${where} must be a list — constraint ignored`);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
for (const item of items)
|
|
305
|
+
validatePredicate(item, where, warnings);
|
|
306
|
+
}
|
|
307
|
+
function validateFactPredicate(p, where, warnings) {
|
|
308
|
+
const op = str(p.op) ?? 'exists';
|
|
309
|
+
if (!PREDICATE_OPS.has(op)) {
|
|
310
|
+
warnings.push(`${where}: unknown predicate op "${op}" — rule will never apply`);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (op === 'matches' && typeof p.value === 'string') {
|
|
314
|
+
try {
|
|
315
|
+
new RegExp(p.value);
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
warnings.push(`${where}: invalid regex in matches predicate ("${String(p.value)}") — rule will never apply`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/** The four grep_* kinds share a shape; they differ only in polarity. */
|
|
323
|
+
function grepCheck(kind, c) {
|
|
324
|
+
return {
|
|
325
|
+
kind,
|
|
326
|
+
pattern: str(c.pattern) ?? '',
|
|
327
|
+
include: toStringArray(c.include),
|
|
328
|
+
exclude: c.exclude ? toStringArray(c.exclude) : undefined,
|
|
329
|
+
flags: str(c.flags),
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
function toStringArray(v) {
|
|
333
|
+
if (Array.isArray(v))
|
|
334
|
+
return v.map(String);
|
|
335
|
+
if (typeof v === 'string')
|
|
336
|
+
return [v];
|
|
337
|
+
return [];
|
|
338
|
+
}
|
|
339
|
+
function normalizeSeverity(v) {
|
|
340
|
+
const s = String(v ?? '').toUpperCase();
|
|
341
|
+
return SEVERITY_LADDER.includes(s) ? s : null;
|
|
342
|
+
}
|
|
343
|
+
function isDepth(v) {
|
|
344
|
+
return v === 'quick' || v === 'standard' || v === 'deep';
|
|
345
|
+
}
|
|
346
|
+
/** Merge config-driven rule overrides onto loaded packs. */
|
|
347
|
+
export function applyRuleOverrides(packs, overrides) {
|
|
348
|
+
for (const pack of packs) {
|
|
349
|
+
for (const rule of pack.rules) {
|
|
350
|
+
const o = overrides[rule.id];
|
|
351
|
+
if (!o)
|
|
352
|
+
continue;
|
|
353
|
+
if (o.severity)
|
|
354
|
+
rule.severity = o.severity;
|
|
355
|
+
if (typeof o.weight === 'number')
|
|
356
|
+
rule.weight = o.weight;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/engine/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Maturity, RuleClass, Severity } from '../types.js';
|
|
2
|
+
export interface MaturityProfile {
|
|
3
|
+
key: Maturity;
|
|
4
|
+
label: string;
|
|
5
|
+
summary: string;
|
|
6
|
+
dampen: Record<RuleClass, number>;
|
|
7
|
+
expectedBand: [number, number];
|
|
8
|
+
focus: string[];
|
|
9
|
+
defer: string[];
|
|
10
|
+
}
|
|
11
|
+
export declare function loadProfiles(rulesDir: string): Record<Maturity, MaturityProfile>;
|
|
12
|
+
/**
|
|
13
|
+
* Apply the maturity profile to a rule's declared severity.
|
|
14
|
+
* CRITICAL is never dampened — see the hard rule in profiles/maturity.yaml.
|
|
15
|
+
*/
|
|
16
|
+
export declare function dampen(severity: Severity, ruleClass: RuleClass, profile: MaturityProfile): {
|
|
17
|
+
severity: Severity;
|
|
18
|
+
dampened: boolean;
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=maturity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"maturity.d.ts","sourceRoot":"","sources":["../../src/engine/maturity.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,QAAQ,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAClC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AA+GD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,QAAQ,EAAE,eAAe,CAAC,CAwBhF;AAED;;;GAGG;AACH,wBAAgB,MAAM,CACpB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,eAAe,GACvB;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAU3C"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { parse as parseYaml } from 'yaml';
|
|
2
|
+
import { asMap, list, num, str, strList } from '../util/yaml.js';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
const LADDER = ['FUTURE', 'LOW', 'MEDIUM', 'HIGH', 'CRITICAL'];
|
|
6
|
+
const FALLBACK = {
|
|
7
|
+
prototype: {
|
|
8
|
+
key: 'prototype',
|
|
9
|
+
label: 'Prototype / Spike',
|
|
10
|
+
summary: 'Proving an idea.',
|
|
11
|
+
dampen: {
|
|
12
|
+
security: 1,
|
|
13
|
+
'supply-chain': 1,
|
|
14
|
+
correctness: 1,
|
|
15
|
+
maintainability: 2,
|
|
16
|
+
operations: 2,
|
|
17
|
+
performance: 2,
|
|
18
|
+
compliance: 2,
|
|
19
|
+
documentation: 2,
|
|
20
|
+
style: 2,
|
|
21
|
+
},
|
|
22
|
+
expectedBand: [30, 65],
|
|
23
|
+
focus: [],
|
|
24
|
+
defer: [],
|
|
25
|
+
},
|
|
26
|
+
mvp: {
|
|
27
|
+
key: 'mvp',
|
|
28
|
+
label: 'MVP / Early product',
|
|
29
|
+
summary: 'Real users, real data.',
|
|
30
|
+
dampen: {
|
|
31
|
+
security: 0,
|
|
32
|
+
'supply-chain': 1,
|
|
33
|
+
correctness: 1,
|
|
34
|
+
maintainability: 1,
|
|
35
|
+
operations: 1,
|
|
36
|
+
performance: 1,
|
|
37
|
+
compliance: 1,
|
|
38
|
+
documentation: 2,
|
|
39
|
+
style: 2,
|
|
40
|
+
},
|
|
41
|
+
expectedBand: [45, 75],
|
|
42
|
+
focus: [],
|
|
43
|
+
defer: [],
|
|
44
|
+
},
|
|
45
|
+
beta: {
|
|
46
|
+
key: 'beta',
|
|
47
|
+
label: 'Beta / Growing',
|
|
48
|
+
summary: 'Scaling users and contributors.',
|
|
49
|
+
dampen: {
|
|
50
|
+
security: 0,
|
|
51
|
+
'supply-chain': 0,
|
|
52
|
+
correctness: 0,
|
|
53
|
+
maintainability: 1,
|
|
54
|
+
operations: 1,
|
|
55
|
+
performance: 1,
|
|
56
|
+
compliance: 1,
|
|
57
|
+
documentation: 1,
|
|
58
|
+
style: 1,
|
|
59
|
+
},
|
|
60
|
+
expectedBand: [60, 85],
|
|
61
|
+
focus: [],
|
|
62
|
+
defer: [],
|
|
63
|
+
},
|
|
64
|
+
production: {
|
|
65
|
+
key: 'production',
|
|
66
|
+
label: 'Production / GA',
|
|
67
|
+
summary: 'Full bar.',
|
|
68
|
+
dampen: {
|
|
69
|
+
security: 0,
|
|
70
|
+
'supply-chain': 0,
|
|
71
|
+
correctness: 0,
|
|
72
|
+
maintainability: 0,
|
|
73
|
+
operations: 0,
|
|
74
|
+
performance: 0,
|
|
75
|
+
compliance: 0,
|
|
76
|
+
documentation: 0,
|
|
77
|
+
style: 0,
|
|
78
|
+
},
|
|
79
|
+
expectedBand: [75, 95],
|
|
80
|
+
focus: [],
|
|
81
|
+
defer: [],
|
|
82
|
+
},
|
|
83
|
+
legacy: {
|
|
84
|
+
key: 'legacy',
|
|
85
|
+
label: 'Legacy / Maintenance',
|
|
86
|
+
summary: 'Contain risk, plan exit.',
|
|
87
|
+
dampen: {
|
|
88
|
+
security: 0,
|
|
89
|
+
'supply-chain': 0,
|
|
90
|
+
correctness: 0,
|
|
91
|
+
maintainability: 1,
|
|
92
|
+
operations: 0,
|
|
93
|
+
performance: 1,
|
|
94
|
+
compliance: 0,
|
|
95
|
+
documentation: 1,
|
|
96
|
+
style: 1,
|
|
97
|
+
},
|
|
98
|
+
expectedBand: [40, 70],
|
|
99
|
+
focus: [],
|
|
100
|
+
defer: [],
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
/** `[min, max]` if the pack wrote a two-number band, else the built-in. */
|
|
104
|
+
function expectedBand(v, fallback) {
|
|
105
|
+
const band = list(v);
|
|
106
|
+
if (!band || band.length < 2)
|
|
107
|
+
return fallback;
|
|
108
|
+
const min = num(band[0]);
|
|
109
|
+
const max = num(band[1]);
|
|
110
|
+
return min !== undefined && max !== undefined ? [min, max] : fallback;
|
|
111
|
+
}
|
|
112
|
+
export function loadProfiles(rulesDir) {
|
|
113
|
+
const file = path.join(rulesDir, 'profiles', 'maturity.yaml');
|
|
114
|
+
if (!fs.existsSync(file))
|
|
115
|
+
return { ...FALLBACK };
|
|
116
|
+
try {
|
|
117
|
+
const doc = asMap(parseYaml(fs.readFileSync(file, 'utf8')));
|
|
118
|
+
const profiles = asMap(doc.profiles);
|
|
119
|
+
const out = { ...FALLBACK };
|
|
120
|
+
for (const key of Object.keys(FALLBACK)) {
|
|
121
|
+
const p = asMap(profiles[key]);
|
|
122
|
+
if (!profiles[key])
|
|
123
|
+
continue;
|
|
124
|
+
out[key] = {
|
|
125
|
+
key,
|
|
126
|
+
label: str(p.label) ?? FALLBACK[key].label,
|
|
127
|
+
summary: (str(p.summary) ?? '').trim(),
|
|
128
|
+
dampen: { ...FALLBACK[key].dampen, ...asMap(p.dampen) },
|
|
129
|
+
expectedBand: expectedBand(p.expected_band, FALLBACK[key].expectedBand),
|
|
130
|
+
focus: strList(p.focus) ?? [],
|
|
131
|
+
defer: strList(p.defer) ?? [],
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return { ...FALLBACK };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Apply the maturity profile to a rule's declared severity.
|
|
142
|
+
* CRITICAL is never dampened — see the hard rule in profiles/maturity.yaml.
|
|
143
|
+
*/
|
|
144
|
+
export function dampen(severity, ruleClass, profile) {
|
|
145
|
+
if (severity === 'CRITICAL')
|
|
146
|
+
return { severity, dampened: false };
|
|
147
|
+
const raw = profile.dampen[ruleClass] ?? 0;
|
|
148
|
+
// Pack-supplied dampen steps are untrusted input: a fractional step indexes
|
|
149
|
+
// the ladder at a hole (severity: undefined → NaN scores downstream), so
|
|
150
|
+
// only non-negative integers dampen. Anything else means full severity.
|
|
151
|
+
if (!Number.isInteger(raw) || raw <= 0)
|
|
152
|
+
return { severity, dampened: false };
|
|
153
|
+
const idx = LADDER.indexOf(severity);
|
|
154
|
+
const next = Math.max(0, idx - raw);
|
|
155
|
+
return { severity: LADDER[next], dampened: next !== idx };
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=maturity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"maturity.js","sourceRoot":"","sources":["../../src/engine/maturity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAa7B,MAAM,MAAM,GAAe,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;AAE3E,MAAM,QAAQ,GAAsC;IAClD,SAAS,EAAE;QACT,GAAG,EAAE,WAAW;QAChB,KAAK,EAAE,mBAAmB;QAC1B,OAAO,EAAE,kBAAkB;QAC3B,MAAM,EAAE;YACN,QAAQ,EAAE,CAAC;YACX,cAAc,EAAE,CAAC;YACjB,WAAW,EAAE,CAAC;YACd,eAAe,EAAE,CAAC;YAClB,UAAU,EAAE,CAAC;YACb,WAAW,EAAE,CAAC;YACd,UAAU,EAAE,CAAC;YACb,aAAa,EAAE,CAAC;YAChB,KAAK,EAAE,CAAC;SACT;QACD,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;QACtB,KAAK,EAAE,EAAE;QACT,KAAK,EAAE,EAAE;KACV;IACD,GAAG,EAAE;QACH,GAAG,EAAE,KAAK;QACV,KAAK,EAAE,qBAAqB;QAC5B,OAAO,EAAE,wBAAwB;QACjC,MAAM,EAAE;YACN,QAAQ,EAAE,CAAC;YACX,cAAc,EAAE,CAAC;YACjB,WAAW,EAAE,CAAC;YACd,eAAe,EAAE,CAAC;YAClB,UAAU,EAAE,CAAC;YACb,WAAW,EAAE,CAAC;YACd,UAAU,EAAE,CAAC;YACb,aAAa,EAAE,CAAC;YAChB,KAAK,EAAE,CAAC;SACT;QACD,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;QACtB,KAAK,EAAE,EAAE;QACT,KAAK,EAAE,EAAE;KACV;IACD,IAAI,EAAE;QACJ,GAAG,EAAE,MAAM;QACX,KAAK,EAAE,gBAAgB;QACvB,OAAO,EAAE,iCAAiC;QAC1C,MAAM,EAAE;YACN,QAAQ,EAAE,CAAC;YACX,cAAc,EAAE,CAAC;YACjB,WAAW,EAAE,CAAC;YACd,eAAe,EAAE,CAAC;YAClB,UAAU,EAAE,CAAC;YACb,WAAW,EAAE,CAAC;YACd,UAAU,EAAE,CAAC;YACb,aAAa,EAAE,CAAC;YAChB,KAAK,EAAE,CAAC;SACT;QACD,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;QACtB,KAAK,EAAE,EAAE;QACT,KAAK,EAAE,EAAE;KACV;IACD,UAAU,EAAE;QACV,GAAG,EAAE,YAAY;QACjB,KAAK,EAAE,iBAAiB;QACxB,OAAO,EAAE,WAAW;QACpB,MAAM,EAAE;YACN,QAAQ,EAAE,CAAC;YACX,cAAc,EAAE,CAAC;YACjB,WAAW,EAAE,CAAC;YACd,eAAe,EAAE,CAAC;YAClB,UAAU,EAAE,CAAC;YACb,WAAW,EAAE,CAAC;YACd,UAAU,EAAE,CAAC;YACb,aAAa,EAAE,CAAC;YAChB,KAAK,EAAE,CAAC;SACT;QACD,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;QACtB,KAAK,EAAE,EAAE;QACT,KAAK,EAAE,EAAE;KACV;IACD,MAAM,EAAE;QACN,GAAG,EAAE,QAAQ;QACb,KAAK,EAAE,sBAAsB;QAC7B,OAAO,EAAE,0BAA0B;QACnC,MAAM,EAAE;YACN,QAAQ,EAAE,CAAC;YACX,cAAc,EAAE,CAAC;YACjB,WAAW,EAAE,CAAC;YACd,eAAe,EAAE,CAAC;YAClB,UAAU,EAAE,CAAC;YACb,WAAW,EAAE,CAAC;YACd,UAAU,EAAE,CAAC;YACb,aAAa,EAAE,CAAC;YAChB,KAAK,EAAE,CAAC;SACT;QACD,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;QACtB,KAAK,EAAE,EAAE;QACT,KAAK,EAAE,EAAE;KACV;CACF,CAAC;AAEF,2EAA2E;AAC3E,SAAS,YAAY,CAAC,CAAU,EAAE,QAA0B;IAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACrB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC9C,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACzB,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACzB,OAAO,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC;IAC9D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IACjD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,GAAG,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC5B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAe,EAAE,CAAC;YACtD,MAAM,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;YAC/B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC7B,GAAG,CAAC,GAAG,CAAC,GAAG;gBACT,GAAG;gBACH,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK;gBAC1C,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;gBACtC,MAAM,EAAE,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE;gBACvD,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC,aAAa,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC;gBACvE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE;gBAC7B,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE;aAC9B,CAAC;QACJ,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IACzB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,MAAM,CACpB,QAAkB,EAClB,SAAoB,EACpB,OAAwB;IAExB,IAAI,QAAQ,KAAK,UAAU;QAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClE,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC3C,4EAA4E;IAC5E,yEAAyE;IACzE,wEAAwE;IACxE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC7E,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;IACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAE,EAAE,QAAQ,EAAE,IAAI,KAAK,GAAG,EAAE,CAAC;AAC7D,CAAC"}
|