@openwop/openwop-conformance 1.153.0 → 1.155.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.
Files changed (67) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +11 -2
  3. package/api/openapi.yaml +17 -1
  4. package/coverage.md +3 -0
  5. package/dist/cli.js +7 -2
  6. package/package.json +31 -2
  7. package/requirements.json +256 -20
  8. package/schemas/CORPUS-STAMP.json +100 -3
  9. package/schemas/README.md +1 -0
  10. package/schemas/capabilities.schema.json +7 -0
  11. package/schemas/run-event-payloads.schema.json +9 -0
  12. package/schemas/run-snapshot.schema.json +4 -0
  13. package/schemas/subject.schema.json +56 -0
  14. package/src/cli.ts +7 -5
  15. package/src/global-setup.ts +13 -0
  16. package/src/lib/corpus-stamp.ts +125 -0
  17. package/src/scenarios/anonymous-actor-shape.test.ts +6 -1
  18. package/src/scenarios/auth-subject-link.test.ts +20 -1
  19. package/src/scenarios/discovery.test.ts +12 -0
  20. package/src/scenarios/identity-owner-shape.test.ts +7 -3
  21. package/src/scenarios/owner-subject-echo.test.ts +127 -0
  22. package/src/scenarios/owner-subject-shape.test.ts +96 -0
  23. package/src/scenarios/protocol-versions-array.test.ts +103 -0
  24. package/src/scenarios/webhook-signed-delivery.test.ts +22 -0
  25. package/src/lib/capabilities-auth-subject-link.test.ts +0 -103
  26. package/src/lib/fork-availability.test.ts +0 -69
  27. package/src/lib/global-setup.test.ts +0 -76
  28. package/src/lib/grpc-framing.test.ts +0 -96
  29. package/src/lib/oidc-issuer.test.ts +0 -328
  30. package/src/lib/otel-collector-grpc.test.ts +0 -191
  31. package/src/lib/otel-collector.test.ts +0 -303
  32. package/src/lib/otlp-protobuf.test.ts +0 -461
  33. package/src/lib/polling.test.ts +0 -80
  34. package/src/lib/requirement-ids.test.ts +0 -83
  35. package/src/lib/requirement-ledger.test.ts +0 -75
  36. package/src/lib/risk-disposition.test.ts +0 -91
  37. package/src/lib/saml-idp.test.ts +0 -127
  38. package/src/lib/spec-coherence-registry.test.ts +0 -155
  39. package/src/lib/webhook-receiver.test.ts +0 -144
  40. package/src/scenarios/artifact-schema-compile-bounded.test.ts +0 -126
  41. package/src/scenarios/artifact-type-legacy-ids.test.ts +0 -124
  42. package/src/scenarios/capability-example-root-layout.test.ts +0 -272
  43. package/src/scenarios/certification-floor-enforcement.test.ts +0 -204
  44. package/src/scenarios/chain-subchain-unsupported-refused.test.ts +0 -70
  45. package/src/scenarios/compensation-profile.test.ts +0 -340
  46. package/src/scenarios/core-manifest-and-extension-registry.test.ts +0 -250
  47. package/src/scenarios/discovery-canonical-family-no-shadow.test.ts +0 -219
  48. package/src/scenarios/edge-condition-truthy-falsy.test.ts +0 -108
  49. package/src/scenarios/effect-identity-composition.test.ts +0 -129
  50. package/src/scenarios/effect-identity-cross-scope.test.ts +0 -82
  51. package/src/scenarios/error-envelope-canonical-shape.test.ts +0 -64
  52. package/src/scenarios/form-content-packs.test.ts +0 -415
  53. package/src/scenarios/multi-region-effect-vocabulary.test.ts +0 -175
  54. package/src/scenarios/normative-example-extraction.test.ts +0 -242
  55. package/src/scenarios/openapi-asyncapi-sdk-parity.test.ts +0 -309
  56. package/src/scenarios/pack-manifest-extensions.test.ts +0 -203
  57. package/src/scenarios/protocol-version-grammar.test.ts +0 -119
  58. package/src/scenarios/registry-declarative-kinds.test.ts +0 -121
  59. package/src/scenarios/rfc-0147-self-audit.test.ts +0 -104
  60. package/src/scenarios/rfc-lifecycle-coherence.test.ts +0 -215
  61. package/src/scenarios/semantic-digest-v2.test.ts +0 -128
  62. package/src/scenarios/spec-corpus-validity.test.ts +0 -1727
  63. package/src/scenarios/spec-section-citations.test.ts +0 -132
  64. package/src/scenarios/tool-result-trust-monotone.test.ts +0 -168
  65. package/src/scenarios/versioned-composition-profiles.test.ts +0 -201
  66. package/src/scenarios/workflow-chain-internal-flag.test.ts +0 -84
  67. package/src/scenarios/workload-identity-profile.test.ts +0 -184
