From 4b3122e65fbc60a2afaa46a2ed772035b20f2f7d Mon Sep 17 00:00:00 2001 From: Sunil Date: Wed, 5 Aug 2026 22:35:47 +0530 Subject: [PATCH] security: validate --behavioral skillName to prevent path traversal The skillName argument from --behavioral has zero validation and is used directly in path.join() for reading .json files, reading SKILL.md, and WRITING .grading.json results. A value like '../../etc/passwd' traverses outside the project directory for both reads and writes. Fix: Validate skillName against /^[a-z0-9]+(-[a-z0-9]+)*$/ before any filesystem operations. This matches the existing KEBAB_CASE validation in skill-lint.js. --- scripts/run-evals.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/run-evals.js b/scripts/run-evals.js index 063c5064..e37573d5 100644 --- a/scripts/run-evals.js +++ b/scripts/run-evals.js @@ -443,7 +443,16 @@ function parseGrading(raw) { return ok ? g : null; } +// Skill name must be a valid kebab-case identifier — no path separators, +// no "..", no absolute paths. Without this, --behavioral "../../x" would +// resolve to files outside the project tree for both reads and writes. +const VALID_SKILL_NAME = /^[a-z0-9]+(-[a-z0-9]+)*$/; + function runBehavioral(skillName, dryRun) { + if (!skillName || !VALID_SKILL_NAME.test(skillName)) { + console.error(`Invalid skill name: "${skillName}" — must be kebab-case (e.g. "my-skill")`); + process.exit(1); + } const caseFile = path.join(CASES_DIR, `${skillName}.json`); if (!fs.existsSync(caseFile)) { console.error(`No eval case file for "${skillName}"`);