fix: isolate watcher task runtimes

This commit is contained in:
Eyejoker
2026-03-23 07:16:07 +09:00
parent 867a6a80ef
commit 9def49f7fb
14 changed files with 407 additions and 61 deletions

View File

@@ -3,6 +3,7 @@ import path from 'path';
import { DATA_DIR, GROUPS_DIR } from './config.js';
const GROUP_FOLDER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
const RUNTIME_SEGMENT_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
const RESERVED_FOLDERS = new Set(['global']);
export function isValidGroupFolder(folder: string): boolean {
@@ -21,6 +22,18 @@ export function assertValidGroupFolder(folder: string): void {
}
}
function assertValidRuntimeSegment(segment: string, label: string): void {
if (!segment || segment !== segment.trim()) {
throw new Error(`Invalid ${label} "${segment}"`);
}
if (!RUNTIME_SEGMENT_PATTERN.test(segment)) {
throw new Error(`Invalid ${label} "${segment}"`);
}
if (segment.includes('/') || segment.includes('\\') || segment.includes('..')) {
throw new Error(`Invalid ${label} "${segment}"`);
}
}
function ensureWithinBase(baseDir: string, resolvedPath: string): void {
const rel = path.relative(baseDir, resolvedPath);
if (rel.startsWith('..') || path.isAbsolute(rel)) {
@@ -42,3 +55,32 @@ export function resolveGroupIpcPath(folder: string): string {
ensureWithinBase(ipcBaseDir, ipcPath);
return ipcPath;
}
export function resolveGroupSessionsPath(folder: string): string {
assertValidGroupFolder(folder);
const sessionsBaseDir = path.resolve(DATA_DIR, 'sessions');
const sessionsPath = path.resolve(sessionsBaseDir, folder);
ensureWithinBase(sessionsBaseDir, sessionsPath);
return sessionsPath;
}
export function resolveTaskRuntimeIpcPath(
folder: string,
taskId: string,
): string {
assertValidGroupFolder(folder);
assertValidRuntimeSegment(taskId, 'task ID');
const ipcBaseDir = path.resolve(DATA_DIR, 'ipc');
const ipcPath = path.resolve(ipcBaseDir, folder, 'tasks', taskId);
ensureWithinBase(ipcBaseDir, ipcPath);
return ipcPath;
}
export function resolveTaskSessionsPath(folder: string, taskId: string): string {
assertValidGroupFolder(folder);
assertValidRuntimeSegment(taskId, 'task ID');
const sessionsBaseDir = path.resolve(DATA_DIR, 'sessions');
const sessionsPath = path.resolve(sessionsBaseDir, folder, 'tasks', taskId);
ensureWithinBase(sessionsBaseDir, sessionsPath);
return sessionsPath;
}