mirror of
https://github.com/BradGroux/veritas-kanban.git
synced 2026-10-08 04:37:51 +00:00
fix(security): SEC-001 path traversal — add validation to trace + template services
Extended path traversal protection to two services missed in initial audit: - trace-service.ts: validate attemptId, taskId, traceId before path.join - template-service.ts: validate templateId in templatePath() Both now use validatePathSegment() + ensureWithinBase() from utils/sanitize.ts. Ref: RF-002a Batch 3a Findings (High+Medium severity)
This commit is contained in:
parent
ed1fbb3fb4
commit
30e18393f4
2 changed files with 17 additions and 1 deletions
|
|
@ -8,6 +8,7 @@ import type {
|
|||
UpdateTemplateInput,
|
||||
} from '@veritas-kanban/shared';
|
||||
import { createLogger } from '../lib/logger.js';
|
||||
import { validatePathSegment, ensureWithinBase } from '../utils/sanitize.js';
|
||||
const log = createLogger('template-service');
|
||||
|
||||
export class TemplateService {
|
||||
|
|
@ -30,7 +31,10 @@ export class TemplateService {
|
|||
}
|
||||
|
||||
private templatePath(id: string): string {
|
||||
return join(this.templatesDir, `${id}.md`);
|
||||
validatePathSegment(id);
|
||||
const filepath = join(this.templatesDir, `${id}.md`);
|
||||
ensureWithinBase(this.templatesDir, filepath);
|
||||
return filepath;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import fs from 'fs/promises';
|
|||
import path from 'path';
|
||||
import type { AgentType } from '@veritas-kanban/shared';
|
||||
import { getTelemetryService } from './telemetry-service.js';
|
||||
import { validatePathSegment, ensureWithinBase } from '../utils/sanitize.js';
|
||||
|
||||
const PROJECT_ROOT = path.resolve(process.cwd(), '..');
|
||||
const TRACES_DIR = path.join(PROJECT_ROOT, '.veritas-kanban', 'traces');
|
||||
|
|
@ -174,6 +175,9 @@ export class TraceService {
|
|||
* Get a completed trace from disk
|
||||
*/
|
||||
async getTrace(attemptId: string): Promise<Trace | null> {
|
||||
// Validate attemptId to prevent path traversal
|
||||
validatePathSegment(attemptId);
|
||||
|
||||
// Check active traces first
|
||||
const active = activeTraces.get(attemptId);
|
||||
if (active) return active;
|
||||
|
|
@ -181,6 +185,7 @@ export class TraceService {
|
|||
// Try to load from disk
|
||||
try {
|
||||
const filepath = path.join(this.tracesDir, `${attemptId}.json`);
|
||||
ensureWithinBase(this.tracesDir, filepath);
|
||||
const content = await fs.readFile(filepath, 'utf-8');
|
||||
return JSON.parse(content) as Trace;
|
||||
} catch {
|
||||
|
|
@ -193,6 +198,9 @@ export class TraceService {
|
|||
* List all traces for a task
|
||||
*/
|
||||
async listTraces(taskId: string): Promise<Trace[]> {
|
||||
// Validate taskId (used for filtering, not path construction, but good practice)
|
||||
validatePathSegment(taskId);
|
||||
|
||||
const traces: Trace[] = [];
|
||||
|
||||
// Add active traces for this task
|
||||
|
|
@ -230,8 +238,12 @@ export class TraceService {
|
|||
* Save a trace to disk
|
||||
*/
|
||||
private async saveTrace(trace: Trace): Promise<void> {
|
||||
// Validate traceId to prevent path traversal
|
||||
validatePathSegment(trace.traceId);
|
||||
|
||||
await fs.mkdir(this.tracesDir, { recursive: true });
|
||||
const filepath = path.join(this.tracesDir, `${trace.traceId}.json`);
|
||||
ensureWithinBase(this.tracesDir, filepath);
|
||||
await fs.writeFile(filepath, JSON.stringify(trace, null, 2), 'utf-8');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue