Back to skill

Security audit

Reading Buddy

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent local reading and chat app, but it needs review because its multi-user chat and room controls trust caller-supplied user IDs.

Install only if you understand this as a local prototype. Do not expose it as a shared API, WebSocket service, or multi-user platform until it has real authentication, room membership authorization, export controls, deletion confirmations, and updated dependencies.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/services/chatService.ts:10
Finding
Unauthenticated Chat Access and User Impersonation## Vulnerability Details **File Location**: `src/services/chatService.ts:10-59`, `src/services/chatService.ts:111-118`, and `src/services/chatService.ts:146-169` **Vulnerability Type**: Missing authentication and room-level authorization **Risk Level**: High ### Vulnerable Code ```ts static sendMessage( roomId: number, userId: string, userName: string, content: string, messageType: MessageType = MessageType.TEXT ): ChatMessage { const db = getDatabase(); const result = db.prepare(` INSERT INTO chat_messages (room_id, user_id, user_name, content, message_type) VALUES (?, ?, ?, ?, ?) `).run(roomId, userId, userName, content, messageType); const message = this.getById(result.lastInsertRowid as number)!; chatEvents.emit('message', { roomId, message }); return message; } ``` ```ts static getRoomMessages( roomId: number, limit: number = 100, beforeId?: number ): ChatMessage[] { const db = getDatabase(); let sql = 'SELECT * FROM chat_messages WHERE room_id = ?'; const params: any[] = [roomId]; if (beforeId) { sql += ' AND id < ?'; params.push(beforeId); } sql += ' ORDER BY created_at DESC LIMIT ?'; params.push(limit); const rows = db.prepare(sql).all(...params) as any[]; return rows.map(this.mapRowToMessage).reverse(); } ``` ```ts static deleteMessage(messageId: number, userId: string): boolean { const db = getDatabase(); const message = this.getById(messageId); if (!message) throw new Error('Message does not exist'); if (message.userId !== userId) throw new Error('Users may only delete their own messages'); const result = db.prepare('DELETE FROM chat_messages WHERE id = ?').run(messageId); return result.changes > 0; } ``` ```ts static exportChat(roomId: number): string { const messages = this.getRoomMessages(roomId, 10000); let markdow ...[truncated 2712 chars]
Remediation
## Remediation Suggestions 1. Introduce an authenticated principal derived from a verified session or signed token. Do not accept the acting user ID as an authoritative request parameter. 2. Change service methods to receive a trusted authentication context, such as `AuthContext`, and derive `userId` from that context. 3. Before every chat read, search, statistics, or export operation, verify that the authenticated user belongs to the requested room. 4. Before sending a message, verify that the room exists, that the authenticated user is a member, and that the room status permits messaging. 5. Load the display name from the registered user record instead of accepting `userName` from the caller. 6. For deletion, compare the message owner with the authenticated principal rather than a supplied `userId`. Define explicit moderator or host permissions if privileged deletion is required. 7. Apply pagination and bounded limits to exports and history operations to reduce denial-of-service exposure. 8. Centralize room authorization in a reusable guard so new chat methods cannot accidentally omit membership checks. 9. Add negative tests covering non-members, spoofed identities, unauthorized exports, and deletion attempts using another user's ID.

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/services/roomService.ts:98
Finding
Spoofable Host Authorization and Unauthenticated Membership Removal## Vulnerability Details **File Location**: `src/services/roomService.ts:98-103`, `src/services/roomService.ts:132-159`, and `src/services/roomService.ts:176-183` **Vulnerability Type**: Caller-controlled authorization identity **Risk Level**: High ### Vulnerable Code ```ts static leaveRoom(roomId: number, userId: string): boolean { const db = getDatabase(); const result = db.prepare(` DELETE FROM room_members WHERE room_id = ? AND user_id = ? `).run(roomId, userId); return result.changes > 0; } ``` ```ts static updateStatus(roomId: number, status: RoomStatus): boolean { const db = getDatabase(); const result = db.prepare(` UPDATE reading_rooms SET status = ? WHERE id = ? `).run(status, roomId); return result.changes > 0; } static startRoom(roomId: number, hostId: string): boolean { const db = getDatabase(); const room = this.getById(roomId); if (!room) throw new Error('Reading room does not exist'); if (room.hostId !== hostId) throw new Error('Only the host can start the reading room'); if (room.status !== RoomStatus.PENDING) throw new Error('The reading room has already started or ended'); return this.updateStatus(roomId, RoomStatus.ACTIVE); } static endRoom(roomId: number, hostId: string): boolean { const db = getDatabase(); const room = this.getById(roomId); if (!room) throw new Error('Reading room does not exist'); if (room.hostId !== hostId) throw new Error('Only the host can end the reading room'); if (room.status === RoomStatus.ENDED) throw new Error('The reading room has ended'); return this.updateStatus(roomId, RoomStatus.ENDED); } ``` ```ts static delete(roomId: number, hostId: string): boolean { const db = getDatabase(); const room = this.getById(roomId); if (!room) throw new Error('Reading room does not exist'); if (room.hostId !== hostId) throw new Error('Only the host can delete the reading ...[truncated 2237 chars]
Remediation
## Remediation Suggestions 1. Derive the acting user from a verified authentication context rather than accepting `hostId` or `userId` as proof of identity. 2. Replace host-ID parameters with a trusted principal, then compare `room.hostId` against `principal.userId`. 3. Make `updateStatus` private or require the same centralized host-authorization check used by all state transitions. 4. Validate allowed status transitions so callers cannot assign arbitrary or invalid room states. 5. Modify `leaveRoom` so normal users can remove only their own membership. Implement a separate, explicitly authorized host or moderator removal method if needed. 6. Require reauthorization immediately before destructive operations such as room deletion. 7. Use transactions for room creation, membership changes, state changes, and deletion where multiple related records are affected. 8. Enable and verify SQLite foreign-key enforcement with `PRAGMA foreign_keys = ON` if cascade behavior is relied upon. 9. Add authorization tests for spoofed host IDs, direct `updateStatus` calls, removal of another member, and destructive actions by non-host users.
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Rogue AgentSelf-Modification, Session Persistence
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (29)

