@openwop/openwop-conformance 1.162.0 → 2.0.0-rc.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 +11 -2
- package/dist/cli.js +54 -1
- package/dist/lib/paths.js +25 -3
- package/dist/lib/requirement-ids.js +34 -4
- package/dist/lib/scenario-disposition.js +1 -0
- package/dist/lib/spec-coherence.js +13 -32
- package/dist/spec-artifacts.lock.json +5 -0
- package/package.json +11 -33
- package/requirements.json +20303 -11809
- package/scenario-majors.json +1343 -0
- package/schemas/CORPUS-STAMP.json +138 -6
- package/src/cli.ts +47 -1
- package/src/global-setup.ts +3 -2
- package/src/lib/corpus-stamp.ts +29 -1
- package/src/lib/paths.ts +25 -3
- package/src/lib/requirement-ids.ts +33 -4
- package/src/lib/scenario-disposition.ts +1 -0
- package/src/lib/spec-coherence.ts +15 -32
- package/src/scenarios/a2a-1-0-agent-card.test.ts +17 -16
- package/src/scenarios/a2a-1-0-task-roundtrip.test.ts +11 -10
- package/src/scenarios/a2a-card-runtime-consistency.test.ts +18 -17
- package/src/scenarios/a2a-peer-authority.test.ts +7 -6
- package/src/scenarios/a2a-task-roundtrip.test.ts +30 -29
- package/src/scenarios/a2a-version-negotiation.test.ts +16 -14
- package/src/scenarios/a2ui-surface-degrades.test.ts +5 -3
- package/src/scenarios/a2ui-surface-delta-transport.test.ts +35 -33
- package/src/scenarios/a2ui-surface-replay.test.ts +6 -4
- package/src/scenarios/a2ui-surface-shape.test.ts +11 -10
- package/src/scenarios/a2ui-surface-version-refusal.test.ts +6 -4
- package/src/scenarios/a2ui-untrusted-blocks-approval.test.ts +5 -3
- package/src/scenarios/agent-capability-degraded-projection.test.ts +12 -11
- package/src/scenarios/agent-channel-dispatch.test.ts +18 -16
- package/src/scenarios/agent-deployment-lifecycle.test.ts +20 -19
- package/src/scenarios/agent-deployment-shape.test.ts +22 -24
- package/src/scenarios/agent-eval-run.test.ts +19 -18
- package/src/scenarios/agent-eval-suite-shape.test.ts +20 -22
- package/src/scenarios/agent-live-allowlist-enforced.test.ts +6 -5
- package/src/scenarios/agent-live-invocation-bracket.test.ts +13 -12
- package/src/scenarios/agent-live-runtime-shape.test.ts +10 -12
- package/src/scenarios/agent-live-structured-output.test.ts +7 -6
- package/src/scenarios/agent-loop-iteration-monotonic.test.ts +3 -3
- package/src/scenarios/agent-loop-stateful-resume.test.ts +2 -2
- package/src/scenarios/agent-loop-version5-shape.test.ts +4 -4
- package/src/scenarios/agent-loop-workspace-snapshot.test.ts +3 -3
- package/src/scenarios/agent-manifest-role-profile.test.ts +9 -9
- package/src/scenarios/agent-manifest-runtime.test.ts +13 -12
- package/src/scenarios/agent-org-chart-scoping.test.ts +14 -13
- package/src/scenarios/agent-org-chart-shape.test.ts +11 -13
- package/src/scenarios/agent-platform-aggregate-evidence.test.ts +5 -4
- package/src/scenarios/agent-platform-profile.test.ts +18 -19
- package/src/scenarios/agent-requires-capabilities-shape.test.ts +4 -4
- package/src/scenarios/agent-roster-attribution.test.ts +18 -17
- package/src/scenarios/agent-roster-shape.test.ts +17 -19
- package/src/scenarios/agent-verifier-shape.test.ts +11 -11
- package/src/scenarios/agentConfidenceEscalation.test.ts +2 -1
- package/src/scenarios/agentMemoryCrossTenantIsolation.test.ts +6 -5
- package/src/scenarios/agentMemoryRedactionContract.test.ts +2 -1
- package/src/scenarios/agentMemoryRoundTrip.test.ts +2 -1
- package/src/scenarios/agentMemoryTtlExpiry.test.ts +4 -3
- package/src/scenarios/agentMessageReducer.test.ts +2 -1
- package/src/scenarios/agentMetadata.test.ts +2 -1
- package/src/scenarios/agentPackCatalog.test.ts +23 -21
- package/src/scenarios/agentPackExport.test.ts +4 -2
- package/src/scenarios/agentPackHandoffSchemaValidation.test.ts +5 -4
- package/src/scenarios/agentPackInstall.test.ts +4 -2
- package/src/scenarios/agentPackProvenance.test.ts +2 -1
- package/src/scenarios/agentReasoningEvents.test.ts +3 -2
- package/src/scenarios/agentReasoningStreaming.test.ts +11 -10
- package/src/scenarios/agents-run-tool-allowlist.test.ts +14 -12
- package/src/scenarios/ai-envelope-shape.test.ts +31 -29
- package/src/scenarios/aiEnvelope.capBreached.test.ts +26 -24
- package/src/scenarios/aiEnvelope.contractRefusal.test.ts +36 -34
- package/src/scenarios/aiEnvelope.correlationReplay.test.ts +17 -15
- package/src/scenarios/aiEnvelope.redaction.test.ts +22 -20
- package/src/scenarios/aiEnvelope.schemaDrift.test.ts +18 -16
- package/src/scenarios/aiEnvelope.trustBoundaryPropagation.test.ts +38 -36
- package/src/scenarios/aiEnvelope.universalKinds.test.ts +31 -29
- package/src/scenarios/aiproviders-input-shape.test.ts +5 -5
- package/src/scenarios/aiproviders-realtimevoice-shape.test.ts +14 -16
- package/src/scenarios/aiproviders-selfhosted-honesty.test.ts +6 -4
- package/src/scenarios/aiproviders-selfhosted-shape.test.ts +15 -17
- package/src/scenarios/aiproviders-speechsynth-shape.test.ts +6 -8
- package/src/scenarios/aiproviders-subscription-scope.test.ts +7 -8
- package/src/scenarios/anonymous-actor-audit-opaque.test.ts +9 -8
- package/src/scenarios/anonymous-actor-default-deny.test.ts +10 -9
- package/src/scenarios/anonymous-actor-egress-guarded.test.ts +6 -5
- package/src/scenarios/anonymous-actor-no-secret-reach.test.ts +7 -6
- package/src/scenarios/anonymous-actor-shape.test.ts +17 -19
- package/src/scenarios/anonymous-actor-write-gated.test.ts +9 -8
- package/src/scenarios/append-ordering.test.ts +7 -5
- package/src/scenarios/approval-gate-events.test.ts +4 -3
- package/src/scenarios/approval-gate-flow.test.ts +4 -3
- package/src/scenarios/approval-payload.test.ts +5 -4
- package/src/scenarios/artifact-auth.test.ts +7 -6
- package/src/scenarios/artifact-type-pack-install.test.ts +6 -6
- package/src/scenarios/artifact-type-pack-manifest-validation.test.ts +9 -8
- package/src/scenarios/artifact-type-registration-source.test.ts +20 -20
- package/src/scenarios/artifact-type-store-emission.test.ts +15 -18
- package/src/scenarios/artifact-type-store-without-render.test.ts +4 -4
- package/src/scenarios/audit-log-integrity.test.ts +8 -6
- package/src/scenarios/auth-api-key-rotation.test.ts +11 -9
- package/src/scenarios/auth-mtls.test.ts +22 -20
- package/src/scenarios/auth-oauth2-client-credentials.test.ts +16 -14
- package/src/scenarios/auth-oidc-user-bearer.test.ts +26 -24
- package/src/scenarios/auth-saml-profile.test.ts +14 -12
- package/src/scenarios/auth-scim-profile.test.ts +3 -2
- package/src/scenarios/auth-subject-link-key-class.test.ts +8 -7
- package/src/scenarios/auth-subject-link.test.ts +11 -10
- package/src/scenarios/auth.test.ts +5 -4
- package/src/scenarios/authorization-fail-closed.test.ts +3 -2
- package/src/scenarios/authorization-roles-shape.test.ts +9 -7
- package/src/scenarios/blob-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/blob-presign-expiry.test.ts +12 -10
- package/src/scenarios/blob-roundtrip.test.ts +0 -0
- package/src/scenarios/budget-enforcement.test.ts +15 -14
- package/src/scenarios/budget-policy-shape.test.ts +18 -19
- package/src/scenarios/bulk-cancel.test.ts +9 -8
- package/src/scenarios/byok-auth-modes.test.ts +13 -14
- package/src/scenarios/byok-roundtrip.test.ts +12 -10
- package/src/scenarios/cache-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/cache-ttl-expiry.test.ts +7 -5
- package/src/scenarios/callai-multimodal.test.ts +5 -3
- package/src/scenarios/cancellation.test.ts +6 -5
- package/src/scenarios/cap-breach.test.ts +15 -14
- package/src/scenarios/capability-arm-witness.test.ts +5 -3
- package/src/scenarios/certification-bundle-non-vacuous.test.ts +20 -19
- package/src/scenarios/certification-bundle-redaction.test.ts +11 -10
- package/src/scenarios/certification-bundle-v2.test.ts +11 -10
- package/src/scenarios/chain-compensation-expansion.test.ts +14 -13
- package/src/scenarios/chain-produced-var-roundtrip.test.ts +13 -14
- package/src/scenarios/chain-subchain-cycle-rejected.test.ts +9 -10
- package/src/scenarios/chain-subchain-fanout.test.ts +8 -9
- package/src/scenarios/chain-subchain-sibling.test.ts +14 -16
- package/src/scenarios/channel-presence-behavioral.test.ts +9 -7
- package/src/scenarios/channel-presence-shape.test.ts +11 -12
- package/src/scenarios/channel-ttl.test.ts +6 -5
- package/src/scenarios/chat-card-pack-execution.test.ts +4 -4
- package/src/scenarios/chat-card-pack-manifest-validation.test.ts +9 -8
- package/src/scenarios/commitment-fired.test.ts +6 -5
- package/src/scenarios/compensation-behavior.test.ts +24 -23
- package/src/scenarios/compensation-recovery.test.ts +28 -27
- package/src/scenarios/configurable-schema.test.ts +8 -6
- package/src/scenarios/conformance-advertised-seam-required.test.ts +6 -5
- package/src/scenarios/conformance-execution-witness.test.ts +9 -8
- package/src/scenarios/connection-pack-apihosts.test.ts +19 -17
- package/src/scenarios/connection-pack-manifest-valid.test.ts +12 -11
- package/src/scenarios/connection-pack-no-credential-material.test.ts +8 -6
- package/src/scenarios/connection-pack-reach-exclusive.test.ts +5 -4
- package/src/scenarios/connection-pack-write-reconsent.test.ts +6 -4
- package/src/scenarios/connection-provider-resolution.test.ts +18 -16
- package/src/scenarios/connector-manifest-validity.test.ts +8 -7
- package/src/scenarios/context-budget-transcript-bound.test.ts +17 -15
- package/src/scenarios/context-summarization-replay.test.ts +14 -12
- package/src/scenarios/contract-provenance.test.ts +18 -18
- package/src/scenarios/conversation-turn-model-provenance-shape.test.ts +14 -15
- package/src/scenarios/conversationCapabilityNegotiation.test.ts +2 -1
- package/src/scenarios/conversationLifecycle.test.ts +2 -1
- package/src/scenarios/conversationReplayDeterminism.test.ts +4 -2
- package/src/scenarios/conversationVsLegacySuspend.test.ts +2 -1
- package/src/scenarios/core-standard-profile.test.ts +8 -9
- package/src/scenarios/cost-attribution.test.ts +23 -21
- package/src/scenarios/credential-payload-redaction.test.ts +4 -3
- package/src/scenarios/credentials-capability-shape.test.ts +5 -4
- package/src/scenarios/cross-engine-append-behavior.test.ts +11 -10
- package/src/scenarios/cross-engine-append-ordering.test.ts +4 -3
- package/src/scenarios/cross-host-ancestry-endpoint.test.ts +11 -10
- package/src/scenarios/cross-host-causation-shape.test.ts +7 -6
- package/src/scenarios/cross-workspace-isolation.test.ts +4 -3
- package/src/scenarios/data-residency-admission.test.ts +15 -14
- package/src/scenarios/deadletter-capability-shape.test.ts +6 -4
- package/src/scenarios/deadletter-retry-exhaustion.test.ts +4 -3
- package/src/scenarios/debug-bundle-truncation.test.ts +9 -7
- package/src/scenarios/debugBundle.test.ts +25 -23
- package/src/scenarios/discovery-families-view.test.ts +5 -4
- package/src/scenarios/discovery.test.ts +30 -29
- package/src/scenarios/dispatch-cross-worker-handoff.test.ts +10 -8
- package/src/scenarios/dispatch-fanout-parallel.test.ts +24 -22
- package/src/scenarios/dispatch-input-mapping.test.ts +12 -10
- package/src/scenarios/dispatch-output-mapping.test.ts +9 -7
- package/src/scenarios/dispatch-per-item-input.test.ts +22 -20
- package/src/scenarios/dispatchLoop.test.ts +3 -2
- package/src/scenarios/distillation-index-roundtrip.test.ts +3 -3
- package/src/scenarios/distillation-secret-carryforward.test.ts +2 -2
- package/src/scenarios/distillation-shape.test.ts +4 -4
- package/src/scenarios/distillation-stable-archive.test.ts +6 -6
- package/src/scenarios/distillation-token-budget.test.ts +5 -5
- package/src/scenarios/durability-poison-exhaustion.test.ts +7 -6
- package/src/scenarios/egress-audience-binding.test.ts +11 -10
- package/src/scenarios/egress-decision-content-free.test.ts +6 -5
- package/src/scenarios/egress-provenance-shape.test.ts +17 -18
- package/src/scenarios/envelope-completion-distinguishes-truncation.test.ts +27 -25
- package/src/scenarios/envelope-nl-to-format-engaged.test.ts +19 -17
- package/src/scenarios/envelope-reasoning-secret-redaction.test.ts +28 -26
- package/src/scenarios/envelope-reasoning-shape.test.ts +19 -17
- package/src/scenarios/envelope-recovery-applied.test.ts +24 -22
- package/src/scenarios/envelope-refusal-shape.test.ts +31 -29
- package/src/scenarios/envelope-rendering-hint.test.ts +5 -4
- package/src/scenarios/envelope-retry-attempted.test.ts +33 -31
- package/src/scenarios/envelope-retry-exhausted.test.ts +22 -20
- package/src/scenarios/envelope-tier-one-subset-static.test.ts +9 -7
- package/src/scenarios/envelope-truncated.test.ts +19 -17
- package/src/scenarios/envelope-truncation-cap-exhaustion.test.ts +19 -17
- package/src/scenarios/envelope-variant-discriminator-static.test.ts +4 -3
- package/src/scenarios/errors.test.ts +20 -19
- package/src/scenarios/eventOrdering.test.ts +16 -14
- package/src/scenarios/exec-not-protocol-tier.test.ts +5 -7
- package/src/scenarios/experimental-tier-shape.test.ts +17 -15
- package/src/scenarios/export-bundle-portability.test.ts +17 -17
- package/src/scenarios/failure-path.test.ts +7 -6
- package/src/scenarios/feedback-capability-shape.test.ts +4 -3
- package/src/scenarios/feedback-correction-redaction.test.ts +2 -1
- package/src/scenarios/feedback-cross-tenant-isolation.test.ts +2 -1
- package/src/scenarios/feedback-fork-not-copied.test.ts +3 -2
- package/src/scenarios/feedback-on-terminal-run.test.ts +3 -2
- package/src/scenarios/feedback-record-and-list.test.ts +2 -1
- package/src/scenarios/feedback-unsupported-501.test.ts +2 -1
- package/src/scenarios/fixtures-gating.test.ts +29 -28
- package/src/scenarios/fixtures-valid.test.ts +16 -15
- package/src/scenarios/form-content-instantiation.test.ts +15 -14
- package/src/scenarios/frontend-plugin-packs.test.ts +29 -27
- package/src/scenarios/fs-path-traversal.test.ts +14 -12
- package/src/scenarios/goal-standing-continuation.test.ts +21 -21
- package/src/scenarios/grpc-transport.test.ts +9 -7
- package/src/scenarios/heartbeat-capability-shape.test.ts +3 -3
- package/src/scenarios/heartbeat-fires-once-per-tick.test.ts +2 -2
- package/src/scenarios/heartbeat-idempotent-no-spam.test.ts +3 -3
- package/src/scenarios/heartbeat-runtime-bound.test.ts +2 -2
- package/src/scenarios/highConcurrency.test.ts +14 -13
- package/src/scenarios/host-callback-declaration.test.ts +8 -7
- package/src/scenarios/http-client-ssrf.test.ts +7 -5
- package/src/scenarios/i18n-negotiation.test.ts +14 -12
- package/src/scenarios/idempotency-concurrent-claim.test.ts +14 -13
- package/src/scenarios/idempotency-key-determinism.test.ts +20 -18
- package/src/scenarios/idempotency.test.ts +8 -7
- package/src/scenarios/idempotencyRetry.test.ts +13 -11
- package/src/scenarios/identity-owner-shape.test.ts +6 -5
- package/src/scenarios/identity-passthrough.test.ts +4 -3
- package/src/scenarios/interrupt-approval.test.ts +6 -5
- package/src/scenarios/interrupt-approver-routing.test.ts +15 -15
- package/src/scenarios/interrupt-auth-required-resume.test.ts +5 -3
- package/src/scenarios/interrupt-clarification.test.ts +4 -3
- package/src/scenarios/interrupt-external-event-correlation.test.ts +6 -5
- package/src/scenarios/interrupt-parent-child-cascade.test.ts +4 -3
- package/src/scenarios/interrupt-quorum-resolution.test.ts +5 -4
- package/src/scenarios/interrupt-token-matrix.test.ts +8 -6
- package/src/scenarios/interruptRace.test.ts +12 -10
- package/src/scenarios/kv-atomic-increment.test.ts +11 -9
- package/src/scenarios/kv-cas.test.ts +14 -12
- package/src/scenarios/kv-cross-tenant-isolation.test.ts +12 -10
- package/src/scenarios/kv-ttl-expiry.test.ts +7 -5
- package/src/scenarios/localized-content-delivery.test.ts +24 -26
- package/src/scenarios/maliciousManifest.test.ts +10 -8
- package/src/scenarios/mcp-2026-07-28-discover.test.ts +15 -14
- package/src/scenarios/mcp-cache-tenant-scope.test.ts +6 -5
- package/src/scenarios/mcp-current-auth-boundary.test.ts +7 -6
- package/src/scenarios/mcp-discoverability.test.ts +9 -7
- package/src/scenarios/mcp-extension-opacity.test.ts +7 -6
- package/src/scenarios/mcp-mrtr-roundtrip.test.ts +18 -17
- package/src/scenarios/mcp-server-elicitation-bridge.test.ts +13 -12
- package/src/scenarios/mcp-server-prompt-roundtrip.test.ts +11 -10
- package/src/scenarios/mcp-server-resource-roundtrip.test.ts +11 -10
- package/src/scenarios/mcp-server-sampling-bridge.test.ts +12 -11
- package/src/scenarios/mcp-server-tool-roundtrip.test.ts +14 -13
- package/src/scenarios/mcp-server-untrusted-args.test.ts +14 -13
- package/src/scenarios/mcp-stateless-request.test.ts +11 -10
- package/src/scenarios/mcp-tool-roundtrip.test.ts +4 -3
- package/src/scenarios/mcp-toolcall-redaction.test.ts +4 -3
- package/src/scenarios/mcp-version-negotiation.test.ts +15 -14
- package/src/scenarios/media-url-inline-cap.test.ts +21 -19
- package/src/scenarios/memory-attribution-emits-on-write.test.ts +5 -5
- package/src/scenarios/memory-attribution-no-content.test.ts +4 -4
- package/src/scenarios/memory-attribution-replay-stable.test.ts +4 -3
- package/src/scenarios/memory-attribution-shape.test.ts +4 -3
- package/src/scenarios/memory-attribution-tenant-scoped.test.ts +3 -3
- package/src/scenarios/memory-capability-model-shape.test.ts +14 -15
- package/src/scenarios/memory-compaction-event-emitted.test.ts +7 -6
- package/src/scenarios/memory-compaction-provenance-tag.test.ts +3 -2
- package/src/scenarios/memory-compaction-sr1-carry-forward.test.ts +7 -6
- package/src/scenarios/memory-consolidation-idempotent.test.ts +5 -4
- package/src/scenarios/memory-consolidation-shape.test.ts +9 -11
- package/src/scenarios/memory-degraded-projection.test.ts +10 -9
- package/src/scenarios/memory-injection-budget.test.ts +15 -14
- package/src/scenarios/metric-emission.test.ts +4 -3
- package/src/scenarios/model-capability-insufficient.test.ts +19 -17
- package/src/scenarios/model-capability-substituted.test.ts +20 -18
- package/src/scenarios/multi-agent-confidence-escalation.test.ts +12 -11
- package/src/scenarios/multi-agent-handoff-state-machine.test.ts +10 -9
- package/src/scenarios/multi-agent-memory-lifecycle.test.ts +11 -10
- package/src/scenarios/multi-node-ordering.test.ts +4 -3
- package/src/scenarios/multi-party-conversation-behavioral.test.ts +6 -6
- package/src/scenarios/multi-party-conversation-shape.test.ts +21 -23
- package/src/scenarios/multi-region-idempotency-behavior.test.ts +16 -15
- package/src/scenarios/multi-region-idempotency.test.ts +6 -5
- package/src/scenarios/node-failed-payload-shape.test.ts +5 -4
- package/src/scenarios/node-module-required-capabilities-shape.test.ts +10 -8
- package/src/scenarios/oauth-authorization-code-roundtrip.test.ts +5 -4
- package/src/scenarios/oauth-capability-shape.test.ts +5 -4
- package/src/scenarios/oauth-connector-redaction.test.ts +4 -3
- package/src/scenarios/openapi-resolved-paths.test.ts +6 -5
- package/src/scenarios/orchestratorConservativePath.test.ts +2 -1
- package/src/scenarios/orchestratorDispatch.test.ts +2 -1
- package/src/scenarios/orchestratorTermination.test.ts +2 -1
- package/src/scenarios/org-position-no-authority-escalation.test.ts +2 -2
- package/src/scenarios/otel-collector-canary-inspection.test.ts +8 -7
- package/src/scenarios/otel-emission-grpc.test.ts +3 -2
- package/src/scenarios/otel-emission.test.ts +6 -5
- package/src/scenarios/otel-trace-propagation-subworkflow.test.ts +7 -6
- package/src/scenarios/otel-trace-propagation.test.ts +2 -1
- package/src/scenarios/owner-subject-echo.test.ts +13 -12
- package/src/scenarios/owner-subject-shape.test.ts +11 -10
- package/src/scenarios/pack-manifest-extension-opacity.test.ts +8 -8
- package/src/scenarios/pack-registry-isolation.test.ts +4 -3
- package/src/scenarios/pack-registry-publish.test.ts +65 -63
- package/src/scenarios/pack-registry.test.ts +38 -36
- package/src/scenarios/pause-resume.test.ts +18 -16
- package/src/scenarios/policies.test.ts +20 -18
- package/src/scenarios/production-backpressure.test.ts +18 -16
- package/src/scenarios/production-retention-expiry.test.ts +8 -6
- package/src/scenarios/profile-discovery-core-alias.test.ts +13 -11
- package/src/scenarios/profileDerivation.test.ts +34 -33
- package/src/scenarios/prompt-all-four-kinds-events.test.ts +11 -9
- package/src/scenarios/prompt-composed-secret-redaction.test.ts +11 -9
- package/src/scenarios/prompt-composed-trust-marker.test.ts +10 -8
- package/src/scenarios/prompt-end-to-end-events.test.ts +17 -15
- package/src/scenarios/prompt-list-and-fetch.test.ts +16 -14
- package/src/scenarios/prompt-mutable-lifecycle.test.ts +13 -11
- package/src/scenarios/prompt-mutation-workspace-membership-enforced.test.ts +6 -4
- package/src/scenarios/prompt-pack-install.test.ts +14 -12
- package/src/scenarios/prompt-prefix-cache.test.ts +19 -17
- package/src/scenarios/prompt-read-workspace-membership-enforced.test.ts +11 -9
- package/src/scenarios/prompt-render-deterministic.test.ts +19 -17
- package/src/scenarios/prompt-resolution-chain-agent-intrinsic.test.ts +7 -5
- package/src/scenarios/prompt-resolution-chain-event.test.ts +12 -10
- package/src/scenarios/prompt-resolution-chain-fallback-cascade.test.ts +10 -8
- package/src/scenarios/prompt-resolution-chain-node-wins.test.ts +8 -6
- package/src/scenarios/prompt-template-shape.test.ts +30 -28
- package/src/scenarios/proposal-reviewable-learning.test.ts +19 -19
- package/src/scenarios/protocol-versions-array.test.ts +10 -9
- package/src/scenarios/provider-usage.test.ts +17 -15
- package/src/scenarios/providerPolicyEnforcement.test.ts +16 -14
- package/src/scenarios/purpose-propagation.test.ts +23 -21
- package/src/scenarios/queue-ack-nack-dlq.test.ts +12 -10
- package/src/scenarios/queue-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/queue-publish-consume-roundtrip.test.ts +10 -8
- package/src/scenarios/rate-limit-envelope.test.ts +6 -5
- package/src/scenarios/redaction.test.ts +13 -11
- package/src/scenarios/redactionAdversarial.test.ts +11 -9
- package/src/scenarios/registry-public.test.ts +12 -11
- package/src/scenarios/replay-divergence-at-refusal.test.ts +16 -15
- package/src/scenarios/replay-fanout-suppression.test.ts +16 -15
- package/src/scenarios/replay-fork-arbitrary.test.ts +24 -23
- package/src/scenarios/replay-fork.test.ts +20 -19
- package/src/scenarios/replay-llm-cache-key-portable.test.ts +11 -9
- package/src/scenarios/replay-llm-cache-key.test.ts +22 -21
- package/src/scenarios/replay-observable-sequence-determinism.test.ts +4 -3
- package/src/scenarios/replay-retention-expiry.test.ts +11 -9
- package/src/scenarios/replay-side-effect-suppression.test.ts +21 -19
- package/src/scenarios/replayDeterminism.test.ts +13 -12
- package/src/scenarios/requirement-ledger.test.ts +15 -14
- package/src/scenarios/restart-during-run.test.ts +3 -2
- package/src/scenarios/route-coverage.test.ts +20 -19
- package/src/scenarios/run-diff.test.ts +22 -20
- package/src/scenarios/run-execution-bounds-shape.test.ts +14 -12
- package/src/scenarios/run-transport-economy.test.ts +13 -11
- package/src/scenarios/runner-ledger.test.ts +17 -16
- package/src/scenarios/runs-lifecycle.test.ts +7 -6
- package/src/scenarios/runtime-capabilities.test.ts +12 -10
- package/src/scenarios/runtime-requires-install-gate.test.ts +8 -8
- package/src/scenarios/runtime-requires-shape.test.ts +7 -6
- package/src/scenarios/safefetch-behavior.test.ts +22 -21
- package/src/scenarios/safefetch-live-audit.test.ts +14 -13
- package/src/scenarios/sandbox-memory-cap.test.ts +2 -1
- package/src/scenarios/sandbox-mvp-behavior.test.ts +24 -23
- package/src/scenarios/sandbox-no-host-fs-escape.test.ts +3 -2
- package/src/scenarios/sandbox-timeout-cap.test.ts +2 -1
- package/src/scenarios/sandbox-wasm-isolation.test.ts +14 -15
- package/src/scenarios/sandbox-wasm-timeout.test.ts +5 -6
- package/src/scenarios/scheduling-capability-shape.test.ts +8 -6
- package/src/scenarios/scheduling-cron-fires-once.test.ts +8 -6
- package/src/scenarios/search-bm25-roundtrip.test.ts +8 -6
- package/src/scenarios/secret-leakage-otel-attribute.test.ts +22 -20
- package/src/scenarios/self-hosted-runner.test.ts +26 -24
- package/src/scenarios/semantic-digest-vectors.test.ts +8 -7
- package/src/scenarios/speech-synthesis-roundtrip.test.ts +9 -7
- package/src/scenarios/speech-synthesis-unadvertised.test.ts +5 -3
- package/src/scenarios/sql-injection-rejection.test.ts +8 -6
- package/src/scenarios/sql-transaction-atomicity.test.ts +10 -8
- package/src/scenarios/staleClaim.test.ts +5 -4
- package/src/scenarios/stream-modes-buffer.test.ts +8 -7
- package/src/scenarios/stream-modes-mixed.test.ts +10 -9
- package/src/scenarios/stream-modes.test.ts +12 -11
- package/src/scenarios/stream-subscribe-from-beginning.test.ts +11 -9
- package/src/scenarios/stream-text-fixture.test.ts +14 -13
- package/src/scenarios/streamReconnect.test.ts +13 -11
- package/src/scenarios/strict-behavior-gate.test.ts +9 -8
- package/src/scenarios/subrun-approval-fail-closed.test.ts +2 -2
- package/src/scenarios/subrun-approval-gate.test.ts +3 -3
- package/src/scenarios/subrun-attestation-shape.test.ts +2 -2
- package/src/scenarios/subrun-checksum-stable.test.ts +4 -4
- package/src/scenarios/subworkflow-input-mapping.test.ts +16 -14
- package/src/scenarios/subworkflow.test.ts +10 -9
- package/src/scenarios/table-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/table-cursor-pagination.test.ts +8 -6
- package/src/scenarios/table-schema-enforcement.test.ts +7 -5
- package/src/scenarios/test-seam-unauthenticated.test.ts +2 -2
- package/src/scenarios/tool-catalog-compact-projection.test.ts +10 -8
- package/src/scenarios/tool-catalog-projection.test.ts +11 -9
- package/src/scenarios/tool-descriptor-shape.test.ts +14 -15
- package/src/scenarios/tool-hooks-authorization-fail-closed.test.ts +3 -3
- package/src/scenarios/tool-hooks-content-free.test.ts +4 -4
- package/src/scenarios/tool-hooks-failure-honesty.test.ts +4 -4
- package/src/scenarios/tool-hooks-rate-limit.test.ts +2 -2
- package/src/scenarios/tool-hooks-secret-redaction.test.ts +2 -2
- package/src/scenarios/tool-hooks-shape.test.ts +3 -3
- package/src/scenarios/tool-session-lifecycle.test.ts +14 -13
- package/src/scenarios/trigger-bridge-delivery.test.ts +14 -14
- package/src/scenarios/trigger-bridge-shape.test.ts +19 -20
- package/src/scenarios/trigger-ingestion.test.ts +19 -17
- package/src/scenarios/trigger-stream-cdc-sources.test.ts +16 -14
- package/src/scenarios/vector-knn-roundtrip.test.ts +9 -7
- package/src/scenarios/verifier-gating.test.ts +8 -6
- package/src/scenarios/version-fold.test.ts +9 -8
- package/src/scenarios/version-negotiation.test.ts +12 -11
- package/src/scenarios/voice-bargein-no-partial-leak.test.ts +8 -6
- package/src/scenarios/voice-event-payloads-shape.test.ts +10 -11
- package/src/scenarios/voice-interim-not-durable.test.ts +7 -5
- package/src/scenarios/voice-streamref-tenant-bound.test.ts +5 -3
- package/src/scenarios/voice-synthesis-streaming.test.ts +11 -9
- package/src/scenarios/voice-transcription-streaming.test.ts +8 -6
- package/src/scenarios/voice-transcription-unadvertised.test.ts +5 -3
- package/src/scenarios/wasm-pack-abi-version-rejection.test.ts +3 -2
- package/src/scenarios/wasm-pack-invoke-completed.test.ts +3 -2
- package/src/scenarios/wasm-pack-invoke-suspended.test.ts +3 -2
- package/src/scenarios/wasm-pack-load.test.ts +4 -3
- package/src/scenarios/wasm-pack-memory-cap.test.ts +5 -4
- package/src/scenarios/wasm-pack-replay-determinism.test.ts +2 -1
- package/src/scenarios/webhook-negative.test.ts +11 -9
- package/src/scenarios/webhook-receiver-adversarial.test.ts +8 -7
- package/src/scenarios/webhook-sig-algorithm.test.ts +5 -3
- package/src/scenarios/webhook-signed-delivery.test.ts +16 -15
- package/src/scenarios/webhook-tenant-isolation.test.ts +16 -14
- package/src/scenarios/workflow-chain-deferred-parameters.test.ts +28 -30
- package/src/scenarios/workflow-chain-expansion.test.ts +21 -20
- package/src/scenarios/workflow-chain-host-expansion.test.ts +21 -20
- package/src/scenarios/workflow-chain-pack-manifest-validation.test.ts +14 -12
- package/src/scenarios/workflow-chain-pack-signature-verification.test.ts +5 -4
- package/src/scenarios/workflow-chain-unresolvable-typeid.test.ts +9 -8
- package/src/scenarios/workflow-primary-output-annotation.test.ts +7 -6
- package/src/scenarios/workflow-variable-format.test.ts +24 -24
- package/src/scenarios/workload-identity-behavior.test.ts +15 -13
- package/src/scenarios/workload-identity-chain-bounds.test.ts +18 -17
- package/src/scenarios/workspace-behavior.test.ts +19 -18
- package/src/scenarios/workspace-capability-shape.test.ts +3 -2
- package/src/scenarios/workspace-cross-tenant-isolation-blackbox.test.ts +6 -5
- package/src/scenarios/workspace-cross-tenant-isolation.test.ts +7 -6
- package/src/scenarios/x-openwop-form-pack-manifest.test.ts +8 -7
- package/src/setup.ts +4 -2
- package/vitest.config.ts +5 -1
- package/api/.redocly.lint-ignore.yaml +0 -22
- package/api/asyncapi.yaml +0 -834
- package/api/grpc/openwop.proto +0 -251
- package/api/openapi.yaml +0 -2693
- package/api/redocly.yaml +0 -23
- package/schemas/README.md +0 -144
- package/schemas/a2a-task-state.schema.json +0 -78
- package/schemas/a2ui-surface-delta-frame.schema.json +0 -48
- package/schemas/agent-deployment-transition.schema.json +0 -49
- package/schemas/agent-deployment.schema.json +0 -54
- package/schemas/agent-eval-suite.schema.json +0 -140
- package/schemas/agent-inventory-response.schema.json +0 -115
- package/schemas/agent-manifest.schema.json +0 -156
- package/schemas/agent-org-chart.schema.json +0 -82
- package/schemas/agent-ref.schema.json +0 -63
- package/schemas/agent-roster-entry.schema.json +0 -81
- package/schemas/agent-roster-response.schema.json +0 -21
- package/schemas/ai-envelope.schema.json +0 -134
- package/schemas/annotation-create.schema.json +0 -37
- package/schemas/annotation.schema.json +0 -56
- package/schemas/artifact-type-pack-manifest.schema.json +0 -244
- package/schemas/audit-verify-result.schema.json +0 -90
- package/schemas/budget-policy.schema.json +0 -18
- package/schemas/capabilities.schema.json +0 -4124
- package/schemas/certification-bundle-v2.schema.json +0 -118
- package/schemas/channel-presence-payload.schema.json +0 -41
- package/schemas/channel-written-payload.schema.json +0 -55
- package/schemas/chat-card-pack-manifest.schema.json +0 -253
- package/schemas/compact-tool-descriptor.schema.json +0 -51
- package/schemas/compensation-policy.schema.json +0 -64
- package/schemas/conformance-certification-bundle.schema.json +0 -86
- package/schemas/connection-pack-manifest.schema.json +0 -353
- package/schemas/conversation-event.schema.json +0 -168
- package/schemas/conversation-turn.schema.json +0 -117
- package/schemas/core-conformance-mock-agent-config.schema.json +0 -152
- package/schemas/credential-provenance.schema.json +0 -18
- package/schemas/credential-reference.schema.json +0 -21
- package/schemas/debug-bundle.schema.json +0 -196
- package/schemas/dispatch-config.schema.json +0 -112
- package/schemas/envelopes/clarification.request.schema.json +0 -52
- package/schemas/envelopes/error.schema.json +0 -30
- package/schemas/envelopes/media.audio.schema.json +0 -38
- package/schemas/envelopes/media.file.schema.json +0 -37
- package/schemas/envelopes/media.image.schema.json +0 -33
- package/schemas/envelopes/schema.request.schema.json +0 -26
- package/schemas/envelopes/schema.response.schema.json +0 -22
- package/schemas/envelopes/ui.a2ui-surface.schema.json +0 -154
- package/schemas/error-envelope.schema.json +0 -25
- package/schemas/eval-summary.schema.json +0 -92
- package/schemas/export-bundle.schema.json +0 -66
- package/schemas/form-content-pack-manifest.schema.json +0 -243
- package/schemas/frontend-plugin-manifest.schema.json +0 -109
- package/schemas/goal.schema.json +0 -104
- package/schemas/heartbeat-evaluated.schema.json +0 -14
- package/schemas/heartbeat-state-changed.schema.json +0 -14
- package/schemas/localized-content-language-settings.schema.json +0 -26
- package/schemas/localized-content-page-response.schema.json +0 -60
- package/schemas/localized-content-page.schema.json +0 -62
- package/schemas/localized-content-section.schema.json +0 -51
- package/schemas/memory-entry.schema.json +0 -36
- package/schemas/memory-list-options.schema.json +0 -37
- package/schemas/node-pack-manifest.schema.json +0 -654
- package/schemas/orchestrator-decision.schema.json +0 -90
- package/schemas/org-chart-responsibility-view.schema.json +0 -26
- package/schemas/pack-lockfile.schema.json +0 -108
- package/schemas/prompt-kind.schema.json +0 -8
- package/schemas/prompt-pack-manifest.schema.json +0 -117
- package/schemas/prompt-ref.schema.json +0 -40
- package/schemas/prompt-template.schema.json +0 -149
- package/schemas/proposal.schema.json +0 -84
- package/schemas/registry-version-manifest.schema.json +0 -208
- package/schemas/residency.schema.json +0 -16
- package/schemas/run-ancestry-response.schema.json +0 -54
- package/schemas/run-diff-response.schema.json +0 -64
- package/schemas/run-event-payloads.schema.json +0 -4452
- package/schemas/run-event.schema.json +0 -211
- package/schemas/run-options.schema.json +0 -80
- package/schemas/run-orchestrator-decided-event.schema.json +0 -20
- package/schemas/run-snapshot.schema.json +0 -148
- package/schemas/security-advisory.schema.json +0 -109
- package/schemas/self-hosted-runner-dispatch-frame.schema.json +0 -79
- package/schemas/self-hosted-runner-registration.schema.json +0 -53
- package/schemas/self-hosted-runner-result-frame.schema.json +0 -38
- package/schemas/subject.schema.json +0 -56
- package/schemas/suspend-request.schema.json +0 -207
- package/schemas/tool-descriptor.schema.json +0 -63
- package/schemas/trigger-event.schema.json +0 -222
- package/schemas/trigger-subscription-registration.schema.json +0 -67
- package/schemas/trigger-subscription.schema.json +0 -26
- package/schemas/ui-plugin-message.schema.json +0 -90
- package/schemas/workflow-chain-pack-manifest.schema.json +0 -615
- package/schemas/workflow-definition.schema.json +0 -850
- package/schemas/workload-identity.schema.json +0 -79
- package/schemas/workspace-file-create.schema.json +0 -20
- package/schemas/workspace-file.schema.json +0 -39
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
|
|
26
26
|
import { describe, it, expect } from 'vitest';
|
|
27
27
|
import { softSkip } from '../lib/soft-skip.js';
|
|
28
|
-
import { driver } from '../lib/driver.js';
|
|
29
28
|
import { behaviorGate, behaviorGatePresent } from '../lib/behavior-gate.js';
|
|
30
29
|
|
|
31
30
|
const PROFILE = 'openwop-artifact-type-packs';
|
|
@@ -36,6 +35,7 @@ import {
|
|
|
36
35
|
produceArtifact,
|
|
37
36
|
sampleArtifactTypePack,
|
|
38
37
|
} from '../lib/artifactTypes.js';
|
|
38
|
+
import { req } from '../lib/requirement-ids.js';
|
|
39
39
|
|
|
40
40
|
describe('artifact-type-store-without-render: store-only hosts must not fail the run (RFC 0071)', () => {
|
|
41
41
|
it('a stored-but-unrendered artifact completes the run', async () => {
|
|
@@ -54,15 +54,15 @@ describe('artifact-type-store-without-render: store-only hosts must not fail the
|
|
|
54
54
|
|
|
55
55
|
expect(
|
|
56
56
|
produced.json['stored'],
|
|
57
|
-
|
|
57
|
+
req('openwop.it.artifact-type-store-without-render.a-stored-but-unrendered-artifact-completes-the-run', 'artifact-type-packs.md §host.artifactTypes', 'a host advertising store:true MUST persist the artifact'),
|
|
58
58
|
).toBe(true);
|
|
59
59
|
expect(
|
|
60
60
|
produced.json['rendered'],
|
|
61
|
-
|
|
61
|
+
req('openwop.it.artifact-type-store-without-render.a-stored-but-unrendered-artifact-completes-the-run', 'artifact-type-packs.md §host.artifactTypes', 'render:false host MUST NOT render'),
|
|
62
62
|
).toBe(false);
|
|
63
63
|
expect(
|
|
64
64
|
produced.json['runStatus'],
|
|
65
|
-
|
|
65
|
+
req('openwop.it.artifact-type-store-without-render.a-stored-but-unrendered-artifact-completes-the-run', 'artifact-type-packs.md §host.artifactTypes', 'a host MUST NOT fail the run solely because it lacks a renderer for a stored artifact type'),
|
|
66
66
|
).toBe('completed');
|
|
67
67
|
});
|
|
68
68
|
});
|
|
@@ -19,6 +19,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
19
19
|
import { driver } from '../lib/driver.js';
|
|
20
20
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
21
21
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
22
|
+
import { req } from '../lib/requirement-ids.js';
|
|
23
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
22
24
|
|
|
23
25
|
interface AuditIntegrityCaps {
|
|
24
26
|
hashChain?: boolean;
|
|
@@ -50,11 +52,11 @@ describe('audit-log-integrity: profile shape', () => {
|
|
|
50
52
|
capabilityFamily<AuthCaps>(disco.json, 'auth')
|
|
51
53
|
?.auditLogIntegrity ?? {};
|
|
52
54
|
|
|
53
|
-
expect(integrity.hashChain,
|
|
55
|
+
expect(integrity.hashChain, req('openwop.it.audit-log-integrity.host-that-claims-the-profile-advertises-required-capability-fields',
|
|
54
56
|
'auth-profiles.md §"Audit-log integrity"',
|
|
55
57
|
"openwop-audit-log-integrity profile MUST advertise auditLogIntegrity.hashChain: true",
|
|
56
58
|
)).toBe(true);
|
|
57
|
-
expect(integrity.checkpointSignatureAlgorithm,
|
|
59
|
+
expect(integrity.checkpointSignatureAlgorithm, req('openwop.it.audit-log-integrity.host-that-claims-the-profile-advertises-required-capability-fields',
|
|
58
60
|
'auth-profiles.md §"Audit-log integrity" §"Key management"',
|
|
59
61
|
'checkpointSignatureAlgorithm MUST be present (canonical: ed25519)',
|
|
60
62
|
)).toBeDefined();
|
|
@@ -72,11 +74,11 @@ describe('audit-log-integrity: verify endpoint returns chainValid', () => {
|
|
|
72
74
|
if (verify.status === 404) {
|
|
73
75
|
// Host claims the profile but doesn't expose the endpoint — that's
|
|
74
76
|
// a profile-claim violation. Fail explicitly.
|
|
75
|
-
expect(verify.status,
|
|
77
|
+
expect(verify.status, req('openwop.it.audit-log-integrity.get-v1-audit-verify-on-a-recent-range-reports-chainvalid-true',
|
|
76
78
|
'auth-profiles.md §"Audit-log integrity" §"Verification endpoint"',
|
|
77
79
|
'claiming openwop-audit-log-integrity profile REQUIRES exposing GET /v1/audit/verify',
|
|
78
80
|
)).not.toBe(404);
|
|
79
|
-
return;
|
|
81
|
+
return softSkip('blocked', 'precondition not met — `verify.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
80
82
|
}
|
|
81
83
|
expect(verify.status).toBe(200);
|
|
82
84
|
|
|
@@ -88,7 +90,7 @@ describe('audit-log-integrity: verify endpoint returns chainValid', () => {
|
|
|
88
90
|
anomalies?: unknown[];
|
|
89
91
|
};
|
|
90
92
|
|
|
91
|
-
expect(body.chainValid,
|
|
93
|
+
expect(body.chainValid, req('openwop.it.audit-log-integrity.get-v1-audit-verify-on-a-recent-range-reports-chainvalid-true',
|
|
92
94
|
'auth-profiles.md §"Audit-log integrity"',
|
|
93
95
|
'unmodified audit range MUST report chainValid: true',
|
|
94
96
|
)).toBe(true);
|
|
@@ -97,7 +99,7 @@ describe('audit-log-integrity: verify endpoint returns chainValid', () => {
|
|
|
97
99
|
|
|
98
100
|
if (Array.isArray(body.checkpoints) && body.checkpoints.length > 0) {
|
|
99
101
|
const cp = body.checkpoints[0];
|
|
100
|
-
expect(typeof cp.signature, 'checkpoint signature MUST be a non-empty string').toBe('string');
|
|
102
|
+
expect(typeof cp.signature, req('openwop.it.audit-log-integrity.get-v1-audit-verify-on-a-recent-range-reports-chainvalid-true', 'auth-profiles.md §"Audit-log integrity"', 'checkpoint signature MUST be a non-empty string')).toBe('string');
|
|
101
103
|
expect((cp.signature ?? '').length).toBeGreaterThan(0);
|
|
102
104
|
}
|
|
103
105
|
});
|
|
@@ -29,6 +29,8 @@ import { driver } from '../lib/driver.js';
|
|
|
29
29
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
30
30
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
31
31
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
32
|
+
import { req } from '../lib/requirement-ids.js';
|
|
33
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
32
34
|
|
|
33
35
|
interface RotationCaps {
|
|
34
36
|
supported?: boolean;
|
|
@@ -65,12 +67,12 @@ describe('auth-api-key-rotation: capability shape', () => {
|
|
|
65
67
|
return;
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
expect(auth?.profiles?.includes(PROFILE),
|
|
70
|
+
expect(auth?.profiles?.includes(PROFILE), req('openwop.it.auth-api-key-rotation.host-claiming-rotation-profile-advertises-required-fields',
|
|
69
71
|
'auth-profiles.md §`openwop-auth-api-key-rotation`',
|
|
70
72
|
'capabilities.auth.profiles MUST include openwop-auth-api-key-rotation when the profile is claimed',
|
|
71
73
|
)).toBe(true);
|
|
72
74
|
|
|
73
|
-
expect(auth?.rotation?.supported,
|
|
75
|
+
expect(auth?.rotation?.supported, req('openwop.it.auth-api-key-rotation.host-claiming-rotation-profile-advertises-required-fields',
|
|
74
76
|
'auth-profiles.md §`openwop-auth-api-key-rotation`',
|
|
75
77
|
'capabilities.auth.rotation.supported MUST be true when the profile is claimed',
|
|
76
78
|
)).toBe(true);
|
|
@@ -79,7 +81,7 @@ describe('auth-api-key-rotation: capability shape', () => {
|
|
|
79
81
|
expect(
|
|
80
82
|
Number.isInteger(auth.rotation.minGraceSeconds) &&
|
|
81
83
|
auth.rotation.minGraceSeconds >= 0,
|
|
82
|
-
|
|
84
|
+
req('openwop.it.auth-api-key-rotation.host-claiming-rotation-profile-advertises-required-fields',
|
|
83
85
|
'capabilities.schema.json auth.rotation.minGraceSeconds',
|
|
84
86
|
'minGraceSeconds MUST be a non-negative integer when advertised',
|
|
85
87
|
),
|
|
@@ -109,7 +111,7 @@ describe('auth-api-key-rotation: two-key overlap', () => {
|
|
|
109
111
|
console.warn(
|
|
110
112
|
'[auth-api-key-rotation] OPENWOP_TEST_SECONDARY_API_KEY not supplied; skipping two-key overlap assertion',
|
|
111
113
|
);
|
|
112
|
-
return;
|
|
114
|
+
return softSkip('blocked', 'precondition not met — `!secondaryKey` returned early ([auth-api-key-rotation] OPENWOP_TEST_SECONDARY_API_KEY not supplied; skipping two-key overlap assertion) (seam, prior step, or fixture unavailable)');
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
if (!isFixtureAdvertised(FIXTURE)) {
|
|
@@ -117,12 +119,12 @@ describe('auth-api-key-rotation: two-key overlap', () => {
|
|
|
117
119
|
console.warn(
|
|
118
120
|
`[auth-api-key-rotation] ${FIXTURE} not advertised; skipping overlap assertion`,
|
|
119
121
|
);
|
|
120
|
-
return;
|
|
122
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early ([auth-api-key-rotation] … not advertised; skipping overlap assertion)');
|
|
121
123
|
}
|
|
122
124
|
|
|
123
125
|
// Primary key — uses driver's default Authorization header (env-loaded).
|
|
124
126
|
const primary = await driver.post('/v1/runs', { workflowId: FIXTURE });
|
|
125
|
-
expect(primary.status,
|
|
127
|
+
expect(primary.status, req('openwop.it.auth-api-key-rotation.primary-secondary-keys-both-authenticate-the-same-operation',
|
|
126
128
|
'auth-profiles.md §`openwop-auth-api-key-rotation`',
|
|
127
129
|
'primary key MUST authenticate POST /v1/runs during rotation grace',
|
|
128
130
|
)).toBe(201);
|
|
@@ -137,7 +139,7 @@ describe('auth-api-key-rotation: two-key overlap', () => {
|
|
|
137
139
|
headers: { Authorization: `Bearer ${secondaryKey}` },
|
|
138
140
|
},
|
|
139
141
|
);
|
|
140
|
-
expect(secondary.status,
|
|
142
|
+
expect(secondary.status, req('openwop.it.auth-api-key-rotation.primary-secondary-keys-both-authenticate-the-same-operation',
|
|
141
143
|
'auth-profiles.md §`openwop-auth-api-key-rotation`',
|
|
142
144
|
'secondary key MUST authenticate POST /v1/runs during rotation grace',
|
|
143
145
|
)).toBe(201);
|
|
@@ -167,7 +169,7 @@ describe('auth-api-key-rotation: canary redaction', () => {
|
|
|
167
169
|
},
|
|
168
170
|
);
|
|
169
171
|
|
|
170
|
-
expect(res.status,
|
|
172
|
+
expect(res.status, req('openwop.it.auth-api-key-rotation.invalid-bearer-returns-401-without-echoing-the-canary-credential',
|
|
171
173
|
'auth.md §3',
|
|
172
174
|
'invalid bearer MUST return 401, not 200 or 403',
|
|
173
175
|
)).toBe(401);
|
|
@@ -175,7 +177,7 @@ describe('auth-api-key-rotation: canary redaction', () => {
|
|
|
175
177
|
// The response body MUST NOT echo the canary in any field. We
|
|
176
178
|
// check the serialized JSON to catch echoes even in nested fields.
|
|
177
179
|
const serialized = JSON.stringify(res.json ?? {});
|
|
178
|
-
expect(serialized.includes(CANARY),
|
|
180
|
+
expect(serialized.includes(CANARY), req('openwop.it.auth-api-key-rotation.invalid-bearer-returns-401-without-echoing-the-canary-credential',
|
|
179
181
|
'auth.md §"No credential echo" + threat-model-auth-profiles.md A1',
|
|
180
182
|
'rotation-profile hosts MUST NOT echo the rejected credential in error responses',
|
|
181
183
|
)).toBe(false);
|
|
@@ -46,6 +46,8 @@ import { loadEnv } from '../lib/env.js';
|
|
|
46
46
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
47
47
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
48
48
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
49
|
+
import { req } from '../lib/requirement-ids.js';
|
|
50
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
49
51
|
|
|
50
52
|
interface MtlsCaps {
|
|
51
53
|
supported?: boolean;
|
|
@@ -114,7 +116,7 @@ function mtlsPost(
|
|
|
114
116
|
return new Promise((resolve) => {
|
|
115
117
|
const url = new URL(baseUrl + path);
|
|
116
118
|
const payload = JSON.stringify(body);
|
|
117
|
-
const
|
|
119
|
+
const reqBody = httpsRequest(
|
|
118
120
|
{
|
|
119
121
|
hostname: url.hostname,
|
|
120
122
|
port: url.port ? Number.parseInt(url.port, 10) : 443,
|
|
@@ -138,9 +140,9 @@ function mtlsPost(
|
|
|
138
140
|
});
|
|
139
141
|
},
|
|
140
142
|
);
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
reqBody.on('error', (error) => resolve({ error }));
|
|
144
|
+
reqBody.write(payload);
|
|
145
|
+
reqBody.end();
|
|
144
146
|
});
|
|
145
147
|
}
|
|
146
148
|
|
|
@@ -152,12 +154,12 @@ describe('auth-mtls: capability shape', () => {
|
|
|
152
154
|
return;
|
|
153
155
|
}
|
|
154
156
|
|
|
155
|
-
expect(auth?.profiles?.includes(PROFILE),
|
|
157
|
+
expect(auth?.profiles?.includes(PROFILE), req('openwop.it.auth-mtls.host-claiming-mtls-profile-advertises-required-fields',
|
|
156
158
|
'auth-profiles.md §`openwop-auth-mtls`',
|
|
157
159
|
'capabilities.auth.profiles MUST include openwop-auth-mtls when the profile is claimed',
|
|
158
160
|
)).toBe(true);
|
|
159
161
|
|
|
160
|
-
expect(auth?.mtls?.supported,
|
|
162
|
+
expect(auth?.mtls?.supported, req('openwop.it.auth-mtls.host-claiming-mtls-profile-advertises-required-fields',
|
|
161
163
|
'auth-profiles.md §`openwop-auth-mtls`',
|
|
162
164
|
'capabilities.auth.mtls.supported MUST be true when the profile is claimed',
|
|
163
165
|
)).toBe(true);
|
|
@@ -165,14 +167,14 @@ describe('auth-mtls: capability shape', () => {
|
|
|
165
167
|
if (auth?.mtls?.required !== undefined) {
|
|
166
168
|
expect(
|
|
167
169
|
typeof auth.mtls.required,
|
|
168
|
-
'mtls.required MUST be boolean when advertised',
|
|
170
|
+
req('openwop.it.auth-mtls.host-claiming-mtls-profile-advertises-required-fields', 'auth-profiles.md §`openwop-auth-mtls`', 'mtls.required MUST be boolean when advertised'),
|
|
169
171
|
).toBe('boolean');
|
|
170
172
|
}
|
|
171
173
|
|
|
172
174
|
if (auth?.mtls?.subjectMapping !== undefined) {
|
|
173
175
|
expect(
|
|
174
176
|
['cn', 'san-dns', 'san-uri'].includes(auth.mtls.subjectMapping),
|
|
175
|
-
|
|
177
|
+
req('openwop.it.auth-mtls.host-claiming-mtls-profile-advertises-required-fields',
|
|
176
178
|
'capabilities.schema.json auth.mtls.subjectMapping',
|
|
177
179
|
'subjectMapping MUST be one of cn / san-dns / san-uri',
|
|
178
180
|
),
|
|
@@ -184,7 +186,7 @@ describe('auth-mtls: capability shape', () => {
|
|
|
184
186
|
describe.skipIf(!RUN_BEHAVIOR)('auth-mtls: client cert behavior', () => {
|
|
185
187
|
it('valid client cert + valid bearer → 201', async () => {
|
|
186
188
|
const auth = await readAuthCaps();
|
|
187
|
-
if (!isProfileAdvertised(auth)) return;
|
|
189
|
+
if (!isProfileAdvertised(auth)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isProfileAdvertised(auth)` returned early');
|
|
188
190
|
|
|
189
191
|
const certs = loadClientCerts();
|
|
190
192
|
if (!certs) {
|
|
@@ -192,10 +194,10 @@ describe.skipIf(!RUN_BEHAVIOR)('auth-mtls: client cert behavior', () => {
|
|
|
192
194
|
console.warn(
|
|
193
195
|
'[auth-mtls] OPENWOP_TEST_MTLS=1 but cert paths missing; skipping behavior',
|
|
194
196
|
);
|
|
195
|
-
return;
|
|
197
|
+
return softSkip('blocked', 'precondition not met — `!certs` returned early ([auth-mtls] OPENWOP_TEST_MTLS=1 but cert paths missing; skipping behavior) (seam, prior step, or fixture unavailable)');
|
|
196
198
|
}
|
|
197
199
|
|
|
198
|
-
if (!isFixtureAdvertised(FIXTURE)) return;
|
|
200
|
+
if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
|
|
199
201
|
|
|
200
202
|
const env = loadEnv();
|
|
201
203
|
if (!env.baseUrl.startsWith('https://')) {
|
|
@@ -203,7 +205,7 @@ describe.skipIf(!RUN_BEHAVIOR)('auth-mtls: client cert behavior', () => {
|
|
|
203
205
|
console.warn(
|
|
204
206
|
`[auth-mtls] OPENWOP_BASE_URL is not HTTPS (got ${env.baseUrl}); mTLS requires HTTPS — skipping`,
|
|
205
207
|
);
|
|
206
|
-
return;
|
|
208
|
+
return softSkip('blocked', 'precondition not met — `!env.baseUrl.startsWith(\'https://\')` returned early ([auth-mtls] OPENWOP_BASE_URL is not HTTPS (got …); mTLS requires HTTPS — skipping) (seam, prior step, or fixture unavailable)');
|
|
207
209
|
}
|
|
208
210
|
|
|
209
211
|
const res = await mtlsPost(
|
|
@@ -220,7 +222,7 @@ describe.skipIf(!RUN_BEHAVIOR)('auth-mtls: client cert behavior', () => {
|
|
|
220
222
|
);
|
|
221
223
|
}
|
|
222
224
|
|
|
223
|
-
expect(res.status,
|
|
225
|
+
expect(res.status, req('openwop.it.auth-mtls.valid-client-cert-valid-bearer-201',
|
|
224
226
|
'auth-profiles.md §`openwop-auth-mtls`',
|
|
225
227
|
'valid client cert + valid bearer MUST authenticate POST /v1/runs (201)',
|
|
226
228
|
)).toBe(201);
|
|
@@ -228,16 +230,16 @@ describe.skipIf(!RUN_BEHAVIOR)('auth-mtls: client cert behavior', () => {
|
|
|
228
230
|
|
|
229
231
|
it('no client cert against mtls.required: true → non-2xx or TLS failure', async () => {
|
|
230
232
|
const auth = await readAuthCaps();
|
|
231
|
-
if (!isProfileAdvertised(auth)) return;
|
|
233
|
+
if (!isProfileAdvertised(auth)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isProfileAdvertised(auth)` returned early');
|
|
232
234
|
if (auth?.mtls?.required !== true) {
|
|
233
235
|
// eslint-disable-next-line no-console
|
|
234
236
|
console.warn(
|
|
235
237
|
'[auth-mtls] host advertises mtls.required: false; skipping no-cert rejection (host may accept bearer-only)',
|
|
236
238
|
);
|
|
237
|
-
return;
|
|
239
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `auth?.mtls?.required !== true` returned early ([auth-mtls] host advertises mtls.required: false; skipping no-cert rejection (host may accept bearer-only))');
|
|
238
240
|
}
|
|
239
241
|
|
|
240
|
-
if (!isFixtureAdvertised(FIXTURE)) return;
|
|
242
|
+
if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
|
|
241
243
|
|
|
242
244
|
const env = loadEnv();
|
|
243
245
|
if (!env.baseUrl.startsWith('https://')) {
|
|
@@ -245,7 +247,7 @@ describe.skipIf(!RUN_BEHAVIOR)('auth-mtls: client cert behavior', () => {
|
|
|
245
247
|
console.warn(
|
|
246
248
|
`[auth-mtls] OPENWOP_BASE_URL is not HTTPS (got ${env.baseUrl}); skipping no-cert rejection`,
|
|
247
249
|
);
|
|
248
|
-
return;
|
|
250
|
+
return softSkip('blocked', 'precondition not met — `!env.baseUrl.startsWith(\'https://\')` returned early ([auth-mtls] OPENWOP_BASE_URL is not HTTPS (got …); skipping no-cert rejection) (seam, prior step, or fixture unavailable)');
|
|
249
251
|
}
|
|
250
252
|
|
|
251
253
|
// No certs supplied → either 4xx or transport-layer TLS handshake failure.
|
|
@@ -260,14 +262,14 @@ describe.skipIf(!RUN_BEHAVIOR)('auth-mtls: client cert behavior', () => {
|
|
|
260
262
|
if ('error' in res) {
|
|
261
263
|
// Transport-layer TLS failure is conformant per auth-profiles.md.
|
|
262
264
|
// The handshake rejected the client because no cert was offered.
|
|
263
|
-
expect(true,
|
|
265
|
+
expect(true, req('openwop.it.auth-mtls.no-client-cert-against-mtls-required-true-non-2xx-or-tls-failure',
|
|
264
266
|
'auth-profiles.md §`openwop-auth-mtls`',
|
|
265
267
|
'mtls.required: true MUST reject no-cert requests (TLS handshake failure is conformant)',
|
|
266
268
|
)).toBe(true);
|
|
267
|
-
return;
|
|
269
|
+
return softSkip('blocked', 'precondition not met — `\'error\' in res` returned early (seam, prior step, or fixture unavailable)');
|
|
268
270
|
}
|
|
269
271
|
|
|
270
|
-
expect(res.status >= 400,
|
|
272
|
+
expect(res.status >= 400, req('openwop.it.auth-mtls.no-client-cert-against-mtls-required-true-non-2xx-or-tls-failure',
|
|
271
273
|
'auth-profiles.md §`openwop-auth-mtls`',
|
|
272
274
|
'mtls.required: true MUST reject no-cert requests at the auth layer (4xx) when not rejected at the TLS layer',
|
|
273
275
|
)).toBe(true);
|
|
@@ -34,6 +34,8 @@ import { behaviorGate } from '../lib/behavior-gate.js';
|
|
|
34
34
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
35
35
|
import { createSyntheticOIDCIssuer } from '../lib/oidc-issuer.js';
|
|
36
36
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
37
|
+
import { req } from '../lib/requirement-ids.js';
|
|
38
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
37
39
|
|
|
38
40
|
/**
|
|
39
41
|
* Callback-shaped: the host fetches the token endpoint on the suite's synthetic OIDC issuer.
|
|
@@ -79,12 +81,12 @@ describe('auth-oauth2-client-credentials: capability shape', () => {
|
|
|
79
81
|
return;
|
|
80
82
|
}
|
|
81
83
|
|
|
82
|
-
expect(auth?.profiles?.includes(PROFILE),
|
|
84
|
+
expect(auth?.profiles?.includes(PROFILE), req('openwop.it.auth-oauth2-client-credentials.host-claiming-oauth2-cc-profile-advertises-required-fields',
|
|
83
85
|
'auth-profiles.md §`openwop-auth-oauth2-client-credentials`',
|
|
84
86
|
'capabilities.auth.profiles MUST include openwop-auth-oauth2-client-credentials when the profile is claimed',
|
|
85
87
|
)).toBe(true);
|
|
86
88
|
|
|
87
|
-
expect(auth?.oauth2?.supported,
|
|
89
|
+
expect(auth?.oauth2?.supported, req('openwop.it.auth-oauth2-client-credentials.host-claiming-oauth2-cc-profile-advertises-required-fields',
|
|
88
90
|
'auth-profiles.md §`openwop-auth-oauth2-client-credentials`',
|
|
89
91
|
'capabilities.auth.oauth2.supported MUST be true when the profile is claimed',
|
|
90
92
|
)).toBe(true);
|
|
@@ -92,7 +94,7 @@ describe('auth-oauth2-client-credentials: capability shape', () => {
|
|
|
92
94
|
if (auth?.oauth2?.issuer !== undefined) {
|
|
93
95
|
expect(
|
|
94
96
|
typeof auth.oauth2.issuer === 'string' && auth.oauth2.issuer.length > 0,
|
|
95
|
-
|
|
97
|
+
req('openwop.it.auth-oauth2-client-credentials.host-claiming-oauth2-cc-profile-advertises-required-fields',
|
|
96
98
|
'capabilities.schema.json auth.oauth2.issuer',
|
|
97
99
|
'issuer MUST be a non-empty string when advertised',
|
|
98
100
|
),
|
|
@@ -102,7 +104,7 @@ describe('auth-oauth2-client-credentials: capability shape', () => {
|
|
|
102
104
|
if (auth?.oauth2?.audience !== undefined) {
|
|
103
105
|
expect(
|
|
104
106
|
typeof auth.oauth2.audience === 'string' && auth.oauth2.audience.length > 0,
|
|
105
|
-
|
|
107
|
+
req('openwop.it.auth-oauth2-client-credentials.host-claiming-oauth2-cc-profile-advertises-required-fields',
|
|
106
108
|
'capabilities.schema.json auth.oauth2.audience',
|
|
107
109
|
'audience MUST be a non-empty string when advertised',
|
|
108
110
|
),
|
|
@@ -113,7 +115,7 @@ describe('auth-oauth2-client-credentials: capability shape', () => {
|
|
|
113
115
|
expect(
|
|
114
116
|
Array.isArray(auth.oauth2.supportedAlgorithms) &&
|
|
115
117
|
auth.oauth2.supportedAlgorithms.length > 0,
|
|
116
|
-
|
|
118
|
+
req('openwop.it.auth-oauth2-client-credentials.host-claiming-oauth2-cc-profile-advertises-required-fields',
|
|
117
119
|
'capabilities.schema.json auth.oauth2.supportedAlgorithms',
|
|
118
120
|
'supportedAlgorithms MUST be a non-empty array when advertised',
|
|
119
121
|
),
|
|
@@ -139,13 +141,13 @@ describe('auth-oauth2-client-credentials: malformed JWT rejected', () => {
|
|
|
139
141
|
},
|
|
140
142
|
);
|
|
141
143
|
|
|
142
|
-
expect(res.status,
|
|
144
|
+
expect(res.status, req('openwop.it.auth-oauth2-client-credentials.returns-401-on-bearer-that-is-not-a-valid-jwt-shape',
|
|
143
145
|
'auth.md §3',
|
|
144
146
|
'malformed JWT bearer MUST return 401 (canonical invalid_token envelope)',
|
|
145
147
|
)).toBe(401);
|
|
146
148
|
|
|
147
149
|
const body = res.json as { error?: unknown; message?: unknown } | undefined;
|
|
148
|
-
expect(typeof body?.error,
|
|
150
|
+
expect(typeof body?.error, req('openwop.it.auth-oauth2-client-credentials.returns-401-on-bearer-that-is-not-a-valid-jwt-shape',
|
|
149
151
|
'auth.md §3 + rest-endpoints.md error envelope',
|
|
150
152
|
'response body MUST include `error` (machine code) string',
|
|
151
153
|
)).toBe('string');
|
|
@@ -165,7 +167,7 @@ describe('auth-oauth2-client-credentials: harness-minted negative cases', () =>
|
|
|
165
167
|
console.warn(
|
|
166
168
|
'[auth-oauth2-client-credentials] OPENWOP_TEST_OAUTH_ISSUER_TRUSTED not set; skipping harness-minted negative cases (operator must pre-configure the host to trust the conformance harness)',
|
|
167
169
|
);
|
|
168
|
-
return;
|
|
170
|
+
return softSkip('blocked', 'precondition not met — `process.env.OPENWOP_TEST_OAUTH_ISSUER_TRUSTED !== \'true\'` returned early ([auth-oauth2-client-credentials] OPENWOP_TEST_OAUTH_ISSUER_TRUSTED not set; skipping harness-minted negative cases (ope…');
|
|
169
171
|
}
|
|
170
172
|
|
|
171
173
|
const issuerUrl =
|
|
@@ -187,7 +189,7 @@ describe('auth-oauth2-client-credentials: harness-minted negative cases', () =>
|
|
|
187
189
|
headers: { Authorization: `Bearer ${wrongAud.token}` },
|
|
188
190
|
},
|
|
189
191
|
);
|
|
190
|
-
expect(wrongAudRes.status,
|
|
192
|
+
expect(wrongAudRes.status, req('openwop.it.auth-oauth2-client-credentials.wrong-audience-token-returns-401-when-host-trusts-the-harness',
|
|
191
193
|
'auth-profiles.md §`openwop-auth-oauth2-client-credentials`',
|
|
192
194
|
'token with wrong aud claim MUST return 401',
|
|
193
195
|
)).toBe(401);
|
|
@@ -205,7 +207,7 @@ describe('auth-oauth2-client-credentials: harness-minted negative cases', () =>
|
|
|
205
207
|
headers: { Authorization: `Bearer ${expired.token}` },
|
|
206
208
|
},
|
|
207
209
|
);
|
|
208
|
-
expect(expiredRes.status,
|
|
210
|
+
expect(expiredRes.status, req('openwop.it.auth-oauth2-client-credentials.wrong-audience-token-returns-401-when-host-trusts-the-harness',
|
|
209
211
|
'auth-profiles.md §`openwop-auth-oauth2-client-credentials`',
|
|
210
212
|
'expired token (exp < now) MUST return 401',
|
|
211
213
|
)).toBe(401);
|
|
@@ -223,7 +225,7 @@ describe('auth-oauth2-client-credentials: harness-minted negative cases', () =>
|
|
|
223
225
|
headers: { Authorization: `Bearer ${algSpoofed.token}` },
|
|
224
226
|
},
|
|
225
227
|
);
|
|
226
|
-
expect(algSpoofedRes.status,
|
|
228
|
+
expect(algSpoofedRes.status, req('openwop.it.auth-oauth2-client-credentials.wrong-audience-token-returns-401-when-host-trusts-the-harness',
|
|
227
229
|
'auth-profiles.md §`openwop-auth-oauth2-client-credentials`',
|
|
228
230
|
'token with alg outside supportedAlgorithms MUST return 401',
|
|
229
231
|
)).toBe(401);
|
|
@@ -244,11 +246,11 @@ describe('auth-oauth2-client-credentials: positive token', () => {
|
|
|
244
246
|
console.warn(
|
|
245
247
|
'[auth-oauth2-client-credentials] OPENWOP_TEST_OAUTH_TOKEN not supplied; skipping positive-path assertion',
|
|
246
248
|
);
|
|
247
|
-
return;
|
|
249
|
+
return softSkip('blocked', 'precondition not met — `!token` returned early ([auth-oauth2-client-credentials] OPENWOP_TEST_OAUTH_TOKEN not supplied; skipping positive-path assertion) (seam, prior step, or fixture unavailable)');
|
|
248
250
|
}
|
|
249
251
|
|
|
250
252
|
if (!isFixtureAdvertised(FIXTURE)) {
|
|
251
|
-
return;
|
|
253
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
|
|
252
254
|
}
|
|
253
255
|
|
|
254
256
|
const res = await driver.post(
|
|
@@ -260,7 +262,7 @@ describe('auth-oauth2-client-credentials: positive token', () => {
|
|
|
260
262
|
},
|
|
261
263
|
);
|
|
262
264
|
|
|
263
|
-
expect(res.status,
|
|
265
|
+
expect(res.status, req('openwop.it.auth-oauth2-client-credentials.operator-supplied-valid-token-authenticates-post-v1-runs',
|
|
264
266
|
'auth-profiles.md §`openwop-auth-oauth2-client-credentials`',
|
|
265
267
|
'valid OAuth2-CC token MUST authenticate POST /v1/runs (201)',
|
|
266
268
|
)).toBe(201);
|
|
@@ -48,6 +48,8 @@ import {
|
|
|
48
48
|
type SyntheticOIDCIssuer,
|
|
49
49
|
} from '../lib/oidc-issuer.js';
|
|
50
50
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
51
|
+
import { req } from '../lib/requirement-ids.js';
|
|
52
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
51
53
|
|
|
52
54
|
/**
|
|
53
55
|
* Callback-shaped: the host fetches JWKS and discovery from the suite's synthetic OIDC issuer.
|
|
@@ -94,19 +96,19 @@ describe('auth-oidc-user-bearer: capability shape', () => {
|
|
|
94
96
|
return;
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
expect(auth?.profiles?.includes(PROFILE),
|
|
99
|
+
expect(auth?.profiles?.includes(PROFILE), req('openwop.it.auth-oidc-user-bearer.host-claiming-oidc-profile-advertises-required-fields',
|
|
98
100
|
'auth-profiles.md §`openwop-auth-oidc-user-bearer`',
|
|
99
101
|
'capabilities.auth.profiles MUST include openwop-auth-oidc-user-bearer when the profile is claimed',
|
|
100
102
|
)).toBe(true);
|
|
101
103
|
|
|
102
|
-
expect(auth?.oidc?.supported,
|
|
104
|
+
expect(auth?.oidc?.supported, req('openwop.it.auth-oidc-user-bearer.host-claiming-oidc-profile-advertises-required-fields',
|
|
103
105
|
'auth-profiles.md §`openwop-auth-oidc-user-bearer`',
|
|
104
106
|
'capabilities.auth.oidc.supported MUST be true when the profile is claimed',
|
|
105
107
|
)).toBe(true);
|
|
106
108
|
|
|
107
109
|
expect(
|
|
108
110
|
Array.isArray(auth?.oidc?.issuers) && (auth?.oidc?.issuers?.length ?? 0) > 0,
|
|
109
|
-
|
|
111
|
+
req('openwop.it.auth-oidc-user-bearer.host-claiming-oidc-profile-advertises-required-fields',
|
|
110
112
|
'capabilities.schema.json auth.oidc.issuers',
|
|
111
113
|
'issuers MUST be a non-empty array when the profile is claimed',
|
|
112
114
|
),
|
|
@@ -115,14 +117,14 @@ describe('auth-oidc-user-bearer: capability shape', () => {
|
|
|
115
117
|
for (const issuer of auth?.oidc?.issuers ?? []) {
|
|
116
118
|
expect(
|
|
117
119
|
typeof issuer === 'string' && issuer.length > 0,
|
|
118
|
-
'each issuer entry MUST be a non-empty string',
|
|
120
|
+
req('openwop.it.auth-oidc-user-bearer.host-claiming-oidc-profile-advertises-required-fields', 'capabilities.schema.json auth.oidc.issuers', 'each issuer entry MUST be a non-empty string'),
|
|
119
121
|
).toBe(true);
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
if (auth?.oidc?.audience !== undefined) {
|
|
123
125
|
expect(
|
|
124
126
|
typeof auth.oidc.audience === 'string' && auth.oidc.audience.length > 0,
|
|
125
|
-
'audience MUST be a non-empty string when advertised',
|
|
127
|
+
req('openwop.it.auth-oidc-user-bearer.host-claiming-oidc-profile-advertises-required-fields', 'capabilities.schema.json auth.oidc.issuers', 'audience MUST be a non-empty string when advertised'),
|
|
126
128
|
).toBe(true);
|
|
127
129
|
}
|
|
128
130
|
|
|
@@ -131,7 +133,7 @@ describe('auth-oidc-user-bearer: capability shape', () => {
|
|
|
131
133
|
['group-claim', 'scope-claim', 'host-acl'].includes(
|
|
132
134
|
auth.oidc.supportedScopeMapping,
|
|
133
135
|
),
|
|
134
|
-
|
|
136
|
+
req('openwop.it.auth-oidc-user-bearer.host-claiming-oidc-profile-advertises-required-fields',
|
|
135
137
|
'capabilities.schema.json auth.oidc.supportedScopeMapping',
|
|
136
138
|
'supportedScopeMapping MUST be one of group-claim/scope-claim/host-acl',
|
|
137
139
|
),
|
|
@@ -142,7 +144,7 @@ describe('auth-oidc-user-bearer: capability shape', () => {
|
|
|
142
144
|
expect(
|
|
143
145
|
Number.isInteger(auth.oidc.introspectionIntervalSeconds) &&
|
|
144
146
|
auth.oidc.introspectionIntervalSeconds >= 0,
|
|
145
|
-
'introspectionIntervalSeconds MUST be a non-negative integer when advertised',
|
|
147
|
+
req('openwop.it.auth-oidc-user-bearer.host-claiming-oidc-profile-advertises-required-fields', 'capabilities.schema.json auth.oidc.supportedScopeMapping', 'introspectionIntervalSeconds MUST be a non-negative integer when advertised'),
|
|
146
148
|
).toBe(true);
|
|
147
149
|
}
|
|
148
150
|
});
|
|
@@ -175,16 +177,16 @@ describe('auth-oidc-user-bearer: harness-driven token validation', () => {
|
|
|
175
177
|
const parsed = new URL(harnessUrl);
|
|
176
178
|
const port = parsed.port ? Number.parseInt(parsed.port, 10) : 80;
|
|
177
179
|
|
|
178
|
-
server = createServer((
|
|
180
|
+
server = createServer((reqBody, res) => {
|
|
179
181
|
if (!issuer) {
|
|
180
182
|
res.writeHead(503);
|
|
181
183
|
res.end();
|
|
182
184
|
return;
|
|
183
185
|
}
|
|
184
|
-
if (
|
|
186
|
+
if (reqBody.url === '/.well-known/jwks.json') {
|
|
185
187
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
186
188
|
res.end(issuer.jwksJson);
|
|
187
|
-
} else if (
|
|
189
|
+
} else if (reqBody.url === '/.well-known/openid-configuration') {
|
|
188
190
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
189
191
|
res.end(issuer.discoveryJson);
|
|
190
192
|
} else {
|
|
@@ -230,7 +232,7 @@ describe('auth-oidc-user-bearer: harness-driven token validation', () => {
|
|
|
230
232
|
console.warn(
|
|
231
233
|
'[auth-oidc-user-bearer] harness not wired or host trust not configured; skipping wrong-iss case',
|
|
232
234
|
);
|
|
233
|
-
return;
|
|
235
|
+
return softSkip('blocked', 'precondition not met — `!issuer || !trustWired` returned early ([auth-oidc-user-bearer] harness not wired or host trust not configured; skipping wrong-iss case) (seam, prior step, or fixture unavailable)');
|
|
234
236
|
}
|
|
235
237
|
|
|
236
238
|
const wrongIssIssuer = createSyntheticOIDCIssuer({
|
|
@@ -248,14 +250,14 @@ describe('auth-oidc-user-bearer: harness-driven token validation', () => {
|
|
|
248
250
|
},
|
|
249
251
|
);
|
|
250
252
|
|
|
251
|
-
expect(res.status,
|
|
253
|
+
expect(res.status, req('openwop.it.auth-oidc-user-bearer.wrong-iss-401',
|
|
252
254
|
'auth-profiles.md §`openwop-auth-oidc-user-bearer`',
|
|
253
255
|
'token with non-trusted iss MUST return 401',
|
|
254
256
|
)).toBe(401);
|
|
255
257
|
});
|
|
256
258
|
|
|
257
259
|
it('wrong aud → 401', async () => {
|
|
258
|
-
if (!issuer || !trustWired) return;
|
|
260
|
+
if (!issuer || !trustWired) return softSkip('blocked', 'precondition not met — `!issuer || !trustWired` returned early (seam, prior step, or fixture unavailable)');
|
|
259
261
|
const wrongAud = issuer.mint({ aud: 'wrong-audience', sub: 'attacker' });
|
|
260
262
|
const res = await driver.post(
|
|
261
263
|
'/v1/runs',
|
|
@@ -265,14 +267,14 @@ describe('auth-oidc-user-bearer: harness-driven token validation', () => {
|
|
|
265
267
|
headers: { Authorization: `Bearer ${wrongAud.token}` },
|
|
266
268
|
},
|
|
267
269
|
);
|
|
268
|
-
expect(res.status,
|
|
270
|
+
expect(res.status, req('openwop.it.auth-oidc-user-bearer.wrong-aud-401',
|
|
269
271
|
'auth-profiles.md §`openwop-auth-oidc-user-bearer`',
|
|
270
272
|
'token with wrong aud MUST return 401',
|
|
271
273
|
)).toBe(401);
|
|
272
274
|
});
|
|
273
275
|
|
|
274
276
|
it('expired exp → 401', async () => {
|
|
275
|
-
if (!issuer || !trustWired) return;
|
|
277
|
+
if (!issuer || !trustWired) return softSkip('blocked', 'precondition not met — `!issuer || !trustWired` returned early (seam, prior step, or fixture unavailable)');
|
|
276
278
|
const expired = issuer.mint(
|
|
277
279
|
{ sub: 'conformance-suite' },
|
|
278
280
|
{ expiresInSeconds: -3600 },
|
|
@@ -285,14 +287,14 @@ describe('auth-oidc-user-bearer: harness-driven token validation', () => {
|
|
|
285
287
|
headers: { Authorization: `Bearer ${expired.token}` },
|
|
286
288
|
},
|
|
287
289
|
);
|
|
288
|
-
expect(res.status,
|
|
290
|
+
expect(res.status, req('openwop.it.auth-oidc-user-bearer.expired-exp-401',
|
|
289
291
|
'auth-profiles.md §`openwop-auth-oidc-user-bearer`',
|
|
290
292
|
'expired token (exp < now) MUST return 401',
|
|
291
293
|
)).toBe(401);
|
|
292
294
|
});
|
|
293
295
|
|
|
294
296
|
it('unknown kid → 401', async () => {
|
|
295
|
-
if (!issuer || !trustWired) return;
|
|
297
|
+
if (!issuer || !trustWired) return softSkip('blocked', 'precondition not met — `!issuer || !trustWired` returned early (seam, prior step, or fixture unavailable)');
|
|
296
298
|
const unknownKid = issuer.mint(
|
|
297
299
|
{ sub: 'conformance-suite' },
|
|
298
300
|
{ keyId: 'openwop-conformance-key-NEVER-PUBLISHED' },
|
|
@@ -305,15 +307,15 @@ describe('auth-oidc-user-bearer: harness-driven token validation', () => {
|
|
|
305
307
|
headers: { Authorization: `Bearer ${unknownKid.token}` },
|
|
306
308
|
},
|
|
307
309
|
);
|
|
308
|
-
expect(res.status,
|
|
310
|
+
expect(res.status, req('openwop.it.auth-oidc-user-bearer.unknown-kid-401',
|
|
309
311
|
'auth-profiles.md §`openwop-auth-oidc-user-bearer` + threat-model-auth-profiles.md A3',
|
|
310
312
|
'token referencing a kid not in JWKS MUST return 401',
|
|
311
313
|
)).toBe(401);
|
|
312
314
|
});
|
|
313
315
|
|
|
314
316
|
it('valid token → 201 or 403 (depending on host scope mapping)', async () => {
|
|
315
|
-
if (!issuer || !trustWired) return;
|
|
316
|
-
if (!isFixtureAdvertised(FIXTURE)) return;
|
|
317
|
+
if (!issuer || !trustWired) return softSkip('blocked', 'precondition not met — `!issuer || !trustWired` returned early (seam, prior step, or fixture unavailable)');
|
|
318
|
+
if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early');
|
|
317
319
|
|
|
318
320
|
const valid = issuer.mint({
|
|
319
321
|
sub: 'conformance-suite',
|
|
@@ -334,7 +336,7 @@ describe('auth-oidc-user-bearer: harness-driven token validation', () => {
|
|
|
334
336
|
// decision is a separate host-side policy not normated by RFC 0010.
|
|
335
337
|
expect(
|
|
336
338
|
[201, 403].includes(res.status),
|
|
337
|
-
|
|
339
|
+
req('openwop.it.auth-oidc-user-bearer.valid-token-201-or-403-depending-on-host-scope-mapping',
|
|
338
340
|
'auth-profiles.md §`openwop-auth-oidc-user-bearer`',
|
|
339
341
|
'host-trusted token MUST yield 201 (mapped scope) or 403 (unmapped sub), NOT 401',
|
|
340
342
|
),
|
|
@@ -342,14 +344,14 @@ describe('auth-oidc-user-bearer: harness-driven token validation', () => {
|
|
|
342
344
|
});
|
|
343
345
|
|
|
344
346
|
it('scope-insufficient → 403 (when host uses group-claim mapping)', async () => {
|
|
345
|
-
if (!issuer || !trustWired) return;
|
|
347
|
+
if (!issuer || !trustWired) return softSkip('blocked', 'precondition not met — `!issuer || !trustWired` returned early (seam, prior step, or fixture unavailable)');
|
|
346
348
|
const auth = await readAuthCaps();
|
|
347
349
|
if (auth?.oidc?.supportedScopeMapping !== 'group-claim') {
|
|
348
350
|
// eslint-disable-next-line no-console
|
|
349
351
|
console.warn(
|
|
350
352
|
'[auth-oidc-user-bearer] host scope mapping is not group-claim; skipping scope-insufficient case',
|
|
351
353
|
);
|
|
352
|
-
return;
|
|
354
|
+
return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `auth?.oidc?.supportedScopeMapping !== \'group-claim\'` returned early ([auth-oidc-user-bearer] host scope mapping is not group-claim; skipping scope-insufficient…');
|
|
353
355
|
}
|
|
354
356
|
|
|
355
357
|
const noGroups = issuer.mint({ sub: 'conformance-suite', groups: [] });
|
|
@@ -362,7 +364,7 @@ describe('auth-oidc-user-bearer: harness-driven token validation', () => {
|
|
|
362
364
|
},
|
|
363
365
|
);
|
|
364
366
|
|
|
365
|
-
expect(res.status,
|
|
367
|
+
expect(res.status, req('openwop.it.auth-oidc-user-bearer.scope-insufficient-403-when-host-uses-group-claim-mapping',
|
|
366
368
|
'auth-profiles.md §`openwop-auth-oidc-user-bearer`',
|
|
367
369
|
'token-valid-but-empty-groups against group-claim host MUST return 403 (forbidden), NOT 401',
|
|
368
370
|
)).toBe(403);
|