@stratum-hq/compliance 0.1.0 → 0.1.2
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/README.md +14 -12
- package/dist/scoring.d.ts +1 -1
- package/dist/scoring.d.ts.map +1 -1
- package/dist/scoring.js +4 -2
- package/dist/scoring.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
A content-free compliance **kernel** for [Stratum](https://github.com/stratum-hq/Stratum): the pure mechanics and type shapes any compliance product needs, with none of the content. Zero runtime dependencies, no database, no network, no provider, and no built-in catalog.
|
|
4
4
|
|
|
5
|
+
Read the documentation at [docs.stratum-hq.org](https://docs.stratum-hq.org/).
|
|
6
|
+
|
|
5
7
|
It gives you three things:
|
|
6
8
|
|
|
7
|
-
1. **Coverage scoring
|
|
8
|
-
2. **A finding state machine
|
|
9
|
-
3. **A control type vocabulary
|
|
9
|
+
1. **Coverage scoring:** diff a declared baseline against a resolved state and get a per-control breakdown plus a 0 to 100 score.
|
|
10
|
+
2. **A finding state machine:** a pure decision for whether a new evaluation outcome should open, resolve, or leave a finding alone.
|
|
11
|
+
3. **A control type vocabulary:** structural interfaces for describing a catalog of controls.
|
|
10
12
|
|
|
11
13
|
> [!NOTE]
|
|
12
14
|
> This is **not** a batteries-included compliance solution. It ships no frameworks, no controls, no provider mappings, and no thresholds. **Bring your own catalog:** you supply the content and the persistence; this package supplies the arithmetic and the shapes.
|
|
@@ -41,8 +43,8 @@ const result = scoreCoverage(baseline, resolved);
|
|
|
41
43
|
// }
|
|
42
44
|
```
|
|
43
45
|
|
|
44
|
-
- `status` is `"compliant"` (matches), `"drift"` (present but wrong), or `"missing"` (no resolved entry for that key).
|
|
45
|
-
- `score` is `round(compliant / total * 100)`. An **empty baseline scores 100
|
|
46
|
+
- `status` is `"compliant"` (matches), `"drift"` (present but wrong), or `"missing"` (no own resolved entry for that key).
|
|
47
|
+
- `score` is `round(compliant / total * 100)`. An **empty baseline scores 100**. Nothing is required, so nothing is out of compliance.
|
|
46
48
|
|
|
47
49
|
### Equality
|
|
48
50
|
|
|
@@ -57,16 +59,16 @@ looseEqual(true, "true"); // => true
|
|
|
57
59
|
|
|
58
60
|
## Finding state machine
|
|
59
61
|
|
|
60
|
-
`reconcileFinding` decides what should happen to a control's finding when a new evaluation outcome arrives. It is a pure decision
|
|
62
|
+
`reconcileFinding` decides what should happen to a control's finding when a new evaluation outcome arrives. It is a pure decision; you persist the result.
|
|
61
63
|
|
|
62
64
|
```typescript
|
|
63
65
|
import { reconcileFinding } from "@stratum-hq/compliance";
|
|
64
66
|
|
|
65
|
-
reconcileFinding("fail", "none"); // { type: "open" }
|
|
66
|
-
reconcileFinding("fail", "open"); // { type: "noop" }
|
|
67
|
-
reconcileFinding("pass", "open"); // { type: "resolve" }
|
|
68
|
-
reconcileFinding("pass", "accepted"); // { type: "noop" }
|
|
69
|
-
reconcileFinding("na", "open"); // { type: "noop" }
|
|
67
|
+
reconcileFinding("fail", "none"); // { type: "open" } (new gap)
|
|
68
|
+
reconcileFinding("fail", "open"); // { type: "noop" } (already tracked)
|
|
69
|
+
reconcileFinding("pass", "open"); // { type: "resolve" } (gap closed)
|
|
70
|
+
reconcileFinding("pass", "accepted"); // { type: "noop" } (accepted risk untouched)
|
|
71
|
+
reconcileFinding("na", "open"); // { type: "noop" } (na/error never change a finding)
|
|
70
72
|
```
|
|
71
73
|
|
|
72
74
|
The rules:
|
|
@@ -75,7 +77,7 @@ The rules:
|
|
|
75
77
|
- `pass` **resolves** an active finding (`open` / `remediating`), and leaves an `accepted` finding untouched; otherwise there is nothing to resolve.
|
|
76
78
|
- `na` and `error` are always no-ops.
|
|
77
79
|
|
|
78
|
-
Map `{ type: "open" }` and `{ type: "resolve" }` onto your own inserts and updates
|
|
80
|
+
Map `{ type: "open" }` and `{ type: "resolve" }` onto your own inserts and updates. The state machine has no opinion about storage.
|
|
79
81
|
|
|
80
82
|
## Type vocabulary
|
|
81
83
|
|
package/dist/scoring.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export declare function looseEqual(expected: unknown, actual: unknown): boolean;
|
|
|
41
41
|
* Diff a declared baseline against a resolved value map. Pure. No IO.
|
|
42
42
|
*
|
|
43
43
|
* Only the keys present in `baseline` are scored; extra keys in `resolved` are
|
|
44
|
-
* ignored. A baseline key with no
|
|
44
|
+
* ignored. A baseline key with no own entry in `resolved` is `missing`; a
|
|
45
45
|
* matching entry whose value satisfies `equals` is `compliant`, otherwise it is
|
|
46
46
|
* `drift`. An empty baseline scores 100 (nothing is required, so nothing is out
|
|
47
47
|
* of compliance).
|
package/dist/scoring.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../src/scoring.ts"],"names":[],"mappings":"AAQA,8DAA8D;AAC9D,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,OAAO,GAAG,SAAS,CAAC;AAE9D,8DAA8D;AAC9D,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,OAAO,CAAC;IAClB,sEAAsE;IACtE,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;CAC1D;AAMD;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAMtE;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,EAC5C,OAAO,CAAC,EAAE,YAAY,GACrB,cAAc,
|
|
1
|
+
{"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../src/scoring.ts"],"names":[],"mappings":"AAQA,8DAA8D;AAC9D,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,OAAO,GAAG,SAAS,CAAC;AAE9D,8DAA8D;AAC9D,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,OAAO,CAAC;IAClB,sEAAsE;IACtE,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;CAC1D;AAMD;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAMtE;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,EAC5C,OAAO,CAAC,EAAE,YAAY,GACrB,cAAc,CAiChB"}
|
package/dist/scoring.js
CHANGED
|
@@ -34,7 +34,7 @@ function looseEqual(expected, actual) {
|
|
|
34
34
|
* Diff a declared baseline against a resolved value map. Pure. No IO.
|
|
35
35
|
*
|
|
36
36
|
* Only the keys present in `baseline` are scored; extra keys in `resolved` are
|
|
37
|
-
* ignored. A baseline key with no
|
|
37
|
+
* ignored. A baseline key with no own entry in `resolved` is `missing`; a
|
|
38
38
|
* matching entry whose value satisfies `equals` is `compliant`, otherwise it is
|
|
39
39
|
* `drift`. An empty baseline scores 100 (nothing is required, so nothing is out
|
|
40
40
|
* of compliance).
|
|
@@ -47,7 +47,9 @@ function scoreCoverage(baseline, resolved, options) {
|
|
|
47
47
|
let missing = 0;
|
|
48
48
|
for (const key of Object.keys(baseline)) {
|
|
49
49
|
const expected = baseline[key];
|
|
50
|
-
|
|
50
|
+
// Only an own key counts: a plain object inherits `constructor`,
|
|
51
|
+
// `toString`, and other members that are not resolved controls.
|
|
52
|
+
const entry = Object.hasOwn(resolved, key) ? resolved[key] : undefined;
|
|
51
53
|
if (entry === undefined) {
|
|
52
54
|
missing++;
|
|
53
55
|
details.push({ key, expected, actual: undefined, status: "missing" });
|
package/dist/scoring.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scoring.js","sourceRoot":"","sources":["../src/scoring.ts"],"names":[],"mappings":";AAAA,6BAA6B;AAC7B,EAAE;AACF,gFAAgF;AAChF,0EAA0E;AAC1E,iFAAiF;AACjF,4EAA4E;AAC5E,4EAA4E;;AAgD5E,gCAMC;AAWD,
|
|
1
|
+
{"version":3,"file":"scoring.js","sourceRoot":"","sources":["../src/scoring.ts"],"names":[],"mappings":";AAAA,6BAA6B;AAC7B,EAAE;AACF,gFAAgF;AAChF,0EAA0E;AAC1E,iFAAiF;AACjF,4EAA4E;AAC5E,4EAA4E;;AAgD5E,gCAMC;AAWD,sCAqCC;AApED,SAAS,YAAY,CAAC,KAAc;IAClC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,UAAU,CAAC,QAAiB,EAAE,MAAe;IAC3D,IAAI,YAAY,CAAC,QAAQ,CAAC,IAAI,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;YAAE,OAAO,KAAK,CAAC;QACnE,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,aAAa,CAC3B,QAAiC,EACjC,QAA4C,EAC5C,OAAsB;IAEtB,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,UAAU,CAAC;IAC7C,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxC,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC/B,iEAAiE;QACjE,gEAAgE;QAChE,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAEvE,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,EAAE,CAAC;YACV,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;YACtE,SAAS;QACX,CAAC;QAED,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC;QAC3B,IAAI,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC;YAC7B,SAAS,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;QAC/D,CAAC;aAAM,CAAC;YACN,KAAK,EAAE,CAAC;YACR,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC;IAC7B,MAAM,KAAK,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;IAExE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAC9D,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stratum-hq/compliance",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Content-free compliance kernel for Stratum: baseline coverage scoring, a finding state machine, and a control type vocabulary",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"compliance",
|
|
@@ -39,11 +39,11 @@
|
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"typescript": "^5.4.0",
|
|
42
|
-
"vitest": "^1.
|
|
42
|
+
"vitest": "^4.1.11"
|
|
43
43
|
},
|
|
44
44
|
"license": "MIT",
|
|
45
45
|
"author": "Christian Crank",
|
|
46
|
-
"homepage": "https://
|
|
46
|
+
"homepage": "https://docs.stratum-hq.org/",
|
|
47
47
|
"bugs": "https://github.com/stratum-hq/Stratum/issues",
|
|
48
48
|
"engines": {
|
|
49
49
|
"node": ">=20.0.0"
|