@opensoha/contracts 0.1.16 → 0.1.18
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/COMPATIBILITY.md +91 -0
- package/README.md +49 -3
- package/auth/catalog_generated.go +2 -2
- package/auth/catalog_test.go +32 -0
- package/auth/outpost_headers.go +17 -0
- package/auth/outpost_headers_test.go +16 -0
- package/auth/permission-catalog.json +1097 -187
- package/compat/consumer-revisions.json +3 -0
- package/compat/json-schema-ecosystem-0.1.0.json +1 -1
- package/compat/openapi-loose-boundaries-0.1.0.json +78 -46
- package/delivery/canonical.go +136 -0
- package/delivery/document.go +203 -0
- package/delivery/document.schema.json +1527 -0
- package/delivery/document_test.go +191 -0
- package/delivery/parser.go +155 -0
- package/evaluation/evaluation-run.schema.json +64 -10
- package/fixtures/json-schema/gateway-manifest/invalid/bad-execution-mode.json +73 -0
- package/fixtures/json-schema/gateway-manifest/invalid/unversioned-check.json +85 -0
- package/fixtures/json-schema/gateway-manifest/valid/checks.json +86 -0
- package/fixtures/json-schema/gateway-manifest/valid/versioned-task.json +73 -0
- package/fixtures/json-schema/network-ingest-event/invalid/duplicate-event.json +33 -0
- package/fixtures/json-schema/network-ingest-event/invalid/message-payload-mismatch.json +23 -0
- package/fixtures/json-schema/network-ingest-event/invalid/password.json +22 -0
- package/fixtures/json-schema/network-ingest-event/invalid/proxy-flow-destination.json +28 -0
- package/fixtures/json-schema/network-ingest-event/invalid/vpn-probe-negative-rtt.json +34 -0
- package/fixtures/json-schema/network-ingest-event/invalid/vpn-stats-secret.json +27 -0
- package/fixtures/json-schema/network-ingest-event/valid/endpoint-proxy-flow.json +27 -0
- package/fixtures/json-schema/network-ingest-event/valid/freeradius-accounting.json +25 -0
- package/fixtures/json-schema/network-ingest-event/valid/gateway.json +53 -0
- package/fixtures/json-schema/network-ingest-event/valid/vpn-probe-batch.json +45 -0
- package/fixtures/json-schema/network-ingest-event/valid/vpn-tunnel-stats.json +26 -0
- package/fixtures/json-schema/network-radius-accounting/invalid/password.json +9 -0
- package/fixtures/json-schema/network-radius-accounting/valid/accounting.json +16 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/invalid-cidr.json +31 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/message-payload-mismatch.json +19 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/mihomo-raw-subscription.json +33 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/nas-authorization-password.json +18 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/nas-eap-without-certificate.json +17 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/private-key.json +20 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/raw-credential-reference.json +16 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/runtime-interval-too-short.json +23 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/vpn-managed-bad-token.json +15 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/vpn-managed-mode-override.json +16 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/wireguard-capability-without-key.json +19 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/wireguard-default-route.json +31 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/ztna-without-access-grant.json +17 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/configuration-desired.json +86 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/direct-ztna-connect.json +19 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/direct-ztna-result.json +33 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/internal-ztna-connect.json +19 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/managed-vpn-macos-enrollment.json +27 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/mihomo-app-subscription-configuration.json +32 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/mihomo-manual-node-configuration.json +32 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/mihomo-wireguard-configuration.json +70 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/nas-authorization-request.json +19 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/nas-authorization-result.json +24 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/nas-session-command-result.json +17 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/nas-session-command.json +27 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/vpn-connect-request.json +17 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/vpn-connect-result.json +34 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/vpn-managed-connect.json +15 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/wireguard-enrollment.json +20 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/wireguard-site-link.json +48 -0
- package/fixtures/openapi/AgentChatInput/invalid/empty-question.json +4 -0
- package/fixtures/openapi/AgentChatInput/invalid/system-role.json +9 -0
- package/fixtures/openapi/AgentChatInput/valid/authorized-background.json +5 -0
- package/fixtures/openapi/AgentChatInput/valid/conversation.json +15 -0
- package/fixtures/openapi/AgentRunnerToolCallRequest/invalid/model-identity.json +1 -0
- package/fixtures/openapi/AgentRunnerToolCallRequest/valid/search.json +1 -0
- package/fixtures/openapi/AnnouncementReceiptPageEnvelope/invalid/missing-read-count.json +9 -0
- package/fixtures/openapi/AnnouncementReceiptPageEnvelope/valid/read-and-unread.json +28 -0
- package/fixtures/openapi/ApplicationInput/invalid/negative-version.json +6 -0
- package/fixtures/openapi/ApplicationInput/invalid/zero-version.json +6 -0
- package/fixtures/openapi/ApplicationInput/valid/versioned.json +6 -0
- package/fixtures/openapi/ApprovalRequest/invalid/resolved-scopes.json +20 -0
- package/fixtures/openapi/ApprovalRequest/valid/resolved-scopes.json +21 -0
- package/fixtures/openapi/BuildTemplateInput/invalid/negative-revision.json +6 -0
- package/fixtures/openapi/BuildTemplateInput/valid/draft.json +11 -0
- package/fixtures/openapi/BuildTriggerRequest/invalid/no-ref.json +1 -0
- package/fixtures/openapi/BuildTriggerRequest/valid/no-environment.json +1 -0
- package/fixtures/openapi/BuildpacksConfiguration/invalid/mutable-builder.json +5 -0
- package/fixtures/openapi/BuildpacksConfiguration/invalid/privilege-override.json +6 -0
- package/fixtures/openapi/BuildpacksConfiguration/invalid/unsupported-platform.json +5 -0
- package/fixtures/openapi/BuildpacksConfiguration/valid/pinned.json +5 -0
- package/fixtures/openapi/CapabilityAssessment/invalid/contract.json +16 -0
- package/fixtures/openapi/CapabilityAssessment/valid/contract.json +16 -0
- package/fixtures/openapi/CapabilityPlan/invalid/contract.json +16 -0
- package/fixtures/openapi/CapabilityPlan/valid/contract.json +19 -0
- package/fixtures/openapi/CapabilityPlanValidation/invalid/contract.json +5 -0
- package/fixtures/openapi/CapabilityPlanValidation/valid/contract.json +11 -0
- package/fixtures/openapi/CapabilityTask/invalid/contract.json +33 -0
- package/fixtures/openapi/CapabilityTask/valid/contract.json +34 -0
- package/fixtures/openapi/CapabilityTaskInput/invalid/contract.json +22 -0
- package/fixtures/openapi/CapabilityTaskInput/valid/contract.json +22 -0
- package/fixtures/openapi/CapabilityValueSemantic/invalid/contract.json +4 -0
- package/fixtures/openapi/CapabilityValueSemantic/valid/contract.json +7 -0
- package/fixtures/openapi/DeliveryAccessResult/invalid/release-verification.json +8 -0
- package/fixtures/openapi/DeliveryAccessResult/valid/release-verification.json +8 -0
- package/fixtures/openapi/DeliveryBatchAssessment/invalid/release-verification.json +11 -0
- package/fixtures/openapi/DeliveryBatchAssessment/valid/release-verification.json +11 -0
- package/fixtures/openapi/DeliveryBatchAssessmentInput/invalid/release-verification.json +9 -0
- package/fixtures/openapi/DeliveryBatchAssessmentInput/valid/release-verification.json +8 -0
- package/fixtures/openapi/DeliveryBatchInput/invalid/ambiguous-source.json +19 -0
- package/fixtures/openapi/DeliveryBatchInput/invalid/missing-idempotency.json +16 -0
- package/fixtures/openapi/DeliveryBatchInput/valid/quick-deploy.json +17 -0
- package/fixtures/openapi/DeliveryBatchInput/valid/saved-workflow.json +5 -0
- package/fixtures/openapi/DeliveryBlueprintInput/invalid/unversioned-preset.json +31 -0
- package/fixtures/openapi/DeliveryBlueprintInput/valid/service-preset.json +32 -0
- package/fixtures/openapi/DeliveryDocument/invalid/unknown-field.json +24 -0
- package/fixtures/openapi/DeliveryDocument/invalid/unknown-kind.json +23 -0
- package/fixtures/openapi/DeliveryDocument/invalid/unknown-version.json +23 -0
- package/fixtures/openapi/DeliveryDocument/valid/build-template.json +26 -0
- package/fixtures/openapi/DeliveryDocument/valid/deployment-template.json +26 -0
- package/fixtures/openapi/DeliveryDocument/valid/legacy-dag.json +39 -0
- package/fixtures/openapi/DeliveryDocument/valid/workflow-template.json +23 -0
- package/fixtures/openapi/DeliveryDocument/valid/workflow.json +20 -0
- package/fixtures/openapi/DeliveryDraftInput/invalid/short-idempotency-key.json +30 -0
- package/fixtures/openapi/DeliveryDraftInput/valid/idempotent-create.json +30 -0
- package/fixtures/openapi/DeliveryDraftService/invalid/zero-version.json +7 -0
- package/fixtures/openapi/DeliveryDraftService/valid/versioned-template.json +9 -0
- package/fixtures/openapi/DeliveryExecutionHistoryPage/invalid-kind.json +9 -0
- package/fixtures/openapi/DeliveryExecutionHistoryPage/valid-build.json +16 -0
- package/fixtures/openapi/DeliveryExecutionHistoryPage/valid-empty.json +3 -0
- package/fixtures/openapi/DeliveryPlanApprovalInput/invalid/confirm.json +1 -0
- package/fixtures/openapi/DeliveryPlanApprovalInput/valid/approve.json +1 -0
- package/fixtures/openapi/DeliveryPlanInput/invalid/draft-manifest-revision.json +1 -0
- package/fixtures/openapi/DeliveryPlanInput/valid/manifest-revision.json +1 -0
- package/fixtures/openapi/DeliveryTargetInput/invalid/deploy-without-environment.json +7 -0
- package/fixtures/openapi/DeliveryTargetInput/valid/build-without-environment.json +6 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/duplicate-kind.json +13 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/embedded-credentials.json +16 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/empty-kinds.json +10 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/negative-generation.json +15 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/unknown-kind.json +12 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/valid/manual-source.json +15 -0
- package/fixtures/openapi/DeliveryWorkflowDefinition/invalid/unversioned-template.json +15 -0
- package/fixtures/openapi/DeliveryWorkflowDefinition/valid/cross-app.json +24 -0
- package/fixtures/openapi/DeliveryWorkflowInput/invalid/short-key.json +1 -0
- package/fixtures/openapi/DeliveryWorkflowInput/valid/idempotent-create.json +1 -0
- package/fixtures/openapi/DeploymentTemplatePreviewInput/invalid/invalid-target-override.json +7 -0
- package/fixtures/openapi/DeploymentTemplatePreviewInput/invalid/invalid-zero-version.json +6 -0
- package/fixtures/openapi/DeploymentTemplatePreviewInput/valid/valid.json +6 -0
- package/fixtures/openapi/DockerDeliveryConfiguration/invalid/empty-mappings.json +5 -0
- package/fixtures/openapi/DockerDeliveryConfiguration/valid/compose.json +7 -0
- package/fixtures/openapi/DockerDeliverySnapshot/invalid/missing-required-field.json +19 -0
- package/fixtures/openapi/DockerDeliverySnapshot/valid/frozen-project.json +20 -0
- package/fixtures/openapi/DockerOperationCallbackRequest/invalid/cancellation-acknowledged-not-boolean.json +13 -0
- package/fixtures/openapi/DockerOperationCallbackRequest/valid/cancellation-acknowledged.json +13 -0
- package/fixtures/openapi/DockerProjectInput/invalid/empty-host.json +4 -0
- package/fixtures/openapi/DockerProjectInput/valid/inline-idempotent.json +7 -0
- package/fixtures/openapi/DockerQuickCreateHostInput/invalid/capacity-type.json +1 -0
- package/fixtures/openapi/DockerQuickCreateHostInput/valid/capacity.json +1 -0
- package/fixtures/openapi/EndpointDevice/invalid/missing-owner.json +8 -0
- package/fixtures/openapi/EndpointDevice/valid/active.json +35 -0
- package/fixtures/openapi/EndpointDeviceRegistrationInput/invalid/client-owned-status.json +5 -0
- package/fixtures/openapi/EndpointDeviceRegistrationInput/invalid/oversized-network-inventory.json +18 -0
- package/fixtures/openapi/EndpointDeviceRegistrationInput/valid/macos.json +28 -0
- package/fixtures/openapi/HelmDeliveryConfiguration/invalid/plain-credential.json +21 -0
- package/fixtures/openapi/HelmDeliveryConfiguration/invalid/release-name.json +20 -0
- package/fixtures/openapi/HelmDeliveryConfiguration/valid/private-oci.json +20 -0
- package/fixtures/openapi/HelmDeliverySnapshot/invalid/secret-plan.json +31 -0
- package/fixtures/openapi/HelmDeliverySnapshot/invalid/unknown-operation.json +28 -0
- package/fixtures/openapi/HelmDeliverySnapshot/valid/first-install.json +28 -0
- package/fixtures/openapi/IdentityApplication/invalid/outpost-status.json +1 -0
- package/fixtures/openapi/IdentityApplicationOnboardingInput/invalid/missing-access.json +12 -0
- package/fixtures/openapi/IdentityApplicationOnboardingInput/valid/link.json +13 -0
- package/fixtures/openapi/IdentityOutpost/invalid/missing-runtime-status.json +9 -0
- package/fixtures/openapi/IdentityOutpost/valid/healthy.json +17 -0
- package/fixtures/openapi/IdentityOutpost/valid/unregistered.json +12 -0
- package/fixtures/openapi/IdentityOutpostDeployment/invalid/missing-protocol.json +3 -0
- package/fixtures/openapi/IdentityOutpostDeployment/valid/remote.json +6 -0
- package/fixtures/openapi/IdentityOutpostInput/invalid/forged-observation.json +7 -0
- package/fixtures/openapi/IdentityOutpostInput/valid/forward-auth.json +6 -0
- package/fixtures/openapi/IdentityOutpostInput/valid/legacy.json +8 -0
- package/fixtures/openapi/IdentityProviderSetup/invalid/fake-verification.json +1 -0
- package/fixtures/openapi/IdentityProviderSetup/valid/incomplete.json +1 -0
- package/fixtures/openapi/IdentityProviderSetup/valid/remote.json +1 -0
- package/fixtures/openapi/IdentityProviderUserMetadata/invalid/attributes.json +8 -0
- package/fixtures/openapi/IdentityProviderUserMetadata/valid/attributes.json +13 -0
- package/fixtures/openapi/KubernetesCustomResource/invalid/negative-generation.json +1 -0
- package/fixtures/openapi/KubernetesCustomResource/valid/deleting-operator.json +1 -0
- package/fixtures/openapi/KubernetesResourceCreationReceipt/invalid/manifest-content.json +20 -0
- package/fixtures/openapi/KubernetesResourceCreationReceipt/valid/confirmed.json +20 -0
- package/fixtures/openapi/KubernetesResourceCreationReceipt/valid/unknown.json +19 -0
- package/fixtures/openapi/LLMUpstreamTestResultEnvelope/invalid/models-not-array.json +9 -0
- package/fixtures/openapi/LLMUpstreamTestResultEnvelope/valid/discovered-models.json +11 -0
- package/fixtures/openapi/ManifestDeploymentEnvelope/invalid/negative-resource-observation.json +57 -0
- package/fixtures/openapi/ManifestDeploymentEnvelope/valid/operator-deleting.json +57 -0
- package/fixtures/openapi/ManifestExecutionTaskPayload/invalid/gitops-empty.json +7 -0
- package/fixtures/openapi/ManifestExecutionTaskPayload/valid/gitops-frozen-children.json +18 -0
- package/fixtures/openapi/ManifestKustomizeOptions/invalid/escaping-entry.json +1 -0
- package/fixtures/openapi/ManifestKustomizeOptions/invalid/mutable-image.json +1 -0
- package/fixtures/openapi/ManifestKustomizeOptions/valid/overlay-images.json +7 -0
- package/fixtures/openapi/ManifestRevisionInput/invalid/missing-version.json +1 -0
- package/fixtures/openapi/ManifestRevisionInput/valid/current-draft.json +1 -0
- package/fixtures/openapi/ManifestServiceTemplateInputs/invalid/mutable-image.json +8 -0
- package/fixtures/openapi/ManifestServiceTemplateInputs/valid/frozen-service-image.json +9 -0
- package/fixtures/openapi/NetworkAccessGrant/invalid/token-digest.json +15 -0
- package/fixtures/openapi/NetworkAccessGrant/valid/issued.json +14 -0
- package/fixtures/openapi/NetworkAccessGrantInput/invalid/no-resources.json +7 -0
- package/fixtures/openapi/NetworkAccessGrantInput/valid/direct-ztna.json +8 -0
- package/fixtures/openapi/NetworkAccessGrantSecret/invalid/short-token.json +17 -0
- package/fixtures/openapi/NetworkAccessGrantSecret/valid/issued.json +17 -0
- package/fixtures/openapi/NetworkAccessPolicy/invalid/missing-version.json +20 -0
- package/fixtures/openapi/NetworkAccessPolicy/valid/published-draft.json +21 -0
- package/fixtures/openapi/NetworkAccessPolicyInput/invalid/duplicate-team.json +17 -0
- package/fixtures/openapi/NetworkAccessPolicyInput/valid/team-and-tag-allow.json +17 -0
- package/fixtures/openapi/NetworkConflictAnalysisRequest/invalid/ipv6-range.json +9 -0
- package/fixtures/openapi/NetworkConflictAnalysisRequest/valid/runtime-ranges.json +15 -0
- package/fixtures/openapi/NetworkConnectionOption/invalid/secret.json +10 -0
- package/fixtures/openapi/NetworkConnectionOption/valid/wifi.json +9 -0
- package/fixtures/openapi/NetworkConnectionOption/valid/wired.json +8 -0
- package/fixtures/openapi/NetworkGateway/invalid/missing-site.json +7 -0
- package/fixtures/openapi/NetworkGateway/valid/online.json +23 -0
- package/fixtures/openapi/NetworkGatewayInput/invalid/default-route-overlay.json +13 -0
- package/fixtures/openapi/NetworkGatewayInput/valid/routed.json +14 -0
- package/fixtures/openapi/NetworkLease/invalid/empty-cidrs.json +12 -0
- package/fixtures/openapi/NetworkLease/valid/active.json +12 -0
- package/fixtures/openapi/NetworkMihomoProfile/invalid/subscription-leak.json +20 -0
- package/fixtures/openapi/NetworkMihomoProfile/valid/managed.json +21 -0
- package/fixtures/openapi/NetworkMihomoProfileInput/invalid/app-subscription-secret.json +14 -0
- package/fixtures/openapi/NetworkMihomoProfileInput/invalid/manual-node-with-subscription.json +20 -0
- package/fixtures/openapi/NetworkMihomoProfileInput/valid/app-subscription.json +14 -0
- package/fixtures/openapi/NetworkMihomoProfileInput/valid/managed.json +16 -0
- package/fixtures/openapi/NetworkMihomoProfileInput/valid/manual-node.json +21 -0
- package/fixtures/openapi/NetworkNASBinding/invalid/secret.json +13 -0
- package/fixtures/openapi/NetworkNASBinding/valid/default.json +16 -0
- package/fixtures/openapi/NetworkNASBindingInput/invalid/secret.json +10 -0
- package/fixtures/openapi/NetworkNASBindingInput/valid/default.json +13 -0
- package/fixtures/openapi/NetworkPolicyPreviewRequest/invalid/unknown-mode.json +6 -0
- package/fixtures/openapi/NetworkPolicyPreviewRequest/valid/external-direct-ztna.json +6 -0
- package/fixtures/openapi/NetworkPolicyPreviewRequest/valid/external-vpn-ztna.json +6 -0
- package/fixtures/openapi/NetworkPolicyPreviewRequest/valid/external-vpn.json +6 -0
- package/fixtures/openapi/NetworkPolicyPreviewRequest/valid/internal-direct.json +7 -0
- package/fixtures/openapi/NetworkPolicyPreviewRequest/valid/internal-ztna.json +7 -0
- package/fixtures/openapi/NetworkPolicyPreviewResult/invalid/bypass-path.json +9 -0
- package/fixtures/openapi/NetworkPolicyPreviewResult/valid/onboarding.json +10 -0
- package/fixtures/openapi/NetworkPolicyPreviewResult/valid/protected-broad-path-denied.json +9 -0
- package/fixtures/openapi/NetworkPolicyPreviewResult/valid/protected-resource.json +9 -0
- package/fixtures/openapi/NetworkPolicyPreviewResult/valid/quarantine.json +10 -0
- package/fixtures/openapi/NetworkPolicyPreviewResult/valid/restricted.json +10 -0
- package/fixtures/openapi/NetworkPolicySnapshot/invalid/zero-version.json +7 -0
- package/fixtures/openapi/NetworkPolicySnapshot/valid/published.json +7 -0
- package/fixtures/openapi/NetworkResourceInput/invalid/invalid-port.json +10 -0
- package/fixtures/openapi/NetworkResourceInput/valid/protected-cidr.json +10 -0
- package/fixtures/openapi/NetworkRuntimeEnrollmentInput/invalid/excessive-ttl.json +7 -0
- package/fixtures/openapi/NetworkRuntimeEnrollmentInput/valid/endpoint.json +7 -0
- package/fixtures/openapi/NetworkRuntimeEnrollmentSecret/invalid/short-token.json +15 -0
- package/fixtures/openapi/NetworkRuntimeEnrollmentSecret/valid/pending.json +15 -0
- package/fixtures/openapi/NetworkSession/invalid/unknown-status.json +14 -0
- package/fixtures/openapi/NetworkSession/valid/restricted-vpn-ztna.json +16 -0
- package/fixtures/openapi/NetworkSessionActionExecuteInput/invalid/short-hash.json +6 -0
- package/fixtures/openapi/NetworkSessionActionExecuteInput/valid/restrict.json +6 -0
- package/fixtures/openapi/NetworkSessionActionInput/invalid/unknown-action.json +5 -0
- package/fixtures/openapi/NetworkSessionActionInput/valid/restrict.json +5 -0
- package/fixtures/openapi/NetworkSessionActionPlan/invalid/missing-hash.json +12 -0
- package/fixtures/openapi/NetworkSessionActionPlan/valid/fallback-disconnect.json +13 -0
- package/fixtures/openapi/NetworkSessionCommand/invalid/unknown-status.json +14 -0
- package/fixtures/openapi/NetworkSessionCommand/valid/pending.json +14 -0
- package/fixtures/openapi/NetworkSiteInput/invalid/unknown-status.json +4 -0
- package/fixtures/openapi/NetworkSiteInput/valid/shanghai.json +5 -0
- package/fixtures/openapi/NetworkSiteProfileBinding/invalid/vlan.json +9 -0
- package/fixtures/openapi/NetworkSiteProfileBinding/valid/default.json +10 -0
- package/fixtures/openapi/NetworkSiteProfileBindingInput/invalid/timeout.json +7 -0
- package/fixtures/openapi/NetworkSiteProfileBindingInput/valid/default.json +7 -0
- package/fixtures/openapi/NetworkSpaceInput/invalid/empty-cidrs.json +6 -0
- package/fixtures/openapi/NetworkSpaceInput/invalid/malformed-cidr.json +6 -0
- package/fixtures/openapi/NetworkSpaceInput/valid/corp.json +6 -0
- package/fixtures/openapi/NetworkTelemetrySummary/invalid/raw-destination.json +29 -0
- package/fixtures/openapi/NetworkTelemetrySummary/valid/proxy-flow.json +36 -0
- package/fixtures/openapi/NetworkVPNConnectionIntentInput/invalid/auto-with-gateway.json +6 -0
- package/fixtures/openapi/NetworkVPNConnectionIntentInput/invalid/missing-manual-gateway.json +5 -0
- package/fixtures/openapi/NetworkVPNConnectionIntentInput/invalid/mode-override.json +7 -0
- package/fixtures/openapi/NetworkVPNConnectionIntentInput/valid/auto-intent.json +5 -0
- package/fixtures/openapi/NetworkVPNConnectionIntentInput/valid/manual-intent.json +6 -0
- package/fixtures/openapi/NetworkVPNProfileInput/invalid/unknown-private-key.json +25 -0
- package/fixtures/openapi/NetworkVPNProfileInput/valid/assigned-profile.json +24 -0
- package/fixtures/openapi/NetworkVPNSelectionPolicyInput/invalid/unbounded-attempts.json +21 -0
- package/fixtures/openapi/NetworkVPNSelectionPolicyInput/valid/provider-policy.json +21 -0
- package/fixtures/openapi/ObservabilityMetricAssessmentInput/invalid/arbitrary-expression.json +10 -0
- package/fixtures/openapi/ObservabilityMetricAssessmentInput/valid/metric-window.json +10 -0
- package/fixtures/openapi/PluginManifest/valid/hermes-api.json +53 -0
- package/fixtures/openapi/ProgressiveRolloutControlInput/invalid/missing-identity.json +5 -0
- package/fixtures/openapi/ProgressiveRolloutControlInput/invalid/skip-analysis.json +5 -0
- package/fixtures/openapi/ProgressiveRolloutControlInput/valid/pause.json +5 -0
- package/fixtures/openapi/ReleaseTargetInput/invalid/missing-required-field.json +15 -0
- package/fixtures/openapi/ReleaseTargetInput/valid/docker-compose.json +16 -0
- package/fixtures/openapi/RepositoryAnalysis/invalid/moving-ref.json +11 -0
- package/fixtures/openapi/RepositoryAnalysis/valid/go.json +29 -0
- package/fixtures/openapi/RepositoryAnalysisInput/invalid/arbitrary-source-url.json +6 -0
- package/fixtures/openapi/RepositoryAnalysisInput/invalid/missing-ref.json +4 -0
- package/fixtures/openapi/RepositoryAnalysisInput/valid/nested-project.json +6 -0
- package/fixtures/openapi/ResourceLease/invalid/missing-session.json +10 -0
- package/fixtures/openapi/ResourceLease/valid/active.json +11 -0
- package/fixtures/openapi/ServiceDeploymentTemplateBinding/invalid/invalid-unpinned.json +4 -0
- package/fixtures/openapi/ServiceDeploymentTemplateBinding/valid/valid.json +7 -0
- package/fixtures/openapi/ServiceDeploymentTemplateInput/invalid/invalid-executable-schema.json +25 -0
- package/fixtures/openapi/ServiceDeploymentTemplateInput/invalid/invalid-missing-schema.json +21 -0
- package/fixtures/openapi/ServiceDeploymentTemplateInput/invalid/invalid-mixed-renderers.json +40 -0
- package/fixtures/openapi/ServiceDeploymentTemplateInput/valid/valid-nested-parameters.json +41 -0
- package/fixtures/openapi/ServiceDeploymentTemplateInput/valid/valid-yaml.json +34 -0
- package/fixtures/openapi/TemplatePublishInput/invalid/missing-revision.json +1 -0
- package/fixtures/openapi/TemplatePublishInput/invalid/unknown-field.json +4 -0
- package/fixtures/openapi/TemplatePublishInput/invalid/zero-revision.json +3 -0
- package/fixtures/openapi/TemplatePublishInput/valid/revision.json +3 -0
- package/fixtures/openapi/ToolCapability/invalid/bad-idempotency.json +35 -0
- package/fixtures/openapi/ToolCapability/invalid/unversioned-check.json +47 -0
- package/fixtures/openapi/ToolCapability/valid/checks.json +48 -0
- package/fixtures/openapi/ToolCapability/valid/versioned-task.json +35 -0
- package/fixtures/openapi/ToolInvocationRequest/invalid/empty-version.json +8 -0
- package/fixtures/openapi/ToolInvocationRequest/valid/versioned.json +8 -0
- package/fixtures/openapi/ToolInvocationResult/invalid/missing-status-call.json +13 -0
- package/fixtures/openapi/ToolInvocationResult/valid/task.json +20 -0
- package/fixtures/openapi/VirtualMachineCreateInput/invalid/capacity-type.json +1 -0
- package/fixtures/openapi/VirtualMachineCreateInput/valid/capacity.json +1 -0
- package/fixtures/openapi/VirtualizationCapacityInput/invalid/missing-disk.json +1 -0
- package/fixtures/openapi/VirtualizationCapacityInput/valid/running-vm.json +1 -0
- package/fixtures/openapi/VirtualizationCapacityResult/invalid/invented-status.json +1 -0
- package/fixtures/openapi/VirtualizationCapacityResult/valid/available.json +1 -0
- package/fixtures/openapi/VirtualizationWorkerCreateInput/invalid/secret-input.json +5 -0
- package/fixtures/openapi/VirtualizationWorkerCreateInput/valid/one-worker.json +4 -0
- package/fixtures/openapi/VirtualizationWorkerPoolInput/invalid/external-owner.json +27 -0
- package/fixtures/openapi/VirtualizationWorkerPoolInput/invalid/zero-budget.json +27 -0
- package/fixtures/openapi/VirtualizationWorkerPoolInput/valid/registered.json +27 -0
- package/fixtures/openapi/VirtualizationWorkerPoolListEnvelope/invalid/nested-data.json +5 -0
- package/fixtures/openapi/VirtualizationWorkerPoolListEnvelope/valid/empty.json +3 -0
- package/fixtures/openapi/WorkbenchContextSelection/invalid/missing-file-content.json +8 -0
- package/fixtures/openapi/WorkbenchContextSelection/invalid/unknown-reference-kind.json +8 -0
- package/fixtures/openapi/WorkbenchContextSelection/valid/text-and-resource.json +17 -0
- package/fixtures/openapi/WorkbenchInspectionTaskInput/invalid/trigger-without-plan.json +10 -0
- package/fixtures/openapi/WorkbenchInspectionTaskInput/invalid/unknown-trigger.json +28 -0
- package/fixtures/openapi/WorkbenchInspectionTaskInput/invalid/zero-interval.json +28 -0
- package/fixtures/openapi/WorkbenchInspectionTaskInput/valid/alert-capability.json +30 -0
- package/fixtures/openapi/WorkbenchInspectionTaskInput/valid/scheduled-capability.json +28 -0
- package/fixtures/openapi/WorkbenchInspectionTrigger/invalid/missing-rule.json +3 -0
- package/fixtures/openapi/WorkbenchInspectionTrigger/invalid/schedule-alert-selector.json +4 -0
- package/fixtures/openapi/WorkbenchInspectionTrigger/valid/schedule.json +3 -0
- package/fixtures/openapi/WorkbenchModelPreferences/invalid/unknown-effort.json +1 -0
- package/fixtures/openapi/WorkbenchModelPreferences/valid/selected.json +1 -0
- package/fixtures/openapi/WorkflowCatalogPage/invalid/unsupported-kind.json +6 -0
- package/fixtures/openapi/WorkflowCatalogPage/valid/build-without-environment.json +6 -0
- package/fixtures/openapi/WorkflowTemplateInput/invalid/zero-revision.json +6 -0
- package/fixtures/openapi/WorkflowTemplateInput/valid/draft.json +10 -0
- package/gen/go/sohaapi/client.go +1 -1
- package/gen/go/sohaapi/types.go +15610 -6415
- package/gen/go/sohaapi/types_test.go +100 -1
- package/gen/go/sohaapi/vpn_models_test.go +21 -0
- package/gen/ts/sohaapi/index.ts +27859 -17518
- package/go.mod +107 -5
- package/go.sum +379 -16
- package/helmrelease/delivery.go +283 -0
- package/helmrelease/delivery_test.go +102 -0
- package/helmrelease/frozen_chart.go +91 -0
- package/helmrelease/result.go +27 -0
- package/helmrelease/runtime/delivery.go +508 -0
- package/helmrelease/runtime/delivery_test.go +236 -0
- package/helmrelease/runtime/resources_test.go +60 -0
- package/helmrelease/values.go +99 -0
- package/knowledge/context-envelope.schema.json +1 -0
- package/mcp/gateway-manifest.schema.json +387 -72
- package/network/README.md +156 -0
- package/network/network-ingest-event.schema.json +415 -0
- package/network/network-radius-accounting.schema.json +41 -0
- package/network/network-runtime-protocol.schema.json +1208 -0
- package/network/schema.go +26 -0
- package/network/schema_test.go +22 -0
- package/openapi/soha-api.json +59260 -35960
- package/openapi/soha-api.yaml +36064 -26232
- package/openapi/spec_generated.go +3 -3
- package/package.json +7 -1
- package/resource/manifest_health.go +155 -0
- package/resource/manifest_health_test.go +37 -0
- package/resource/models.go +14 -0
- package/resource/runtime/argocd.go +166 -0
- package/resource/runtime/argocd_integration_test.go +142 -0
- package/resource/runtime/argocd_observation.go +143 -0
- package/resource/runtime/argocd_operation.go +253 -0
- package/resource/runtime/argocd_resources.go +115 -0
- package/resource/runtime/argocd_resources_test.go +107 -0
- package/resource/runtime/argocd_test.go +386 -0
- package/resource/runtime/health.go +115 -0
- package/resource/runtime/health_test.go +78 -0
- package/resource/runtime/operator_integration_test.go +174 -0
- package/resource/runtime/ownership.go +178 -0
- package/resource/runtime/ownership_test.go +178 -0
- package/resource/runtime/rollout_integration_test.go +287 -0
- package/resource/runtime/rollout_observation.go +252 -0
- package/resource/runtime/rollout_operation.go +398 -0
- package/resource/runtime/rollout_resources.go +385 -0
- package/resource/runtime/rollout_task.go +165 -0
- package/resource/runtime/rollout_test.go +450 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
package helmrelease
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"archive/tar"
|
|
5
|
+
"bytes"
|
|
6
|
+
"compress/gzip"
|
|
7
|
+
"crypto/sha256"
|
|
8
|
+
"encoding/hex"
|
|
9
|
+
"encoding/json"
|
|
10
|
+
"errors"
|
|
11
|
+
"io"
|
|
12
|
+
"net/url"
|
|
13
|
+
"path"
|
|
14
|
+
"regexp"
|
|
15
|
+
"strings"
|
|
16
|
+
|
|
17
|
+
"github.com/opensoha/soha-contracts/gen/go/sohaapi"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
const (
|
|
21
|
+
MaxChartArchiveBytes = 5 << 20
|
|
22
|
+
MaxChartExpandedBytes = 20 << 20
|
|
23
|
+
MaxChartFiles = 1000
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
var (
|
|
27
|
+
helmName = regexp.MustCompile(`^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$`)
|
|
28
|
+
helmVersion = regexp.MustCompile(`^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$`)
|
|
29
|
+
helmSHA256 = regexp.MustCompile(`^sha256:[a-f0-9]{64}$`)
|
|
30
|
+
helmSecretReference = regexp.MustCompile(`^soha://secrets/[A-Za-z0-9._-]+(?:/versions/[1-9][0-9]*)?$`)
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
// ValidateSource rejects executable/local sources and ambiguous, mutable chart references.
|
|
34
|
+
// Credential authorization and resolution belong to the consuming application.
|
|
35
|
+
func ValidateSource(source sohaapi.DeploymentTemplateHelmSource) error {
|
|
36
|
+
u, err := url.Parse(source.RepositoryURL)
|
|
37
|
+
if err != nil || u.Hostname() == "" || (u.Scheme != "https" && u.Scheme != "oci") || u.User != nil || u.RawQuery != "" || u.Fragment != "" || u.Opaque != "" {
|
|
38
|
+
return errors.New("helm repository must be an HTTPS or OCI URL without embedded credentials, query or fragment")
|
|
39
|
+
}
|
|
40
|
+
if strings.Contains(u.Path, "\\") || path.Clean("/"+u.Path) != strings.TrimSuffix("/"+strings.TrimPrefix(u.Path, "/"), "/") && u.Path != "" && u.Path != "/" {
|
|
41
|
+
return errors.New("helm repository path must be canonical")
|
|
42
|
+
}
|
|
43
|
+
if !helmName.MatchString(source.Chart) || len(source.Chart) > 253 || !helmVersion.MatchString(source.Version) {
|
|
44
|
+
return errors.New("helm chart requires a chart name and an exact semantic version")
|
|
45
|
+
}
|
|
46
|
+
if source.Digest != "" && !helmSHA256.MatchString(source.Digest) {
|
|
47
|
+
return errors.New("helm chart digest must be a SHA-256 digest")
|
|
48
|
+
}
|
|
49
|
+
if source.ConnectionID != "" {
|
|
50
|
+
return errors.New("helm chart connections are unavailable; use scoped secret references")
|
|
51
|
+
}
|
|
52
|
+
if source.SecretRefs != nil {
|
|
53
|
+
for alias, reference := range *source.SecretRefs {
|
|
54
|
+
if alias != "CHART_USERNAME" && alias != "CHART_PASSWORD" && alias != "CHART_CA_CERT" {
|
|
55
|
+
return errors.New("unsupported Helm chart credential reference")
|
|
56
|
+
}
|
|
57
|
+
if !helmSecretReference.MatchString(reference) {
|
|
58
|
+
return errors.New("helm chart credentials require Soha secret references")
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return nil
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
func ValidateConfiguration(config sohaapi.HelmDeliveryConfiguration) error {
|
|
66
|
+
if err := ValidateSource(config.Source); err != nil {
|
|
67
|
+
return err
|
|
68
|
+
}
|
|
69
|
+
if !helmName.MatchString(config.ReleaseName) || len(config.ReleaseName) > 53 {
|
|
70
|
+
return errors.New("helm release name must be a DNS label of at most 53 characters")
|
|
71
|
+
}
|
|
72
|
+
if config.TimeoutSeconds < 0 || config.TimeoutSeconds > 3600 {
|
|
73
|
+
return errors.New("helm timeout must be between 1 and 3600 seconds")
|
|
74
|
+
}
|
|
75
|
+
seen := map[string]bool{}
|
|
76
|
+
for _, mapping := range config.ImageMappings {
|
|
77
|
+
if mapping.ContainerName == "" || !mapping.Value.Valid() {
|
|
78
|
+
return errors.New("helm image mapping requires a container and an immutable image field")
|
|
79
|
+
}
|
|
80
|
+
if _, err := pointerParts(mapping.Path); err != nil {
|
|
81
|
+
return err
|
|
82
|
+
}
|
|
83
|
+
if seen[mapping.Path] {
|
|
84
|
+
return errors.New("helm image mapping paths must be unique")
|
|
85
|
+
}
|
|
86
|
+
seen[mapping.Path] = true
|
|
87
|
+
}
|
|
88
|
+
return nil
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
func SHA256(content []byte) string {
|
|
92
|
+
sum := sha256.Sum256(content)
|
|
93
|
+
return "sha256:" + hex.EncodeToString(sum[:])
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ValidateChartArchive bounds both compressed and expanded input before an SDK loads it.
|
|
97
|
+
// Symlinks, duplicate entries and dependency downloads are never followed here.
|
|
98
|
+
func ValidateChartArchive(content []byte) error {
|
|
99
|
+
budget, files := int64(MaxChartExpandedBytes), 0
|
|
100
|
+
return validateChartArchive(content, &budget, &files, 0)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
func validateChartArchive(content []byte, budget *int64, files *int, depth int) error {
|
|
104
|
+
if len(content) == 0 || len(content) > MaxChartArchiveBytes {
|
|
105
|
+
return errors.New("helm chart archive exceeds the 5 MiB limit or is empty")
|
|
106
|
+
}
|
|
107
|
+
if depth > 8 {
|
|
108
|
+
return errors.New("helm chart dependencies exceed the nesting limit")
|
|
109
|
+
}
|
|
110
|
+
zr, err := gzip.NewReader(bytes.NewReader(content))
|
|
111
|
+
if err != nil {
|
|
112
|
+
return errors.New("helm chart must be a gzip archive")
|
|
113
|
+
}
|
|
114
|
+
defer func() { _ = zr.Close() }()
|
|
115
|
+
bounded := chartBudgetReader{reader: zr, remaining: budget}
|
|
116
|
+
reader := tar.NewReader(bounded)
|
|
117
|
+
seen := map[string]bool{}
|
|
118
|
+
root := ""
|
|
119
|
+
for {
|
|
120
|
+
header, err := reader.Next()
|
|
121
|
+
if errors.Is(err, io.EOF) {
|
|
122
|
+
break
|
|
123
|
+
}
|
|
124
|
+
if err != nil || *budget <= 0 || *files >= MaxChartFiles {
|
|
125
|
+
return errors.New("helm chart archive is invalid or exceeds its file/expanded size limit")
|
|
126
|
+
}
|
|
127
|
+
name := strings.TrimSuffix(header.Name, "/")
|
|
128
|
+
if err := validateChartEntry(header, name, seen); err != nil {
|
|
129
|
+
return err
|
|
130
|
+
}
|
|
131
|
+
folder, _, _ := strings.Cut(name, "/")
|
|
132
|
+
if root != "" && root != folder {
|
|
133
|
+
return errors.New("helm chart archive must contain one root directory")
|
|
134
|
+
}
|
|
135
|
+
root, seen[name] = folder, true
|
|
136
|
+
*files++
|
|
137
|
+
if strings.Contains(name, "/crds/") {
|
|
138
|
+
return errors.New("application Helm charts cannot install cluster-scoped CRDs; install them through platform management first")
|
|
139
|
+
}
|
|
140
|
+
if header.Typeflag == tar.TypeReg && path.Base(name) == "values.schema.json" {
|
|
141
|
+
content, err := io.ReadAll(reader)
|
|
142
|
+
if err != nil {
|
|
143
|
+
return errors.New("read Helm values schema")
|
|
144
|
+
}
|
|
145
|
+
if err := validateValuesSchema(content); err != nil {
|
|
146
|
+
return err
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if header.Typeflag == tar.TypeReg && strings.Contains(name, "/charts/") && strings.HasSuffix(name, ".tgz") {
|
|
150
|
+
child, err := io.ReadAll(io.LimitReader(reader, MaxChartArchiveBytes+1))
|
|
151
|
+
if err != nil {
|
|
152
|
+
return errors.New("read Helm chart dependency")
|
|
153
|
+
}
|
|
154
|
+
if err := validateChartArchive(child, budget, files, depth+1); err != nil {
|
|
155
|
+
return err
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if _, err := io.Copy(io.Discard, bounded); err != nil || *budget <= 0 || !seen[root+"/Chart.yaml"] {
|
|
160
|
+
return errors.New("helm chart archive is invalid, oversized or missing Chart.yaml")
|
|
161
|
+
}
|
|
162
|
+
return nil
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
func validateValuesSchema(content []byte) error {
|
|
166
|
+
var schema any
|
|
167
|
+
if err := json.Unmarshal(content, &schema); err != nil {
|
|
168
|
+
return errors.New("helm values schema must be valid JSON")
|
|
169
|
+
}
|
|
170
|
+
return validateSchemaReferences(schema)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
func validateSchemaReferences(value any) error {
|
|
174
|
+
switch item := value.(type) {
|
|
175
|
+
case map[string]any:
|
|
176
|
+
for key, value := range item {
|
|
177
|
+
if reference, ok := value.(string); ok {
|
|
178
|
+
switch key {
|
|
179
|
+
case "$ref", "$dynamicRef", "$recursiveRef":
|
|
180
|
+
if reference != "" && !strings.HasPrefix(reference, "#") {
|
|
181
|
+
return errors.New("helm values schemas require references within the same document")
|
|
182
|
+
}
|
|
183
|
+
case "$schema":
|
|
184
|
+
if !knownSchemaDialect(reference) {
|
|
185
|
+
return errors.New("helm values schema uses an unsupported schema dialect")
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if err := validateSchemaReferences(value); err != nil {
|
|
190
|
+
return err
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
case []any:
|
|
194
|
+
for _, value := range item {
|
|
195
|
+
if err := validateSchemaReferences(value); err != nil {
|
|
196
|
+
return err
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return nil
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
func knownSchemaDialect(value string) bool {
|
|
204
|
+
value = strings.TrimSuffix(strings.TrimPrefix(strings.TrimPrefix(value, "https://"), "http://"), "#")
|
|
205
|
+
switch value {
|
|
206
|
+
case "json-schema.org/draft-04/schema", "json-schema.org/draft-06/schema", "json-schema.org/draft-07/schema", "json-schema.org/draft/2019-09/schema", "json-schema.org/draft/2020-12/schema":
|
|
207
|
+
return true
|
|
208
|
+
default:
|
|
209
|
+
return false
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
func validateChartEntry(header *tar.Header, name string, seen map[string]bool) error {
|
|
214
|
+
if name == "" || strings.Contains(name, "\\") || strings.HasPrefix(name, "/") || path.Clean(name) != name || name == ".." || strings.HasPrefix(name, "../") || seen[name] {
|
|
215
|
+
return errors.New("helm chart archive contains an unsafe or duplicate path")
|
|
216
|
+
}
|
|
217
|
+
if header.Typeflag != tar.TypeReg && header.Typeflag != tar.TypeDir || header.Size < 0 || header.Size > MaxChartExpandedBytes {
|
|
218
|
+
return errors.New("helm chart archive contains an unsupported file type or size")
|
|
219
|
+
}
|
|
220
|
+
return nil
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
type chartBudgetReader struct {
|
|
224
|
+
reader io.Reader
|
|
225
|
+
remaining *int64
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
func (r chartBudgetReader) Read(p []byte) (int, error) {
|
|
229
|
+
if *r.remaining <= 0 {
|
|
230
|
+
return 0, errors.New("helm chart exceeds the expanded size limit")
|
|
231
|
+
}
|
|
232
|
+
if int64(len(p)) > *r.remaining {
|
|
233
|
+
p = p[:*r.remaining]
|
|
234
|
+
}
|
|
235
|
+
n, err := r.reader.Read(p)
|
|
236
|
+
*r.remaining -= int64(n)
|
|
237
|
+
return n, err
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ValidateTaskIdentity bounds the confidential RPC/claim input before native SDK work.
|
|
241
|
+
// Prepare accepts an unfinished render; Execute additionally verifies its digests.
|
|
242
|
+
func ValidateTaskIdentity(input sohaapi.HelmExecutionTaskPayload) error {
|
|
243
|
+
s := input.Snapshot
|
|
244
|
+
for _, value := range []string{s.DeliveryPlanID, s.TargetID, s.ApplicationID, s.ApplicationEnvironmentID, s.ServiceID, s.ClusterID} {
|
|
245
|
+
if strings.TrimSpace(value) == "" || len(value) > 512 {
|
|
246
|
+
return errors.New("helm task identity is incomplete")
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if !input.Action.Valid() || !helmName.MatchString(s.Namespace) || len(s.Namespace) > 63 || !helmName.MatchString(s.ReleaseName) || len(s.ReleaseName) > 53 || s.ExpectedRevision < 0 || s.RollbackRevision < 0 || s.TimeoutSeconds < 0 || s.TimeoutSeconds > 3600 {
|
|
250
|
+
return errors.New("helm task action, namespace, revision or timeout is invalid")
|
|
251
|
+
}
|
|
252
|
+
if len(s.Resources) > 300 {
|
|
253
|
+
return errors.New("helm task exceeds 300 resources")
|
|
254
|
+
}
|
|
255
|
+
if input.Prepared != nil {
|
|
256
|
+
prepared := input.Prepared
|
|
257
|
+
if len(prepared.ChartArchive) > 6990508 || len(prepared.Manifest) > 2<<20 || len(prepared.Hooks) > 50 {
|
|
258
|
+
return errors.New("helm preparation exceeds its content limits")
|
|
259
|
+
}
|
|
260
|
+
total := len(prepared.Manifest)
|
|
261
|
+
for _, hook := range prepared.Hooks {
|
|
262
|
+
total += len(hook)
|
|
263
|
+
}
|
|
264
|
+
encoded, err := json.Marshal(prepared.Values)
|
|
265
|
+
if err != nil || total > 2<<20 || len(encoded) > 1<<20 {
|
|
266
|
+
return errors.New("helm rendered content or values exceeds its limit")
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
encoded, err := json.Marshal(input)
|
|
270
|
+
if err != nil || len(encoded) > 8<<20 {
|
|
271
|
+
return errors.New("helm task exceeds the RPC limit")
|
|
272
|
+
}
|
|
273
|
+
return nil
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// LegacyAllowedActions restricts delivery-owned releases to inspection through
|
|
277
|
+
// the legacy resource API. Nil leaves unmanaged releases to normal authorization.
|
|
278
|
+
func LegacyAllowedActions(labels map[string]string) []string {
|
|
279
|
+
if labels["soha-delivery-owner"] != "" {
|
|
280
|
+
return []string{"view"}
|
|
281
|
+
}
|
|
282
|
+
return nil
|
|
283
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
package helmrelease
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"archive/tar"
|
|
5
|
+
"bytes"
|
|
6
|
+
"compress/gzip"
|
|
7
|
+
"reflect"
|
|
8
|
+
"strings"
|
|
9
|
+
"testing"
|
|
10
|
+
|
|
11
|
+
"github.com/opensoha/soha-contracts/gen/go/sohaapi"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
func TestHelmDeliveryBoundaries(t *testing.T) {
|
|
15
|
+
for _, repository := range []string{"https://charts.example.test", "https://charts.example.test/private/", "oci://registry.example.test/team"} {
|
|
16
|
+
if err := ValidateSource(sohaapi.DeploymentTemplateHelmSource{RepositoryURL: repository, Chart: "app", Version: "1.2.3-beta.1+build"}); err != nil {
|
|
17
|
+
t.Fatal(err)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
for _, repository := range []string{"file:///tmp/chart", "http://example.test", "https://user:pass@example.test", "https://example.test?token=secret", "https://example.test/a/../b", "https://example.test/%2e%2e/b"} {
|
|
21
|
+
if err := ValidateSource(sohaapi.DeploymentTemplateHelmSource{RepositoryURL: repository, Chart: "app", Version: "1.2.3"}); err == nil {
|
|
22
|
+
t.Fatalf("accepted unsafe source %s", repository)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
image := "registry.example.test/app@sha256:" + strings.Repeat("a", 64)
|
|
26
|
+
values := map[string]any{"workers": []any{map[string]any{"image": "old"}}}
|
|
27
|
+
mappings := []sohaapi.HelmImageMapping{{ContainerName: "main", Path: "/workers/0/image", Value: sohaapi.HelmImageMappingValueImage}}
|
|
28
|
+
result, err := MapImages(values, mappings, map[string]string{"main": image})
|
|
29
|
+
if err != nil || !reflect.DeepEqual(result, map[string]any{"workers": []any{map[string]any{"image": image}}}) || !reflect.DeepEqual(values, map[string]any{"workers": []any{map[string]any{"image": "old"}}}) {
|
|
30
|
+
t.Fatalf("image mapping did not preserve input: %v", err)
|
|
31
|
+
}
|
|
32
|
+
for _, invalid := range []string{"/workers/2/image", "/workers/01/image", "/workers/-/image", "/workers/0/image/child", "/bad~2/path"} {
|
|
33
|
+
mappings[0].Path = invalid
|
|
34
|
+
if _, err := MapImages(values, mappings, map[string]string{"main": image}); err == nil {
|
|
35
|
+
t.Fatalf("accepted invalid image path %s", invalid)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
mappings[0].Path = "/image"
|
|
39
|
+
if _, err := MapImages(nil, mappings, map[string]string{"main": image}); err != nil {
|
|
40
|
+
t.Fatal(err)
|
|
41
|
+
}
|
|
42
|
+
if _, err := MapImages(nil, mappings, map[string]string{"main": "registry/app:latest"}); err == nil {
|
|
43
|
+
t.Fatal("accepted mutable image")
|
|
44
|
+
}
|
|
45
|
+
valid := testChartArchive(t, []tar.Header{{Name: "app/Chart.yaml", Mode: 0600, Size: 1}})
|
|
46
|
+
if err := ValidateChartArchive(valid); err != nil {
|
|
47
|
+
t.Fatal(err)
|
|
48
|
+
}
|
|
49
|
+
for _, headers := range [][]tar.Header{
|
|
50
|
+
{{Name: "../Chart.yaml", Size: 1}},
|
|
51
|
+
{{Name: "app/Chart.yaml", Size: 1}, {Name: "app/Chart.yaml", Size: 1}},
|
|
52
|
+
{{Name: "app/Chart.yaml", Typeflag: tar.TypeSymlink, Linkname: "/etc/passwd"}},
|
|
53
|
+
{{Name: "app/Chart.yaml", Size: 1}, {Name: "app/large", Size: MaxChartExpandedBytes}},
|
|
54
|
+
{{Name: "app/Chart.yaml", Size: 1}, {Name: "app/crds/example.yaml", Size: 1}},
|
|
55
|
+
{{Name: "app/Chart.yaml", Size: 1}, {Name: "app/charts/child/crds/example.yaml", Size: 1}},
|
|
56
|
+
} {
|
|
57
|
+
if err := ValidateChartArchive(testChartArchive(t, headers)); err == nil {
|
|
58
|
+
t.Fatal("accepted unsafe archive")
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
budget, files := int64(100), 0
|
|
62
|
+
if err := validateChartArchive(valid, &budget, &files, 0); err == nil {
|
|
63
|
+
t.Fatal("expanded archive budget was not enforced")
|
|
64
|
+
}
|
|
65
|
+
for _, schema := range []string{
|
|
66
|
+
`{"$ref":"file:///etc/config.json"}`,
|
|
67
|
+
`{"properties":{"value":{"$ref":"https://example.test/schema"}}}`,
|
|
68
|
+
`{"allOf":[{"$dynamicRef":"../other.json#/$defs/value"}]}`,
|
|
69
|
+
`{"$schema":"https://example.test/dialect"}`,
|
|
70
|
+
} {
|
|
71
|
+
if err := validateValuesSchema([]byte(schema)); err == nil {
|
|
72
|
+
t.Fatalf("accepted external schema reference: %s", schema)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if err := validateValuesSchema([]byte(`{"$schema":"http://json-schema.org/draft-07/schema#","$defs":{"value":{"type":"string"}},"properties":{"value":{"$ref":"#/$defs/value"}}}`)); err != nil {
|
|
76
|
+
t.Fatal(err)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
func testChartArchive(t *testing.T, headers []tar.Header) []byte {
|
|
81
|
+
t.Helper()
|
|
82
|
+
var buffer bytes.Buffer
|
|
83
|
+
zip := gzip.NewWriter(&buffer)
|
|
84
|
+
writer := tar.NewWriter(zip)
|
|
85
|
+
for _, header := range headers {
|
|
86
|
+
if err := writer.WriteHeader(&header); err != nil {
|
|
87
|
+
t.Fatal(err)
|
|
88
|
+
}
|
|
89
|
+
if header.Size > 0 {
|
|
90
|
+
if _, err := writer.Write(bytes.Repeat([]byte("x"), int(header.Size))); err != nil {
|
|
91
|
+
t.Fatal(err)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if err := writer.Close(); err != nil {
|
|
96
|
+
t.Fatal(err)
|
|
97
|
+
}
|
|
98
|
+
if err := zip.Close(); err != nil {
|
|
99
|
+
t.Fatal(err)
|
|
100
|
+
}
|
|
101
|
+
return buffer.Bytes()
|
|
102
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
package helmrelease
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"archive/tar"
|
|
5
|
+
"bytes"
|
|
6
|
+
"compress/gzip"
|
|
7
|
+
"errors"
|
|
8
|
+
"fmt"
|
|
9
|
+
"io"
|
|
10
|
+
"strconv"
|
|
11
|
+
"strings"
|
|
12
|
+
|
|
13
|
+
"sigs.k8s.io/yaml"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
// FrozenChartArchive transports already rendered resources through native Helm
|
|
17
|
+
// install/upgrade/rollback, including hooks. It never evaluates source templates
|
|
18
|
+
// again. The original source digest remains part of the delivery snapshot.
|
|
19
|
+
func FrozenChartArchive(original []byte, manifest string, hooks []string) ([]byte, error) {
|
|
20
|
+
if len(manifest) > 2<<20 || len(hooks) > 50 {
|
|
21
|
+
return nil, errors.New("helm rendered resources exceed the snapshot limit")
|
|
22
|
+
}
|
|
23
|
+
total := len(manifest)
|
|
24
|
+
for _, hook := range hooks {
|
|
25
|
+
total += len(hook)
|
|
26
|
+
}
|
|
27
|
+
if total > 4<<20 {
|
|
28
|
+
return nil, errors.New("helm rendered hooks exceed the snapshot limit")
|
|
29
|
+
}
|
|
30
|
+
if err := ValidateChartArchive(original); err != nil {
|
|
31
|
+
return nil, err
|
|
32
|
+
}
|
|
33
|
+
metadata, err := chartMetadata(original)
|
|
34
|
+
if err != nil {
|
|
35
|
+
return nil, err
|
|
36
|
+
}
|
|
37
|
+
delete(metadata, "dependencies")
|
|
38
|
+
metadataBytes, err := yaml.Marshal(metadata)
|
|
39
|
+
if err != nil {
|
|
40
|
+
return nil, err
|
|
41
|
+
}
|
|
42
|
+
files := []struct{ name, content string }{{"Chart.yaml", string(metadataBytes)}, {"values.yaml", "{}\n"}, {"templates/resources.yaml", "{{ print " + strconv.Quote(manifest) + " }}"}}
|
|
43
|
+
for index, hook := range hooks {
|
|
44
|
+
files = append(files, struct{ name, content string }{fmt.Sprintf("templates/hook-%d.yaml", index), "{{ print " + strconv.Quote(hook) + " }}"})
|
|
45
|
+
}
|
|
46
|
+
var buffer bytes.Buffer
|
|
47
|
+
zip := gzip.NewWriter(&buffer)
|
|
48
|
+
writer := tar.NewWriter(zip)
|
|
49
|
+
for _, file := range files {
|
|
50
|
+
if err := writer.WriteHeader(&tar.Header{Name: "frozen/" + file.name, Mode: 0600, Size: int64(len(file.content))}); err != nil {
|
|
51
|
+
return nil, err
|
|
52
|
+
}
|
|
53
|
+
if _, err := io.WriteString(writer, file.content); err != nil {
|
|
54
|
+
return nil, err
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if err := writer.Close(); err != nil {
|
|
58
|
+
return nil, err
|
|
59
|
+
}
|
|
60
|
+
if err := zip.Close(); err != nil {
|
|
61
|
+
return nil, err
|
|
62
|
+
}
|
|
63
|
+
return buffer.Bytes(), nil
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
func chartMetadata(content []byte) (map[string]any, error) {
|
|
67
|
+
zip, err := gzip.NewReader(bytes.NewReader(content))
|
|
68
|
+
if err != nil {
|
|
69
|
+
return nil, err
|
|
70
|
+
}
|
|
71
|
+
defer func() { _ = zip.Close() }()
|
|
72
|
+
reader := tar.NewReader(zip)
|
|
73
|
+
for {
|
|
74
|
+
header, err := reader.Next()
|
|
75
|
+
if err != nil {
|
|
76
|
+
return nil, errors.New("helm chart metadata is missing")
|
|
77
|
+
}
|
|
78
|
+
if strings.Count(header.Name, "/") != 1 || !strings.HasSuffix(header.Name, "/Chart.yaml") {
|
|
79
|
+
continue
|
|
80
|
+
}
|
|
81
|
+
content, err := io.ReadAll(reader)
|
|
82
|
+
if err != nil {
|
|
83
|
+
return nil, err
|
|
84
|
+
}
|
|
85
|
+
var metadata map[string]any
|
|
86
|
+
if err := yaml.Unmarshal(content, &metadata); err != nil {
|
|
87
|
+
return nil, errors.New("helm chart metadata is invalid")
|
|
88
|
+
}
|
|
89
|
+
return metadata, nil
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
package helmrelease
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"errors"
|
|
5
|
+
|
|
6
|
+
"github.com/opensoha/soha-contracts/gen/go/sohaapi"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// ValidateCompletedResult binds completion to the frozen plan and native revision.
|
|
10
|
+
func ValidateCompletedResult(payload sohaapi.HelmExecutionTaskPayload, result sohaapi.HelmExecutionTaskResult) error {
|
|
11
|
+
if !result.Stopped || result.RenderedDigest != payload.Snapshot.RenderedDigest || !helmSHA256.MatchString(result.RenderedDigest) {
|
|
12
|
+
return errors.New("helm completion must confirm executor stop and the approved render")
|
|
13
|
+
}
|
|
14
|
+
if payload.Action == sohaapi.Preflight {
|
|
15
|
+
if !result.Ready || result.Status != "preflighted" || result.Revision != payload.Snapshot.ExpectedRevision {
|
|
16
|
+
return errors.New("helm preflight must match the expected native revision")
|
|
17
|
+
}
|
|
18
|
+
return nil
|
|
19
|
+
}
|
|
20
|
+
if payload.Action != sohaapi.Apply && payload.Action != sohaapi.Observe || result.Status != "deployed" || result.Revision != payload.Snapshot.ExpectedRevision+1 {
|
|
21
|
+
return errors.New("helm completion must match the deployed native revision")
|
|
22
|
+
}
|
|
23
|
+
if payload.Action == sohaapi.Observe && !result.Ready {
|
|
24
|
+
return errors.New("helm health observation is not ready")
|
|
25
|
+
}
|
|
26
|
+
return nil
|
|
27
|
+
}
|