mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-12 11:00:51 -07:00
feat(workspaces): implement secure GitHub PAT injection via memory-only mounts
This commit is contained in:
@@ -49,7 +49,7 @@ describe('SSHService', () => {
|
||||
'--tunnel-through-iap',
|
||||
'--ssh-flag=-A',
|
||||
],
|
||||
expect.objectContaining({ stdio: 'inherit' })
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
});
|
||||
|
||||
@@ -67,4 +67,34 @@ describe('SSHService', () => {
|
||||
|
||||
await expect(promise).rejects.toThrow('gcloud ssh exited with code 1');
|
||||
});
|
||||
|
||||
it('should construct correct gcloud command for pushSecret', async () => {
|
||||
const mockChild = new EventEmitter() as any;
|
||||
vi.mocked(spawn).mockReturnValue(mockChild);
|
||||
|
||||
const secretValue = 'secret-val';
|
||||
const promise = service.pushSecret(
|
||||
{ instanceName: 'test-inst', zone: 'z1', project: 'p1' },
|
||||
'.gh_token',
|
||||
secretValue
|
||||
);
|
||||
|
||||
setTimeout(() => mockChild.emit('exit', 0), 10);
|
||||
|
||||
await promise;
|
||||
|
||||
expect(spawn).toHaveBeenCalledWith(
|
||||
'gcloud',
|
||||
[
|
||||
'compute',
|
||||
'ssh',
|
||||
'test-inst',
|
||||
'--zone=z1',
|
||||
'--project=p1',
|
||||
'--tunnel-through-iap',
|
||||
'--command',
|
||||
`cat << 'EOF' > /dev/shm/.gh_token\n${secretValue}\nEOF\nchmod 600 /dev/shm/.gh_token`,
|
||||
]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -66,4 +66,41 @@ export class SSHService {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes a secret string to a temporary, memory-only file on the remote VM.
|
||||
* Uses gcloud compute ssh with a heredoc to write to /dev/shm.
|
||||
*/
|
||||
async pushSecret(options: SSHOptions, secretName: string, secretValue: string): Promise<void> {
|
||||
const { instanceName, zone, project } = options;
|
||||
const remotePath = `/dev/shm/${secretName}`;
|
||||
|
||||
// Command to write secret securely without it appearing in process list
|
||||
const remoteCommand = `cat << 'EOF' > ${remotePath}
|
||||
${secretValue}
|
||||
EOF
|
||||
chmod 600 ${remotePath}`;
|
||||
|
||||
const args = [
|
||||
'compute',
|
||||
'ssh',
|
||||
instanceName,
|
||||
`--zone=${zone}`,
|
||||
`--project=${project}`,
|
||||
'--tunnel-through-iap',
|
||||
'--command',
|
||||
remoteCommand,
|
||||
];
|
||||
|
||||
debugLogger.log(`[SSHService] Pushing secret ${secretName} to ${instanceName}...`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn('gcloud', args);
|
||||
child.on('exit', (code) => {
|
||||
if (code === 0) resolve();
|
||||
else reject(new Error(`Failed to push secret ${secretName}, exit code ${code}`));
|
||||
});
|
||||
child.on('error', (err) => reject(err));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user