Skip to content

Commit b9e09b0

Browse files
authored
Revert "fix(cloud-agent-next): canary managed SCM containment" (#3876)
Revert "fix(cloud-agent-next): canary managed SCM containment (#3805)" This reverts commit 32f26ca.
1 parent 9b81ba1 commit b9e09b0

38 files changed

Lines changed: 343 additions & 4746 deletions

dev/local/env-sync/plan.test.ts

Lines changed: 0 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -57,39 +57,6 @@ function computeCloudAgentNextPlan(root: string) {
5757
return plan;
5858
}
5959

60-
const gitTokenServiceDir = 'services/git-token-service';
61-
const annotatedCapabilitySecretBinding = `{
62-
"binding": "SCM_SESSION_CAPABILITY_ENCRYPTION_KEY",
63-
"store_id": "store-id",
64-
// @dev-generate base64 32
65-
"secret_name": "SCM_SESSION_CAPABILITY_ENCRYPTION_KEY_DEV"
66-
}`;
67-
68-
function createGitTokenServiceRepo(options: {
69-
envLocal?: string;
70-
devSecretBinding?: string;
71-
rootSecretBinding?: string;
72-
}): TestRepo {
73-
const rootSecrets = options.rootSecretBinding
74-
? `"secrets_store_secrets": [${options.rootSecretBinding}],`
75-
: '';
76-
return createRepo({
77-
'.env.local': options.envLocal ?? '',
78-
[`${gitTokenServiceDir}/package.json`]: JSON.stringify({
79-
scripts: { dev: 'wrangler dev --env dev' },
80-
}),
81-
[`${gitTokenServiceDir}/.dev.vars.example`]: '',
82-
[`${gitTokenServiceDir}/wrangler.jsonc`]: `{
83-
${rootSecrets}
84-
"env": {
85-
"dev": {
86-
"secrets_store_secrets": [${options.devSecretBinding ?? annotatedCapabilitySecretBinding}]
87-
}
88-
}
89-
}`,
90-
});
91-
}
92-
9360
function withFakePnpm(output: string, fn: () => void): void {
9461
const binDir = fs.mkdtempSync(path.join(os.tmpdir(), 'env-sync-bin-'));
9562
const oldPath = process.env.PATH;
@@ -294,132 +261,6 @@ test('writes example defaults to .dev.vars when they override wrangler vars', ()
294261
}
295262
});
296263

