T09 · Insecure Skill Coding Practices
- Location
scripts/token-collector.js:27- Finding
Path Traversal Allows Overwriting Arbitrary JSON Files
- Content
View full analysis
Vulnerability Details
File Location:
scripts/token-collector.js, lines 27-31; attacker-controlled input enters the sink at lines 64-72
Vulnerability Type: Path traversal and arbitrary file overwrite
Risk Level: HighVulnerable Code
javascript function saveDailyData(date, data) { ensureDataDirs(); data.collectedAt = new Date().toISOString(); const filePath = path.join(DAILY_DIR, `${date}.json`); fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8'); return filePath; }The
datevalue is taken directly from externally supplied JSON without format or path validation:javascript function save(jsonStr) { ensureDataDirs(); let data; try { data = JSON.parse(jsonStr); } catch (e) { console.error('❌ 无效的 JSON 输入'); process.exit(1); } if (!data.date) { console.error('❌ 数据缺少 date 字段'); process.exit(1); } const filePath = saveDailyData(data.date, data);Technical Analysis
The
savecommand accepts JSON through a command-line argument. Itsdateproperty is interpolated directly into a file path:javascript path.join(DAILY_DIR, `${date}.json`)Checking only that
data.dateis truthy does not prevent path separators or traversal sequences such as../. Node.js normalizes these sequences when constructing the path, allowing the resulting destination to escape the intendeddata/daily/directory.The appended
.jsonextension limits the primitive to files ending in.json, but this still includes security-relevant project files such aspackage.jsonandconfig.json, as well as JSON files elsewhere that are writable by the Node.js process.This behavior exceeds the minimum filesystem privileges required by the declared functionality. Saving daily statistics requires access only to a filename derived from a strict calendar date inside
data/daily/, not arbitrary writable JSON pat ...[truncated 1811 chars]- Remediation
View remediation
Remediation Suggestions
-
Validate
dateagainst an exact date format before using it in a path:javascript function validateDate(date) { if (typeof date !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(date)) { throw new Error('Date must use YYYY-MM-DD format'); } const parsed = new Date(`${date}T00:00:00Z`); if ( Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== date ) { throw new Error('Date is not a valid calendar date'); } } -
Resolve the destination and enforce directory containment:
javascript function saveDailyData(date, data) { ensureDataDirs(); validateDate(date); const baseDir = path.resolve(DAILY_DIR); const filePath = path.resolve(baseDir, `${date}.json`); if (path.dirname(filePath) !== baseDir) { throw new Error('Invalid destination path'); } const output = { ...data, date, collectedAt: new Date().toISOString() }; fs.writeFileSync(filePath, JSON.stringify(output, null, 2), { encoding: 'utf-8', flag: 'w', mode: 0o600 }); return filePath; } -
Apply the same strict date validation to
loadDailyData(date), because that exported function constructs a path from its argument using the same pattern. -
Validate the complete input structure, including numeric ranges, object types, array sizes, and permitted property names.
-
Run the Skill with a restricted account that cannot modify application code, package metadata, or unrelated users' files.
-
Add regression tests covering values such as
../config,../../package, absolute paths, encoded separators, malformed dates, and invalid calendar dates.
-
