@tokenoftrust/storefront-runner 2.0.23 → 2.0.25
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 +5 -0
- package/apps/storefront/integrations/lifecycle-host-asset.mjs +125 -0
- package/apps/storefront/src/layouts/Layout.astro +8 -10
- package/apps/storefront/src/lib/integration/bakedManagedInstalls.ts +56 -0
- package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +11 -3
- package/apps/storefront/src/lib/integration/stageDestinationIds.ts +103 -0
- package/apps/storefront/src/middleware/renderChain.ts +15 -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 +2 -1
- package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +79 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +79 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +79 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +79 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +79 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +79 -0
- package/packages/public-runtime/src/gtm-migration/draft-generator.ts +5 -2
- package/packages/public-runtime/src/integration/baked-managed-installs.ts +188 -0
- package/packages/public-runtime/src/integration/index.ts +1 -0
- package/packages/public-runtime/src/integration/lifecycle-boot.ts +72 -0
- package/packages/public-runtime/src/integration/lifecycle-host-asset.d.mts +3 -0
- package/packages/public-runtime/src/integration/lifecycle-host-asset.mjs +17 -0
- package/packages/public-runtime/src/integration/lifecycle-host-entry.ts +16 -0
- package/packages/public-runtime/src/integration/lifecycle-runtime.ts +59 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +18 -1
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +48 -3
- package/packages/public-runtime/src/integration/render-phases.ts +30 -2
|
@@ -58,6 +58,7 @@ import preact from "@astrojs/preact";
|
|
|
58
58
|
import tailwindcss from "@tailwindcss/vite";
|
|
59
59
|
import { tenantHotReload } from "./dev-plugins/tenant-hot-reload.mjs";
|
|
60
60
|
import { materializeGuard } from "./integrations/materialize-guard.mjs";
|
|
61
|
+
import { lifecycleHostAsset } from "./integrations/lifecycle-host-asset.mjs";
|
|
61
62
|
|
|
62
63
|
// SSR on Cloudflare Workers. `output: 'server'` because every response is
|
|
63
64
|
// tenant-resolved at the edge from the Host header (see src/middleware).
|
|
@@ -94,6 +95,10 @@ export default defineConfig({
|
|
|
94
95
|
// resolves from KV/R2 at request time. The same integration becomes strict
|
|
95
96
|
// only when TOT_BUILD_TENANTS names a per-tenant static publish input.
|
|
96
97
|
materializeGuard(),
|
|
98
|
+
// The document.deferred lifecycle host, emitted as one self-contained module at a
|
|
99
|
+
// stable same-origin URL so BOTH document mounts load the same boot — the raw
|
|
100
|
+
// marketing mount has no bundler to hoist a module import through.
|
|
101
|
+
lifecycleHostAsset(),
|
|
97
102
|
preact({ compat: true }),
|
|
98
103
|
// Astro's file-based router ignores ANY path segment starting with "_" (see
|
|
99
104
|
// `astro/dist/core/routing/create-manifest.js`), so a page literally named
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emit the document.deferred lifecycle host as ONE self-contained module at a stable
|
|
3
|
+
* same-origin URL, so both document mounts can load the same host.
|
|
4
|
+
*
|
|
5
|
+
* WHY NOT A ROLLUP CHUNK. `Layout.astro` gets its client code through Astro's bundler,
|
|
6
|
+
* which hashes filenames and splits shared modules into sibling chunks. The raw marketing
|
|
7
|
+
* mount can use neither: it serves a tenant's own document verbatim, so it needs a URL it
|
|
8
|
+
* can hardcode. And a SPLIT bundle is actively unsafe here — a published static bundle
|
|
9
|
+
* (`scripts/publish/materialize-tenant.mjs`) copies only what the rendered HTML references
|
|
10
|
+
* and follows CSS `url()` but NOT JavaScript imports, so a sibling chunk would resolve on
|
|
11
|
+
* the Cloudflare plane and 404 on the S3/CloudFront one. That asymmetry is exactly the
|
|
12
|
+
* both-CDNs failure this repo treats as an invariant, and nothing downstream would report
|
|
13
|
+
* it: validate-bundle only checks HTML-referenced paths.
|
|
14
|
+
*
|
|
15
|
+
* So the host is bundled ONCE by esbuild with no code splitting, and this REFUSES to emit
|
|
16
|
+
* anything carrying an import. A single import-free file behaves identically on both
|
|
17
|
+
* providers by construction.
|
|
18
|
+
*
|
|
19
|
+
* It is an Astro INTEGRATION rather than a bare Vite plugin because Astro is the only
|
|
20
|
+
* thing that knows where the client output actually goes: every one of Astro's build
|
|
21
|
+
* passes — the client pass included — runs with `build.ssr === true`, so a Vite plugin
|
|
22
|
+
* cannot tell them apart, while `config.build.client` says it outright.
|
|
23
|
+
*/
|
|
24
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
25
|
+
import { createRequire } from "node:module";
|
|
26
|
+
import { dirname } from "node:path";
|
|
27
|
+
import { fileURLToPath } from "node:url";
|
|
28
|
+
import { LIFECYCLE_HOST_ASSET_PATH } from "@tot/public-runtime/integration/lifecycle-host-asset";
|
|
29
|
+
|
|
30
|
+
const HOST_ENTRY = fileURLToPath(
|
|
31
|
+
new URL(
|
|
32
|
+
"../../../packages/public-runtime/src/integration/lifecycle-host-entry.ts",
|
|
33
|
+
import.meta.url,
|
|
34
|
+
),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Bundle the host to a single self-contained ES module.
|
|
39
|
+
*
|
|
40
|
+
* esbuild is resolved through `astro` (a direct dependency of this app, always present
|
|
41
|
+
* whenever this integration runs) rather than declared again here, so its version cannot
|
|
42
|
+
* drift from the one the build already uses.
|
|
43
|
+
* @returns {Promise<string>}
|
|
44
|
+
*/
|
|
45
|
+
export async function bundleLifecycleHost() {
|
|
46
|
+
const require = createRequire(import.meta.url);
|
|
47
|
+
const esbuild = createRequire(require.resolve("astro/package.json"))("esbuild");
|
|
48
|
+
const result = await esbuild.build({
|
|
49
|
+
entryPoints: [HOST_ENTRY],
|
|
50
|
+
bundle: true,
|
|
51
|
+
write: false,
|
|
52
|
+
metafile: true,
|
|
53
|
+
format: "esm",
|
|
54
|
+
platform: "browser",
|
|
55
|
+
target: "es2022",
|
|
56
|
+
minify: true,
|
|
57
|
+
treeShaking: true,
|
|
58
|
+
legalComments: "none",
|
|
59
|
+
sourcemap: false,
|
|
60
|
+
logLevel: "silent",
|
|
61
|
+
});
|
|
62
|
+
if (result.outputFiles.length !== 1) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`The lifecycle host bundled to ${result.outputFiles.length} files; it must be one ` +
|
|
65
|
+
`self-contained module (a sibling chunk 404s on the S3/CloudFront plane).`,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
// The metafile is authoritative about imports in a way a regex over minified code is
|
|
69
|
+
// not: any entry here is a file the browser would have to fetch separately.
|
|
70
|
+
for (const output of Object.values(result.metafile.outputs)) {
|
|
71
|
+
const external = (output.imports ?? []).filter((i) => i.kind !== "file-loader");
|
|
72
|
+
if (external.length > 0) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`The lifecycle host bundle imports ${external.map((i) => i.path).join(", ")}; it ` +
|
|
75
|
+
`must carry no imports, because the static publish pipeline does not follow them.`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return result.outputFiles[0].text;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @returns {import("astro").AstroIntegration}
|
|
84
|
+
*/
|
|
85
|
+
export function lifecycleHostAsset() {
|
|
86
|
+
/** @type {URL | null} */
|
|
87
|
+
let clientDir = null;
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
name: "tot-lifecycle-host-asset",
|
|
91
|
+
hooks: {
|
|
92
|
+
"astro:config:done": ({ config }) => {
|
|
93
|
+
clientDir = config.build.client;
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
// Dev serves it from memory: the raw marketing mount bypasses Astro's pipeline
|
|
97
|
+
// entirely, so without this the host 404s in `astro dev` and `tot dev` on exactly
|
|
98
|
+
// the pages this asset exists to cover. Rebundled per request (esbuild is fast) so
|
|
99
|
+
// an edit to the runtime shows up on reload rather than being pinned to server boot.
|
|
100
|
+
"astro:server:setup": ({ server }) => {
|
|
101
|
+
server.middlewares.use((req, res, next) => {
|
|
102
|
+
const path = (req.url ?? "").split("?")[0];
|
|
103
|
+
if (path !== `/${LIFECYCLE_HOST_ASSET_PATH}`) return next();
|
|
104
|
+
bundleLifecycleHost().then(
|
|
105
|
+
(code) => {
|
|
106
|
+
res.setHeader("content-type", "text/javascript; charset=utf-8");
|
|
107
|
+
res.setHeader("cache-control", "no-store");
|
|
108
|
+
res.end(code);
|
|
109
|
+
},
|
|
110
|
+
(error) => next(error),
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
"astro:build:done": async ({ logger }) => {
|
|
116
|
+
if (!clientDir) throw new Error("the lifecycle host has no resolved client output dir");
|
|
117
|
+
const code = await bundleLifecycleHost();
|
|
118
|
+
const target = fileURLToPath(new URL(LIFECYCLE_HOST_ASSET_PATH, clientDir));
|
|
119
|
+
await mkdir(dirname(target), { recursive: true });
|
|
120
|
+
await writeFile(target, code, "utf8");
|
|
121
|
+
logger.info(`lifecycle host → /${LIFECYCLE_HOST_ASSET_PATH} (${code.length} bytes)`);
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -618,16 +618,14 @@ const footerWidgets = await renderWidgetSlot(
|
|
|
618
618
|
entry in lib/cspStaticHashes.ts. */}
|
|
619
619
|
<script is:inline nonce={cspNonce} set:html={INTERACTION_TRACKING_BOOT_JS}></script>
|
|
620
620
|
|
|
621
|
-
{/* document.deferred global-entry boundary (ext-kernel-04) — the last phase,
|
|
622
|
-
|
|
621
|
+
{/* document.deferred global-entry boundary (ext-kernel-04) — the last phase, the
|
|
622
|
+
renderer-owned mount point for the deferred integration entry. This region also
|
|
623
|
+
CARRIES the lifecycle host tag (ext-kernel-05), which initializes the plan's
|
|
624
|
+
integrations exactly once per document, emits the governed page view, wires the
|
|
625
|
+
reserved completed-navigation event, and disposes on teardown; a no-op for an
|
|
626
|
+
empty plan. It ships with the phase markup rather than as a per-mount addition,
|
|
627
|
+
so the same bytes reach the raw marketing mount — which serves a tenant's
|
|
628
|
+
document verbatim and has no bundler to hoist a module import through. */}
|
|
623
629
|
<Fragment set:html={integrationPhases.bodyDeferred} />
|
|
624
|
-
|
|
625
|
-
{/* document.deferred lifecycle boot (ext-kernel-05) — initializes the plan's
|
|
626
|
-
integrations exactly once per document, wires the reserved completed-
|
|
627
|
-
navigation event, and disposes on teardown; a no-op for an empty plan. */}
|
|
628
|
-
<script>
|
|
629
|
-
import { bootIntegrationLifecycleFromDocument } from "@tot/public-runtime";
|
|
630
|
-
bootIntegrationLifecycleFromDocument();
|
|
631
|
-
</script>
|
|
632
630
|
</body>
|
|
633
631
|
</html>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a tenant's BAKED managed installs off the materialize-only signed header.
|
|
3
|
+
*
|
|
4
|
+
* This is the read half of the wire documented in
|
|
5
|
+
* `packages/public-runtime/src/integration/baked-managed-installs.ts`. It exists because
|
|
6
|
+
* ADR 0013 gives a public page no per-request access to admin-installed state — the
|
|
7
|
+
* static bake has no D1 binding at all — so an active install can only reach a published
|
|
8
|
+
* bundle by being resolved and signed once, by the Worker, at go-live.
|
|
9
|
+
*
|
|
10
|
+
* Fail-safe at every step: not in materialize mode, no header, no secret, a signature
|
|
11
|
+
* that does not verify, or a payload that does not parse all yield an EMPTY list, and the
|
|
12
|
+
* render then resolves the environment-only plan exactly as it would for a tenant with no
|
|
13
|
+
* integrations. Nothing here can widen what a public request may inject, because gate one
|
|
14
|
+
* is a binding the deployed Worker never has.
|
|
15
|
+
*/
|
|
16
|
+
import {
|
|
17
|
+
MANAGED_INSTALLS_INPUT_HEADER,
|
|
18
|
+
MANAGED_INSTALLS_SIG_HEADER,
|
|
19
|
+
parseManagedInstalls,
|
|
20
|
+
verifyManagedInstallsSig,
|
|
21
|
+
MATERIALIZE_MODE_BINDING,
|
|
22
|
+
CONSENT_SNAPSHOT_HMAC_SECRET_BINDING,
|
|
23
|
+
type IntegrationDeclarationInput,
|
|
24
|
+
} from "@tot/public-runtime";
|
|
25
|
+
|
|
26
|
+
/** Reads a runtime binding; injected so this is testable without a Worker env. */
|
|
27
|
+
export type ReadEnv = (key: string) => Promise<string | undefined>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The signed, materialize-only managed installs for `tenantPublicId`, or `[]`.
|
|
31
|
+
*
|
|
32
|
+
* The signature is TENANT-BOUND, so a payload signed for one tenant cannot be replayed
|
|
33
|
+
* into another tenant's bundle even by something that legitimately holds a signed header
|
|
34
|
+
* — which matters because one publish job builds one tenant at a time from a shared
|
|
35
|
+
* pipeline.
|
|
36
|
+
*/
|
|
37
|
+
export async function readBakedManagedInstalls(
|
|
38
|
+
request: Request,
|
|
39
|
+
tenantPublicId: string,
|
|
40
|
+
readEnv: ReadEnv,
|
|
41
|
+
): Promise<IntegrationDeclarationInput[]> {
|
|
42
|
+
// Gate 1: the materialize-mode binding. The deployed Worker never declares it, so this
|
|
43
|
+
// header is unreadable on any public request and can never enter a cache key.
|
|
44
|
+
if ((await readEnv(MATERIALIZE_MODE_BINDING)) !== "1") return [];
|
|
45
|
+
|
|
46
|
+
const payload = request.headers.get(MANAGED_INSTALLS_INPUT_HEADER);
|
|
47
|
+
if (!payload) return [];
|
|
48
|
+
|
|
49
|
+
// Gate 2: the tenant-bound HMAC, verified constant-time against the go-live secret.
|
|
50
|
+
const secret = await readEnv(CONSENT_SNAPSHOT_HMAC_SECRET_BINDING);
|
|
51
|
+
if (!secret) return [];
|
|
52
|
+
const signature = request.headers.get(MANAGED_INSTALLS_SIG_HEADER);
|
|
53
|
+
if (!(await verifyManagedInstallsSig(secret, tenantPublicId, payload, signature))) return [];
|
|
54
|
+
|
|
55
|
+
return parseManagedInstalls(payload) ?? [];
|
|
56
|
+
}
|
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
import {
|
|
7
7
|
ENVIRONMENT_SCHEMA_VERSION,
|
|
8
8
|
buildPlatformAdapterDeclaration,
|
|
9
|
+
getPlatformAdapter,
|
|
9
10
|
validateIntegrationManifest,
|
|
10
11
|
type IntegrationDeclarationInput,
|
|
11
12
|
} from "@tot/public-runtime";
|
|
12
13
|
import { AppVersionStore } from "../apps/appVersionStore.js";
|
|
13
14
|
import type { Queryable } from "../d1/catalog.js";
|
|
15
|
+
import { parseStageDestinationIds } from "./stageDestinationIds.js";
|
|
14
16
|
|
|
15
17
|
const MANAGED_KIND = "managed-analytics-v1";
|
|
16
18
|
|
|
@@ -47,11 +49,17 @@ export async function loadActiveManagedInstallDeclarations(
|
|
|
47
49
|
if (!manifest.ok || !current || current.tenantId !== tenantPublicId) continue;
|
|
48
50
|
if (current.appVersion !== row.app_version || current.facets.appVersion !== row.app_version) continue;
|
|
49
51
|
if (current.facets.digest !== manifest.value.app.digest || manifest.value.app.id !== row.app_id) continue;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
// The stored per-stage map, re-parsed through the SAME validator the API used, so a
|
|
53
|
+
// row that no longer satisfies the contract disables its install rather than
|
|
54
|
+
// resolving a partial or stale destination set.
|
|
55
|
+
const ids = parseStageDestinationIds(
|
|
56
|
+
current.facets.publicConfig["destinationIds"],
|
|
57
|
+
getPlatformAdapter(row.app_id),
|
|
58
|
+
);
|
|
59
|
+
if (!ids.ok) continue;
|
|
52
60
|
const built = buildPlatformAdapterDeclaration({
|
|
53
61
|
adapterId: row.app_id,
|
|
54
|
-
ids:
|
|
62
|
+
ids: ids.value,
|
|
55
63
|
tenantPublicId,
|
|
56
64
|
environment: {
|
|
57
65
|
schemaVersion: ENVIRONMENT_SCHEMA_VERSION,
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-stage destination map, parsed once for both callers that need it: the
|
|
3
|
+
* managed-analytics API (validating what a merchant submits) and the render plane
|
|
4
|
+
* (rebuilding a stored install's declaration). One parser, so what the console accepts
|
|
5
|
+
* and what actually fires can never disagree.
|
|
6
|
+
*
|
|
7
|
+
* WHY PER STAGE. `resolveAdapterDestinationId` reads only the CURRENT stage's id, and
|
|
8
|
+
* `buildPlatformAdapterDeclaration` refuses a non-live stage that reuses the live one. So
|
|
9
|
+
* with a single id the runtime is already correct — a non-live stage resolves nothing and
|
|
10
|
+
* dispatches nothing — but the merchant has no way to measure anything off live either.
|
|
11
|
+
* A per-stage map is what makes non-live verification possible without pointing preview
|
|
12
|
+
* traffic at the property that carries real reporting.
|
|
13
|
+
*/
|
|
14
|
+
import {
|
|
15
|
+
ADAPTER_CONFIG_STAGES,
|
|
16
|
+
type AdapterConfigStage,
|
|
17
|
+
type DestinationIdsByEnvironment,
|
|
18
|
+
type PlatformAdapterDefinition,
|
|
19
|
+
} from "@tot/public-runtime";
|
|
20
|
+
|
|
21
|
+
export interface StageDestinationIdsOk {
|
|
22
|
+
ok: true;
|
|
23
|
+
value: DestinationIdsByEnvironment;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface StageDestinationIdsError {
|
|
27
|
+
ok: false;
|
|
28
|
+
details: { path: string; code: string; message: string }[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type StageDestinationIdsResult = StageDestinationIdsOk | StageDestinationIdsError;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Parse and validate a submitted per-stage map fail-closed.
|
|
35
|
+
*
|
|
36
|
+
* `live` is required because it is the stage that publishes; a non-live stage is optional
|
|
37
|
+
* and simply resolves nothing when absent. Every id is checked against the ADAPTER's own
|
|
38
|
+
* format validator, so a Meta pixel id cannot be accepted where a GA4 measurement id
|
|
39
|
+
* belongs. Reusing the live id on a non-live stage is refused HERE with a field-level
|
|
40
|
+
* message rather than left to the declaration builder, so the console can point at the
|
|
41
|
+
* offending stage instead of failing the whole submission opaquely.
|
|
42
|
+
*/
|
|
43
|
+
export function parseStageDestinationIds(
|
|
44
|
+
input: unknown,
|
|
45
|
+
platform: PlatformAdapterDefinition | undefined,
|
|
46
|
+
): StageDestinationIdsResult {
|
|
47
|
+
const base = "body.configuration.destinationIds";
|
|
48
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) {
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
details: [{ path: base, code: "type", message: "Expected a map of stage to destination identifier." }],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (!platform) {
|
|
55
|
+
return { ok: false, details: [{ path: "body.adapterId", code: "unknown_adapter", message: "Unknown adapter." }] };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const entries = Object.entries(input as Record<string, unknown>);
|
|
59
|
+
const details: StageDestinationIdsError["details"] = [];
|
|
60
|
+
const value: DestinationIdsByEnvironment = {};
|
|
61
|
+
|
|
62
|
+
for (const [stage, id] of entries) {
|
|
63
|
+
const path = `${base}.${stage}`;
|
|
64
|
+
if (!(ADAPTER_CONFIG_STAGES as readonly string[]).includes(stage)) {
|
|
65
|
+
details.push({
|
|
66
|
+
path,
|
|
67
|
+
code: "unknown_enum",
|
|
68
|
+
message: `Stage must be one of: ${ADAPTER_CONFIG_STAGES.join(", ")}.`,
|
|
69
|
+
});
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (typeof id !== "string" || !platform.validateId(id)) {
|
|
73
|
+
details.push({ path, code: "format", message: "Destination identifier has an invalid format." });
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
value[stage as AdapterConfigStage] = id;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!value.live) {
|
|
80
|
+
details.push({
|
|
81
|
+
path: `${base}.live`,
|
|
82
|
+
code: "required",
|
|
83
|
+
message: "A live destination identifier is required.",
|
|
84
|
+
});
|
|
85
|
+
} else {
|
|
86
|
+
for (const stage of ADAPTER_CONFIG_STAGES) {
|
|
87
|
+
if (stage === "live") continue;
|
|
88
|
+
if (value[stage] === value.live) {
|
|
89
|
+
details.push({
|
|
90
|
+
path: `${base}.${stage}`,
|
|
91
|
+
code: "live_destination_reused",
|
|
92
|
+
message:
|
|
93
|
+
"A non-live stage must use its own TEST destination, never the live one — " +
|
|
94
|
+
"otherwise preview and candidate traffic lands in the property that carries " +
|
|
95
|
+
"real reporting.",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (details.length > 0) return { ok: false, details };
|
|
102
|
+
return { ok: true, value };
|
|
103
|
+
}
|
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
MATERIALIZE_MODE_BINDING,
|
|
63
63
|
verifyConsentSnapshotSig,
|
|
64
64
|
} from "@tot/public-runtime";
|
|
65
|
+
import { readBakedManagedInstalls } from "@/lib/integration/bakedManagedInstalls";
|
|
65
66
|
import type {
|
|
66
67
|
CapabilityConfig,
|
|
67
68
|
ConsentSnapshot,
|
|
@@ -219,7 +220,20 @@ export async function runRenderChain(input: RenderChainInput): Promise<Response>
|
|
|
219
220
|
? publicationSource.publicationTimestamp
|
|
220
221
|
: exactPublicationTimestamp(await readEnv("TOT_PUBLICATION_TIMESTAMP"));
|
|
221
222
|
|
|
222
|
-
|
|
223
|
+
// Managed installs reach this render one of two ways, never both. The control plane
|
|
224
|
+
// reads them from D1 per request (`controlPlane.ts`). A PUBLIC render supplies none by
|
|
225
|
+
// design (ADR 0013: "baked at publish, never read per request on a public page") — so
|
|
226
|
+
// on the materialize render, which is a public render with no D1 at all, they arrive
|
|
227
|
+
// BAKED: resolved and signed once by the go-live Worker, carried in a materialize-only
|
|
228
|
+
// signed header. Without this the bundle serves a plan whose `integrations` is empty
|
|
229
|
+
// for a tenant that has an active install, and nothing anywhere reports the difference.
|
|
230
|
+
//
|
|
231
|
+
// Read here, before declarations are assembled below, because the resolved plan and its
|
|
232
|
+
// compiled CSP both depend on the full declaration set.
|
|
233
|
+
const managedIntegrationDeclarations =
|
|
234
|
+
input.managedIntegrationDeclarations.length > 0
|
|
235
|
+
? input.managedIntegrationDeclarations
|
|
236
|
+
: await readBakedManagedInstalls(request, tenant.tenant_id, readEnv);
|
|
223
237
|
|
|
224
238
|
// Digest-addressed static-bundle serving. Deliberately before the edge page
|
|
225
239
|
// cache/catalog/theme machinery. A selected publication bypasses this legacy
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.25",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
|
|
6
6
|
"packageManager": "pnpm@11.9.0",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"types": "./src/index.ts",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": "./src/index.ts",
|
|
12
|
-
"./blog-visibility": "./src/blog-visibility.mjs"
|
|
12
|
+
"./blog-visibility": "./src/blog-visibility.mjs",
|
|
13
|
+
"./integration/lifecycle-host-asset": "./src/integration/lifecycle-host-asset.mjs"
|
|
13
14
|
},
|
|
14
15
|
"scripts": {
|
|
15
16
|
"typecheck": "tsc --noEmit",
|
|
@@ -147,6 +147,85 @@
|
|
|
147
147
|
},
|
|
148
148
|
{
|
|
149
149
|
"type": "integer"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"type": "object",
|
|
153
|
+
"additionalProperties": false,
|
|
154
|
+
"minProperties": 1,
|
|
155
|
+
"properties": {
|
|
156
|
+
"candidate": {
|
|
157
|
+
"type": "string",
|
|
158
|
+
"not": {
|
|
159
|
+
"anyOf": [
|
|
160
|
+
{
|
|
161
|
+
"type": "string",
|
|
162
|
+
"pattern": "(?:[Gg][Tt][Mm]-[A-Za-z0-9]+|[Uu][Aa]-[0-9]+-[0-9]+)"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"type": "string",
|
|
166
|
+
"pattern": "(?:-----BEGIN(?: [A-Z]+)? PRIVATE KEY-----|\\bAKIA[0-9A-Z]{16}\\b|\\bsk-proj-[A-Za-z0-9_-]{8,}\\b|\\bsk-[A-Za-z0-9_-]{20,}\\b|\\b(?:sk|rk)_(?:live|test|prod)_[A-Za-z0-9_-]{8,}\\b|\\b(?:gh[pousr]_|github_pat_)[A-Za-z0-9_]{20,}\\b|\\b(?:npm_|glpat-)[A-Za-z0-9_.-]{8,}\\b|\\bxox[baprs]-[A-Za-z0-9-]{10,}\\b|\\bAIza[A-Za-z0-9_-]{20,}\\b|\\bSG\\.[A-Za-z0-9_.-]{8,}\\b|\\b(?:[Ss]ecret|[Pp]assword|[Cc]redential)[_-][A-Za-z0-9_-]{4,}\\b|\\b[Bb]earer\\s+[A-Za-z0-9._~+/-]{12,}={0,2}\\b|\\beyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]+\\b|\\b(?:api[_ -]?[Kk]ey|client[_ -]?[Ss]ecret|[Pp]assword|[Cc]redential|private[_ -]?[Kk]ey)\\s*[:=]\\s*\\S+)"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"type": "string",
|
|
170
|
+
"pattern": "(?:[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}|\\b[0-9]{3}-[0-9]{2}-[0-9]{4}\\b|\\b(?:[0-9][ -]?){13,19}\\b|(?:^|[^0-9])(?:\\+?1[ .-]?)?(?:\\([2-9][0-9]{2}\\)|[2-9][0-9]{2})[ .-]?[0-9]{3}[ .-]?[0-9]{4}(?=$|[^0-9]))"
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"type": "string",
|
|
174
|
+
"pattern": "(?:</?[A-Za-z][^>]*>|[Jj][Aa][Vv][Aa][Ss][Cc][Rr][Ii][Pp][Tt]\\s*:|[Ee][Vv][Aa][Ll]\\s*\\(|(?:[Aa][Ll][Ee][Rr][Tt]|[Ff][Ee][Tt][Cc][Hh])\\s*\\(|\\{\\{|\\}\\}|\\$\\{|#[A-Za-z_][A-Za-z0-9_-]*|(?:^|[\\s,>+~])\\.[A-Za-z_-][A-Za-z0-9_-]*(?:$|[\\s,>+~.#[\\:])|\\[[A-Za-z_:][-A-Za-z0-9_:.]*(?:[~|^$*]?=[^\\]]+)?\\]|(?:[Ll][Oo][Cc][Aa][Ll]|[Ss][Ee][Ss][Ss][Ii][Oo][Nn])[Ss][Tt][Oo][Rr][Aa][Gg][Ee]|[Ii][Nn][Dd][Ee][Xx][Ee][Dd][Dd][Bb]|[Qq][Uu][Ee][Rr][Yy][Ss][Ee][Ll][Ee][Cc][Tt][Oo][Rr]|(?:[Ww][Ii][Nn][Dd][Oo][Ww]|[Dd][Oo][Cc][Uu][Mm][Ee][Nn][Tt]|[Gg][Ll][Oo][Bb][Aa][Ll][Tt][Hh][Ii][Ss]|[Cc][Oo][Nn][Ss][Oo][Ll][Ee])\\s*(?:\\.|\\[)|[A-Za-z_$][A-Za-z0-9_$]*\\s*\\.\\s*[A-Za-z_$][A-Za-z0-9_$]*\\s*\\(|[A-Za-z_$][A-Za-z0-9_$]*\\s*\\([^)]*\\)\\s*;|[Rr][Ee][Tt][Uu][Rr][Nn][^;{}]*;|[Ff][Uu][Nn][Cc][Tt][Ii][Oo][Nn]|=>|(?:[Vv][Aa][Rr]|[Ll][Ee][Tt]|[Cc][Oo][Nn][Ss][Tt])\\s+[A-Za-z_$])"
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
},
|
|
178
|
+
"minLength": 1
|
|
179
|
+
},
|
|
180
|
+
"staged": {
|
|
181
|
+
"type": "string",
|
|
182
|
+
"not": {
|
|
183
|
+
"anyOf": [
|
|
184
|
+
{
|
|
185
|
+
"type": "string",
|
|
186
|
+
"pattern": "(?:[Gg][Tt][Mm]-[A-Za-z0-9]+|[Uu][Aa]-[0-9]+-[0-9]+)"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"type": "string",
|
|
190
|
+
"pattern": "(?:-----BEGIN(?: [A-Z]+)? PRIVATE KEY-----|\\bAKIA[0-9A-Z]{16}\\b|\\bsk-proj-[A-Za-z0-9_-]{8,}\\b|\\bsk-[A-Za-z0-9_-]{20,}\\b|\\b(?:sk|rk)_(?:live|test|prod)_[A-Za-z0-9_-]{8,}\\b|\\b(?:gh[pousr]_|github_pat_)[A-Za-z0-9_]{20,}\\b|\\b(?:npm_|glpat-)[A-Za-z0-9_.-]{8,}\\b|\\bxox[baprs]-[A-Za-z0-9-]{10,}\\b|\\bAIza[A-Za-z0-9_-]{20,}\\b|\\bSG\\.[A-Za-z0-9_.-]{8,}\\b|\\b(?:[Ss]ecret|[Pp]assword|[Cc]redential)[_-][A-Za-z0-9_-]{4,}\\b|\\b[Bb]earer\\s+[A-Za-z0-9._~+/-]{12,}={0,2}\\b|\\beyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]+\\b|\\b(?:api[_ -]?[Kk]ey|client[_ -]?[Ss]ecret|[Pp]assword|[Cc]redential|private[_ -]?[Kk]ey)\\s*[:=]\\s*\\S+)"
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"type": "string",
|
|
194
|
+
"pattern": "(?:[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}|\\b[0-9]{3}-[0-9]{2}-[0-9]{4}\\b|\\b(?:[0-9][ -]?){13,19}\\b|(?:^|[^0-9])(?:\\+?1[ .-]?)?(?:\\([2-9][0-9]{2}\\)|[2-9][0-9]{2})[ .-]?[0-9]{3}[ .-]?[0-9]{4}(?=$|[^0-9]))"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"type": "string",
|
|
198
|
+
"pattern": "(?:</?[A-Za-z][^>]*>|[Jj][Aa][Vv][Aa][Ss][Cc][Rr][Ii][Pp][Tt]\\s*:|[Ee][Vv][Aa][Ll]\\s*\\(|(?:[Aa][Ll][Ee][Rr][Tt]|[Ff][Ee][Tt][Cc][Hh])\\s*\\(|\\{\\{|\\}\\}|\\$\\{|#[A-Za-z_][A-Za-z0-9_-]*|(?:^|[\\s,>+~])\\.[A-Za-z_-][A-Za-z0-9_-]*(?:$|[\\s,>+~.#[\\:])|\\[[A-Za-z_:][-A-Za-z0-9_:.]*(?:[~|^$*]?=[^\\]]+)?\\]|(?:[Ll][Oo][Cc][Aa][Ll]|[Ss][Ee][Ss][Ss][Ii][Oo][Nn])[Ss][Tt][Oo][Rr][Aa][Gg][Ee]|[Ii][Nn][Dd][Ee][Xx][Ee][Dd][Dd][Bb]|[Qq][Uu][Ee][Rr][Yy][Ss][Ee][Ll][Ee][Cc][Tt][Oo][Rr]|(?:[Ww][Ii][Nn][Dd][Oo][Ww]|[Dd][Oo][Cc][Uu][Mm][Ee][Nn][Tt]|[Gg][Ll][Oo][Bb][Aa][Ll][Tt][Hh][Ii][Ss]|[Cc][Oo][Nn][Ss][Oo][Ll][Ee])\\s*(?:\\.|\\[)|[A-Za-z_$][A-Za-z0-9_$]*\\s*\\.\\s*[A-Za-z_$][A-Za-z0-9_$]*\\s*\\(|[A-Za-z_$][A-Za-z0-9_$]*\\s*\\([^)]*\\)\\s*;|[Rr][Ee][Tt][Uu][Rr][Nn][^;{}]*;|[Ff][Uu][Nn][Cc][Tt][Ii][Oo][Nn]|=>|(?:[Vv][Aa][Rr]|[Ll][Ee][Tt]|[Cc][Oo][Nn][Ss][Tt])\\s+[A-Za-z_$])"
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
},
|
|
202
|
+
"minLength": 1
|
|
203
|
+
},
|
|
204
|
+
"live": {
|
|
205
|
+
"type": "string",
|
|
206
|
+
"not": {
|
|
207
|
+
"anyOf": [
|
|
208
|
+
{
|
|
209
|
+
"type": "string",
|
|
210
|
+
"pattern": "(?:[Gg][Tt][Mm]-[A-Za-z0-9]+|[Uu][Aa]-[0-9]+-[0-9]+)"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"type": "string",
|
|
214
|
+
"pattern": "(?:-----BEGIN(?: [A-Z]+)? PRIVATE KEY-----|\\bAKIA[0-9A-Z]{16}\\b|\\bsk-proj-[A-Za-z0-9_-]{8,}\\b|\\bsk-[A-Za-z0-9_-]{20,}\\b|\\b(?:sk|rk)_(?:live|test|prod)_[A-Za-z0-9_-]{8,}\\b|\\b(?:gh[pousr]_|github_pat_)[A-Za-z0-9_]{20,}\\b|\\b(?:npm_|glpat-)[A-Za-z0-9_.-]{8,}\\b|\\bxox[baprs]-[A-Za-z0-9-]{10,}\\b|\\bAIza[A-Za-z0-9_-]{20,}\\b|\\bSG\\.[A-Za-z0-9_.-]{8,}\\b|\\b(?:[Ss]ecret|[Pp]assword|[Cc]redential)[_-][A-Za-z0-9_-]{4,}\\b|\\b[Bb]earer\\s+[A-Za-z0-9._~+/-]{12,}={0,2}\\b|\\beyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]+\\b|\\b(?:api[_ -]?[Kk]ey|client[_ -]?[Ss]ecret|[Pp]assword|[Cc]redential|private[_ -]?[Kk]ey)\\s*[:=]\\s*\\S+)"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"type": "string",
|
|
218
|
+
"pattern": "(?:[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}|\\b[0-9]{3}-[0-9]{2}-[0-9]{4}\\b|\\b(?:[0-9][ -]?){13,19}\\b|(?:^|[^0-9])(?:\\+?1[ .-]?)?(?:\\([2-9][0-9]{2}\\)|[2-9][0-9]{2})[ .-]?[0-9]{3}[ .-]?[0-9]{4}(?=$|[^0-9]))"
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"type": "string",
|
|
222
|
+
"pattern": "(?:</?[A-Za-z][^>]*>|[Jj][Aa][Vv][Aa][Ss][Cc][Rr][Ii][Pp][Tt]\\s*:|[Ee][Vv][Aa][Ll]\\s*\\(|(?:[Aa][Ll][Ee][Rr][Tt]|[Ff][Ee][Tt][Cc][Hh])\\s*\\(|\\{\\{|\\}\\}|\\$\\{|#[A-Za-z_][A-Za-z0-9_-]*|(?:^|[\\s,>+~])\\.[A-Za-z_-][A-Za-z0-9_-]*(?:$|[\\s,>+~.#[\\:])|\\[[A-Za-z_:][-A-Za-z0-9_:.]*(?:[~|^$*]?=[^\\]]+)?\\]|(?:[Ll][Oo][Cc][Aa][Ll]|[Ss][Ee][Ss][Ss][Ii][Oo][Nn])[Ss][Tt][Oo][Rr][Aa][Gg][Ee]|[Ii][Nn][Dd][Ee][Xx][Ee][Dd][Dd][Bb]|[Qq][Uu][Ee][Rr][Yy][Ss][Ee][Ll][Ee][Cc][Tt][Oo][Rr]|(?:[Ww][Ii][Nn][Dd][Oo][Ww]|[Dd][Oo][Cc][Uu][Mm][Ee][Nn][Tt]|[Gg][Ll][Oo][Bb][Aa][Ll][Tt][Hh][Ii][Ss]|[Cc][Oo][Nn][Ss][Oo][Ll][Ee])\\s*(?:\\.|\\[)|[A-Za-z_$][A-Za-z0-9_$]*\\s*\\.\\s*[A-Za-z_$][A-Za-z0-9_$]*\\s*\\(|[A-Za-z_$][A-Za-z0-9_$]*\\s*\\([^)]*\\)\\s*;|[Rr][Ee][Tt][Uu][Rr][Nn][^;{}]*;|[Ff][Uu][Nn][Cc][Tt][Ii][Oo][Nn]|=>|(?:[Vv][Aa][Rr]|[Ll][Ee][Tt]|[Cc][Oo][Nn][Ss][Tt])\\s+[A-Za-z_$])"
|
|
223
|
+
}
|
|
224
|
+
]
|
|
225
|
+
},
|
|
226
|
+
"minLength": 1
|
|
227
|
+
}
|
|
228
|
+
}
|
|
150
229
|
}
|
|
151
230
|
]
|
|
152
231
|
}
|
|
@@ -120,6 +120,85 @@
|
|
|
120
120
|
},
|
|
121
121
|
{
|
|
122
122
|
"type": "integer"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"type": "object",
|
|
126
|
+
"additionalProperties": false,
|
|
127
|
+
"minProperties": 1,
|
|
128
|
+
"properties": {
|
|
129
|
+
"candidate": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"not": {
|
|
132
|
+
"anyOf": [
|
|
133
|
+
{
|
|
134
|
+
"type": "string",
|
|
135
|
+
"pattern": "(?:[Gg][Tt][Mm]-[A-Za-z0-9]+|[Uu][Aa]-[0-9]+-[0-9]+)"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"type": "string",
|
|
139
|
+
"pattern": "(?:-----BEGIN(?: [A-Z]+)? PRIVATE KEY-----|\\bAKIA[0-9A-Z]{16}\\b|\\bsk-proj-[A-Za-z0-9_-]{8,}\\b|\\bsk-[A-Za-z0-9_-]{20,}\\b|\\b(?:sk|rk)_(?:live|test|prod)_[A-Za-z0-9_-]{8,}\\b|\\b(?:gh[pousr]_|github_pat_)[A-Za-z0-9_]{20,}\\b|\\b(?:npm_|glpat-)[A-Za-z0-9_.-]{8,}\\b|\\bxox[baprs]-[A-Za-z0-9-]{10,}\\b|\\bAIza[A-Za-z0-9_-]{20,}\\b|\\bSG\\.[A-Za-z0-9_.-]{8,}\\b|\\b(?:[Ss]ecret|[Pp]assword|[Cc]redential)[_-][A-Za-z0-9_-]{4,}\\b|\\b[Bb]earer\\s+[A-Za-z0-9._~+/-]{12,}={0,2}\\b|\\beyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]+\\b|\\b(?:api[_ -]?[Kk]ey|client[_ -]?[Ss]ecret|[Pp]assword|[Cc]redential|private[_ -]?[Kk]ey)\\s*[:=]\\s*\\S+)"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"type": "string",
|
|
143
|
+
"pattern": "(?:[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}|\\b[0-9]{3}-[0-9]{2}-[0-9]{4}\\b|\\b(?:[0-9][ -]?){13,19}\\b|(?:^|[^0-9])(?:\\+?1[ .-]?)?(?:\\([2-9][0-9]{2}\\)|[2-9][0-9]{2})[ .-]?[0-9]{3}[ .-]?[0-9]{4}(?=$|[^0-9]))"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"type": "string",
|
|
147
|
+
"pattern": "(?:</?[A-Za-z][^>]*>|[Jj][Aa][Vv][Aa][Ss][Cc][Rr][Ii][Pp][Tt]\\s*:|[Ee][Vv][Aa][Ll]\\s*\\(|(?:[Aa][Ll][Ee][Rr][Tt]|[Ff][Ee][Tt][Cc][Hh])\\s*\\(|\\{\\{|\\}\\}|\\$\\{|#[A-Za-z_][A-Za-z0-9_-]*|(?:^|[\\s,>+~])\\.[A-Za-z_-][A-Za-z0-9_-]*(?:$|[\\s,>+~.#[\\:])|\\[[A-Za-z_:][-A-Za-z0-9_:.]*(?:[~|^$*]?=[^\\]]+)?\\]|(?:[Ll][Oo][Cc][Aa][Ll]|[Ss][Ee][Ss][Ss][Ii][Oo][Nn])[Ss][Tt][Oo][Rr][Aa][Gg][Ee]|[Ii][Nn][Dd][Ee][Xx][Ee][Dd][Dd][Bb]|[Qq][Uu][Ee][Rr][Yy][Ss][Ee][Ll][Ee][Cc][Tt][Oo][Rr]|(?:[Ww][Ii][Nn][Dd][Oo][Ww]|[Dd][Oo][Cc][Uu][Mm][Ee][Nn][Tt]|[Gg][Ll][Oo][Bb][Aa][Ll][Tt][Hh][Ii][Ss]|[Cc][Oo][Nn][Ss][Oo][Ll][Ee])\\s*(?:\\.|\\[)|[A-Za-z_$][A-Za-z0-9_$]*\\s*\\.\\s*[A-Za-z_$][A-Za-z0-9_$]*\\s*\\(|[A-Za-z_$][A-Za-z0-9_$]*\\s*\\([^)]*\\)\\s*;|[Rr][Ee][Tt][Uu][Rr][Nn][^;{}]*;|[Ff][Uu][Nn][Cc][Tt][Ii][Oo][Nn]|=>|(?:[Vv][Aa][Rr]|[Ll][Ee][Tt]|[Cc][Oo][Nn][Ss][Tt])\\s+[A-Za-z_$])"
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
},
|
|
151
|
+
"minLength": 1
|
|
152
|
+
},
|
|
153
|
+
"staged": {
|
|
154
|
+
"type": "string",
|
|
155
|
+
"not": {
|
|
156
|
+
"anyOf": [
|
|
157
|
+
{
|
|
158
|
+
"type": "string",
|
|
159
|
+
"pattern": "(?:[Gg][Tt][Mm]-[A-Za-z0-9]+|[Uu][Aa]-[0-9]+-[0-9]+)"
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"type": "string",
|
|
163
|
+
"pattern": "(?:-----BEGIN(?: [A-Z]+)? PRIVATE KEY-----|\\bAKIA[0-9A-Z]{16}\\b|\\bsk-proj-[A-Za-z0-9_-]{8,}\\b|\\bsk-[A-Za-z0-9_-]{20,}\\b|\\b(?:sk|rk)_(?:live|test|prod)_[A-Za-z0-9_-]{8,}\\b|\\b(?:gh[pousr]_|github_pat_)[A-Za-z0-9_]{20,}\\b|\\b(?:npm_|glpat-)[A-Za-z0-9_.-]{8,}\\b|\\bxox[baprs]-[A-Za-z0-9-]{10,}\\b|\\bAIza[A-Za-z0-9_-]{20,}\\b|\\bSG\\.[A-Za-z0-9_.-]{8,}\\b|\\b(?:[Ss]ecret|[Pp]assword|[Cc]redential)[_-][A-Za-z0-9_-]{4,}\\b|\\b[Bb]earer\\s+[A-Za-z0-9._~+/-]{12,}={0,2}\\b|\\beyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]+\\b|\\b(?:api[_ -]?[Kk]ey|client[_ -]?[Ss]ecret|[Pp]assword|[Cc]redential|private[_ -]?[Kk]ey)\\s*[:=]\\s*\\S+)"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"type": "string",
|
|
167
|
+
"pattern": "(?:[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}|\\b[0-9]{3}-[0-9]{2}-[0-9]{4}\\b|\\b(?:[0-9][ -]?){13,19}\\b|(?:^|[^0-9])(?:\\+?1[ .-]?)?(?:\\([2-9][0-9]{2}\\)|[2-9][0-9]{2})[ .-]?[0-9]{3}[ .-]?[0-9]{4}(?=$|[^0-9]))"
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"type": "string",
|
|
171
|
+
"pattern": "(?:</?[A-Za-z][^>]*>|[Jj][Aa][Vv][Aa][Ss][Cc][Rr][Ii][Pp][Tt]\\s*:|[Ee][Vv][Aa][Ll]\\s*\\(|(?:[Aa][Ll][Ee][Rr][Tt]|[Ff][Ee][Tt][Cc][Hh])\\s*\\(|\\{\\{|\\}\\}|\\$\\{|#[A-Za-z_][A-Za-z0-9_-]*|(?:^|[\\s,>+~])\\.[A-Za-z_-][A-Za-z0-9_-]*(?:$|[\\s,>+~.#[\\:])|\\[[A-Za-z_:][-A-Za-z0-9_:.]*(?:[~|^$*]?=[^\\]]+)?\\]|(?:[Ll][Oo][Cc][Aa][Ll]|[Ss][Ee][Ss][Ss][Ii][Oo][Nn])[Ss][Tt][Oo][Rr][Aa][Gg][Ee]|[Ii][Nn][Dd][Ee][Xx][Ee][Dd][Dd][Bb]|[Qq][Uu][Ee][Rr][Yy][Ss][Ee][Ll][Ee][Cc][Tt][Oo][Rr]|(?:[Ww][Ii][Nn][Dd][Oo][Ww]|[Dd][Oo][Cc][Uu][Mm][Ee][Nn][Tt]|[Gg][Ll][Oo][Bb][Aa][Ll][Tt][Hh][Ii][Ss]|[Cc][Oo][Nn][Ss][Oo][Ll][Ee])\\s*(?:\\.|\\[)|[A-Za-z_$][A-Za-z0-9_$]*\\s*\\.\\s*[A-Za-z_$][A-Za-z0-9_$]*\\s*\\(|[A-Za-z_$][A-Za-z0-9_$]*\\s*\\([^)]*\\)\\s*;|[Rr][Ee][Tt][Uu][Rr][Nn][^;{}]*;|[Ff][Uu][Nn][Cc][Tt][Ii][Oo][Nn]|=>|(?:[Vv][Aa][Rr]|[Ll][Ee][Tt]|[Cc][Oo][Nn][Ss][Tt])\\s+[A-Za-z_$])"
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
},
|
|
175
|
+
"minLength": 1
|
|
176
|
+
},
|
|
177
|
+
"live": {
|
|
178
|
+
"type": "string",
|
|
179
|
+
"not": {
|
|
180
|
+
"anyOf": [
|
|
181
|
+
{
|
|
182
|
+
"type": "string",
|
|
183
|
+
"pattern": "(?:[Gg][Tt][Mm]-[A-Za-z0-9]+|[Uu][Aa]-[0-9]+-[0-9]+)"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"type": "string",
|
|
187
|
+
"pattern": "(?:-----BEGIN(?: [A-Z]+)? PRIVATE KEY-----|\\bAKIA[0-9A-Z]{16}\\b|\\bsk-proj-[A-Za-z0-9_-]{8,}\\b|\\bsk-[A-Za-z0-9_-]{20,}\\b|\\b(?:sk|rk)_(?:live|test|prod)_[A-Za-z0-9_-]{8,}\\b|\\b(?:gh[pousr]_|github_pat_)[A-Za-z0-9_]{20,}\\b|\\b(?:npm_|glpat-)[A-Za-z0-9_.-]{8,}\\b|\\bxox[baprs]-[A-Za-z0-9-]{10,}\\b|\\bAIza[A-Za-z0-9_-]{20,}\\b|\\bSG\\.[A-Za-z0-9_.-]{8,}\\b|\\b(?:[Ss]ecret|[Pp]assword|[Cc]redential)[_-][A-Za-z0-9_-]{4,}\\b|\\b[Bb]earer\\s+[A-Za-z0-9._~+/-]{12,}={0,2}\\b|\\beyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]+\\b|\\b(?:api[_ -]?[Kk]ey|client[_ -]?[Ss]ecret|[Pp]assword|[Cc]redential|private[_ -]?[Kk]ey)\\s*[:=]\\s*\\S+)"
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"type": "string",
|
|
191
|
+
"pattern": "(?:[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}|\\b[0-9]{3}-[0-9]{2}-[0-9]{4}\\b|\\b(?:[0-9][ -]?){13,19}\\b|(?:^|[^0-9])(?:\\+?1[ .-]?)?(?:\\([2-9][0-9]{2}\\)|[2-9][0-9]{2})[ .-]?[0-9]{3}[ .-]?[0-9]{4}(?=$|[^0-9]))"
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"type": "string",
|
|
195
|
+
"pattern": "(?:</?[A-Za-z][^>]*>|[Jj][Aa][Vv][Aa][Ss][Cc][Rr][Ii][Pp][Tt]\\s*:|[Ee][Vv][Aa][Ll]\\s*\\(|(?:[Aa][Ll][Ee][Rr][Tt]|[Ff][Ee][Tt][Cc][Hh])\\s*\\(|\\{\\{|\\}\\}|\\$\\{|#[A-Za-z_][A-Za-z0-9_-]*|(?:^|[\\s,>+~])\\.[A-Za-z_-][A-Za-z0-9_-]*(?:$|[\\s,>+~.#[\\:])|\\[[A-Za-z_:][-A-Za-z0-9_:.]*(?:[~|^$*]?=[^\\]]+)?\\]|(?:[Ll][Oo][Cc][Aa][Ll]|[Ss][Ee][Ss][Ss][Ii][Oo][Nn])[Ss][Tt][Oo][Rr][Aa][Gg][Ee]|[Ii][Nn][Dd][Ee][Xx][Ee][Dd][Dd][Bb]|[Qq][Uu][Ee][Rr][Yy][Ss][Ee][Ll][Ee][Cc][Tt][Oo][Rr]|(?:[Ww][Ii][Nn][Dd][Oo][Ww]|[Dd][Oo][Cc][Uu][Mm][Ee][Nn][Tt]|[Gg][Ll][Oo][Bb][Aa][Ll][Tt][Hh][Ii][Ss]|[Cc][Oo][Nn][Ss][Oo][Ll][Ee])\\s*(?:\\.|\\[)|[A-Za-z_$][A-Za-z0-9_$]*\\s*\\.\\s*[A-Za-z_$][A-Za-z0-9_$]*\\s*\\(|[A-Za-z_$][A-Za-z0-9_$]*\\s*\\([^)]*\\)\\s*;|[Rr][Ee][Tt][Uu][Rr][Nn][^;{}]*;|[Ff][Uu][Nn][Cc][Tt][Ii][Oo][Nn]|=>|(?:[Vv][Aa][Rr]|[Ll][Ee][Tt]|[Cc][Oo][Nn][Ss][Tt])\\s+[A-Za-z_$])"
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
"minLength": 1
|
|
200
|
+
}
|
|
201
|
+
}
|
|
123
202
|
}
|
|
124
203
|
]
|
|
125
204
|
}
|