297-
test('generates an annotated missing secret directly into the local Secrets Store', () => {
298-
const repo = createGitTokenServiceRepo({});
299-
try {
300-
withFakePnpm('', () => {
301-
const plan = computePlan(repo.root, new Set(['cloudflare-git-token-service']));
302-
assert.equal(plan.missingEnvLocal, false);
303-
assert.deepEqual(plan.devVarsChanges, []);
304-
assert.deepEqual(plan.envLocalAutoCreates, []);
305-
assert.deepEqual(plan.secretStoreWarnings, []);
306-
assert.equal(plan.secretStoreAutoCreates.length, 1);
307-
const [create] = plan.secretStoreAutoCreates;
308-
assert.ok(create);
309-
assert.equal(create.binding.secret_name, 'SCM_SESSION_CAPABILITY_ENCRYPTION_KEY_DEV');
310-
assert.equal(create.sourceKey, '@dev-generate base64 32');
311-
assert.equal(Buffer.from(create.value, 'base64').length, 32);
312-
});
313-
} finally {
314-
repo.cleanup();
315-
}
316-
});
317-
318-
test('generates an annotated secret instead of copying a plaintext env source', () => {
319-
const repo = createGitTokenServiceRepo({
320-
envLocal: 'SCM_SESSION_CAPABILITY_ENCRYPTION_KEY=plaintext-source\n',
321-
});
322-
try {
323-
withFakePnpm('', () => {
324-
const plan = computePlan(repo.root, new Set(['cloudflare-git-token-service']));
325-
const [create] = plan.secretStoreAutoCreates;
326-
assert.ok(create);
327-
assert.equal(create.sourceKey, '@dev-generate base64 32');
328-
assert.notEqual(create.value, 'plaintext-source');
329-
assert.equal(Buffer.from(create.value, 'base64').length, 32);
330-
});
331-
} finally {
332-
repo.cleanup();
333-
}
334-
});
335-
336-
test('applies generation only to the annotated Secrets Store binding', () => {
337-
const repo = createGitTokenServiceRepo({
338-
rootSecretBinding: `{
339-
"binding": "SHARED_SECRET",
340-
"store_id": "shared-store",
341-
// @dev-generate base64 32
342-
"secret_name": "SHARED_SECRET"
343-
}`,
344-
devSecretBinding: `{
345-
"binding": "SHARED_SECRET",
346-
"store_id": "shared-store",
347-
"secret_name": "SHARED_SECRET"
348-
}`,
349-
});
350-
try {
351-
withFakePnpm('', () => {
352-
const plan = computePlan(repo.root, new Set(['cloudflare-git-token-service']));
353-
assert.deepEqual(plan.secretStoreAutoCreates, []);
354-
assert.deepEqual(plan.secretStoreWarnings, [
355-
{
356-
workerDir: 'services/git-token-service',
357-
bindings: [
358-
{
359-
binding: 'SHARED_SECRET',
360-
store_id: 'shared-store',
361-
secret_name: 'SHARED_SECRET',
362-
},
363-
],
364-
},
365-
]);
366-
});
367-
} finally {
368-
repo.cleanup();
369-
}
370-
});
371-
372-
test('rejects malformed Secrets Store generation annotations', () => {
373-
const repo = createGitTokenServiceRepo({
374-
devSecretBinding: `{
375-
"binding": "SCM_SESSION_CAPABILITY_ENCRYPTION_KEY",
376-
"store_id": "store-id",
377-
// @dev-generate base64 nope
378-
"secret_name": "SCM_SESSION_CAPABILITY_ENCRYPTION_KEY_DEV"
379-
}`,
380-
});
381-
try {
382-
assert.throws(
383-
() => computePlan(repo.root, new Set(['cloudflare-git-token-service'])),
384-
/Invalid @dev-generate directive/
385-
);
386-
} finally {
387-
repo.cleanup();
388-
}
389-
});
390-
391-
test('rejects reserved generated-secret metadata in source Wrangler config', () => {
392-
const repo = createGitTokenServiceRepo({
393-
devSecretBinding: `{
394-
"binding": "SCM_SESSION_CAPABILITY_ENCRYPTION_KEY",
395-
"store_id": "store-id",
396-
"__kilo\\u005fdev_generated_base64_bytes_0": 4096,
397-
"secret_name": "SCM_SESSION_CAPABILITY_ENCRYPTION_KEY_DEV"
398-
}`,
399-
});
400-
try {
401-
assert.throws(
402-
() => computePlan(repo.root, new Set(['cloudflare-git-token-service'])),
403-
/reserved for generated-secret metadata/
404-
);
405-
} finally {
406-
repo.cleanup();
407-
}
408-
});
409-
410-
test('preserves an existing annotated local Secrets Store secret', () => {
411-
const repo = createGitTokenServiceRepo({});
412-
try {
413-
withFakePnpm('SCM_SESSION_CAPABILITY_ENCRYPTION_KEY_DEV\n', () => {
414-
const plan = computePlan(repo.root, new Set(['cloudflare-git-token-service']));
415-
assert.deepEqual(plan.secretStoreAutoCreates, []);
416-
assert.deepEqual(plan.secretStoreWarnings, []);
417-
});
418-
} finally {
419-
repo.cleanup();
420-
}
421-
});
422-
423264
test('auto-creates event-service NEXTAUTH Secrets Store binding from .env.local', () => {
424265
const repo = createRepo({
425266
'.env.local': 'NEXTAUTH_SECRET=local-nextauth-secret\n',

dev/local/env-sync/plan.ts

Lines changed: 5 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { spawnSync } from 'node:child_process';
2-
import { randomBytes } from 'node:crypto';
32
import * as fs from 'node:fs';
43
import * as os from 'node:os';
54
import * as path from 'node:path';
@@ -220,124 +219,12 @@ function isProvidedByWranglerVars(
220219
return entry.defaultValue === wranglerValue;
221220
}
222221

223-
const DEV_GENERATED_BASE64_BYTES_FIELD_PREFIX = '__kilo_dev_generated_base64_bytes_';
224-
const MAX_DEV_GENERATED_SECRET_BYTES = 1024;
225-
226-
function injectDevSecretGenerationMetadata(content: string): string {
227-
let directiveIndex = 0;
228-
const transformed = content.replace(
229-
/^(\s*)\/\/\s*@dev-generate([^\r\n]*)$/gm,
230-
(_line, indent, args: string) => {
231-
const directive = args.trim().match(/^base64\s+(\d+)$/);
232-
if (!directive) {
233-
throw new Error(
234-
'Invalid @dev-generate directive; expected `// @dev-generate base64 <bytes>`'
235-
);
236-
}
237-
238-
const bytes = Number(directive[1]);
239-
if (!Number.isSafeInteger(bytes) || bytes < 1 || bytes > MAX_DEV_GENERATED_SECRET_BYTES) {
240-
throw new Error(
241-
`@dev-generate bytes must be between 1 and ${MAX_DEV_GENERATED_SECRET_BYTES}`
242-
);
243-
}
244-
245-
return `${indent}"${DEV_GENERATED_BASE64_BYTES_FIELD_PREFIX}${directiveIndex++}": ${bytes},`;
246-
}
247-
);
248-
249-
if (/\/\/\s*@dev-generate/.test(transformed)) {
250-
throw new Error('@dev-generate must be a standalone comment inside a Secrets Store binding');
251-
}
252-
return transformed;
253-
}
254-
255-
function rejectReservedDevSecretGenerationMetadata(value: unknown): void {
256-
if (Array.isArray(value)) {
257-
for (const item of value) rejectReservedDevSecretGenerationMetadata(item);
258-
return;
259-
}
260-
if (!isJsonObject(value)) return;
261-
262-
if (Object.keys(value).some(key => key.startsWith(DEV_GENERATED_BASE64_BYTES_FIELD_PREFIX))) {
263-
throw new Error('@dev-generate field prefix is reserved for generated-secret metadata');
264-
}
265-
for (const nestedValue of Object.values(value))
266-
rejectReservedDevSecretGenerationMetadata(nestedValue);
267-
}
268-
269-
function getDevGeneratedBase64Bytes(value: JsonObject): number | undefined {
270-
const metadata = Object.entries(value).filter(([key]) =>
271-
key.startsWith(DEV_GENERATED_BASE64_BYTES_FIELD_PREFIX)
272-
);
273-
if (metadata.length === 0) return undefined;
274-
if (metadata.length > 1) {
275-
throw new Error('A Secrets Store binding can have only one @dev-generate directive');
276-
}
277-
278-
const bytes = metadata[0]?.[1];
279-
if (
280-
typeof bytes !== 'number' ||
281-
!Number.isSafeInteger(bytes) ||
282-
bytes < 1 ||
283-
bytes > MAX_DEV_GENERATED_SECRET_BYTES
284-
) {
285-
throw new Error('Invalid @dev-generate metadata');
286-
}
287-
return bytes;
288-
}
289-
290-
function validateDevSecretGenerationMetadata(value: unknown): void {
291-
if (Array.isArray(value)) {
292-
for (const item of value) validateDevSecretGenerationMetadata(item);
293-
return;
294-
}
295-
if (!isJsonObject(value)) return;
296-
297-
if (getDevGeneratedBase64Bytes(value) !== undefined) {
298-
if (
299-
typeof value.binding !== 'string' ||
300-
typeof value.store_id !== 'string' ||
301-
typeof value.secret_name !== 'string'
302-
) {
303-
throw new Error('@dev-generate must annotate a Secrets Store binding object');
304-
}
305-
}
306-
for (const nestedValue of Object.values(value)) validateDevSecretGenerationMetadata(nestedValue);
307-
}
308-
309222
function extractSecretsStoreBindings(repoRoot: string, workerDir: string): SecretStoreBinding[] {
310-
const wranglerPath = path.join(repoRoot, workerDir, 'wrangler.jsonc');
311-
if (!fs.existsSync(wranglerPath)) return [];
312-
313-
const content = fs.readFileSync(wranglerPath, 'utf-8');
314-
try {
315-
const originalConfig = parseJsonc(content);
316-
if (!isJsonObject(originalConfig)) return [];
317-
rejectReservedDevSecretGenerationMetadata(originalConfig);
318-
} catch (error) {
319-
if (error instanceof Error && error.message.includes('@dev-generate')) throw error;
320-
return [];
321-
}
322-
323-
let annotatedConfig: JsonObject;
324-
try {
325-
const parsed = parseJsonc(injectDevSecretGenerationMetadata(content));
326-
if (!isJsonObject(parsed)) return [];
327-
validateDevSecretGenerationMetadata(parsed);
328-
annotatedConfig = parsed;
329-
} catch (error) {
330-
if (error instanceof Error && error.message.includes('@dev-generate')) throw error;
331-
if (content.includes('@dev-generate')) {
332-
throw new Error('@dev-generate must annotate a Secrets Store binding object', {
333-
cause: error,
334-
});
335-
}
336-
return [];
337-
}
223+
const config = readWranglerConfig(repoRoot, workerDir);
224+
if (!config) return [];
338225

339226
const envName = detectWranglerEnv(repoRoot, workerDir);
340-
const secretsSection = getWranglerSection(annotatedConfig, envName, 'secrets_store_secrets');
227+
const secretsSection = getWranglerSection(config, envName, 'secrets_store_secrets');
341228
if (!Array.isArray(secretsSection)) return [];
342229

343230
const bindings: SecretStoreBinding[] = [];
@@ -353,13 +240,7 @@ function extractSecretsStoreBindings(repoRoot: string, workerDir: string): Secre
353240
) {
354241
continue;
355242
}
356-
const generatedBytes = getDevGeneratedBase64Bytes(secret);
357-
bindings.push({
358-
binding,
359-
store_id: storeId,
360-
secret_name: secretName,
361-
...(typeof generatedBytes === 'number' ? { devGeneratedBase64Bytes: generatedBytes } : {}),
362-
});
243+
bindings.push({ binding, store_id: storeId, secret_name: secretName });
363244
}
364245
return bindings;
365246
}
@@ -689,17 +570,8 @@ function computePlan(repoRoot: string, serviceFilter?: Set<string>): EnvSyncPlan
689570
continue; // Secret exists, nothing to do
690571
}
691572

692-
if (b.devGeneratedBase64Bytes) {
693-
secretStoreAutoCreates.push({
694-
workerDir: svc.dir,
695-
binding: b,
696-
sourceKey: `@dev-generate base64 ${b.devGeneratedBase64Bytes}`,
697-
value: randomBytes(b.devGeneratedBase64Bytes).toString('base64'),
698-
});
699-
continue;
700-
}
701-
702573
const source = resolveSecretStoreSource(b.secret_name, envLocal, localSecretSources);
574+
703575
if (source) {
704576
// Can auto-create from .env.local or another local worker's dev vars.
705577
secretStoreAutoCreates.push({

dev/local/env-sync/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ type SecretStoreBinding = {
3333
binding: string;
3434
store_id: string;
3535
secret_name: string;
36-
devGeneratedBase64Bytes?: number;
3736
};
3837

3938
type SecretStoreWarning = {

services/cloud-agent-next/.dev.vars.example

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,6 @@ REAPER_INTERVAL_MS=300000
4444
# Comma-separated list of org IDs that use per-session sandboxes, or `*` for all orgs
4545
PER_SESSION_SANDBOX_ORG_IDS=
4646

47-
# Managed SCM containment canaries (optional)
48-
# Exact GitHub owner/repo values or GitLab repository URLs, comma-separated
49-
SCM_CONTAINMENT_CANARY_REPOSITORIES=
50-
5147
# GitHub App credentials for git commit attribution
5248
# These identify commits as coming from the Kilo Code GitHub App
5349
# Format for git config:

services/cloud-agent-next/Dockerfile.dev

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ ARG KILOCODE_CLI_VERSION="7.3.21"
1111
#
1212
# This builds kilo-cli from source and copies the linux-x64 binary here.
1313

14-
# Install latest stable git + git-lfs from the git-core PPA, GitHub CLI, plus supporting CLI tools,
14+
# Install latest stable git + git-lfs from the git-core PPA, plus supporting CLI tools,
1515
# and generate locales to suppress setlocale warnings.
1616
# The default Ubuntu git (2.34.1 on 22.04) is outdated; the git-core PPA ships the latest
1717
# stable release. git-lfs is installed from the same PPA so it stays in lockstep with git,
@@ -21,20 +21,12 @@ RUN set -eux; \
2121
apt-get install -y --no-install-recommends \
2222
ca-certificates \
2323
gnupg \
24-
software-properties-common \
25-
wget; \
24+
software-properties-common; \
2625
add-apt-repository -y ppa:git-core/ppa; \
27-
mkdir -p -m 755 /etc/apt/keyrings; \
28-
wget -nv -O /etc/apt/keyrings/githubcli-archive-keyring.gpg https://cli.github.com/packages/githubcli-archive-keyring.gpg; \
29-
chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg; \
30-
mkdir -p -m 755 /etc/apt/sources.list.d; \
31-
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
32-
| tee /etc/apt/sources.list.d/github-cli.list > /dev/null; \
3326
apt-get update; \
3427
apt-get install -y --no-install-recommends \
3528
git \
3629
git-lfs \
37-
gh \
3830
jq \
3931
locales \
4032
openssh-client \

0 commit comments

Comments
 (0)