Skip to content

Commit bdff590

Browse files
Pigbibicodex
andcommitted
fix: Redact live Worker identities from preflight evidence
Co-Authored-By: Codex <noreply@openai.com>
1 parent 1cede1e commit bdff590

2 files changed

Lines changed: 37 additions & 16 deletions

File tree

‎scripts/console_m09_release.mjs‎

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,16 @@ async function inspectBackendRelease(api, referenceHash) {
8181
const initial=await api.deployments();
8282
const traffic=initial?.deployments?.[0]?.versions;
8383
if (Array.isArray(traffic) && traffic.length<=10 && traffic.every(v=>uuid(v?.version_id)&&Number.isFinite(v?.percentage)))
84-
result.traffic=traffic.map(v=>({version_prefix:v.version_id.slice(0,8),percentage:v.percentage}));
84+
result.traffic=traffic.map((v,index)=>({slot:index+1,percentage:v.percentage}));
8585
const prior=activeIdentity(initial);
86-
result.active={deployment_prefix:prior.deployment.slice(0,8),version_prefix:prior.version.slice(0,8)};
86+
result.single_active_version=true;
8787
stage='version_list';
8888
const listed=versions(await api.versions());
8989
result.active_listed=listed.includes(prior.version);
9090
result.active_is_latest=listed[0]===prior.version;
9191
stage='module';
9292
const bytes=readModule(await api.betaVersion(prior.version),prior.version);
93-
result.module_sha256=createHash('sha256').update(bytes).digest('hex');
94-
result.module_matches_reference=result.module_sha256===referenceHash;
93+
result.module_matches_reference=hash(bytes)===referenceHash;
9594
stage='configuration';
9695
const live=readConfiguration(await api.scriptVersion(prior.version),prior.version);
9796
const runtime=live.script_runtime;
@@ -109,19 +108,33 @@ async function inspectBackendRelease(api, referenceHash) {
109108
stage='recheck';
110109
if (JSON.stringify(activeIdentity(await api.deployments()))!==JSON.stringify(prior) ||
111110
JSON.stringify(versions(await api.versions()))!==JSON.stringify(listed)) throw Error('active_drift');
112-
return {result:{...result,ok:true},version:prior.version};
111+
return {result:{...result,ok:true,active_identity_unchanged:true},version:prior.version};
113112
} catch {
114113
return {result:{...result,ok:false,stage}};
115114
}
116115
}
117116

