@openwop/openwop-conformance 2.35.0 → 2.35.1
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/CHANGELOG.md +7 -0
- package/README.md +1 -1
- package/dist/lib/certification-bundle-v3.js +21 -4
- package/dist/spec-artifacts.lock.json +2 -2
- package/package.json +2 -2
- package/schemas/CORPUS-STAMP.json +3 -3
- package/src/lib/behavior-gate.ts +7 -1
- package/src/lib/certification-bundle-v3.ts +18 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# `@openwop/openwop-conformance` Changelog
|
|
2
2
|
|
|
3
|
+
## [2.35.1] — 2026-09-22 — the opt-out check could never fire on a real bundle
|
|
4
|
+
|
|
5
|
+
- **RFC 0195 §3's `opted-out-but-advertised` rule, as shipped in 2.35.0, derived an empty set from every real bundle.** It read `openwop.profile.<name>` rows, and those rows never reach a bundle; the skipped rows that do named no profile. Measured on MyndHyve's committed 2.35.0 bundle, four declared opt-outs: `--verify` printed `opted out: (none)`. The rule's self-tests used synthetic rows that no real bundle contains — a check that cannot fail on the thing it exists to check.
|
|
6
|
+
- **The opted-out name now rides on the signed per-test rows' detail** (`…via OPENWOP_OPTED_OUT_PROFILES: <name>`), and the verifier derives the set from that as well as from the old carrier. Measured on the reference host with `connections.packsSupported` opted out: the requirement rows a bundle carries (`0177.provider-conflict.*`) now name it. A regression test uses rows shaped exactly like the committed MyndHyve bundle and pins that they derive nothing without the name.
|
|
7
|
+
- Bundles cut before 2.35.1 carry no names and derive an empty set — honest, not an error. RFC 0195 returns to `Active` until a committed bundle shows the rule deriving real opt-outs.
|
|
8
|
+
- Suite patch; corpus release stays `2.35.0`.
|
|
9
|
+
|
|
3
10
|
## [2.35.0] — 2026-09-21 — blocked means blocked, the declarations are signed, and the header a subscriber reads is checked
|
|
4
11
|
|
|
5
12
|
### Security
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
# --legacy-peer-deps is REQUIRED, not optional: the exact peer pin is what npm's
|
|
12
12
|
# default resolver refuses. npm 10.9 fails outright with
|
|
13
13
|
# "Cannot read properties of null (reading 'edgesOut')" — use npm >= 11.
|
|
14
|
-
npm install --legacy-peer-deps @openwop/openwop-conformance@2.35.
|
|
14
|
+
npm install --legacy-peer-deps @openwop/openwop-conformance@2.35.1 @openwop/spec-artifacts@2.35.1
|
|
15
15
|
# or run without install:
|
|
16
16
|
npx @openwop/openwop-conformance --base-url https://api.example.com --api-key hk_test_...
|
|
17
17
|
```
|
|
@@ -74,11 +74,28 @@ export function publicKeyFromPrivate(privateKeyPem) {
|
|
|
74
74
|
}
|
|
75
75
|
const OPT_OUT_ROW = 'openwop.profile.';
|
|
76
76
|
/** The opt-outs a bundle's signed rows record. */
|
|
77
|
+
/**
|
|
78
|
+
* 2.35.1. The 2.35.0 version read only `openwop.profile.<name>` rows — which
|
|
79
|
+
* never reach a bundle — so on MyndHyve's real bundle (four declared opt-outs)
|
|
80
|
+
* it derived NOTHING, and `opted-out-but-advertised` could not fire: a rule
|
|
81
|
+
* pinned only by synthetic self-test rows. The name now rides on the signed
|
|
82
|
+
* per-test rows' detail (`…OPENWOP_OPTED_OUT_PROFILES: <name>`); both carriers
|
|
83
|
+
* are read. A bundle cut before 2.35.1 carries no names and derives an empty set.
|
|
84
|
+
*/
|
|
85
|
+
const OPT_OUT_DETAIL = /OPENWOP_OPTED_OUT_PROFILES: ([a-z][A-Za-z0-9_.-]*)/;
|
|
77
86
|
export function optedOutFromRows(rows) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
87
|
+
const names = new Set();
|
|
88
|
+
for (const r of rows) {
|
|
89
|
+
if (r.result !== 'skipped')
|
|
90
|
+
continue;
|
|
91
|
+
const d = r.detail ?? '';
|
|
92
|
+
if (r.id.startsWith(OPT_OUT_ROW) && d.includes('OPENWOP_OPTED_OUT_PROFILES'))
|
|
93
|
+
names.add(r.id.slice(OPT_OUT_ROW.length));
|
|
94
|
+
const m = OPT_OUT_DETAIL.exec(d);
|
|
95
|
+
if (m)
|
|
96
|
+
names.add(m[1]);
|
|
97
|
+
}
|
|
98
|
+
return [...names].sort();
|
|
82
99
|
}
|
|
83
100
|
export function verifyBundleV3(bundle, opts = {}) {
|
|
84
101
|
const rejections = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openwop/openwop-conformance",
|
|
3
|
-
"version": "2.35.
|
|
3
|
+
"version": "2.35.1",
|
|
4
4
|
"description": "Production-ready black-box conformance suite for OpenWOP v1.0 compliant servers.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -56,6 +56,6 @@
|
|
|
56
56
|
"@openwop/spec-artifacts": "file:../spec-artifacts"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"@openwop/spec-artifacts": "2.35.
|
|
59
|
+
"@openwop/spec-artifacts": "2.35.1"
|
|
60
60
|
}
|
|
61
61
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_comment": "Provenance of @openwop/spec-artifacts (RFC 0168 §D.2). files: SHA-256 per file; the conformance suite compares the installed peer against dist/spec-artifacts.lock.json at start.",
|
|
3
3
|
"package": "@openwop/spec-artifacts",
|
|
4
|
-
"version": "2.35.
|
|
4
|
+
"version": "2.35.1",
|
|
5
5
|
"corpusTag": "v2.35.0",
|
|
6
6
|
"files": {
|
|
7
7
|
"api/.redocly.lint-ignore.yaml": "bf5a8350b88a72fa43f59605ed8d903ed24b6cfccda5e45509c9f6ed9ee4e712",
|
|
@@ -201,7 +201,7 @@
|
|
|
201
201
|
"schemas/workspace-file.schema.json": "464de85c2a068243084ee9c1d969bc7cd5d8f7948574e58450d6493c38a0e1e4",
|
|
202
202
|
"spec/v1/alias-detectors.json": "40069d5976eeb6ba1384a648e57e5cfd673db3fce36175115fb8293bee9664d4",
|
|
203
203
|
"spec/v1/capability-declaration-classes.json": "e7729aed5c4b4e1dd02abab0530f14cc95f5d4070fe51fb139e7f5cccefa00c6",
|
|
204
|
-
"spec/v1/core-standard-manifest.json": "
|
|
204
|
+
"spec/v1/core-standard-manifest.json": "f053ba1cbab7b8a0b79d1af9f9453f263197af0b5146a14e01dd8900516017ba",
|
|
205
205
|
"spec/v1/deprecations.json": "307083ce29c23fd406015951f99a30d78d6187ff061d38dc9732f62191b40f3f",
|
|
206
206
|
"spec/v1/deprecations.schema.json": "18c87e78bedc210431f795ae44c5b5d202f2f3317850d5cf86867d4f1fa1cdfb",
|
|
207
207
|
"spec/v1/event-codemap.json": "3da60d884157793a360da532a9fcbbfb5285636db325a74cec94b34622186d97",
|
|
@@ -282,5 +282,5 @@
|
|
|
282
282
|
"spec/v2/release.json": "5b9a956506b011835f1bd2fec54064d9cc8657d8a45a371314850da377d69069",
|
|
283
283
|
"spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624"
|
|
284
284
|
},
|
|
285
|
-
"corpusCommit": "
|
|
285
|
+
"corpusCommit": "5281a4ee3f878f9fe401e6f6a9faf33558cdf894"
|
|
286
286
|
}
|
package/src/lib/behavior-gate.ts
CHANGED
|
@@ -105,7 +105,13 @@ export function behaviorGate(profileName: string, advertised: boolean): boolean
|
|
|
105
105
|
recordRequirement(
|
|
106
106
|
profileRequirementId(profileName),
|
|
107
107
|
'skipped',
|
|
108
|
-
|
|
108
|
+
// 2.35.1: the NAME rides on the detail. This row id never reaches a
|
|
109
|
+
// bundle; the per-test rows that do inherit this detail, and they are
|
|
110
|
+
// inside witnessSha256 — so the verifier can derive which profile or
|
|
111
|
+
// family was opted out from signed rows (RFC 0195 §3). Until 2.35.1 the
|
|
112
|
+
// detail named nothing and the verifier derived an empty set on every
|
|
113
|
+
// real bundle.
|
|
114
|
+
`operator declared an honest opt-out via OPENWOP_OPTED_OUT_PROFILES: ${profileName}`,
|
|
109
115
|
);
|
|
110
116
|
// eslint-disable-next-line no-console
|
|
111
117
|
console.warn(
|
|
@@ -163,11 +163,25 @@ export interface V3Verdict {
|
|
|
163
163
|
|
|
164
164
|
const OPT_OUT_ROW = 'openwop.profile.';
|
|
165
165
|
/** The opt-outs a bundle's signed rows record. */
|
|
166
|
+
/**
|
|
167
|
+
* 2.35.1. The 2.35.0 version read only `openwop.profile.<name>` rows — which
|
|
168
|
+
* never reach a bundle — so on MyndHyve's real bundle (four declared opt-outs)
|
|
169
|
+
* it derived NOTHING, and `opted-out-but-advertised` could not fire: a rule
|
|
170
|
+
* pinned only by synthetic self-test rows. The name now rides on the signed
|
|
171
|
+
* per-test rows' detail (`…OPENWOP_OPTED_OUT_PROFILES: <name>`); both carriers
|
|
172
|
+
* are read. A bundle cut before 2.35.1 carries no names and derives an empty set.
|
|
173
|
+
*/
|
|
174
|
+
const OPT_OUT_DETAIL = /OPENWOP_OPTED_OUT_PROFILES: ([a-z][A-Za-z0-9_.-]*)/;
|
|
166
175
|
export function optedOutFromRows(rows: ReadonlyArray<{ readonly id: string; readonly result: string; readonly detail?: string }>): string[] {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
.
|
|
176
|
+
const names = new Set<string>();
|
|
177
|
+
for (const r of rows) {
|
|
178
|
+
if (r.result !== 'skipped') continue;
|
|
179
|
+
const d = r.detail ?? '';
|
|
180
|
+
if (r.id.startsWith(OPT_OUT_ROW) && d.includes('OPENWOP_OPTED_OUT_PROFILES')) names.add(r.id.slice(OPT_OUT_ROW.length));
|
|
181
|
+
const m = OPT_OUT_DETAIL.exec(d);
|
|
182
|
+
if (m) names.add(m[1] as string);
|
|
183
|
+
}
|
|
184
|
+
return [...names].sort();
|
|
171
185
|
}
|
|
172
186
|
|
|
173
187
|
export function verifyBundleV3(bundle: BundleV3, opts: VerifyV3Options = {}): V3Verdict {
|