@mitre/hdf-diff 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.md +55 -0
- package/README.md +87 -0
- package/dist/diff.d.ts +49 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +791 -0
- package/dist/diff.js.map +1 -0
- package/dist/exit-codes.d.ts +42 -0
- package/dist/exit-codes.d.ts.map +1 -0
- package/dist/exit-codes.js +74 -0
- package/dist/exit-codes.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/matching/cci-match.d.ts +14 -0
- package/dist/matching/cci-match.d.ts.map +1 -0
- package/dist/matching/cci-match.js +102 -0
- package/dist/matching/cci-match.js.map +1 -0
- package/dist/matching/exact-id.d.ts +9 -0
- package/dist/matching/exact-id.d.ts.map +1 -0
- package/dist/matching/exact-id.js +88 -0
- package/dist/matching/exact-id.js.map +1 -0
- package/dist/matching/fuzzy-match.d.ts +26 -0
- package/dist/matching/fuzzy-match.d.ts.map +1 -0
- package/dist/matching/fuzzy-match.js +145 -0
- package/dist/matching/fuzzy-match.js.map +1 -0
- package/dist/matching/index.d.ts +36 -0
- package/dist/matching/index.d.ts.map +1 -0
- package/dist/matching/index.js +71 -0
- package/dist/matching/index.js.map +1 -0
- package/dist/matching/mapped-id.d.ts +13 -0
- package/dist/matching/mapped-id.d.ts.map +1 -0
- package/dist/matching/mapped-id.js +101 -0
- package/dist/matching/mapped-id.js.map +1 -0
- package/dist/matching/types.d.ts +34 -0
- package/dist/matching/types.d.ts.map +1 -0
- package/dist/matching/types.js +2 -0
- package/dist/matching/types.js.map +1 -0
- package/dist/normalize.d.ts +20 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/normalize.js +120 -0
- package/dist/normalize.js.map +1 -0
- package/dist/renderers/csv.d.ts +15 -0
- package/dist/renderers/csv.d.ts.map +1 -0
- package/dist/renderers/csv.js +77 -0
- package/dist/renderers/csv.js.map +1 -0
- package/dist/renderers/filter.d.ts +11 -0
- package/dist/renderers/filter.d.ts.map +1 -0
- package/dist/renderers/filter.js +29 -0
- package/dist/renderers/filter.js.map +1 -0
- package/dist/renderers/index.d.ts +17 -0
- package/dist/renderers/index.d.ts.map +1 -0
- package/dist/renderers/index.js +29 -0
- package/dist/renderers/index.js.map +1 -0
- package/dist/renderers/json.d.ts +13 -0
- package/dist/renderers/json.d.ts.map +1 -0
- package/dist/renderers/json.js +41 -0
- package/dist/renderers/json.js.map +1 -0
- package/dist/renderers/markdown.d.ts +13 -0
- package/dist/renderers/markdown.d.ts.map +1 -0
- package/dist/renderers/markdown.js +133 -0
- package/dist/renderers/markdown.js.map +1 -0
- package/dist/renderers/terminal.d.ts +14 -0
- package/dist/renderers/terminal.d.ts.map +1 -0
- package/dist/renderers/terminal.js +173 -0
- package/dist/renderers/terminal.js.map +1 -0
- package/dist/renderers/types.d.ts +12 -0
- package/dist/renderers/types.d.ts.map +1 -0
- package/dist/renderers/types.js +2 -0
- package/dist/renderers/types.js.map +1 -0
- package/dist/sbom.d.ts +42 -0
- package/dist/sbom.d.ts.map +1 -0
- package/dist/sbom.js +203 -0
- package/dist/sbom.js.map +1 -0
- package/dist/status.d.ts +27 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +137 -0
- package/dist/status.js.map +1 -0
- package/dist/summary.d.ts +6 -0
- package/dist/summary.d.ts.map +1 -0
- package/dist/summary.js +47 -0
- package/dist/summary.js.map +1 -0
- package/dist/types.d.ts +208 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +20 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +108 -0
- package/dist/validate.js.map +1 -0
- package/package.json +54 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why a requirement's effective status changed between two evaluations.
|
|
3
|
+
*
|
|
4
|
+
* Aligned with the hdf-comparison schema's Change_Reason enum:
|
|
5
|
+
* - resultChanged: The underlying test results differ (e.g., a fix was deployed)
|
|
6
|
+
* - overrideAdded: A new statusOverride (waiver/attestation) was added
|
|
7
|
+
* - overrideExpired: A statusOverride present in the old scan has expired by the new scan's timestamp
|
|
8
|
+
* - overrideRemoved: A statusOverride was removed between scans
|
|
9
|
+
* - overrideModified: An existing statusOverride was modified (reserved for future use)
|
|
10
|
+
* - impactChanged: The impact score changed (e.g., from 0.7 to 0.0 means N/A)
|
|
11
|
+
* - baselineUpgraded: The baseline version changed (reserved for future use)
|
|
12
|
+
* - controlMapped: A control was mapped to a different framework (reserved for future use)
|
|
13
|
+
* - scannerChanged: A different scanner was used (reserved for future use)
|
|
14
|
+
* - targetChanged: The scan target changed (reserved for future use)
|
|
15
|
+
* - configChanged: The scan configuration changed (reserved for future use)
|
|
16
|
+
* - metadataChanged: Non-impact baseline metadata changed (tags, descriptions, title, etc.)
|
|
17
|
+
*/
|
|
18
|
+
export type ChangeReason = 'resultChanged' | 'overrideAdded' | 'overrideExpired' | 'overrideRemoved' | 'overrideModified' | 'impactChanged' | 'baselineUpgraded' | 'controlMapped' | 'scannerChanged' | 'targetChanged' | 'configChanged' | 'metadataChanged';
|
|
19
|
+
/**
|
|
20
|
+
* Classification of how a requirement's state changed between evaluations.
|
|
21
|
+
* Uses SARIF-inspired vocabulary.
|
|
22
|
+
*
|
|
23
|
+
* - new: Requirement exists only in the new evaluation (was "added")
|
|
24
|
+
* - absent: Requirement exists only in the old evaluation (was "removed")
|
|
25
|
+
* - unchanged: Same effective status in both evaluations
|
|
26
|
+
* - updated: Status changed but doesn't fit fixed/regressed (was "changed")
|
|
27
|
+
* - fixed: Was failing/error, now passing
|
|
28
|
+
* - regressed: Was passing, now failing/error
|
|
29
|
+
* - moved: Requirement ID changed but content is the same (future use)
|
|
30
|
+
* - split: One requirement became multiple (future use)
|
|
31
|
+
* - merged: Multiple requirements became one (future use)
|
|
32
|
+
*/
|
|
33
|
+
export type RequirementState = 'new' | 'absent' | 'unchanged' | 'updated' | 'fixed' | 'regressed' | 'moved' | 'split' | 'merged';
|
|
34
|
+
/**
|
|
35
|
+
* A field-level difference on a requirement, following JSON Patch-like conventions.
|
|
36
|
+
*/
|
|
37
|
+
export interface FieldChange {
|
|
38
|
+
/** The operation type: add, remove, or replace */
|
|
39
|
+
op: 'add' | 'remove' | 'replace';
|
|
40
|
+
/** Dot-notation path to the changed field (e.g., 'impact', 'tags.cci') */
|
|
41
|
+
path: string;
|
|
42
|
+
/** Value in the old evaluation (undefined for 'add' operations) */
|
|
43
|
+
oldValue?: unknown;
|
|
44
|
+
/** Value in the new evaluation (undefined for 'remove' operations) */
|
|
45
|
+
newValue?: unknown;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The diff for a single requirement across two evaluations.
|
|
49
|
+
*/
|
|
50
|
+
export interface RequirementDiff {
|
|
51
|
+
/** The requirement ID (e.g., 'SV-238196') */
|
|
52
|
+
id: string;
|
|
53
|
+
/** Classification of the change */
|
|
54
|
+
state: RequirementState;
|
|
55
|
+
/** Why the status changed — empty array if unchanged */
|
|
56
|
+
changeReasons: ChangeReason[];
|
|
57
|
+
/** Full snapshot of the requirement from the old evaluation (null when state = 'new') */
|
|
58
|
+
before: Record<string, unknown> | null;
|
|
59
|
+
/** Full snapshot of the requirement from the new evaluation (null when state = 'absent') */
|
|
60
|
+
after: Record<string, unknown> | null;
|
|
61
|
+
/** The requirement title (from whichever evaluation has it) */
|
|
62
|
+
title?: string;
|
|
63
|
+
/** Effective status in the old evaluation (undefined if new) */
|
|
64
|
+
oldEffectiveStatus?: string;
|
|
65
|
+
/** Effective status in the new evaluation (undefined if absent) */
|
|
66
|
+
newEffectiveStatus?: string;
|
|
67
|
+
/** Impact in the old evaluation */
|
|
68
|
+
oldImpact?: number;
|
|
69
|
+
/** Impact in the new evaluation */
|
|
70
|
+
newImpact?: number;
|
|
71
|
+
/** Field-level diffs for non-status fields */
|
|
72
|
+
fieldChanges: FieldChange[];
|
|
73
|
+
/** The matching strategy that paired this requirement (e.g., 'exactId') */
|
|
74
|
+
matchStrategy?: string;
|
|
75
|
+
/** Confidence of the match (0.0-1.0) */
|
|
76
|
+
matchConfidence?: number;
|
|
77
|
+
/** Index into the sources array for fleet mode */
|
|
78
|
+
sourceIndex?: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Summary counts for the comparison.
|
|
82
|
+
*/
|
|
83
|
+
export interface ComparisonSummary {
|
|
84
|
+
/** Requirements that went from failing/error to passing */
|
|
85
|
+
fixed: number;
|
|
86
|
+
/** Requirements that went from passing to failing/error */
|
|
87
|
+
regressed: number;
|
|
88
|
+
/** Requirements present only in the new evaluation (was "added") */
|
|
89
|
+
new: number;
|
|
90
|
+
/** Requirements present only in the old evaluation (was "removed") */
|
|
91
|
+
absent: number;
|
|
92
|
+
/** Requirements with the same effective status */
|
|
93
|
+
unchanged: number;
|
|
94
|
+
/** Requirements whose status changed in a way other than fixed/regressed (was "changed") */
|
|
95
|
+
updated: number;
|
|
96
|
+
/** Total unique requirements across both evaluations */
|
|
97
|
+
total: number;
|
|
98
|
+
/** Number of requirements matched between old and new */
|
|
99
|
+
matchedCount: number;
|
|
100
|
+
/** Number of requirements only in old (unmatched) */
|
|
101
|
+
unmatchedOldCount: number;
|
|
102
|
+
/** Number of requirements only in new (unmatched) */
|
|
103
|
+
unmatchedNewCount: number;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The diff for a single component across two system documents.
|
|
107
|
+
* Used in systemDrift comparison mode.
|
|
108
|
+
*/
|
|
109
|
+
export interface ComponentDiff {
|
|
110
|
+
/** Component name */
|
|
111
|
+
name: string;
|
|
112
|
+
/** Classification of the change: new, absent, unchanged, or updated */
|
|
113
|
+
state: 'new' | 'absent' | 'unchanged' | 'updated';
|
|
114
|
+
/** Component snapshot from the old system (null when state = 'new') */
|
|
115
|
+
before: Record<string, unknown> | null;
|
|
116
|
+
/** Component snapshot from the new system (null when state = 'absent') */
|
|
117
|
+
after: Record<string, unknown> | null;
|
|
118
|
+
/** Field-level diffs between old and new component */
|
|
119
|
+
fieldChanges: FieldChange[];
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The diff for a single baseline across two evaluations.
|
|
123
|
+
*/
|
|
124
|
+
export interface BaselineDiff {
|
|
125
|
+
/** Baseline name */
|
|
126
|
+
name: string;
|
|
127
|
+
/** Version in the old evaluation */
|
|
128
|
+
oldVersion?: string;
|
|
129
|
+
/** Version in the new evaluation */
|
|
130
|
+
newVersion?: string;
|
|
131
|
+
/** Whether this baseline was new, absent, updated, or unchanged */
|
|
132
|
+
state: 'new' | 'absent' | 'updated' | 'unchanged';
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Metadata about a source document used in the comparison.
|
|
136
|
+
*/
|
|
137
|
+
export interface Source {
|
|
138
|
+
/** Role of the source in the comparison */
|
|
139
|
+
role: 'old' | 'new' | 'golden' | 'reference' | 'system';
|
|
140
|
+
/** Human-readable label */
|
|
141
|
+
label: string;
|
|
142
|
+
/** URI to the source document */
|
|
143
|
+
uri?: string;
|
|
144
|
+
/** Original format of the source (e.g., 'hdf-results-v2', 'inspec-exec-json-v1') */
|
|
145
|
+
originalFormat?: string;
|
|
146
|
+
/** Assessment timestamp from the source document */
|
|
147
|
+
assessmentTimestamp?: string;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* An annotation attached to a requirement diff.
|
|
151
|
+
*/
|
|
152
|
+
export interface Annotation {
|
|
153
|
+
/** Human-readable label */
|
|
154
|
+
label: string;
|
|
155
|
+
/** Description of the annotation */
|
|
156
|
+
text: string;
|
|
157
|
+
/** When the annotation was created */
|
|
158
|
+
timestamp?: string;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* The top-level comparison result comparing two or more HDF evaluations.
|
|
162
|
+
*/
|
|
163
|
+
export interface HdfComparison {
|
|
164
|
+
/** Schema version for the comparison format */
|
|
165
|
+
formatVersion: '1.0.0';
|
|
166
|
+
/** The mode of comparison */
|
|
167
|
+
comparisonMode: 'temporal' | 'baseline' | 'fleet' | 'multiSource' | 'baselineEvolution' | 'systemDrift';
|
|
168
|
+
/** When the comparison was generated */
|
|
169
|
+
timestamp?: string;
|
|
170
|
+
/** Source documents used in the comparison */
|
|
171
|
+
sources: Source[];
|
|
172
|
+
/** Matching configuration used */
|
|
173
|
+
matching?: MatchingConfig;
|
|
174
|
+
/** Aggregate counts */
|
|
175
|
+
summary: ComparisonSummary;
|
|
176
|
+
/** Per-baseline diffs */
|
|
177
|
+
baselineDiffs: BaselineDiff[];
|
|
178
|
+
/** Per-requirement diffs, sorted by id */
|
|
179
|
+
requirementDiffs: RequirementDiff[];
|
|
180
|
+
/** Per-component diffs (systemDrift mode only) */
|
|
181
|
+
componentDiffs?: ComponentDiff[];
|
|
182
|
+
/** Per-package diffs from embedded SBOM comparison (systemDrift mode only) */
|
|
183
|
+
packageDiffs?: import('./sbom.js').PackageDiff[];
|
|
184
|
+
/** URI identifying the system being compared (systemDrift mode only) */
|
|
185
|
+
systemRef?: string;
|
|
186
|
+
/** Requirements that drifted from a golden baseline (future use) */
|
|
187
|
+
drift?: RequirementDiff[];
|
|
188
|
+
/** Annotations keyed by requirement ID */
|
|
189
|
+
annotations?: Record<string, Annotation>;
|
|
190
|
+
/** Extension data for custom integrations */
|
|
191
|
+
extensions?: Record<string, unknown>;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Configuration for how requirements are matched between evaluations.
|
|
195
|
+
*/
|
|
196
|
+
export interface MatchingConfig {
|
|
197
|
+
/** The primary strategy used for matching requirements across sources */
|
|
198
|
+
primaryStrategy: string;
|
|
199
|
+
/** Minimum confidence threshold for a match */
|
|
200
|
+
confidenceThreshold?: number;
|
|
201
|
+
}
|
|
202
|
+
/** @deprecated Use RequirementState instead */
|
|
203
|
+
export type DiffStatus = RequirementState;
|
|
204
|
+
/** @deprecated Use ComparisonSummary instead */
|
|
205
|
+
export type DiffSummary = ComparisonSummary;
|
|
206
|
+
/** @deprecated Use HdfComparison instead */
|
|
207
|
+
export type HdfDiff = HdfComparison;
|
|
208
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of validating a document against the hdf-comparison schema.
|
|
3
|
+
*/
|
|
4
|
+
export interface ValidationResult {
|
|
5
|
+
/** Whether the document conforms to the schema */
|
|
6
|
+
valid: boolean;
|
|
7
|
+
/** Human-readable error messages (only present when valid is false) */
|
|
8
|
+
errors?: string[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Validate a document against the hdf-comparison schema.
|
|
12
|
+
*
|
|
13
|
+
* Loads and compiles all required schemas from the sibling hdf-schema package.
|
|
14
|
+
* The compiled validator is cached for performance on subsequent calls.
|
|
15
|
+
*
|
|
16
|
+
* @param doc - The document to validate (typically the output of `diffHdf()`)
|
|
17
|
+
* @returns Validation result with `valid` boolean and optional `errors` array
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateComparison(doc: unknown): ValidationResult;
|
|
20
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AASA;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,kDAAkD;IAClD,KAAK,EAAE,OAAO,CAAC;IACf,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAkGD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,CAQjE"}
|
package/dist/validate.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import Ajv2020 from 'ajv/dist/2020.js';
|
|
2
|
+
import addFormats from 'ajv-formats';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { resolve, dirname } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
/** Schema ID for the hdf-comparison schema */
|
|
8
|
+
const COMPARISON_SCHEMA_ID = 'https://mitre.github.io/hdf-libs/schemas/hdf-comparison/v1.0.0';
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the path to the hdf-schema package's schemas directory.
|
|
11
|
+
*
|
|
12
|
+
* In the monorepo layout, hdf-schema is a sibling package at `../hdf-schema`.
|
|
13
|
+
* The schemas are in `src/schemas/` within that package.
|
|
14
|
+
*/
|
|
15
|
+
function schemasDir() {
|
|
16
|
+
// From src/validate.ts (or dist/validate.js), go up to hdf-diff root, then to sibling
|
|
17
|
+
return resolve(__dirname, '..', '..', 'hdf-schema', 'src', 'schemas');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Load a JSON schema file from the hdf-schema package.
|
|
21
|
+
*/
|
|
22
|
+
function loadSchema(relativePath) {
|
|
23
|
+
const fullPath = resolve(schemasDir(), relativePath);
|
|
24
|
+
return JSON.parse(readFileSync(fullPath, 'utf-8'));
|
|
25
|
+
}
|
|
26
|
+
/** Cached compiled validator function */
|
|
27
|
+
let cachedValidator = null;
|
|
28
|
+
/**
|
|
29
|
+
* Build and cache an Ajv 2020-12 validator for the hdf-comparison schema.
|
|
30
|
+
*
|
|
31
|
+
* Loads schemas in dependency order:
|
|
32
|
+
* 1. All primitive schemas (common, platform, target, runner, statistics, result, extensions, comparison)
|
|
33
|
+
* 2. hdf-results schema (defines Evaluated_Requirement, referenced by comparison)
|
|
34
|
+
* 3. hdf-comparison schema (the top-level schema we validate against)
|
|
35
|
+
*
|
|
36
|
+
* The validator is compiled once and cached for all subsequent calls.
|
|
37
|
+
*/
|
|
38
|
+
function getValidator() {
|
|
39
|
+
if (cachedValidator)
|
|
40
|
+
return cachedValidator;
|
|
41
|
+
const ajv = new Ajv2020({
|
|
42
|
+
strict: false,
|
|
43
|
+
allErrors: true,
|
|
44
|
+
validateFormats: true,
|
|
45
|
+
});
|
|
46
|
+
addFormats(ajv);
|
|
47
|
+
// Load all primitive schemas first (order matters for $ref resolution)
|
|
48
|
+
const primitiveFiles = [
|
|
49
|
+
'primitives/common.schema.json',
|
|
50
|
+
'primitives/platform.schema.json',
|
|
51
|
+
'primitives/target.schema.json',
|
|
52
|
+
'primitives/runner.schema.json',
|
|
53
|
+
'primitives/statistics.schema.json',
|
|
54
|
+
'primitives/result.schema.json',
|
|
55
|
+
'primitives/amendments.schema.json',
|
|
56
|
+
'primitives/extensions.schema.json',
|
|
57
|
+
'primitives/parameter.schema.json',
|
|
58
|
+
'primitives/component.schema.json',
|
|
59
|
+
'primitives/data-flow.schema.json',
|
|
60
|
+
'primitives/system.schema.json',
|
|
61
|
+
'primitives/comparison.schema.json',
|
|
62
|
+
];
|
|
63
|
+
for (const file of primitiveFiles) {
|
|
64
|
+
ajv.addSchema(loadSchema(file));
|
|
65
|
+
}
|
|
66
|
+
// Load hdf-results (defines Evaluated_Requirement referenced by comparison)
|
|
67
|
+
ajv.addSchema(loadSchema('hdf-results.schema.json'));
|
|
68
|
+
// Load and compile hdf-comparison (the top-level schema we validate against)
|
|
69
|
+
ajv.addSchema(loadSchema('hdf-comparison.schema.json'));
|
|
70
|
+
// getSchema compiles on first access; the schema was just added so this always succeeds
|
|
71
|
+
cachedValidator = ajv.getSchema(COMPARISON_SCHEMA_ID);
|
|
72
|
+
return cachedValidator;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Format Ajv validation errors into human-readable strings.
|
|
76
|
+
*/
|
|
77
|
+
function formatErrors(validate) {
|
|
78
|
+
/* c8 ignore next -- Ajv always populates errors array when validation fails */
|
|
79
|
+
return (validate.errors ?? []).map((err) => {
|
|
80
|
+
const path = err.instancePath || '/';
|
|
81
|
+
/* c8 ignore next -- Ajv always populates err.message */
|
|
82
|
+
const msg = err.message ?? 'unknown error';
|
|
83
|
+
// Ajv always populates err.params on validation errors.
|
|
84
|
+
// The else branch exists for defensive typing since params is typed as optional.
|
|
85
|
+
/* c8 ignore start */
|
|
86
|
+
return err.params
|
|
87
|
+
? `${path}: ${msg} (${JSON.stringify(err.params)})`
|
|
88
|
+
: `${path}: ${msg}`;
|
|
89
|
+
/* c8 ignore stop */
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Validate a document against the hdf-comparison schema.
|
|
94
|
+
*
|
|
95
|
+
* Loads and compiles all required schemas from the sibling hdf-schema package.
|
|
96
|
+
* The compiled validator is cached for performance on subsequent calls.
|
|
97
|
+
*
|
|
98
|
+
* @param doc - The document to validate (typically the output of `diffHdf()`)
|
|
99
|
+
* @returns Validation result with `valid` boolean and optional `errors` array
|
|
100
|
+
*/
|
|
101
|
+
export function validateComparison(doc) {
|
|
102
|
+
const validate = getValidator();
|
|
103
|
+
if (validate(doc)) {
|
|
104
|
+
return { valid: true };
|
|
105
|
+
}
|
|
106
|
+
return { valid: false, errors: formatErrors(validate) };
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mitre/hdf-diff",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Structured comparison of HDF evaluation results — tracks what changed, why, and by how much",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/mitre/hdf-libs.git",
|
|
23
|
+
"directory": "hdf-diff"
|
|
24
|
+
},
|
|
25
|
+
"author": "MITRE Corporation",
|
|
26
|
+
"license": "Apache-2.0",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"ajv": "^8.17.0",
|
|
29
|
+
"ajv-formats": "^3.0.0"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=20.0.0"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"hdf",
|
|
36
|
+
"heimdall",
|
|
37
|
+
"diff",
|
|
38
|
+
"comparison",
|
|
39
|
+
"security",
|
|
40
|
+
"compliance"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "pnpm clean && tsc",
|
|
44
|
+
"clean": "rimraf dist",
|
|
45
|
+
"test": "pnpm run test:ts",
|
|
46
|
+
"test:ts": "vitest run",
|
|
47
|
+
"test:go": "echo 'No Go tests yet in hdf-diff'",
|
|
48
|
+
"test:watch": "vitest",
|
|
49
|
+
"test:coverage": "vitest run --coverage",
|
|
50
|
+
"type-check": "tsc --noEmit",
|
|
51
|
+
"lint": "eslint src test",
|
|
52
|
+
"lint:fix": "eslint src test --fix"
|
|
53
|
+
}
|
|
54
|
+
}
|