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:
jajanet
2025-10-20 20:05:47 +00:00
committed by GitHub
parent 36de686224
commit 49bde9fca0
2 changed files with 47 additions and 0 deletions

View File

@@ -227,6 +227,35 @@ describe('GCSTaskStore', () => {
'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', () => {
@@ -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', () => {

View File

@@ -23,6 +23,13 @@ type ObjectType = 'metadata' | 'workspace';
const getTmpArchiveFilename = (taskId: string): string =>
`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 {
private storage: Storage;
private bucketName: string;
@@ -78,6 +85,9 @@ export class GCSTaskStore implements TaskStore {
}
private getObjectPath(taskId: string, type: ObjectType): string {
if (!isTaskIdValid(taskId)) {
throw new Error(`Invalid taskId: ${taskId}`);
}
return `tasks/${taskId}/${type}.tar.gz`;
}