Skip to content

Commit b816b5c

Browse files
V40 Promotion: Harden staging URL evidence checks
Replace substring-based Supabase REST URL evidence checks with parsed exact URL matching so canonical V40 reports validate the staging endpoint without admitting arbitrary host prefixes or suffixes.\n\nProof: generate-v40-ledger-storage-sync --check; generate-v40-local-staging-rehearsal-automation --check; focused V40 ledger/local staging protocol tests; check-v40-gate11 promoted mode; protocol typecheck; git diff --check.
1 parent 9420bec commit b816b5c

2 files changed

Lines changed: 56 additions & 4 deletions

File tree

packages/protocol/src/canonical/v40-ledger-storage-sync.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ const SOURCE_ROOTS = Object.freeze({
8989
canonWorkflow: '.github/workflows/bitcode-canon-quality.yml',
9090
});
9191

92+
const STAGING_SUPABASE_PROJECT_REF = 'tkpyosihuouusyaxtbau';
93+
const STAGING_SUPABASE_REST_URL = new URL(
94+
`https://${STAGING_SUPABASE_PROJECT_REF}.supabase.co/rest/v1/`,
95+
);
96+
const HTTPS_URL_LITERAL_PATTERN = /https:\/\/[^\s'"`<>)]*/g;
97+
9298
function digest(value) {
9399
return crypto.createHash('sha256').update(value).digest('hex').slice(0, 24);
94100
}
@@ -110,6 +116,26 @@ function predicateResult(id, sourcePath, passed) {
110116
return { id, sourcePath, passed: Boolean(passed) };
111117
}
112118

119+
function sourceContainsExactHttpsUrl(source, expectedUrl) {
120+
return Array.from(source.matchAll(HTTPS_URL_LITERAL_PATTERN)).some((match) => {
121+
try {
122+
const parsed = new URL(match[0]);
123+
return (
124+
parsed.protocol === expectedUrl.protocol &&
125+
parsed.username === '' &&
126+
parsed.password === '' &&
127+
parsed.hostname === expectedUrl.hostname &&
128+
parsed.port === expectedUrl.port &&
129+
parsed.pathname === expectedUrl.pathname &&
130+
parsed.search === expectedUrl.search &&
131+
parsed.hash === expectedUrl.hash
132+
);
133+
} catch {
134+
return false;
135+
}
136+
});
137+
}
138+
113139
function row(input) {
114140
return {
115141
...input,
@@ -324,8 +350,8 @@ function buildPredicateResults(repoRoot) {
324350
'local-staging-sync-readback-covered',
325351
SOURCE_ROOTS.localStagingRehearsal,
326352
sources.localStagingRehearsal.includes('ledgerDatabaseStorageSynchronized') &&
327-
sources.localStagingRehearsal.includes('tkpyosihuouusyaxtbau') &&
328-
sources.localStagingRehearsal.includes('https://tkpyosihuouusyaxtbau.supabase.co/rest/v1/'),
353+
sources.localStagingRehearsal.includes(STAGING_SUPABASE_PROJECT_REF) &&
354+
sourceContainsExactHttpsUrl(sources.localStagingRehearsal, STAGING_SUPABASE_REST_URL),
329355
),
330356
predicateResult(
331357
'uapi-contract-covers-ledger-storage-sync',

packages/protocol/src/canonical/v40-local-staging-rehearsal-automation.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ const SOURCE_ROOTS = Object.freeze({
8888
canonWorkflow: '.github/workflows/bitcode-canon-quality.yml',
8989
});
9090

91+
const STAGING_SUPABASE_PROJECT_REF = 'tkpyosihuouusyaxtbau';
92+
const STAGING_SUPABASE_REST_URL = new URL(
93+
`https://${STAGING_SUPABASE_PROJECT_REF}.supabase.co/rest/v1/`,
94+
);
95+
const HTTPS_URL_LITERAL_PATTERN = /https:\/\/[^\s'"`<>)]*/g;
96+
9197
function digest(value) {
9298
return crypto.createHash('sha256').update(value).digest('hex').slice(0, 24);
9399
}
@@ -109,6 +115,26 @@ function predicateResult(id, sourcePath, passed) {
109115
return { id, sourcePath, passed: Boolean(passed) };
110116
}
111117

118+
function sourceContainsExactHttpsUrl(source, expectedUrl) {
119+
return Array.from(source.matchAll(HTTPS_URL_LITERAL_PATTERN)).some((match) => {
120+
try {
121+
const parsed = new URL(match[0]);
122+
return (
123+
parsed.protocol === expectedUrl.protocol &&
124+
parsed.username === '' &&
125+
parsed.password === '' &&
126+
parsed.hostname === expectedUrl.hostname &&
127+
parsed.port === expectedUrl.port &&
128+
parsed.pathname === expectedUrl.pathname &&
129+
parsed.search === expectedUrl.search &&
130+
parsed.hash === expectedUrl.hash
131+
);
132+
} catch {
133+
return false;
134+
}
135+
});
136+
}
137+
112138
function row(input) {
113139
return {
114140
...input,
@@ -283,8 +309,8 @@ function buildPredicateResults(repoRoot) {
283309
predicateResult(
284310
'operator-script-binds-staging-project',
285311
SOURCE_ROOTS.operatorScript,
286-
sources.operatorScript.includes('tkpyosihuouusyaxtbau') &&
287-
sources.operatorScript.includes('https://tkpyosihuouusyaxtbau.supabase.co/rest/v1/') &&
312+
sources.operatorScript.includes(STAGING_SUPABASE_PROJECT_REF) &&
313+
sourceContainsExactHttpsUrl(sources.operatorScript, STAGING_SUPABASE_REST_URL) &&
288314
sources.operatorScript.includes('staging-testnet-real-inference'),
289315
),
290316
predicateResult(

0 commit comments

Comments
 (0)