118-
export async function preflight(api) {
117+
export function readRollbackTargets(git) {
118+
const workflow=git('show','HEAD:.github/workflows/deploy-strategy-switch-console.yml');
119+
const fixed=workflow.split(' - name: Restore fixed pre-release Worker version');
120+
if(fixed.length!==2)throw Error('rollback_policy');
121+
const target=pattern=>{
122+
const matches=[...fixed[1].matchAll(pattern)];
123+
if(matches.length!==1||!uuid(matches[0][1]))throw Error('rollback_policy');
124+
return matches[0][1];
125+
};
126+
return {
127+
fixed:[target(/const target = process\.env\.ROLLBACK_VERSION \|\| '([^']+)';/g),
128+
target(/const fe11 = target === '([^']+)';/g)],
129+
fixedSuccessor:target(/const dailyRestore = target === '([^']+)';/g),
130+
};
131+
}
132+
export async function preflight(api,rollbackTargets={fixed:[],fixedSuccessor:null}) {
119133
const {result,version}=await inspectBackendRelease(api,POLICY.before);
120134
if(!result.ok)return result;
121-
// Only the first two fixed rollback targets can follow an arbitrary deployment.
122-
// The daily restore target requires an unrelated fixed successor deployment.
123-
const route=['2beea000-cce5-43a5-9255-9724f7318e83','c8dc4852-fa9d-4d14-bca3-7941587a494f'].includes(version)
124-
? 'fixed_target' : version==='06043e24-f8c9-4129-8c59-abb9957217bf' ? 'fixed_successor_only' : 'no_matching_target';
135+
// The daily restore target requires a specific, unrelated successor deployment.
136+
const route=rollbackTargets.fixed.includes(version) ? 'fixed_target'
137+
: version===rollbackTargets.fixedSuccessor ? 'fixed_successor_only' : 'no_matching_target';
125138
return {...result,source:POLICY.source,candidate_sha256:POLICY.candidate,reference_sha256:POLICY.before,
126139
rollback:{route,prior_version_readable:true,write_rehearsed:false,
127140
eligible_after_release:route==='fixed_target'&&result.active_listed&&result.module_matches_reference&&result.configuration.migration_tag_present},
@@ -140,7 +153,7 @@ async function main() {
140153
evidence.source!==POLICY.source || evidence.tree!==POLICY.tree || evidence.frontendBlob!==POLICY.frontendBlob)
141154
throw Error('provenance');
142155
if (action==='preflight') {
143-
const result=await preflight(createPreflightApi());
156+
const result=await preflight(createPreflightApi(),readRollbackTargets(git));
144157
console.log(JSON.stringify(result));
145158
if(!result.ok)process.exitCode=1;
146159
return;

‎tests/console_m09_release.test.mjs‎

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {test} from 'node:test';
22
import assert from 'node:assert/strict';
33
import {createHash} from 'node:crypto';
4-
import {spawnSync} from 'node:child_process';
5-
import {POLICY,createPreflightApi,pinned,preflight,release} from '../scripts/console_m09_release.mjs';
4+
import {execFileSync,spawnSync} from 'node:child_process';
5+
import {POLICY,createPreflightApi,pinned,preflight,readRollbackTargets,release} from '../scripts/console_m09_release.mjs';
66
const hash=x=>createHash('sha256').update(x).digest('hex');
77
const old=Buffer.from('old'),candidate=Buffer.from('candidate');
88
const version='aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',deployment='bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb';
@@ -48,14 +48,14 @@ test('preflight reads active state with write pins unset and returns redacted ev
4848
const f=fixture();
4949
const result=await preflight(f.api);
5050
assert.equal(result.ok,true);
51-
assert.equal(result.active.version_prefix,version.slice(0,8));
52-
assert.equal(result.module_sha256,hash(old));
51+
assert.deepEqual(result.traffic,[{slot:1,percentage:100}]);
52+
assert.equal(result.active_identity_unchanged,true);
5353
assert.equal(result.module_matches_reference,false);
5454
assert.equal(result.configuration.bindings.secret_text,1);
5555
assert.equal(result.configuration.candidate_provider_configuration_observed,false);
5656
assert.equal(result.rollback.eligible_after_release,false);
5757
assert.equal(result.hold,true);
58-
assert.doesNotMatch(JSON.stringify(result),/namespace_id|STRATEGY_SWITCH_CONFIG|SECRET|aaaaaaaa-aaaa/);
58+
assert.doesNotMatch(JSON.stringify(result),/namespace_id|STRATEGY_SWITCH_CONFIG|SECRET|aaaaaaaa|bbbbbbbb|module_sha256/);
5959
assert.deepEqual(f.calls,['deployments','versions','module','config','deployments','versions']);
6060
});
6161

@@ -87,3 +87,11 @@ test('preflight adapter exposes only the fixed read methods',async()=>{
8787
assert.equal((await preflight(api)).ok,true);
8888
assert.deepEqual(f.calls,['deployments','versions','module','config','deployments','versions']);
8989
});
90+
91+
test('preflight derives rollback classes from the existing workflow without emitting targets',async()=>{
92+
const targets=readRollbackTargets((...args)=>execFileSync('git',args,{encoding:'utf8'}));
93+
assert.equal(new Set([...targets.fixed,targets.fixedSuccessor]).size,3);
94+
const result=await preflight(fixture().api,targets);
95+
assert.ok([...targets.fixed,targets.fixedSuccessor].every(id=>!JSON.stringify(result).includes(id)));
96+
assert.throws(()=>readRollbackTargets(()=>''),/rollback_policy/);
97+
});

0 commit comments

Comments
 (0)