@openwop/openwop-conformance 1.138.1 → 1.140.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/README.md +1 -1
- package/coverage.md +1 -1
- package/dist/lib/scenario-disposition.js +14 -1
- package/dist/lib/spec-coherence.js +103 -0
- package/package.json +1 -1
- package/schemas/CORPUS-STAMP.json +2 -2
- package/src/global-setup.ts +169 -0
- package/src/lib/fork-availability.test.ts +69 -0
- package/src/lib/fork-availability.ts +61 -0
- package/src/lib/global-setup.test.ts +76 -0
- package/src/lib/scenario-disposition.ts +16 -0
- package/src/lib/spec-coherence-registry.test.ts +100 -0
- package/src/lib/spec-coherence.ts +106 -0
- package/src/scenarios/a2a-task-roundtrip.test.ts +66 -10
- package/src/scenarios/agent-channel-dispatch.test.ts +3 -3
- package/src/scenarios/replay-fanout-suppression.test.ts +39 -6
- package/src/scenarios/replay-fork-arbitrary.test.ts +9 -3
- package/src/scenarios/replay-fork.test.ts +10 -2
- package/src/scenarios/replay-side-effect-suppression.test.ts +3 -2
- package/src/scenarios/replayDeterminism.test.ts +8 -6
- package/src/scenarios/webhook-signed-delivery.test.ts +53 -7
- package/src/setup.ts +1 -1
- package/vitest.config.ts +7 -0
|
@@ -16,13 +16,59 @@
|
|
|
16
16
|
* Capability-gated: skips when the host does not advertise
|
|
17
17
|
* `capabilities.webhooks.supported = true`.
|
|
18
18
|
*
|
|
19
|
-
* Operator contract
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* `OPENWOP_WEBHOOK_ALLOW_PRIVATE=true`
|
|
24
|
-
*
|
|
25
|
-
*
|
|
19
|
+
* Operator contract — THREE gates, not one (clarified 2026-08-25).
|
|
20
|
+
*
|
|
21
|
+
* The test receiver is `http://127.0.0.1:{port}/`, and `webhooks.md`
|
|
22
|
+
* forbids it three separate times. A host honoring
|
|
23
|
+
* `OPENWOP_WEBHOOK_ALLOW_PRIVATE=true` (or an equivalent opt-in) MUST
|
|
24
|
+
* relax ALL THREE for this scenario to be witnessable:
|
|
25
|
+
*
|
|
26
|
+
* 1. **Scheme.** §"SSRF protection" bullet 1 rejects non-`https://`
|
|
27
|
+
* protocols, and §"Register" says `url` MUST be `https://`. The
|
|
28
|
+
* receiver is plain `http`. This gate is the one the flag's
|
|
29
|
+
* description omitted until 2026-08-25, and it fires FIRST on a
|
|
30
|
+
* host that validates scheme before address.
|
|
31
|
+
* 2. **Registration-time address check.** §"SSRF protection": the
|
|
32
|
+
* server MUST validate subscription URLs at registration time and
|
|
33
|
+
* reject loopback / RFC1918 / link-local / ULA / metadata.
|
|
34
|
+
* 3. **Delivery-time re-resolution.** §"Delivery-time egress
|
|
35
|
+
* validation (RFC 0093)": the dispatcher MUST re-resolve at
|
|
36
|
+
* delivery time and validate every resolved address against the
|
|
37
|
+
* same ranges.
|
|
38
|
+
*
|
|
39
|
+
* Gates 2 and 3 are INDEPENDENT MUSTs at different layers, so "which
|
|
40
|
+
* layer must the opt-in reach" is not a matter of taste: a relaxation
|
|
41
|
+
* reaching only one layer cannot produce a witness. Delivery-only leaves
|
|
42
|
+
* registration returning `400 webhook_url_rejected` (observed on a tier-2
|
|
43
|
+
* host, 2026-08-25); registration-only leaves the dispatcher re-resolving
|
|
44
|
+
* `127.0.0.1` and refusing to connect. A host whose opt-in reaches one
|
|
45
|
+
* layer is not non-conformant — it simply cannot witness this scenario,
|
|
46
|
+
* and that is a property of the test posture, not of its webhook signing.
|
|
47
|
+
*
|
|
48
|
+
* What that costs is more than a checkmark, and it is worth stating plainly
|
|
49
|
+
* because the cost is invisible from inside such a host. This scenario is
|
|
50
|
+
* typically the ONLY automated oracle a host has on its delivery header
|
|
51
|
+
* NAMES: a host-local delivery test almost always compares a hand-written
|
|
52
|
+
* literal in the test to a hand-written literal in the implementation, which
|
|
53
|
+
* is a mirror — typo both and it stays green — while the HMAC assertion
|
|
54
|
+
* beside it recomputes from `node:crypto` and is real. So the names go
|
|
55
|
+
* unchecked exactly where the bytes are checked well. All three reference
|
|
56
|
+
* hosts emitted invented header names for 16 days (openwop-examples#22) and
|
|
57
|
+
* the runs that would have caught it were the ones this scenario could not
|
|
58
|
+
* reach. A host that runs it is not thereby careful; it is thereby measured.
|
|
59
|
+
*
|
|
60
|
+
* Relaxing gates 2 and 3 is test-only posture. See
|
|
61
|
+
* `SECURITY/threat-model-secret-leakage.md` §4.9 for why a
|
|
62
|
+
* registration-time relaxation is the more dangerous of the two: it
|
|
63
|
+
* writes a durable subscription row that survives the flag being turned
|
|
64
|
+
* back off.
|
|
65
|
+
*
|
|
66
|
+
* When the host rejects with `400 webhook_url_rejected`, this scenario
|
|
67
|
+
* records `blocked` (RFC 0148 §A) — NOT a pass. Under a plain
|
|
68
|
+
* `vitest run` the console still prints "1 passed", because that is
|
|
69
|
+
* vitest reporting test outcomes rather than conformance dispositions;
|
|
70
|
+
* the run-end disposition summary (`src/global-setup.ts`) is where the
|
|
71
|
+
* `blocked` becomes visible without `--certify`.
|
|
26
72
|
*
|
|
27
73
|
* @see spec/v1/webhooks.md §"Signature scheme"
|
|
28
74
|
*/
|
package/src/setup.ts
CHANGED
|
@@ -278,7 +278,7 @@ afterAll(({}, suite) => {
|
|
|
278
278
|
// the marker detail — never to a pass. Floors still REJECT that row, so the
|
|
279
279
|
// honest bundle row and the pressure to say why both survive. The rule is
|
|
280
280
|
// `resolveFileRecord` (pinned by conformance-execution-witness.test.ts).
|
|
281
|
-
const { disposition, detail } = resolveFileRecord(states, gateReason, assertionCount, softSkipDisposition(file));
|
|
281
|
+
const { disposition, detail } = resolveFileRecord(states, gateReason, assertionCount, softSkipDisposition(file), file);
|
|
282
282
|
const fileRequirementId = requirementIdForFile(file);
|
|
283
283
|
// A scenario that classified ITSELF wins outright — including its `detail` and
|
|
284
284
|
// its `assertionCount`.
|
package/vitest.config.ts
CHANGED
|
@@ -17,5 +17,12 @@ export default defineConfig({
|
|
|
17
17
|
// populates `lib/fixtures.ts` so `describe.skipIf(...)` predicates
|
|
18
18
|
// see the cached set when scenarios register their tests.
|
|
19
19
|
setupFiles: ['src/setup.ts'],
|
|
20
|
+
// RFC 0148 §A run-end disposition summary. `setup.ts` records an honest
|
|
21
|
+
// disposition per scenario file on EVERY run; before this, only
|
|
22
|
+
// `--certify` set OPENWOP_LEDGER_PATH, so on a plain `vitest run` the
|
|
23
|
+
// recording was computed and then discarded, leaving "N passed" as the
|
|
24
|
+
// sole artifact for rows the suite had classified `blocked`. This routes
|
|
25
|
+
// the ledger to a temp file and prints what did not witness.
|
|
26
|
+
globalSetup: ['src/global-setup.ts'],
|
|
20
27
|
},
|
|
21
28
|
});
|