@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,19 @@
|
|
|
1
|
+
import type { Finding, Rule, ScoreCard } from '../types.js';
|
|
2
|
+
import type { SectionDef } from './sections.js';
|
|
3
|
+
import type { MaturityProfile } from './maturity.js';
|
|
4
|
+
export interface ScoredRule {
|
|
5
|
+
rule: Rule;
|
|
6
|
+
finding: Finding;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Deterministic, reproducible scoring.
|
|
10
|
+
*
|
|
11
|
+
* credit_i = weight_i × CREDIT[status_i]
|
|
12
|
+
* section = 10 × Σcredit_i / Σweight_i (resolved applicable rules)
|
|
13
|
+
* overall = 100 × Σ(credit_i × sectionWeight) / Σ(weight_i × sectionWeight)
|
|
14
|
+
*
|
|
15
|
+
* Rule-level weighting (rather than averaging section scores) means a section
|
|
16
|
+
* with three rules cannot swing the total as hard as one with thirty.
|
|
17
|
+
*/
|
|
18
|
+
export declare function score(evaluated: ScoredRule[], sections: SectionDef[], profile: MaturityProfile): ScoreCard;
|
|
19
|
+
//# sourceMappingURL=score.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.d.ts","sourceRoot":"","sources":["../../src/engine/score.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAkC,MAAM,aAAa,CAAC;AAE5F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;CAClB;AAkCD;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CACnB,SAAS,EAAE,UAAU,EAAE,EACvB,QAAQ,EAAE,UAAU,EAAE,EACtB,OAAO,EAAE,eAAe,GACvB,SAAS,CAgCX"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { ruleWeight } from './evaluate.js';
|
|
2
|
+
/** How much credit a rule earns for a given outcome. */
|
|
3
|
+
const CREDIT = {
|
|
4
|
+
PASS: 1,
|
|
5
|
+
EXPERIMENTAL: 0.5,
|
|
6
|
+
DEPRECATED: 0.4,
|
|
7
|
+
WRONG: 0.15,
|
|
8
|
+
MISSING: 0,
|
|
9
|
+
FAIL: 0,
|
|
10
|
+
// Not counted: unresolved judgement or deliberately skipped.
|
|
11
|
+
UNKNOWN: 0,
|
|
12
|
+
NOT_APPLICABLE: 0,
|
|
13
|
+
};
|
|
14
|
+
const EMPTY_COUNTS = () => ({
|
|
15
|
+
PASS: 0,
|
|
16
|
+
FAIL: 0,
|
|
17
|
+
WRONG: 0,
|
|
18
|
+
MISSING: 0,
|
|
19
|
+
DEPRECATED: 0,
|
|
20
|
+
EXPERIMENTAL: 0,
|
|
21
|
+
UNKNOWN: 0,
|
|
22
|
+
NOT_APPLICABLE: 0,
|
|
23
|
+
});
|
|
24
|
+
const EMPTY_SEVERITY = () => ({
|
|
25
|
+
CRITICAL: 0,
|
|
26
|
+
HIGH: 0,
|
|
27
|
+
MEDIUM: 0,
|
|
28
|
+
LOW: 0,
|
|
29
|
+
FUTURE: 0,
|
|
30
|
+
});
|
|
31
|
+
/**
|
|
32
|
+
* Deterministic, reproducible scoring.
|
|
33
|
+
*
|
|
34
|
+
* credit_i = weight_i × CREDIT[status_i]
|
|
35
|
+
* section = 10 × Σcredit_i / Σweight_i (resolved applicable rules)
|
|
36
|
+
* overall = 100 × Σ(credit_i × sectionWeight) / Σ(weight_i × sectionWeight)
|
|
37
|
+
*
|
|
38
|
+
* Rule-level weighting (rather than averaging section scores) means a section
|
|
39
|
+
* with three rules cannot swing the total as hard as one with thirty.
|
|
40
|
+
*/
|
|
41
|
+
export function score(evaluated, sections, profile) {
|
|
42
|
+
const counts = EMPTY_COUNTS();
|
|
43
|
+
const severityCounts = EMPTY_SEVERITY();
|
|
44
|
+
const sectionMap = new Map(sections.map((s) => [s.id, s]));
|
|
45
|
+
const acc = {
|
|
46
|
+
bySection: new Map(),
|
|
47
|
+
counts,
|
|
48
|
+
severityCounts,
|
|
49
|
+
totalW: 0,
|
|
50
|
+
totalCredit: 0,
|
|
51
|
+
applicableAll: 0,
|
|
52
|
+
resolvedAll: 0,
|
|
53
|
+
};
|
|
54
|
+
for (const { rule, finding } of evaluated) {
|
|
55
|
+
accumulateScoredRule(acc, sectionMap, rule, finding);
|
|
56
|
+
}
|
|
57
|
+
const sectionScores = sections
|
|
58
|
+
.filter((s) => acc.bySection.has(s.id))
|
|
59
|
+
.map((s) => buildSectionScore(s, acc.bySection.get(s.id)));
|
|
60
|
+
return {
|
|
61
|
+
overall: acc.totalW > 0 ? round1((100 * acc.totalCredit) / acc.totalW) : 0,
|
|
62
|
+
sections: sectionScores,
|
|
63
|
+
counts,
|
|
64
|
+
severityCounts,
|
|
65
|
+
automationCoverage: acc.applicableAll > 0 ? round1((100 * acc.resolvedAll) / acc.applicableAll) : 0,
|
|
66
|
+
expectedBand: profile.expectedBand,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function emptyBucket() {
|
|
70
|
+
return { w: 0, credit: 0, applicable: 0, resolved: 0, passed: 0, failed: 0, unknown: 0 };
|
|
71
|
+
}
|
|
72
|
+
function bucketFor(bySection, id) {
|
|
73
|
+
let bucket = bySection.get(id);
|
|
74
|
+
if (!bucket) {
|
|
75
|
+
bucket = emptyBucket();
|
|
76
|
+
bySection.set(id, bucket);
|
|
77
|
+
}
|
|
78
|
+
return bucket;
|
|
79
|
+
}
|
|
80
|
+
function accumulateScoredRule(acc, sectionMap, rule, finding) {
|
|
81
|
+
if (finding.status === 'NOT_APPLICABLE')
|
|
82
|
+
return;
|
|
83
|
+
const sec = sectionMap.get(finding.section) ?? {
|
|
84
|
+
id: finding.section,
|
|
85
|
+
title: finding.sectionTitle,
|
|
86
|
+
weight: 1,
|
|
87
|
+
};
|
|
88
|
+
const bucket = bucketFor(acc.bySection, sec.id);
|
|
89
|
+
bucket.applicable++;
|
|
90
|
+
acc.applicableAll++;
|
|
91
|
+
// Suppressed findings live under Accepted Risk only: counting them as open
|
|
92
|
+
// once produced "HIGH: 1" next to "No CRITICAL or HIGH findings. Nice."
|
|
93
|
+
// in the same report. Unsuppressed UNKNOWN still counts — the report
|
|
94
|
+
// narrative ("N checks need a human") is driven by counts.UNKNOWN.
|
|
95
|
+
if (finding.suppressedReason) {
|
|
96
|
+
if (finding.status === 'UNKNOWN')
|
|
97
|
+
bucket.unknown++;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
acc.counts[finding.status]++;
|
|
101
|
+
if (finding.status !== 'PASS' && finding.status !== 'UNKNOWN') {
|
|
102
|
+
acc.severityCounts[finding.severity]++;
|
|
103
|
+
}
|
|
104
|
+
if (finding.status === 'UNKNOWN') {
|
|
105
|
+
bucket.unknown++;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
applyRuleCredit(acc, bucket, rule, finding, sec.weight);
|
|
109
|
+
}
|
|
110
|
+
function applyRuleCredit(acc, bucket, rule, finding, sectionWeight) {
|
|
111
|
+
const w = ruleWeight(rule) * sectionWeight;
|
|
112
|
+
const credit = w * CREDIT[finding.status];
|
|
113
|
+
bucket.w += w;
|
|
114
|
+
bucket.credit += credit;
|
|
115
|
+
bucket.resolved++;
|
|
116
|
+
acc.resolvedAll++;
|
|
117
|
+
if (finding.status === 'PASS')
|
|
118
|
+
bucket.passed++;
|
|
119
|
+
else
|
|
120
|
+
bucket.failed++;
|
|
121
|
+
acc.totalW += w;
|
|
122
|
+
acc.totalCredit += credit;
|
|
123
|
+
}
|
|
124
|
+
function buildSectionScore(s, b) {
|
|
125
|
+
return {
|
|
126
|
+
id: s.id,
|
|
127
|
+
title: s.title,
|
|
128
|
+
score: b.w > 0 ? round1((10 * b.credit) / b.w) : null,
|
|
129
|
+
weight: s.weight,
|
|
130
|
+
applicable: b.applicable,
|
|
131
|
+
resolved: b.resolved,
|
|
132
|
+
passed: b.passed,
|
|
133
|
+
failed: b.failed,
|
|
134
|
+
unknown: b.unknown,
|
|
135
|
+
confidence: b.applicable > 0 ? round1((100 * b.resolved) / b.applicable) : 100,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function round1(n) {
|
|
139
|
+
return Math.round(n * 10) / 10;
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.js","sourceRoot":"","sources":["../../src/engine/score.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface SectionDef {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
/** Multiplier applied to every rule in the section when computing the total. */
|
|
5
|
+
weight: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Section registry. Security, supply chain and testing carry the heaviest
|
|
9
|
+
* multipliers: a repo that fails an auth control is worse than one with a
|
|
10
|
+
* slightly untidy README, and the score should say so.
|
|
11
|
+
*/
|
|
12
|
+
export declare const DEFAULT_SECTIONS: SectionDef[];
|
|
13
|
+
export declare function loadSections(rulesDir: string): SectionDef[];
|
|
14
|
+
//# sourceMappingURL=sections.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sections.d.ts","sourceRoot":"","sources":["../../src/engine/sections.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,UAAU,EAiBxC,CAAC;AAEF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,CAkB3D"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { parse as parseYaml } from 'yaml';
|
|
2
|
+
import { asMap, isMap, list, num, str } from '../util/yaml.js';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
/**
|
|
6
|
+
* Section registry. Security, supply chain and testing carry the heaviest
|
|
7
|
+
* multipliers: a repo that fails an auth control is worse than one with a
|
|
8
|
+
* slightly untidy README, and the score should say so.
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_SECTIONS = [
|
|
11
|
+
{ id: 'S1', title: 'Repository & Project Structure', weight: 0.7 },
|
|
12
|
+
{ id: 'S2', title: 'Security', weight: 1.7 },
|
|
13
|
+
{ id: 'S3', title: 'Supply Chain & Build Provenance', weight: 1.3 },
|
|
14
|
+
{ id: 'S4', title: 'Architecture & Design', weight: 1.0 },
|
|
15
|
+
{ id: 'S5', title: 'Code Quality', weight: 1.0 },
|
|
16
|
+
{ id: 'S6', title: 'Data & Database', weight: 1.0 },
|
|
17
|
+
{ id: 'S7', title: 'Testing & Quality Assurance', weight: 1.3 },
|
|
18
|
+
{ id: 'S8', title: 'CI/CD, Infrastructure & Observability', weight: 1.0 },
|
|
19
|
+
{ id: 'S9', title: 'Release & Change Management', weight: 0.7 },
|
|
20
|
+
{ id: 'S10', title: 'Dependencies & Third-Party', weight: 1.2 },
|
|
21
|
+
{ id: 'S11', title: 'Performance & Resilience', weight: 0.8 },
|
|
22
|
+
{ id: 'S12', title: 'Documentation & Knowledge', weight: 0.7 },
|
|
23
|
+
{ id: 'S13', title: 'Accessibility, i18n & Compliance', weight: 0.9 },
|
|
24
|
+
{ id: 'S14', title: 'AI / LLM-Era Risks', weight: 1.1 },
|
|
25
|
+
{ id: 'S15', title: 'Platform-Specific', weight: 1.0 },
|
|
26
|
+
{ id: 'S16', title: 'Future Readiness', weight: 0.4 },
|
|
27
|
+
];
|
|
28
|
+
export function loadSections(rulesDir) {
|
|
29
|
+
const file = path.join(rulesDir, 'sections.yaml');
|
|
30
|
+
if (!fs.existsSync(file))
|
|
31
|
+
return DEFAULT_SECTIONS;
|
|
32
|
+
try {
|
|
33
|
+
const doc = asMap(parseYaml(fs.readFileSync(file, 'utf8')));
|
|
34
|
+
const raw = list(doc.sections);
|
|
35
|
+
if (!raw)
|
|
36
|
+
return DEFAULT_SECTIONS;
|
|
37
|
+
const parsed = raw.filter(isMap).map((s) => ({
|
|
38
|
+
id: str(s.id) ?? 'S0',
|
|
39
|
+
title: str(s.title) ?? str(s.id) ?? 'Untitled',
|
|
40
|
+
weight: num(s.weight) ?? 1,
|
|
41
|
+
}));
|
|
42
|
+
// Keep defaults for any section the override file omits.
|
|
43
|
+
const ids = new Set(parsed.map((s) => s.id));
|
|
44
|
+
return [...parsed, ...DEFAULT_SECTIONS.filter((s) => !ids.has(s.id))];
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return DEFAULT_SECTIONS;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=sections.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sections.js","sourceRoot":"","sources":["../../src/engine/sections.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAS7B;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAiB;IAC5C,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,gCAAgC,EAAE,MAAM,EAAE,GAAG,EAAE;IAClE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE;IAC5C,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,iCAAiC,EAAE,MAAM,EAAE,GAAG,EAAE;IACnE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,uBAAuB,EAAE,MAAM,EAAE,GAAG,EAAE;IACzD,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE;IAChD,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,GAAG,EAAE;IACnD,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,6BAA6B,EAAE,MAAM,EAAE,GAAG,EAAE;IAC/D,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,uCAAuC,EAAE,MAAM,EAAE,GAAG,EAAE;IACzE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,6BAA6B,EAAE,MAAM,EAAE,GAAG,EAAE;IAC/D,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,4BAA4B,EAAE,MAAM,EAAE,GAAG,EAAE;IAC/D,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,0BAA0B,EAAE,MAAM,EAAE,GAAG,EAAE;IAC7D,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,2BAA2B,EAAE,MAAM,EAAE,GAAG,EAAE;IAC9D,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,kCAAkC,EAAE,MAAM,EAAE,GAAG,EAAE;IACrE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,oBAAoB,EAAE,MAAM,EAAE,GAAG,EAAE;IACvD,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,EAAE,MAAM,EAAE,GAAG,EAAE;IACtD,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,GAAG,EAAE;CACtD,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;IAClD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,gBAAgB,CAAC;IAClD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;QAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC/B,IAAI,CAAC,GAAG;YAAE,OAAO,gBAAgB,CAAC;QAClC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3C,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI;YACrB,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,UAAU;YAC9C,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAC,CAAC;QACJ,yDAAyD;QACzD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,MAAM,EAAE,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,gBAAgB,CAAC;IAC1B,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* USAT — Universal Software Audit Template
|
|
3
|
+
* Programmatic API. Everything the CLI does is available here.
|
|
4
|
+
*/
|
|
5
|
+
export { Project } from './util/project.js';
|
|
6
|
+
export { detect, loadDetectors, classifyMaturity, loadDetectorFile } from './detect/index.js';
|
|
7
|
+
export { loadRulePacks, applyRuleOverrides, validatePredicate, DEFAULT_WEIGHT, SEVERITY_LADDER, } from './engine/loader.js';
|
|
8
|
+
export { loadSections, DEFAULT_SECTIONS } from './engine/sections.js';
|
|
9
|
+
export { loadProfiles, dampen } from './engine/maturity.js';
|
|
10
|
+
export { runAudit } from './engine/audit.js';
|
|
11
|
+
export { score } from './engine/score.js';
|
|
12
|
+
export { evaluateRule, evalPredicate, ruleApplies, packApplies } from './engine/evaluate.js';
|
|
13
|
+
export { renderMarkdown, parseTrailer, trailer } from './report/markdown.js';
|
|
14
|
+
export { diffReports } from './engine/diff.js';
|
|
15
|
+
export { evaluateGate, blockingFindings } from './engine/gate.js';
|
|
16
|
+
export { loadConfig, EXAMPLE_CONFIG, CONFIG_FILE } from './config.js';
|
|
17
|
+
export type * from './types.js';
|
|
18
|
+
export type { SectionDef } from './engine/sections.js';
|
|
19
|
+
export type { MaturityProfile } from './engine/maturity.js';
|
|
20
|
+
export type { AuditOutcome, AuditOptions } from './engine/audit.js';
|
|
21
|
+
export type { ScoredRule } from './engine/score.js';
|
|
22
|
+
export type { EvalContext } from './engine/evaluate.js';
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC9F,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtE,mBAAmB,YAAY,CAAC;AAChC,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACpE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* USAT — Universal Software Audit Template
|
|
3
|
+
* Programmatic API. Everything the CLI does is available here.
|
|
4
|
+
*/
|
|
5
|
+
export { Project } from './util/project.js';
|
|
6
|
+
export { detect, loadDetectors, classifyMaturity, loadDetectorFile } from './detect/index.js';
|
|
7
|
+
export { loadRulePacks, applyRuleOverrides, validatePredicate, DEFAULT_WEIGHT, SEVERITY_LADDER, } from './engine/loader.js';
|
|
8
|
+
export { loadSections, DEFAULT_SECTIONS } from './engine/sections.js';
|
|
9
|
+
export { loadProfiles, dampen } from './engine/maturity.js';
|
|
10
|
+
export { runAudit } from './engine/audit.js';
|
|
11
|
+
export { score } from './engine/score.js';
|
|
12
|
+
export { evaluateRule, evalPredicate, ruleApplies, packApplies } from './engine/evaluate.js';
|
|
13
|
+
export { renderMarkdown, parseTrailer, trailer } from './report/markdown.js';
|
|
14
|
+
export { diffReports } from './engine/diff.js';
|
|
15
|
+
export { evaluateGate, blockingFindings } from './engine/gate.js';
|
|
16
|
+
export { loadConfig, EXAMPLE_CONFIG, CONFIG_FILE } from './config.js';
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC9F,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AuditReport, Finding } from '../types.js';
|
|
2
|
+
import type { MaturityProfile } from '../engine/maturity.js';
|
|
3
|
+
/** Renders the Section 14 report template from USAT.md. */
|
|
4
|
+
export declare function renderMarkdown(report: AuditReport, profile: MaturityProfile): string;
|
|
5
|
+
export declare function primaryLocation(f: Finding): string;
|
|
6
|
+
/**
|
|
7
|
+
* Compact, stable YAML used by `usat diff`. Kept in the Markdown on purpose —
|
|
8
|
+
* the report stays a single self-contained artifact you can commit, email, or
|
|
9
|
+
* paste into a PR.
|
|
10
|
+
*/
|
|
11
|
+
export declare const TRAILER_BEGIN = "<!-- USAT:TRAILER:BEGIN -->";
|
|
12
|
+
export declare const TRAILER_END = "<!-- USAT:TRAILER:END -->";
|
|
13
|
+
export declare function trailer(report: AuditReport): string;
|
|
14
|
+
/** Extracts the machine-readable trailer from a previously rendered report. */
|
|
15
|
+
export declare function parseTrailer(markdown: string): string | null;
|
|
16
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/report/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAoB,MAAM,aAAa,CAAC;AAC1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AA0C7D,2DAA2D;AAC3D,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,GAAG,MAAM,CAuBpF;AAwaD,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAGlD;AA8CD;;;;GAIG;AACH,eAAO,MAAM,aAAa,gCAAgC,CAAC;AAC3D,eAAO,MAAM,WAAW,8BAA8B,CAAC;AAEvD,wBAAgB,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAqBnD;AAWD,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyB5D"}
|