mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-13 05:12:55 -07:00
fix(core): address GCS path input (#11221)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: cornmander <shikhman@google.com>
This commit is contained in:
@@ -227,6 +227,35 @@ describe('GCSTaskStore', () => {
|
|||||||
'tar.c command failed to create',
|
'tar.c command failed to create',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw an error if taskId contains path traversal sequences', async () => {
|
||||||
|
const store = new GCSTaskStore('test-bucket');
|
||||||
|
const maliciousTask: SDKTask = {
|
||||||
|
id: '../../../malicious-task',
|
||||||
|
metadata: {
|
||||||
|
_internal: {
|
||||||
|
agentSettings: {
|
||||||
|
cacheDir: '/tmp/cache',
|
||||||
|
dataDir: '/tmp/data',
|
||||||
|
logDir: '/tmp/logs',
|
||||||
|
tempDir: '/tmp/temp',
|
||||||
|
},
|
||||||
|
taskState: 'working',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
kind: 'task',
|
||||||
|
status: {
|
||||||
|
state: 'working',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
contextId: 'test-context',
|
||||||
|
history: [],
|
||||||
|
artifacts: [],
|
||||||
|
};
|
||||||
|
await expect(store.save(maliciousTask)).rejects.toThrow(
|
||||||
|
'Invalid taskId: ../../../malicious-task',
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('load', () => {
|
describe('load', () => {
|
||||||
@@ -323,6 +352,14 @@ describe('GCSTaskStore', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw an error if taskId contains path traversal sequences', async () => {
|
||||||
|
const store = new GCSTaskStore('test-bucket');
|
||||||
|
const maliciousTaskId = '../../../malicious-task';
|
||||||
|
await expect(store.load(maliciousTaskId)).rejects.toThrow(
|
||||||
|
`Invalid taskId: ${maliciousTaskId}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('NoOpTaskStore', () => {
|
describe('NoOpTaskStore', () => {
|
||||||
|
|||||||
@@ -23,6 +23,13 @@ type ObjectType = 'metadata' | 'workspace';
|
|||||||
const getTmpArchiveFilename = (taskId: string): string =>
|
const getTmpArchiveFilename = (taskId: string): string =>
|
||||||
`task-${taskId}-workspace-${uuidv4()}.tar.gz`;
|
`task-${taskId}-workspace-${uuidv4()}.tar.gz`;
|
||||||
|
|
||||||
|
// Validate the taskId to prevent path traversal attacks by ensuring it only contains safe characters.
|
||||||
|
const isTaskIdValid = (taskId: string): boolean => {
|
||||||
|
// Allow only alphanumeric characters, dashes, and underscores, and ensure it's not empty.
|
||||||
|
const validTaskIdRegex = /^[a-zA-Z0-9_-]+$/;
|
||||||
|
return validTaskIdRegex.test(taskId);
|
||||||
|
};
|
||||||
|
|
||||||
export class GCSTaskStore implements TaskStore {
|
export class GCSTaskStore implements TaskStore {
|
||||||
private storage: Storage;
|
private storage: Storage;
|
||||||
private bucketName: string;
|
private bucketName: string;
|
||||||
@@ -78,6 +85,9 @@ export class GCSTaskStore implements TaskStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getObjectPath(taskId: string, type: ObjectType): string {
|
private getObjectPath(taskId: string, type: ObjectType): string {
|
||||||
|
if (!isTaskIdValid(taskId)) {
|
||||||
|
throw new Error(`Invalid taskId: ${taskId}`);
|
||||||
|
}
|
||||||
return `tasks/${taskId}/${type}.tar.gz`;
|
return `tasks/${taskId}/${type}.tar.gz`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user