T09 · Insecure Skill Coding Practices
Warning
- Location
- dist/core/memory.js:36
- Finding
- Caller-Controlled Storage Paths Permit Filesystem Traversal Outside the Intended Memory Directory<![CDATA[ ## Vulnerability Details **File Location**: `dist/core/memory.js:36-38, 56-67, 141-142, 175-176, 219-220, 247-248, 269-275` **Vulnerability Type**: Path traversal and unrestricted filesystem access **Risk Level**: Medium ### Vulnerable Code ```javascript constructor(skillName = 'ai_system', baseDir = 'memory', config) { // ... this.skillName = skillName; this.baseDir = baseDir; // ... this.initialize(); } initialize() { this.createDirectories(); this.loadMemories(); } createDirectories() { const dirs = [ 'L0_flash', 'L1_working', 'L2_experience', 'L3_knowledge', 'L4_wisdom', 'shared', 'logs' ]; for (const dir of dirs) { const fullPath = path.join(this.baseDir, this.skillName, dir); if (!fs.existsSync(fullPath)) { fs.mkdirSync(fullPath, { recursive: true }); } } } saveL1() { fs.writeFileSync( path.join( this.baseDir, this.skillName, 'L1_working', 'WORKING_MEMORY.md' ), this.L1Content, 'utf-8' ); } saveL2() { fs.writeFileSync( path.join( this.baseDir, this.skillName, 'L2_experience', 'EXPERIENCE_MEMORY.json' ), JSON.stringify({ entries: this.L2Entries }, null, 2), 'utf-8' ); } saveL3() { fs.writeFileSync( path.join( this.baseDir, this.skillName, 'L3_knowledge', 'KNOWLEDGE_MEMORY.json' ), JSON.stringify(this.L3Data, null, 2), 'utf-8' ); } saveL4() { fs.writeFileSync( path.join( this.baseDir, this.skillName, 'L4_wisdom', 'WISDOM_MEMORY.json' ), JSON.stringify(this.L4Data, null, 2), 'utf-8' ); } syncToSystem(targetSystem, entries) { fs.writeFileSync( path ...[truncated 3795 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Establish a trusted storage root controlled by the application rather than by end users: ```javascript const storageRoot = path.resolve(configuredStorageRoot); ``` 2. Restrict `skillName` to a simple identifier and reject path syntax: ```javascript function validateIdentifier(value, fieldName) { if ( typeof value !== 'string' || !/^[A-Za-z0-9_-]+$/.test(value) ) { throw new Error(`${fieldName} contains invalid characters`); } return value; } ``` 3. Resolve every target path and enforce containment: ```javascript function resolveInside(root, ...segments) { const canonicalRoot = path.resolve(root); const target = path.resolve(canonicalRoot, ...segments); if ( target !== canonicalRoot && !target.startsWith(canonicalRoot + path.sep) ) { throw new Error('Resolved path escapes the storage root'); } return target; } ``` 4. Do not accept an untrusted `baseDir`. If configurability is required, validate it once at application startup against an allowlist of approved roots. 5. Validate `targetSystem` and `sourceSystem` at runtime, regardless of TypeScript types: ```javascript const ALLOWED_SYSTEMS = new Set([ 'signal', 'workflow', 'goal', 'shared' ]); if (!ALLOWED_SYSTEMS.has(targetSystem)) { throw new Error('Invalid target system'); } ``` 6. Use the same containment function for directory creation, loading, saving, synchronization, and health checks. 7. Add tests covering `..`, nested traversal, path separators, absolute paths, encoded separators, empty identifiers, and cross-tenant access. 8. Run the package under a dedicated low-privilege operating-system account with write access limited to its approved data directory. ]]>
