T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/book.js:18
- Finding
- Path Traversal Through Unvalidated Date Arguments<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/book.js:18-18, 54-54, 115-115` - `scripts/block-time.js:18-18, 33-33` - `scripts/check-schedule.js:13-24` - `scripts/waitlist.js:30-41, 67-73` **Vulnerability Type**: Path traversal leading to unintended JSON file access or modification **Risk Level**: High ### Vulnerable Code From `scripts/book.js`: ```js const date = getArg('--date'); ``` ```js const dailyFile = path.join(DATA_DIR, `${date}.json`); let bookings = []; if (fs.existsSync(dailyFile)) { bookings = JSON.parse(fs.readFileSync(dailyFile, 'utf8')); } ``` ```js const eventFile = path.join(EVENT_DIR, `appointment-${date}.json`); ``` From `scripts/block-time.js`: ```js const date = getArg('--date'); ``` ```js const filePath = path.join(DATA_DIR, `${date}.json`); let bookings = []; if (fs.existsSync(filePath)) { bookings = JSON.parse(fs.readFileSync(filePath, 'utf8')); } ``` From `scripts/check-schedule.js`: ```js const args = process.argv.slice(2); const dateArg = args.includes('--date') ? args[args.indexOf('--date') + 1] : null; const weekMode = args.includes('--week'); const DATA_DIR = path.join(process.env.HOME, '.openclaw', 'workspace', 'data', 'appointments', 'bookings'); function formatDate(date) { return date.toISOString().split('T')[0]; } function getBookingsForDate(dateStr) { const filePath = path.join(DATA_DIR, `${dateStr}.json`); if (!fs.existsSync(filePath)) { return []; } return JSON.parse(fs.readFileSync(filePath, 'utf8')); } ``` From `scripts/waitlist.js`: ```js const date = getArg('--date'); const time = getArg('--time'); const customer = getArg('--customer'); const phone = getArg('--phone'); const email = getArg('--email'); if (!date || !time || !customer) { console.error('Usage: node waitlist.js add --date YYYY-MM-DD --time HH:MM --customer "name" --phone "phone"'); process.exit(1); } const filePath = path.join(WAITLIST_DIR, `${date}.json`); ``` ```js const date = getArg('--date'); ...[truncated 2496 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Enforce the documented date syntax before any filesystem operation: ```js function validateDate(value) { if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) { throw new Error('Date must use YYYY-MM-DD format'); } const parsed = new Date(`${value}T00:00:00Z`); if ( Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== value ) { throw new Error('Invalid calendar date'); } return value; } ``` 2. Resolve and constrain every generated path: ```js function safeJsonPath(baseDirectory, date) { const validatedDate = validateDate(date); const base = path.resolve(baseDirectory); const target = path.resolve(base, `${validatedDate}.json`); if (!target.startsWith(`${base}${path.sep}`)) { throw new Error('Path escapes the permitted data directory'); } return target; } ``` 3. Apply the validation consistently in `book.js`, `block-time.js`, `check-schedule.js`, and `waitlist.js`. 4. Validate dates again when loading persisted booking objects before using `booking.date` to form event or waitlist paths. 5. Add negative tests covering `../`, absolute paths, encoded separators, invalid dates, and values containing path separators. 6. Run the Skill under a dedicated account with write access limited to its configuration and appointment-data directories. ]]>
