mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
fix(docker): gate gh/az seeding on its switch; no shared git sign-in for non-admin clones
Addresses the review on #472. - CRED_STORES: `.config/gh` and `.azure` now carry `enabledByEnv` (CODEMAN_AGENT_IMAGE_INSTALL_GH / _AZ), and resolveDockerCredentialArtifacts skips a store unless that variable is exactly `1`, read at container create. A host that merely has ~/.config/gh/hosts.yml or a plaintext MSAL cache no longer copies them into every case container. Tests: the default environment seeds neither even with the files present, and each store follows only its own switch. - Multi-user mode: a non-admin's Clone Repo clone and preflight run with `git -c credential.helper=` (GIT_NO_CREDENTIAL_HELPERS, placed before the subcommand), so the server account's helpers are never lent to them. Verified against a real private repo that it also clears the URL-scoped credential.<url>.helper entries, and that public clones still work. Tests: the argv in test/git-clone.test.ts, and the route decision (non-admin cleared; admin and single-user kept) in test/routes/case-clone-credential-helpers.test.ts. - Docs: recreate the case container to pick up seeds (docker/README.md, Docker-Cases wiki, docker-cases.md); the multi-user behaviour in docker/README.md and security-architecture.md; "functionally unchanged" instead of "unchanged" for an image built with both switches off (server.Dockerfile comment, README, changeset). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0167CiuzLrmjYWxwKp3rMWjw
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
5cf5a45438
commit
02e40f506b
@@ -351,6 +351,40 @@ describe('resolveDockerCredentialArtifacts (isolated codex/gemini/gcloud/opencod
|
||||
expect(mounts.filter((m) => m.readonly && m.dst.includes('cred-seeds')).length).toBeGreaterThanOrEqual(3);
|
||||
});
|
||||
|
||||
/** Host files for both opt-in stores, present whether or not the switches are on. */
|
||||
function writeGhAzHostFiles(): void {
|
||||
mkdirSync(join(home, '.config', 'gh'), { recursive: true });
|
||||
writeFileSync(join(home, '.config', 'gh', 'hosts.yml'), '');
|
||||
writeFileSync(join(home, '.config', 'gh', 'config.yml'), '');
|
||||
mkdirSync(join(home, '.azure'), { recursive: true });
|
||||
writeFileSync(join(home, '.azure', 'azureProfile.json'), '{}');
|
||||
writeFileSync(join(home, '.azure', 'msal_token_cache.json'), '{}');
|
||||
}
|
||||
const isGhOrAz = (p: string) => /\.azure|\.config[\\/]gh/.test(p);
|
||||
|
||||
it('gh + az: the DEFAULT environment seeds neither, even when the host files exist', () => {
|
||||
writeGhAzHostFiles();
|
||||
for (const env of [{}, { CODEMAN_AGENT_IMAGE_INSTALL_GH: '0', CODEMAN_AGENT_IMAGE_INSTALL_AZ: '' }]) {
|
||||
const { mounts, seedCopies } = resolveDockerCredentialArtifacts(home, env);
|
||||
expect(mounts.filter((m) => isGhOrAz(m.src))).toEqual([]);
|
||||
expect(seedCopies.filter((s) => isGhOrAz(s.to))).toEqual([]);
|
||||
}
|
||||
});
|
||||
|
||||
it('gh + az: each store follows ONLY its own switch, and only the exact value 1', () => {
|
||||
writeGhAzHostFiles();
|
||||
const dests = (env: NodeJS.ProcessEnv) => resolveDockerCredentialArtifacts(home, env).seedCopies.map((s) => s.to);
|
||||
const ghOnly = dests({ CODEMAN_AGENT_IMAGE_INSTALL_GH: '1' });
|
||||
expect(ghOnly).toContain('/home/agent/.config/gh/hosts.yml');
|
||||
expect(ghOnly.some((d) => d.includes('.azure'))).toBe(false);
|
||||
const azOnly = dests({ CODEMAN_AGENT_IMAGE_INSTALL_AZ: '1' });
|
||||
expect(azOnly).toContain('/home/agent/.azure/msal_token_cache.json');
|
||||
expect(azOnly.some((d) => d.includes('.config/gh'))).toBe(false);
|
||||
expect(
|
||||
dests({ CODEMAN_AGENT_IMAGE_INSTALL_GH: 'true', CODEMAN_AGENT_IMAGE_INSTALL_AZ: 'yes' }).some(isGhOrAz)
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('gh + az: seed only the sign-in files, never logs/extensions/caches', () => {
|
||||
mkdirSync(join(home, '.config', 'gh'), { recursive: true });
|
||||
writeFileSync(join(home, '.config', 'gh', 'hosts.yml'), '');
|
||||
@@ -361,7 +395,10 @@ describe('resolveDockerCredentialArtifacts (isolated codex/gemini/gcloud/opencod
|
||||
writeFileSync(join(home, '.azure', 'msal_token_cache.json'), '{}');
|
||||
writeFileSync(join(home, '.azure', 'config'), '');
|
||||
|
||||
const { mounts, seedCopies } = resolveDockerCredentialArtifacts(home);
|
||||
const { mounts, seedCopies } = resolveDockerCredentialArtifacts(home, {
|
||||
CODEMAN_AGENT_IMAGE_INSTALL_GH: '1',
|
||||
CODEMAN_AGENT_IMAGE_INSTALL_AZ: '1',
|
||||
});
|
||||
const dests = seedCopies.map((s) => s.to);
|
||||
expect(dests).toContain('/home/agent/.config/gh/hosts.yml');
|
||||
expect(dests).toContain('/home/agent/.config/gh/config.yml');
|
||||
|
||||
Reference in New Issue
Block a user