fix(task-tool): add task ID validation and improve lock acquisition safety

- Add task ID pattern validation (T-[A-Za-z0-9-]+) to prevent path traversal
- Refactor lock mechanism to use UUID-based IDs for reliable ownership tracking
- Implement atomic lock creation with stale lock detection and cleanup
- Add lock acquisition checks in create/update/delete handlers
- Expand task-reminder hook to track split tool names and clean up on session deletion
- Add comprehensive test coverage for validation and lock handling
This commit is contained in:
YeonGyu-Kim
2026-02-01 23:48:48 +09:00
parent 914a480136
commit 134dc7687e
9 changed files with 206 additions and 54 deletions
+67
View File
@@ -351,6 +351,22 @@ describe("task_tool", () => {
expect(result.task).toBeNull()
})
test("rejects invalid task id", async () => {
//#given
const args = {
action: "get" as const,
id: "../package",
}
//#when
const resultStr = await taskTool.execute(args, TEST_CONTEXT)
const result = JSON.parse(resultStr)
//#then
expect(result).toHaveProperty("error")
expect(result.error).toBe("invalid_task_id")
})
test("returns result as JSON string with task property", async () => {
//#given
const testId = await createTestTask("Test task")
@@ -480,6 +496,41 @@ describe("task_tool", () => {
expect(result.error).toBe("task_not_found")
})
test("rejects invalid task id", async () => {
//#given
const args = {
action: "update" as const,
id: "../package",
title: "New title",
}
//#when
const resultStr = await taskTool.execute(args, TEST_CONTEXT)
const result = JSON.parse(resultStr)
//#then
expect(result).toHaveProperty("error")
expect(result.error).toBe("invalid_task_id")
})
test("returns lock unavailable when lock is held", async () => {
//#given
writeFileSync(join(TEST_DIR, ".lock"), JSON.stringify({ id: "test", timestamp: Date.now() }))
const args = {
action: "update" as const,
id: "T-nonexistent",
title: "New title",
}
//#when
const resultStr = await taskTool.execute(args, TEST_CONTEXT)
const result = JSON.parse(resultStr)
//#then
expect(result).toHaveProperty("error")
expect(result.error).toBe("task_lock_unavailable")
})
test("returns result as JSON string with task property", async () => {
//#given
const testId = await createTestTask("Test task")
@@ -574,6 +625,22 @@ describe("task_tool", () => {
expect(result.error).toBe("task_not_found")
})
test("rejects invalid task id", async () => {
//#given
const args = {
action: "delete" as const,
id: "../package",
}
//#when
const resultStr = await taskTool.execute(args, TEST_CONTEXT)
const result = JSON.parse(resultStr)
//#then
expect(result).toHaveProperty("error")
expect(result.error).toBe("invalid_task_id")
})
test("returns result as JSON string", async () => {
//#given
const testId = await createTestTask("Test task")