T09 · Insecure Skill Coding Practices
- Location
references/user-memory.js:30- Finding
User identifier collisions allow cross-user memory access and modification
- Content
View full analysis
Vulnerability Details
File Location:
references/user-memory.js, lines 30–67
Vulnerability Type: Insufficiently unique user identifier canonicalization
Risk Level: HighVulnerable Code
javascript function extractUsername(senderId) { if (!senderId) { return 'unknown'; } // Take the portion before |, or the entire value if | is absent const username = senderId.split('|')[0]; // Sanitize the filename by replacing unsafe characters return username.replace(/[^a-zA-Z0-9_-]/g, '_'); } function getUserMemoryPath(senderId) { const username = extractUsername(senderId); return path.join(USERS_DIR, `${username}.md`); } function get(userId) { ensureDir(); const memoryPath = getUserMemoryPath(userId); if (fs.existsSync(memoryPath)) { return fs.readFileSync(memoryPath, 'utf-8'); } return ''; } function save(userId, content) { ensureDir(); const memoryPath = getUserMemoryPath(userId); fs.writeFileSync(memoryPath, content, 'utf-8'); }Technical Analysis
The memory filename is derived by taking only the portion of
senderIdbefore the first|and replacing every character outside[a-zA-Z0-9_-]with an underscore. This transformation is not collision-resistant.Distinct user identifiers can consequently resolve to the same file. For example:
alice/admin|session-1alice.admin|session-2alice_admin|session-3
All three identifiers resolve to
users/alice_admin.md. The implementation does not maintain a verified mapping between the original identifier and the resulting file, nor does it validate that the caller is authorized to access the selected memory.The same derived path is used by
get,save, andappend. A collision therefore affects the confidentiality and integrity of another user's persistent memory.Attack Path
- An attacker identifies or guesses the filename-compatible identity o ...[truncated 1177 chars]
- Remediation
View remediation
Remediation Suggestions
- Derive storage keys from a collision-resistant encoding of the complete canonical user identifier, such as a SHA-256 digest.
- Do not discard the portion after
|unless a trusted authentication layer has already established that it is only a session identifier. - Maintain a protected mapping between the complete authenticated identity and its memory file.
- Verify authorization before every read or write instead of relying on caller-supplied identifiers as proof of identity.
- Store the canonical identity inside the file or metadata and reject access when it does not match the authenticated identity.
- Add tests covering identifiers that differ only by punctuation, empty identifiers, repeated delimiters, Unicode characters, and values that normalize to
unknown. - Migrate existing files carefully so that previously colliding users are assigned separate storage records.