Known Vulnerable Dependency: ws==8.19.0 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
97% confidence
Finding
The lockfile pins the application to ws 8.19.0, and the finding cites known advisories for uninitialized memory disclosure and memory-exhaustion denial of service in that version. Because ws is a runtime dependency and this appears to be a networked CLI/agent skill that may accept WebSocket connections or process untrusted WebSocket data, the vulnerable package can expose sensitive process memory or allow a remote attacker to degrade or crash availability.

Known Vulnerable Dependency: ws==8.19.0 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This markdown file describes saving and exporting chat records, which can affect user privacy and data handling, but it does not include any warning or disclosure about what data is retained, who can access exports, or the sensitivity of shared content. Under the markdown-specific SQP-2 criteria, behaviors affecting user data should be accompanied by user-facing warnings.

Session Persistence

Medium
Category
Rogue Agent
Content
### 读书室管理
```bash
# 创建读书室
reading-buddy room create -b <bookId> -n "房间名" -u <userId>

# 列出读书室
reading-buddy room list
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The architecture document is written entirely in Chinese and does not indicate any user opt-in, alternative locale, or justification for restricting the content language. Under the policy, forcing a specific language without user choice can be a natural-language locale violation.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The skill's top-level description is presented only in Chinese, and the rest of the CLI help/output strings also indicate a Chinese-only interface. This creates a language/locale policy concern because the skill appears to force a specific language without user opt-in or any documented region-specific justification.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
This TypeScript file contains natural-language comments and a user-visible console message exclusively in Chinese, such as the initialization status output. The policy allows locale constraints only when they are clearly documented and justified or when the user is given a language choice; neither is present here.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
This code performs a permanent database deletion of a book record, but there is no confirmation prompt, user-facing log/print, or explanatory warning comment/docstring describing the destructive effect. For code files, destructive or irreversible operations should have some visible disclosure unless clearly covered elsewhere.

Missing User Warnings

Medium
Confidence
85% confidence
Finding
This code performs a safety-relevant data export by compiling up to 10,000 chat messages, including usernames, timestamps, and message content, into a markdown document. Although the function name indicates export behavior, there is no confirmation prompt, log/disclosure, or inline warning comment describing the privacy impact of exporting user conversation data.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This code performs a permanent database delete of a reading room, which is a destructive operation. The method contains no confirmation prompt, logging, or explanatory comment/docstring warning callers that the action is irreversible.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The delete method performs a destructive database operation by removing a user record, but the code provides no confirmation prompt, user-facing log/print, or explanatory warning comment beyond the method label. For a safety-relevant delete action in a code file, this lacks any disclosure that the operation is destructive.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The natural-language interface and instructions are presented only in Chinese, which can amount to a language/locale policy issue when no user opt-in, alternative language, or justification is provided. The file does not indicate that the skill is intentionally region-specific or that another language option exists.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
This markdown file is predominantly written in Chinese, but line L120 includes the English phrase "living document" without any user language choice or opt-in. That creates a minor language-policy inconsistency because the document imposes mixed-language wording rather than consistently honoring a single documented locale.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
This markdown file contains user-facing natural language exclusively in Chinese, including headings, comments, and descriptive text. Under the language/locale policy rule, forcing a specific language without user opt-in can be a policy concern when no alternative language option or scope justification is provided.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The entire skill overview is written in Chinese and does not indicate that users may choose another language or that the skill is intentionally limited to a Chinese-speaking audience. Under the policy criteria, forcing a specific language without opt-in can be a natural-language policy violation.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
The entire skill file is written in Chinese and does not indicate that other languages are supported or that language selection is optional. Under the stated policy, forcing a specific language without user opt-in can be a natural-language policy violation.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The manifest description is written entirely in Chinese, indicating a language-specific user-facing experience without any visible opt-in, alternative locale, or justification that this skill is intended only for a Chinese-speaking region. This can violate language/locale policy when users are not offered a choice or informed of the constraint.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"author": "",
  "license": "MIT",
  "dependencies": {
    "better-sqlite3": "^9.4.3",
    "commander": "^12.0.0",
    "ws": "^8.16.0"
  },
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"license": "MIT",
  "dependencies": {
    "better-sqlite3": "^9.4.3",
    "commander": "^12.0.0",
    "ws": "^8.16.0"
  },
  "devDependencies": {
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"dependencies": {
    "better-sqlite3": "^9.4.3",
    "commander": "^12.0.0",
    "ws": "^8.16.0"
  },
  "devDependencies": {
    "@types/better-sqlite3": "^7.6.9",
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"ws": "^8.16.0"
  },
  "devDependencies": {
    "@types/better-sqlite3": "^7.6.9",
    "@types/node": "^20.11.0",
    "@types/ws": "^8.5.10",
    "ts-node": "^10.9.2",
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "devDependencies": {
    "@types/better-sqlite3": "^7.6.9",
    "@types/node": "^20.11.0",
    "@types/ws": "^8.5.10",
    "ts-node": "^10.9.2",
    "typescript": "^5.3.3"
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"devDependencies": {
    "@types/better-sqlite3": "^7.6.9",
    "@types/node": "^20.11.0",
    "@types/ws": "^8.5.10",
    "ts-node": "^10.9.2",
    "typescript": "^5.3.3"
  }
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"@types/better-sqlite3": "^7.6.9",
    "@types/node": "^20.11.0",
    "@types/ws": "^8.5.10",
    "ts-node": "^10.9.2",
    "typescript": "^5.3.3"
  }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"@types/node": "^20.11.0",
    "@types/ws": "^8.5.10",
    "ts-node": "^10.9.2",
    "typescript": "^5.3.3"
  }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Static analysis

No suspicious patterns detected.