@@ -1,184 +0,0 @@
1
- /**
2
- * RFC 0154 §A/§B — workload identity and delegation, shape only.
3
- *
4
- * **What this proves:** the schema admits the identity and delegation shapes §A
5
- * and §B describe, and — more importantly — **rejects credential material**. It
6
- * verifies no host. RFC 0147 §A.5 forbids `Accepted` on shape-only evidence for
7
- * a behavioral requirement, and §A's actual requirements are behavioral:
8
- * cryptographically verify the presented identity, bind it to the request,
9
- * resolve it to a principal before authorization, and fail closed when
10
- * unresolvable. A schema cannot demonstrate any of those.
11
- *
12
- * What a schema *can* do is make the dangerous shape unrepresentable, and that
13
- * is what this checks. The governing rule is negative: **raw certificates,
14
- * tokens, proofs, and credentials MUST NOT enter these objects.** `subject` is
15
- * an opaque identifier; `proofRef` and `thumbprintRef` are digest *references*.
16
- * A verified identity is a fact about a completed verification, not a container
17
- * for the material that proved it — and these objects are projected into events,
18
- * spans, and audit records, which is precisely where credential material must
19
- * never reach.
20
- *
21
- * The second rule the schema encodes is **identity is not authorization**
22
- * (RFC 0147 R12). Knowing which workload called says nothing about what it may
23
- * do. That one cannot be schema-enforced at all, which is why `audience` is
24
- * required on a delegation: an identity minted for somewhere else is the
25
- * confused-deputy path, and the audience check is what closes it.
26
- *
27
- * Server-free.
28
- */
29
-
30
- import { describe, it, expect } from 'vitest';
31
- import { readFileSync } from 'node:fs';
32
- import { join, resolve as pathResolve } from 'node:path';
33
- import Ajv2020 from 'ajv/dist/2020.js';
34
- import addFormats from 'ajv-formats';
35
- import { SCHEMAS_DIR, V1_DIR } from '../lib/paths.js';
36
-
37
- /**
38
- * Schemas ship inside the package; RFC prose does not. The two need different
39
- * treatment, and collapsing them onto one `ROOT` derived from `V1_DIR` is what
40
- * broke this file in the published layout — `V1_DIR` is null there, and the
41
- * `as string` cast carried the null straight into `join()`.
42
- */
43
- const RFCS_DIR = V1_DIR === null ? null : pathResolve(V1_DIR, '..', '..', 'RFCS');
44
-
45
- function validator() {
46
- const schema = JSON.parse(
47
- readFileSync(join(SCHEMAS_DIR, 'workload-identity.schema.json'), 'utf8'),
48
- ) as object;
49
- const ajv = new Ajv2020({ strict: false, allErrors: true });
50
- addFormats(ajv);
51
- return ajv.compile(schema);
52
- }
53
-
54
- const DIGEST = `sha256:${'a'.repeat(64)}`;
55
-
56
- describe('RFC 0154 §A — workload identity shape', () => {
57
- const validate = validator();
58
-
59
- it('a verified identity validates', () => {
60
- expect(
61
- validate({ scheme: 'spiffe', subject: 'spiffe://example/dispatcher', issuer: 'spiffe://example', audience: 'openwop-host' }),
62
- JSON.stringify(validate.errors),
63
- ).toBe(true);
64
- });
65
-
66
- it('the object is closed — credential material cannot be smuggled in', () => {
67
- // The governing rule, and the one a schema can actually enforce. These
68
- // objects reach events, spans, and audit records; an open object is an
69
- // invitation for a token to ride along into all three.
70
- for (const extra of [
71
- { certificate: '-----BEGIN CERTIFICATE-----' },
72
- { token: 'eyJhbGciOiJIUzI1NiJ9.x.y' },
73
- { privateKey: 'secret' },
74
- { proof: 'raw-proof-bytes' },
75
- ]) {
76
- expect(
77
- validate({ scheme: 'spiffe', subject: 'spiffe://example/x', ...extra }),
78
- `RFC 0154 §A: raw credentials MUST NOT enter the identity object — rejected \`${Object.keys(extra)[0]}\``,
79
- ).toBe(false);
80
- }
81
- });
82
-
83
- it('the scheme is a closed enum', () => {
84
- expect(
85
- validate({ scheme: 'trust-me', subject: 'x' }),
86
- 'RFC 0154 §A: an unrecognized scheme is a verification path nobody implemented. Accepting ' +
87
- 'the NAME without the verification is exactly the failure this profile prevents.',
88
- ).toBe(false);
89
- });
90
-
91
- it('subject is required and non-empty', () => {
92
- expect(validate({ scheme: 'spiffe' })).toBe(false);
93
- expect(validate({ scheme: 'spiffe', subject: '' })).toBe(false);
94
- });
95
-
96
- it('a key binding carries a digest reference, never a key', () => {
97
- expect(validate({ scheme: 'mtls-san', subject: 'x', keyBinding: { method: 'mtls', thumbprintRef: DIGEST } })).toBe(true);
98
- expect(
99
- validate({ scheme: 'mtls-san', subject: 'x', keyBinding: { method: 'mtls', thumbprintRef: 'MIIBIjANBgkqh' } }),
100
- 'RFC 0154 §A: `thumbprintRef` is pattern-constrained to a sha256 digest so a raw key cannot ' +
101
- 'be pasted here and still validate.',
102
- ).toBe(false);
103
- expect(validate({ scheme: 'mtls-san', subject: 'x', keyBinding: { method: 'telepathy' } })).toBe(false);
104
- });
105
- });
106
-
107
- describe('RFC 0154 §B — delegated actor chain', () => {
108
- const validate = validator();
109
- const base = { scheme: 'spiffe' as const, subject: 'spiffe://example/planner' };
110
-
111
- it('a verified delegation validates', () => {
112
- expect(
113
- validate({
114
- ...base,
115
- delegation: {
116
- chain: [{ subject: 'spiffe://example/dispatcher', issuer: 'spiffe://example' }],
117
- audience: 'openwop-host',
118
- expiresAt: '2026-08-13T16:00:00Z',
119
- proofRef: DIGEST,
120
- },
121
- }),
122
- JSON.stringify(validate.errors),
123
- ).toBe(true);
124
- });
125
-
126
- it('audience is required on a delegation', () => {
127
- // The confused-deputy guard (RFC 0147 R12). An identity minted for somewhere
128
- // else, accepted here, is how a credential valid elsewhere becomes a
129
- // credential valid here.
130
- expect(
131
- validate({ ...base, delegation: { chain: [{ subject: 'x' }] } }),
132
- 'RFC 0154 §B: a delegation without an audience cannot be checked against this host.',
133
- ).toBe(false);
134
- });
135
-
136
- it('an empty chain is rejected', () => {
137
- expect(
138
- validate({ ...base, delegation: { chain: [], audience: 'openwop-host' } }),
139
- 'RFC 0154 §B: a delegation context with no chain claims a delegation nobody can inspect.',
140
- ).toBe(false);
141
- });
142
-
143
- it('proofRef is a digest reference, not the proof', () => {
144
- expect(
145
- validate({ ...base, delegation: { chain: [{ subject: 'x' }], audience: 'h', proofRef: 'eyJhbGciOiJIUzI1NiJ9.a.b' } }),
146
- 'RFC 0154 §B: `proofRef` is a REFERENCE. A JWT pasted here would put the proof itself into ' +
147
- 'every event and span this object reaches.',
148
- ).toBe(false);
149
- });
150
-
151
- it('a chain hop cannot carry extra fields', () => {
152
- expect(validate({ ...base, delegation: { chain: [{ subject: 'x', token: 'y' }], audience: 'h' } })).toBe(false);
153
- });
154
-
155
- it('a chain hop MAY carry its verified scopes, as a set of non-empty strings', () => {
156
- // RFC 0154 §B "Bounds": scope amplification is only observable if a hop can
157
- // state the scopes its proof carried. Provenance, not authorization.
158
- expect(validate({ ...base, delegation: { chain: [{ subject: 'x', scopes: ['runs:read'] }, { subject: 'y', scopes: ['runs:read'] }], audience: 'h' } })).toBe(true);
159
- expect(validate({ ...base, delegation: { chain: [{ subject: 'x', scopes: 'runs:read' }], audience: 'h' } })).toBe(false);
160
- expect(validate({ ...base, delegation: { chain: [{ subject: 'x', scopes: ['runs:read', 'runs:read'] }], audience: 'h' } })).toBe(false);
161
- expect(validate({ ...base, delegation: { chain: [{ subject: 'x', scopes: [''] }], audience: 'h' } })).toBe(false);
162
- });
163
- });
164
-
165
- describe.skipIf(RFCS_DIR === null)('RFC 0154 — what this does NOT establish', () => {
166
- it('the behavioral and external-audit items stay unticked and annotated', () => {
167
- // §A's real requirements are behavioral — verify, bind, resolve, fail closed
168
- // — and none is schema-checkable. §A.5 forbids `Accepted` on this evidence.
169
- const rfc = readFileSync(
170
- join(RFCS_DIR as string, '0154-workload-identity-delegation-telemetry-and-provenance.md'),
171
- 'utf8',
172
- );
173
- for (const needle of [
174
- 'Verification, fail-closed authorization, sender constraint, and negative chain tests pass.',
175
- 'External auditor reviews the identity and provenance implementation.',
176
- ]) {
177
- expect(
178
- new RegExp(`- \\[ \\] ${needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*\\(`).test(rfc),
179
- `RFC 0154: "${needle}" MUST remain unticked and annotated — a schema cannot demonstrate ` +
180
- 'cryptographic verification, and no host implements this profile.',
181
- ).toBe(true);
182
- }
183
- });
184
- });