@tokenoftrust/storefront-runner 1.4.2-rc.0 → 2.0.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/apps/storefront/astro.config.mjs +3 -3
- package/apps/storefront/env.d.ts +54 -5
- package/apps/storefront/integrations/materialize-guard.mjs +12 -14
- package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
- package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
- package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
- package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
- package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
- package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
- package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
- package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
- package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
- package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
- package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
- package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
- package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
- package/apps/storefront/migrations-apps/meta/_journal.json +42 -0
- package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
- package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
- package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
- package/apps/storefront/public/shared/commerce-chrome.css +20 -2
- package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
- package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
- package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
- package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
- package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
- package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
- package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
- package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
- package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
- package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
- package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
- package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
- package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
- package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
- package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
- package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
- package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
- package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
- package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
- package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
- package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
- package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
- package/apps/storefront/src/components/Img.astro +1 -1
- package/apps/storefront/src/components/admin/AdminBlogTab.astro +19 -16
- package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
- package/apps/storefront/src/components/admin/AdminPublishTab.astro +1057 -412
- package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
- package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
- package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
- package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
- package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
- package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
- package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
- package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
- package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
- package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
- package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
- package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
- package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
- package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
- package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
- package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
- package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
- package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
- package/apps/storefront/src/components/membership/TierCards.astro +2 -2
- package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
- package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
- package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
- package/apps/storefront/src/config/devTenantSeed.ts +9 -9
- package/apps/storefront/src/config/resolver.ts +4 -4
- package/apps/storefront/src/layouts/Layout.astro +100 -20
- package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
- package/apps/storefront/src/lib/activity/changeActorAttribution.ts +86 -6
- package/apps/storefront/src/lib/activity/deployVersion.ts +1 -1
- package/apps/storefront/src/lib/admin/adminShell.ts +2 -0
- package/apps/storefront/src/lib/admin/envelope.ts +1 -2
- package/apps/storefront/src/lib/apps/adminService.ts +10 -0
- package/apps/storefront/src/lib/apps/adminSession.ts +1 -1
- package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
- package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
- package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
- package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
- package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
- package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
- package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
- package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
- package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
- package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
- package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
- package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
- package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +8 -0
- package/apps/storefront/src/lib/apps/orders/ordersStore.ts +1 -1
- package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
- package/apps/storefront/src/lib/apps/registryService.ts +264 -10
- package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
- package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
- package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
- package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
- package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
- package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
- package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
- package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
- package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
- package/apps/storefront/src/lib/auth/adminEntry.ts +2 -2
- package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
- package/apps/storefront/src/lib/blog/collection.ts +97 -10
- package/apps/storefront/src/lib/blog/markdown.ts +1 -1
- package/apps/storefront/src/lib/blog/provider.ts +57 -3
- package/apps/storefront/src/lib/blog/types.ts +9 -9
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
- package/apps/storefront/src/lib/chrome/model.ts +48 -253
- package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
- package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
- package/apps/storefront/src/lib/chrome/variant.ts +39 -0
- package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
- package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
- package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
- package/apps/storefront/src/lib/content/index.ts +5 -5
- package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
- package/apps/storefront/src/lib/content-edit/route.ts +1 -1
- package/apps/storefront/src/lib/d1/catalog.ts +12 -0
- package/apps/storefront/src/lib/d1/schema-apps.ts +276 -0
- package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
- package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
- package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
- package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
- package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
- package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
- package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
- package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
- package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
- package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
- package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
- package/apps/storefront/src/lib/edgeCache.ts +18 -6
- package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
- package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
- package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
- package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
- package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
- package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
- package/apps/storefront/src/lib/membership/model.ts +1 -2
- package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +2 -2
- package/apps/storefront/src/lib/newsletter/consent.ts +2 -2
- package/apps/storefront/src/lib/newsletter/send.ts +3 -3
- package/apps/storefront/src/lib/previewReload.ts +57 -1
- package/apps/storefront/src/lib/publish/adminPublishTab.ts +1775 -669
- package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
- package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
- package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
- package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
- package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
- package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
- package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
- package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
- package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
- package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
- package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
- package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
- package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
- package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
- package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
- package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
- package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
- package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
- package/apps/storefront/src/lib/publish/shipWorkspace.ts +22 -3
- package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
- package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
- package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
- package/apps/storefront/src/lib/rawChrome.ts +125 -47
- package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
- package/apps/storefront/src/lib/seo/alternates.ts +1 -1
- package/apps/storefront/src/lib/social/notify.ts +4 -4
- package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
- package/apps/storefront/src/lib/storyblok/provider.ts +168 -60
- package/apps/storefront/src/lib/subscription/model.ts +4 -4
- package/apps/storefront/src/lib/the-build/provider.ts +9 -4
- package/apps/storefront/src/middleware/index.ts +273 -51
- package/apps/storefront/src/pages/[...slug].astro +9 -4
- package/apps/storefront/src/pages/admin/review-release.astro +265 -0
- package/apps/storefront/src/pages/admin/select.astro +96 -0
- package/apps/storefront/src/pages/admin.astro +16 -2
- package/apps/storefront/src/pages/api/activity.ts +8 -8
- package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
- package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
- package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
- package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
- package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
- package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
- package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
- package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
- package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
- package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
- package/apps/storefront/src/pages/api/rum/vitals.ts +1 -2
- package/apps/storefront/src/pages/blog/[slug].astro +5 -5
- package/apps/storefront/src/pages/blog/author/[author].astro +3 -3
- package/apps/storefront/src/pages/blog/category/[category].astro +2 -2
- package/apps/storefront/src/pages/blog/index.astro +2 -2
- package/apps/storefront/src/pages/blog/tag/[tag].astro +2 -2
- package/apps/storefront/src/pages/cockpit.astro +61 -1362
- package/apps/storefront/src/pages/index.astro +10 -5
- package/apps/storefront/src/pages/llms.txt.ts +1 -1
- package/apps/storefront/src/pages/products/[handle].astro +7 -6
- package/apps/storefront/src/pages/sitemap.xml.ts +2 -2
- package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +31 -27
- package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +1 -1
- package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
- package/apps/storefront/src/pages/the-build/[slug].astro +4 -4
- package/apps/storefront/src/pages/the-build/index.astro +2 -2
- package/apps/storefront/src/pages/the-build/rss.xml.ts +2 -2
- package/apps/storefront/src/styles/admin.css +27 -1
- package/apps/storefront/tsconfig.json +1 -1
- package/package.json +1 -1
- package/packages/public-runtime/package.json +2 -1
- package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
- package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
- package/packages/public-runtime/src/activity/catalog.ts +41 -37
- package/packages/public-runtime/src/activity/event.ts +6 -6
- package/packages/public-runtime/src/activity/index.ts +2 -2
- package/packages/public-runtime/src/activity/redaction.ts +4 -4
- package/packages/public-runtime/src/candidate-index.ts +42 -8
- package/packages/public-runtime/src/checkout-orders.ts +2 -2
- package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
- package/packages/public-runtime/src/checkout-style.ts +7 -7
- package/packages/public-runtime/src/checkout.ts +3 -3
- package/packages/public-runtime/src/chrome.ts +777 -0
- package/packages/public-runtime/src/compliance-obligations.ts +310 -0
- package/packages/public-runtime/src/csp.ts +99 -0
- package/packages/public-runtime/src/customization-runtime.ts +32 -1
- package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
- package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
- package/packages/public-runtime/src/embed-catalog.mjs +40 -0
- package/packages/public-runtime/src/evidence.ts +288 -0
- package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
- package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
- package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
- package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
- package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
- package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
- package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
- package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
- package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
- package/packages/public-runtime/src/index.ts +16 -1
- package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
- package/packages/public-runtime/src/integration/assurance.ts +228 -0
- package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
- package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
- package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
- package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
- package/packages/public-runtime/src/integration/consent-config.ts +260 -0
- package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
- package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
- package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
- package/packages/public-runtime/src/integration/csp.ts +105 -0
- package/packages/public-runtime/src/integration/digest.ts +43 -0
- package/packages/public-runtime/src/integration/environment.ts +228 -0
- package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
- package/packages/public-runtime/src/integration/events.ts +701 -0
- package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
- package/packages/public-runtime/src/integration/index.ts +90 -0
- package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
- package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
- package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
- package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
- package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
- package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
- package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
- package/packages/public-runtime/src/integration/manifest.ts +470 -0
- package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
- package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
- package/packages/public-runtime/src/integration/observability.ts +451 -0
- package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
- package/packages/public-runtime/src/integration/phases.ts +182 -0
- package/packages/public-runtime/src/integration/plan.ts +112 -0
- package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
- package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
- package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
- package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
- package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
- package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
- package/packages/public-runtime/src/integration/render-phases.ts +267 -0
- package/packages/public-runtime/src/integration/resolve.ts +465 -0
- package/packages/public-runtime/src/integration/result.ts +239 -0
- package/packages/public-runtime/src/integration/secrets.ts +92 -0
- package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
- package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
- package/packages/public-runtime/src/integration/version.ts +92 -0
- package/packages/public-runtime/src/integration-capabilities.ts +520 -0
- package/packages/public-runtime/src/membership.ts +2 -2
- package/packages/public-runtime/src/raw-html-policy.ts +2 -4
- package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
- package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
- package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
- package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
- package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
- package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
- package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
- package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
- package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
- package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
- package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
- package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
- package/packages/public-runtime/src/static-bundle.ts +5 -5
- package/packages/public-runtime/src/tenant-assets.ts +143 -4
- package/packages/public-runtime/src/tenant.ts +45 -9
- package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
- package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
- package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
- package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
- package/scripts/dev/file-browser.mjs +2 -2
- package/scripts/dev/file-writer.mjs +2 -2
- package/scripts/dev/integration-guardrails.mjs +498 -0
- package/scripts/dev/safe-path.mjs +2 -2
- package/scripts/dev/tenant-source-root.mjs +1 -1
- package/scripts/tot-dev.mjs +2 -2
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The no-secret-in-the-browser guard — charter invariant 3 (secrets, bindings,
|
|
3
|
+
* preview tokens, signed URLs, and raw PII never enter browser configuration)
|
|
4
|
+
* and ADR 0018 (a CSP nonce is per-response and never serialized).
|
|
5
|
+
*
|
|
6
|
+
* A secret field is made impossible two ways:
|
|
7
|
+
* 1. Every browser-facing contract is CLOSED (`rejectUnknownKeys`), so a field
|
|
8
|
+
* such as `nonce` is not in the schema and is rejected as unknown.
|
|
9
|
+
* 2. This scanner is defense-in-depth: any object about to cross into a
|
|
10
|
+
* serialized surface — evidence artifact, cache entry, log line, API
|
|
11
|
+
* response, or cross-request persistence — runs through
|
|
12
|
+
* `findForbiddenFields`, so a secret introduced by a nested or opaque value
|
|
13
|
+
* is caught even where the schema is open.
|
|
14
|
+
*/
|
|
15
|
+
import type { ContractViolation } from "./result.js";
|
|
16
|
+
import { isPlainObject } from "./result.js";
|
|
17
|
+
|
|
18
|
+
/** Single-word key tokens that denote secret material. */
|
|
19
|
+
const FORBIDDEN_WORDS: readonly string[] = [
|
|
20
|
+
"nonce",
|
|
21
|
+
"token",
|
|
22
|
+
"secret",
|
|
23
|
+
"credential",
|
|
24
|
+
"bearer",
|
|
25
|
+
"password",
|
|
26
|
+
"passwd",
|
|
27
|
+
"passphrase",
|
|
28
|
+
"signature",
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
/** Multi-word key forms (compared against the collapsed, tokenized key). */
|
|
32
|
+
const FORBIDDEN_COLLAPSED: readonly string[] = [
|
|
33
|
+
"signedurl",
|
|
34
|
+
"apikey",
|
|
35
|
+
"privatekey",
|
|
36
|
+
"sessionkey",
|
|
37
|
+
"accesskey",
|
|
38
|
+
"clientsecret",
|
|
39
|
+
"accesstoken",
|
|
40
|
+
"refreshtoken",
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
function tokenize(key: string): string[] {
|
|
44
|
+
return key
|
|
45
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
46
|
+
.replace(/[_\-.$]/g, " ")
|
|
47
|
+
.toLowerCase()
|
|
48
|
+
.split(/\s+/)
|
|
49
|
+
.filter(Boolean);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** True when a field name denotes a nonce, token, credential, or signed URL. */
|
|
53
|
+
export function isForbiddenKey(key: string): boolean {
|
|
54
|
+
const tokens = tokenize(key);
|
|
55
|
+
if (tokens.some((t) => FORBIDDEN_WORDS.includes(t))) return true;
|
|
56
|
+
const collapsed = tokens.join("");
|
|
57
|
+
return FORBIDDEN_COLLAPSED.some((c) => collapsed.includes(c));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Recursively report every field whose name denotes secret material. Returns an
|
|
62
|
+
* empty array for a browser-safe payload. Scans keys only; values are validated
|
|
63
|
+
* by field-level format checks (e.g. exact-origin CSP sources reject query-param
|
|
64
|
+
* signed URLs).
|
|
65
|
+
*/
|
|
66
|
+
export function findForbiddenFields(value: unknown, path = ""): ContractViolation[] {
|
|
67
|
+
const out: ContractViolation[] = [];
|
|
68
|
+
walk(value, path, out);
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function walk(value: unknown, path: string, out: ContractViolation[]): void {
|
|
73
|
+
if (Array.isArray(value)) {
|
|
74
|
+
value.forEach((item, i) => walk(item, `${path}[${i}]`, out));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (!isPlainObject(value)) return;
|
|
78
|
+
for (const [key, child] of Object.entries(value)) {
|
|
79
|
+
const childPath = path ? `${path}.${key}` : key;
|
|
80
|
+
if (isForbiddenKey(key)) {
|
|
81
|
+
out.push({
|
|
82
|
+
path: childPath,
|
|
83
|
+
code: "secret_field",
|
|
84
|
+
message:
|
|
85
|
+
`Field "${key}" names secret material; nonces, tokens, credentials, ` +
|
|
86
|
+
`signed URLs, and secrets must never appear in browser configuration, ` +
|
|
87
|
+
`effective plans, evidence, events, or assurance records.`,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
walk(child, childPath, out);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tamper-evidence beacon contract — the durable, secret-free record a
|
|
3
|
+
* protected-surface sentinel emits when it detects and recovers from third-party
|
|
4
|
+
* mutation of a platform-owned compliance/commerce surface (D-R7,
|
|
5
|
+
* `integration-protected-tamper-recovery`).
|
|
6
|
+
*
|
|
7
|
+
* This is the wire shape shared by the browser sentinel (which BUILDS and EMITS a
|
|
8
|
+
* beacon) and the server recovery path (which VALIDATES it, records it, and
|
|
9
|
+
* suspends the attributable integration). It is scoped to `tenant` + integration
|
|
10
|
+
* `attribution` (appId + version + origin) so a recovery action names exactly what
|
|
11
|
+
* to suspend.
|
|
12
|
+
*
|
|
13
|
+
* This is a DETECTIVE control. Full-parent DOM reach cannot be enforceably
|
|
14
|
+
* narrowed (WS2 security model), so a sentinel cannot PREVENT mutation — it
|
|
15
|
+
* detects it, restores safe state, and attributes it after the fact. The `control`
|
|
16
|
+
* field is the literal `"detective"` and the validator REJECTS any other value, so
|
|
17
|
+
* a beacon can never claim preventative isolation on the wire.
|
|
18
|
+
*
|
|
19
|
+
* Like every browser-facing contract here it is CLOSED and FAIL-CLOSED (unknown
|
|
20
|
+
* fields, unknown enum members, malformed shapes rejected) and secret-free
|
|
21
|
+
* (`findForbiddenFields`): a beacon carries structural facts about WHICH surface
|
|
22
|
+
* was tampered and HOW, never the altered copy itself, so no customer PII or
|
|
23
|
+
* secret can ride it.
|
|
24
|
+
*/
|
|
25
|
+
import type { ContractResult } from "./result.js";
|
|
26
|
+
import {
|
|
27
|
+
Violations,
|
|
28
|
+
reqObject,
|
|
29
|
+
reqString,
|
|
30
|
+
reqEnum,
|
|
31
|
+
reqBool,
|
|
32
|
+
reqLiteral,
|
|
33
|
+
reqIsoTimestamp,
|
|
34
|
+
rejectUnknownKeys,
|
|
35
|
+
} from "./result.js";
|
|
36
|
+
import { TAMPER_EVIDENCE_SCHEMA_VERSION } from "./version.js";
|
|
37
|
+
import { INTEGRATION_ENVIRONMENTS, INTEGRATION_SITE_SURFACES } from "./environment.js";
|
|
38
|
+
import type { IntegrationEnvironment, IntegrationSiteSurface } from "./environment.js";
|
|
39
|
+
import { findForbiddenFields } from "./secrets.js";
|
|
40
|
+
import { PROTECTED_SURFACES, type ProtectedSurface } from "../compliance-obligations.js";
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The one honest label for this class of control. A sentinel recovers safe state
|
|
44
|
+
* AFTER a mutation; it never prevents one. Any other value is rejected by
|
|
45
|
+
* {@link validateTamperEvidenceBeacon}, so "preventative"/"isolation" cannot be
|
|
46
|
+
* asserted on the wire.
|
|
47
|
+
*/
|
|
48
|
+
export const TAMPER_CONTROL = "detective" as const;
|
|
49
|
+
export type TamperControl = typeof TAMPER_CONTROL;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The observed mutation classes a sentinel distinguishes. Every one is a DETECTED
|
|
53
|
+
* tamper that was RECOVERED; the taxonomy drives operator triage, not enforcement.
|
|
54
|
+
* - `removed` — the protected node was detached from the document.
|
|
55
|
+
* - `displaced` — a different node now claims the surface marker.
|
|
56
|
+
* - `listener-stripped` — the node was replaced by an identical-markup clone,
|
|
57
|
+
* dropping its event listeners.
|
|
58
|
+
* - `hidden` — visually suppressed (display/visibility/opacity/hidden/aria-hidden).
|
|
59
|
+
* - `overlaid` — a foreign covering element was inserted over the surface.
|
|
60
|
+
* - `text-altered` — the protected copy/state text was changed in place.
|
|
61
|
+
* - `subtree-mutated` — any other structural mutation within the surface.
|
|
62
|
+
*/
|
|
63
|
+
export const TAMPER_KINDS = [
|
|
64
|
+
"removed",
|
|
65
|
+
"displaced",
|
|
66
|
+
"listener-stripped",
|
|
67
|
+
"hidden",
|
|
68
|
+
"overlaid",
|
|
69
|
+
"text-altered",
|
|
70
|
+
"subtree-mutated",
|
|
71
|
+
] as const;
|
|
72
|
+
export type TamperKind = (typeof TAMPER_KINDS)[number];
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The integration a tamper is attributed to — the exact `appId` + `version` +
|
|
76
|
+
* `origin` a recovery action suspends. `null` attribution is HONEST and expected:
|
|
77
|
+
* anonymous full-parent DOM reach cannot always be pinned to one integration, so
|
|
78
|
+
* an unattributable tamper is still detected, recovered, and alerted, but no
|
|
79
|
+
* specific integration is auto-suspended.
|
|
80
|
+
*/
|
|
81
|
+
export interface TamperAttribution {
|
|
82
|
+
appId: string;
|
|
83
|
+
version: string;
|
|
84
|
+
origin: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The durable evidence a sentinel emits per detected+recovered tamper. Scoped to
|
|
89
|
+
* `tenant` + `attribution` (app/version) and stamped with `environment` +
|
|
90
|
+
* `siteSurface` so the record is unambiguous across tiers.
|
|
91
|
+
*/
|
|
92
|
+
export interface TamperEvidenceBeacon {
|
|
93
|
+
schemaVersion: typeof TAMPER_EVIDENCE_SCHEMA_VERSION;
|
|
94
|
+
/** Always `"detective"` — see {@link TAMPER_CONTROL}. */
|
|
95
|
+
control: TamperControl;
|
|
96
|
+
tenant: string;
|
|
97
|
+
environment: IntegrationEnvironment;
|
|
98
|
+
siteSurface: IntegrationSiteSurface;
|
|
99
|
+
/** The platform-owned protected anchor (policy-04 `PROTECTED_SURFACES`) that was tampered. */
|
|
100
|
+
surface: ProtectedSurface;
|
|
101
|
+
kind: TamperKind;
|
|
102
|
+
/** ISO-8601 instant the tamper was detected. */
|
|
103
|
+
detectedAt: string;
|
|
104
|
+
/** True when the sentinel restored safe state (re-inspected clean after recovery). */
|
|
105
|
+
recovered: boolean;
|
|
106
|
+
/** The attributable integration, or `null` when the mutation is unattributable. */
|
|
107
|
+
attribution: TamperAttribution | null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The fields a caller supplies to {@link buildTamperEvidenceBeacon}. */
|
|
111
|
+
export type TamperEvidenceInput = Omit<
|
|
112
|
+
TamperEvidenceBeacon,
|
|
113
|
+
"schemaVersion" | "control"
|
|
114
|
+
>;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Assemble a beacon with the fixed `schemaVersion` + `control` stamped. The only
|
|
118
|
+
* constructor — so a beacon can never be built claiming a non-detective control.
|
|
119
|
+
*/
|
|
120
|
+
export function buildTamperEvidenceBeacon(
|
|
121
|
+
input: TamperEvidenceInput,
|
|
122
|
+
): TamperEvidenceBeacon {
|
|
123
|
+
return {
|
|
124
|
+
schemaVersion: TAMPER_EVIDENCE_SCHEMA_VERSION,
|
|
125
|
+
control: TAMPER_CONTROL,
|
|
126
|
+
...input,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function validateAttribution(value: unknown, v: Violations): void {
|
|
131
|
+
if (value === null) return;
|
|
132
|
+
const obj = reqObject(value, v, "attribution");
|
|
133
|
+
if (!obj) return;
|
|
134
|
+
rejectUnknownKeys(obj, ["appId", "version", "origin"], v, "attribution");
|
|
135
|
+
reqString(obj, "appId", v, "attribution");
|
|
136
|
+
reqString(obj, "version", v, "attribution");
|
|
137
|
+
reqString(obj, "origin", v, "attribution");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The single fail-CLOSED authority for "is this a well-formed tamper beacon?".
|
|
142
|
+
* Rejects an unknown schema version, a non-`detective` control (so no beacon can
|
|
143
|
+
* claim prevention), an unknown surface or kind, a malformed attribution, or any
|
|
144
|
+
* secret-bearing field. The server recovery path validates every inbound beacon
|
|
145
|
+
* through this before acting on it.
|
|
146
|
+
*/
|
|
147
|
+
export function validateTamperEvidenceBeacon(
|
|
148
|
+
input: unknown,
|
|
149
|
+
): ContractResult<TamperEvidenceBeacon> {
|
|
150
|
+
const v = new Violations();
|
|
151
|
+
const obj = reqObject(input, v, "");
|
|
152
|
+
if (!obj) return { ok: false, violations: v.items };
|
|
153
|
+
|
|
154
|
+
rejectUnknownKeys(
|
|
155
|
+
obj,
|
|
156
|
+
[
|
|
157
|
+
"schemaVersion",
|
|
158
|
+
"control",
|
|
159
|
+
"tenant",
|
|
160
|
+
"environment",
|
|
161
|
+
"siteSurface",
|
|
162
|
+
"surface",
|
|
163
|
+
"kind",
|
|
164
|
+
"detectedAt",
|
|
165
|
+
"recovered",
|
|
166
|
+
"attribution",
|
|
167
|
+
],
|
|
168
|
+
v,
|
|
169
|
+
"",
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
reqLiteral(obj, "schemaVersion", TAMPER_EVIDENCE_SCHEMA_VERSION, v, "");
|
|
173
|
+
reqLiteral(obj, "control", TAMPER_CONTROL, v, "");
|
|
174
|
+
reqString(obj, "tenant", v, "");
|
|
175
|
+
reqEnum(obj, "environment", INTEGRATION_ENVIRONMENTS, v, "");
|
|
176
|
+
reqEnum(obj, "siteSurface", INTEGRATION_SITE_SURFACES, v, "");
|
|
177
|
+
reqEnum(obj, "surface", PROTECTED_SURFACES, v, "");
|
|
178
|
+
reqEnum(obj, "kind", TAMPER_KINDS, v, "");
|
|
179
|
+
reqIsoTimestamp(obj, "detectedAt", v, "");
|
|
180
|
+
reqBool(obj, "recovered", v, "");
|
|
181
|
+
if (!("attribution" in obj)) {
|
|
182
|
+
v.add("attribution", "type", 'Expected "attribution" to be an object or null.');
|
|
183
|
+
} else {
|
|
184
|
+
validateAttribution(obj["attribution"], v);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
for (const violation of findForbiddenFields(obj)) v.items.push(violation);
|
|
188
|
+
|
|
189
|
+
if (!v.ok) return { ok: false, violations: v.items };
|
|
190
|
+
return { ok: true, value: input as TamperEvidenceBeacon };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Resolve attribution from the integrations that could be responsible. Anonymous
|
|
195
|
+
* DOM mutation carries no provenance, so attribution is the honest SOLE-SUSPECT
|
|
196
|
+
* rule: attribute to the one integration when exactly one is present, otherwise
|
|
197
|
+
* `null` (unattributable — recover + alert, but auto-suspend nothing). A caller
|
|
198
|
+
* that positively identifies the actor (e.g. an injected node carrying an
|
|
199
|
+
* integration's marker) resolves it directly instead of relying on this.
|
|
200
|
+
*/
|
|
201
|
+
export function attributeSoleSuspect(
|
|
202
|
+
candidates: readonly TamperAttribution[],
|
|
203
|
+
): TamperAttribution | null {
|
|
204
|
+
return candidates.length === 1 ? candidates[0]! : null;
|
|
205
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edge recovery decisions for protected-surface tamper beacons (D-R7,
|
|
3
|
+
* `integration-protected-tamper-recovery`).
|
|
4
|
+
*
|
|
5
|
+
* The browser sentinel restores safe state IN the document and posts a
|
|
6
|
+
* {@link TamperEvidenceBeacon}. This is the trusted receiver's pure decision core:
|
|
7
|
+
* it VALIDATES the beacon fail-closed, turns it into a durable append-only
|
|
8
|
+
* {@link TamperEvidenceRecord}, and resolves the SUSPEND directive that disables the
|
|
9
|
+
* attributable integration's install. It is edge-safe (no Node) so the storefront
|
|
10
|
+
* Worker — where the beacon lands — can run it directly; the actuation (the actual
|
|
11
|
+
* registry suspend + audit write) belongs to the caller.
|
|
12
|
+
*
|
|
13
|
+
* This is a DETECTIVE control. Suspension happens AFTER a detected+recovered tamper,
|
|
14
|
+
* keyed on the beacon's `attribution` (appId + version), and is deliberately
|
|
15
|
+
* conservative: it suspends only when exactly one ACTIVE install matches. An
|
|
16
|
+
* unattributable beacon, or an ambiguous match, records the evidence and alerts but
|
|
17
|
+
* suspends nothing — recovery never suspends the wrong app.
|
|
18
|
+
*/
|
|
19
|
+
import type { ContractViolation } from "./result.js";
|
|
20
|
+
import {
|
|
21
|
+
validateTamperEvidenceBeacon,
|
|
22
|
+
type TamperEvidenceBeacon,
|
|
23
|
+
} from "./tamper-evidence.js";
|
|
24
|
+
|
|
25
|
+
/** The minimal install facts suspension resolution needs (the registry row projection). */
|
|
26
|
+
export interface AttributableInstall {
|
|
27
|
+
installId: string;
|
|
28
|
+
appId: string;
|
|
29
|
+
appVersion: string;
|
|
30
|
+
status: "active" | "suspended" | "uninstalled";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The durable, append-only record a validated beacon becomes on the server. */
|
|
34
|
+
export interface TamperEvidenceRecord {
|
|
35
|
+
recordId: string;
|
|
36
|
+
/** ISO instant the server received the beacon. */
|
|
37
|
+
receivedAt: string;
|
|
38
|
+
beacon: TamperEvidenceBeacon;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The suspension a recovery drives — names exactly one install to disable. */
|
|
42
|
+
export interface SuspendDirective {
|
|
43
|
+
installId: string;
|
|
44
|
+
appId: string;
|
|
45
|
+
version: string;
|
|
46
|
+
reason: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface TamperRecoveryPlan {
|
|
50
|
+
record: TamperEvidenceRecord;
|
|
51
|
+
/** The install to suspend, or `null` when unattributable / no single active match. */
|
|
52
|
+
suspend: SuspendDirective | null;
|
|
53
|
+
/** Always true — every detected tamper is surfaced to operations. */
|
|
54
|
+
alert: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type TamperRecoveryResult =
|
|
58
|
+
| { ok: true; plan: TamperRecoveryPlan }
|
|
59
|
+
| { ok: false; violations: ContractViolation[] };
|
|
60
|
+
|
|
61
|
+
export interface PlanTamperRecoveryOptions {
|
|
62
|
+
recordId: string;
|
|
63
|
+
/** ISO server receive time; passed in so the record is deterministic/testable. */
|
|
64
|
+
receivedAt: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Resolve which install to suspend for a beacon, or `null`. Fail-SAFE: suspends
|
|
69
|
+
* only when the beacon is attributed AND exactly one ACTIVE install matches that
|
|
70
|
+
* app id and version. Zero matches, an already-suspended/uninstalled install, or an
|
|
71
|
+
* ambiguous multi-match resolves to `null` — the evidence still stands, but no app
|
|
72
|
+
* is auto-suspended on a guess.
|
|
73
|
+
*/
|
|
74
|
+
export function resolveSuspendTarget(
|
|
75
|
+
beacon: TamperEvidenceBeacon,
|
|
76
|
+
installs: readonly AttributableInstall[],
|
|
77
|
+
): SuspendDirective | null {
|
|
78
|
+
const attribution = beacon.attribution;
|
|
79
|
+
if (!attribution) return null;
|
|
80
|
+
|
|
81
|
+
const matches = installs.filter(
|
|
82
|
+
(i) =>
|
|
83
|
+
i.status === "active" &&
|
|
84
|
+
i.appId === attribution.appId &&
|
|
85
|
+
i.appVersion === attribution.version,
|
|
86
|
+
);
|
|
87
|
+
if (matches.length !== 1) return null;
|
|
88
|
+
|
|
89
|
+
const install = matches[0]!;
|
|
90
|
+
return {
|
|
91
|
+
installId: install.installId,
|
|
92
|
+
appId: attribution.appId,
|
|
93
|
+
version: attribution.version,
|
|
94
|
+
reason: `Detective tamper recovery: integration mutated the protected "${beacon.surface}" surface (${beacon.kind}); auto-suspended.`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Validate an inbound beacon and produce the recovery plan: a durable evidence
|
|
100
|
+
* record, the (optional) suspend directive, and the always-on alert. Returns the
|
|
101
|
+
* structured violations on a malformed/secret-bearing/non-detective beacon so the
|
|
102
|
+
* caller can 400 it rather than act on unvalidated input.
|
|
103
|
+
*/
|
|
104
|
+
export function planTamperRecovery(
|
|
105
|
+
rawBeacon: unknown,
|
|
106
|
+
installs: readonly AttributableInstall[],
|
|
107
|
+
opts: PlanTamperRecoveryOptions,
|
|
108
|
+
): TamperRecoveryResult {
|
|
109
|
+
const validated = validateTamperEvidenceBeacon(rawBeacon);
|
|
110
|
+
if (!validated.ok) return { ok: false, violations: validated.violations };
|
|
111
|
+
|
|
112
|
+
const beacon = validated.value;
|
|
113
|
+
const record: TamperEvidenceRecord = {
|
|
114
|
+
recordId: opts.recordId,
|
|
115
|
+
receivedAt: opts.receivedAt,
|
|
116
|
+
beacon,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
ok: true,
|
|
121
|
+
plan: {
|
|
122
|
+
record,
|
|
123
|
+
suspend: resolveSuspendTarget(beacon, installs),
|
|
124
|
+
alert: true,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semver-versioned browser-integration contract (ADR 0006).
|
|
3
|
+
*
|
|
4
|
+
* The browser contract carries a single MAJOR.MINOR.PATCH version. A manifest
|
|
5
|
+
* declares the range it supports in `compatibility.storefrontBrowserContract`;
|
|
6
|
+
* compatibility is resolved fail-closed — an unparseable range, or a current
|
|
7
|
+
* contract version outside the range, is rejected rather than assumed
|
|
8
|
+
* compatible.
|
|
9
|
+
*
|
|
10
|
+
* Each payload family additionally pins an integer `schemaVersion`. Unknown
|
|
11
|
+
* schema versions fail closed.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export const BROWSER_INTEGRATION_CONTRACT_VERSION = "2.0.0" as const;
|
|
15
|
+
|
|
16
|
+
export const ENVIRONMENT_SCHEMA_VERSION = 1 as const;
|
|
17
|
+
export const INTEGRATION_MANIFEST_SCHEMA_VERSION = 2 as const;
|
|
18
|
+
export const EFFECTIVE_PLAN_SCHEMA_VERSION = 1 as const;
|
|
19
|
+
export const COMMERCE_EVENT_STOREFRONT_SCHEMA_VERSION = 1 as const;
|
|
20
|
+
export const CONSENT_SNAPSHOT_SCHEMA_VERSION = 1 as const;
|
|
21
|
+
export const RECEIPT_MEASUREMENT_GRANT_SCHEMA_VERSION = 1 as const;
|
|
22
|
+
export const CONSENT_CONFIG_SCHEMA_VERSION = 1 as const;
|
|
23
|
+
export const ASSURANCE_RECORD_SCHEMA_VERSION = 1 as const;
|
|
24
|
+
export const TAMPER_EVIDENCE_SCHEMA_VERSION = 1 as const;
|
|
25
|
+
export const OBSERVABILITY_SIGNAL_SCHEMA_VERSION = 1 as const;
|
|
26
|
+
|
|
27
|
+
export interface SemVer {
|
|
28
|
+
major: number;
|
|
29
|
+
minor: number;
|
|
30
|
+
patch: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function parseSemVer(input: string): SemVer | null {
|
|
34
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(input.trim());
|
|
35
|
+
if (!match) return null;
|
|
36
|
+
return {
|
|
37
|
+
major: Number(match[1]),
|
|
38
|
+
minor: Number(match[2]),
|
|
39
|
+
patch: Number(match[3]),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function compareSemVer(a: SemVer, b: SemVer): number {
|
|
44
|
+
if (a.major !== b.major) return a.major < b.major ? -1 : 1;
|
|
45
|
+
if (a.minor !== b.minor) return a.minor < b.minor ? -1 : 1;
|
|
46
|
+
if (a.patch !== b.patch) return a.patch < b.patch ? -1 : 1;
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type Comparator = ">=" | "<=" | ">" | "<" | "=";
|
|
51
|
+
|
|
52
|
+
function satisfiesComparator(version: SemVer, op: Comparator, target: SemVer): boolean {
|
|
53
|
+
const cmp = compareSemVer(version, target);
|
|
54
|
+
switch (op) {
|
|
55
|
+
case ">=":
|
|
56
|
+
return cmp >= 0;
|
|
57
|
+
case "<=":
|
|
58
|
+
return cmp <= 0;
|
|
59
|
+
case ">":
|
|
60
|
+
return cmp > 0;
|
|
61
|
+
case "<":
|
|
62
|
+
return cmp < 0;
|
|
63
|
+
case "=":
|
|
64
|
+
return cmp === 0;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* True only when `version` satisfies every space-separated comparator in
|
|
70
|
+
* `range` (AND semantics), e.g. `>=1.0.0 <2.0.0`. A bare version means `=`.
|
|
71
|
+
* Any unparseable comparator makes the whole range unsatisfied (fail closed).
|
|
72
|
+
*/
|
|
73
|
+
export function satisfiesRange(version: string, range: string): boolean {
|
|
74
|
+
const parsed = parseSemVer(version);
|
|
75
|
+
if (!parsed) return false;
|
|
76
|
+
const parts = range.trim().split(/\s+/).filter(Boolean);
|
|
77
|
+
if (parts.length === 0) return false;
|
|
78
|
+
for (const part of parts) {
|
|
79
|
+
const match = /^(>=|<=|>|<|=)?(\d+\.\d+\.\d+)$/.exec(part);
|
|
80
|
+
if (!match) return false;
|
|
81
|
+
const target = parseSemVer(match[2] ?? "");
|
|
82
|
+
if (!target) return false;
|
|
83
|
+
const op = (match[1] ?? "=") as Comparator;
|
|
84
|
+
if (!satisfiesComparator(parsed, op, target)) return false;
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Whether the current browser contract version satisfies a manifest's declared range. */
|
|
90
|
+
export function isCompatibleWithCurrentContract(range: string): boolean {
|
|
91
|
+
return satisfiesRange(BROWSER_INTEGRATION_CONTRACT_VERSION, range);
|
|
92
|
+
}
|