Back to skill

Security audit

Agent Communication Hub

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent agent communication library, but its APIs allow any caller with hub access to read or change all agents' messages, events, subscriptions, sessions, and identities.

Install only if all code sharing the hub and SQLite database is trusted, or if you add an authentication/authorization layer around it. Do not treat the built-in "private" messaging as an enforced security boundary, and avoid storing sensitive payloads unless you control database access, retention, and cleanup.

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 (3)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/CommunicationHub.ts:51
Finding
Cross-Agent Message Disclosure and Unauthorized Acknowledgement## Vulnerability Details **File Location**: `src/CommunicationHub.ts:51-114` **Vulnerability Type**: Missing object-level and function-level authorization **Risk Level**: High ### Vulnerable Code ```ts public acknowledgeMessage(messageId: string): MessageRecord { const record = this.getMessage(messageId); const acknowledgedAt = nowIso(); this.db .prepare( `UPDATE messages SET status = 'acknowledged', acknowledged_at = ? WHERE id = ?`, ) .run(acknowledgedAt, messageId); return this.getMessage(messageId); } public drainOfflineQueue(agentId: string): MessageRecord[] { this.sessions.getAgent(agentId); if (!this.sessions.isAgentOnline(agentId)) { return []; } const deliveredAt = nowIso(); this.db .prepare( `UPDATE messages SET status = 'delivered', delivered_at = ? WHERE recipient_id = ? AND status = 'pending'`, ) .run(deliveredAt, agentId); return this.listMessages({ recipientId: agentId, status: "delivered" }); } public getPendingMessages(agentId: string): MessageRecord[] { return this.listMessages({ recipientId: agentId, status: "pending" }); } public listMessages(query: MessageQuery = {}): MessageRecord[] { const conditions: string[] = []; const values: Array<string | number> = []; if (query.senderId) { conditions.push("sender_id = ?"); values.push(query.senderId); } if (query.recipientId) { conditions.push("recipient_id = ?"); values.push(query.recipientId); } if (query.status) { conditions.push("status = ?"); values.push(query.status); } if (query.kind) { conditions.push("kind = ?"); values.push(query.kind); } const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : ""; const limit = query.limit ?? 100; const rows = this.db .prepare(`SELECT * FROM messages ${w ...[truncated 2414 chars]
Remediation
## Remediation Suggestions 1. Introduce an authenticated caller context for every public operation: ```ts interface CallerContext { agentId: string; roles: string[]; } ``` 2. Derive the requesting agent from a validated session or unforgeable capability rather than accepting it solely as an input parameter. 3. Restrict message reads to records where the authenticated caller is the sender or recipient. 4. Permit global message history only through a separately protected administrator or auditor API. 5. Before acknowledgement, retrieve the message and verify that its `recipientId` equals the authenticated caller's agent ID. 6. Restrict queue inspection and draining to the authenticated recipient. 7. Apply state-transition checks so only delivered messages can be acknowledged and only pending messages can be delivered. 8. Add negative authorization tests covering cross-agent reads, queue draining, and acknowledgement attempts.

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/SessionManager.ts:7
Finding
Agent Impersonation and Unauthorized Presence Manipulation## Vulnerability Details **File Location**: `src/SessionManager.ts:7-78`; `src/CommunicationHub.ts:22-46`; `src/CommunicationHub.ts:121-159` **Vulnerability Type**: Missing identity authentication and authorization **Risk Level**: High ### Vulnerable Code ```ts public registerAgent(input: AgentRegistrationInput): AgentRecord { const existing = this.db .prepare("SELECT * FROM agents WHERE id = ?") .get(input.id) as Record<string, string> | undefined; const timestamp = nowIso(); const metadata = input.metadata ?? {}; const name = input.name ?? input.id; if (existing) { this.db .prepare( `UPDATE agents SET name = ?, metadata_json = ?, updated_at = ?, last_seen_at = ? WHERE id = ?`, ) .run(name, toJson(metadata), timestamp, timestamp, input.id); } else { this.db .prepare( `INSERT INTO agents (id, name, status, metadata_json, last_seen_at, created_at, updated_at) VALUES (?, ?, 'offline', ?, ?, ?, ?)`, ) .run(input.id, name, toJson(metadata), timestamp, timestamp, timestamp); } return this.getAgent(input.id); } public connectAgent(input: SessionConnectInput): SessionRecord { this.ensureAgentExists(input.agentId); const sessionId = createId("session"); const timestamp = nowIso(); this.db .prepare( `INSERT INTO sessions (id, agent_id, status, connected_at, disconnected_at, metadata_json) VALUES (?, ?, 'connected', ?, NULL, ?)`, ) .run(sessionId, input.agentId, timestamp, toJson(input.metadata ?? {})); this.db .prepare( `UPDATE agents SET status = 'online', last_seen_at = ?, updated_at = ? WHERE id = ?`, ) .run(timestamp, timestamp, input.agentId); return this.getSession(sessionId); } public disconnectAgent(agentId: string): void { this.ensureAgentExists(agentId); const ti ...[truncated 3771 chars]
Remediation
## Remediation Suggestions 1. Bind each agent identity to authenticated credentials, a validated session token, or an unforgeable capability during initial registration. 2. Separate initial registration from profile updates. Reject duplicate IDs by default and require identity-owner or administrator authorization for updates. 3. Return an authenticated session handle from `connectAgent()` and require it for all identity-scoped operations. 4. Derive `senderId` from the validated caller context; do not accept it as a freely selectable message parameter. 5. Require ownership verification before connecting or disconnecting an agent. 6. If administrators may manage other agents, implement explicit role checks and audit those actions. 7. Prevent one disconnect request from terminating sessions that do not belong to the authenticated session unless the caller has administrative privileges. 8. Add tests proving that one agent cannot update, connect, disconnect, or send messages as another agent.

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/EventBus.ts:26
Finding
Unauthorized Event History Disclosure and Subscription Deletion## Vulnerability Details **File Location**: `src/EventBus.ts:26-28`; `src/EventBus.ts:66-114` **Vulnerability Type**: Missing subscription ownership and event-access authorization **Risk Level**: High ### Vulnerable Code ```ts public unsubscribe(subscriptionId: string): void { this.db.prepare("DELETE FROM event_subscriptions WHERE id = ?").run(subscriptionId); } public replay(options: EventReplayOptions = {}): EventRecord[] { const conditions: string[] = []; const values: Array<string | number> = []; if (options.type) { conditions.push("type = ?"); values.push(options.type); } if (options.sourceAgentId) { conditions.push("source_agent_id = ?"); values.push(options.sourceAgentId); } if (options.since) { conditions.push("created_at >= ?"); values.push(options.since); } const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : ""; const limit = options.limit ?? 100; const sql = `SELECT * FROM events ${where} ORDER BY created_at DESC LIMIT ?`; const rows = this.db.prepare(sql).all(...values, limit) as Array<Record<string, string | null>>; return rows.map((row) => ({ id: row.id as string, type: row.type as string, sourceAgentId: row.source_agent_id as string | null, payload: fromJson(row.payload_json as string), metadata: fromJson(row.metadata_json as string), createdAt: row.created_at as string, })); } public listSubscriptions(agentId?: string): EventSubscription[] { const rows = agentId ? (this.db .prepare("SELECT * FROM event_subscriptions WHERE agent_id = ? ORDER BY created_at ASC") .all(agentId) as Array<Record<string, string>>) : (this.db.prepare("SELECT * FROM event_subscriptions ORDER BY created_at ASC").all() as Array<Record<string, string>>); return rows.map((row) => ({ id: row.id, ...[truncated 2184 chars]
Remediation
## Remediation Suggestions 1. Require an authenticated caller context for subscription creation, listing, deletion, publication, and replay. 2. Remove arbitrary `agentId` selection from ordinary callers and derive the subscriber identity from the authenticated context. 3. Before deleting a subscription, retrieve it and verify that its `agent_id` belongs to the caller. Permit cross-agent deletion only for an explicitly authorized administrator. 4. Scope `listSubscriptions()` to the current agent by default; expose global enumeration only through a protected administrative API. 5. Restrict event replay to event types the authenticated agent is permitted to receive, or implement explicit per-event access-control policies. 6. Consider storing recipient or access-policy information with persisted events so replay authorization can be enforced consistently. 7. Return a not-found or generic authorization error for unauthorized subscription IDs to reduce enumeration. 8. Add tests for cross-agent subscription enumeration, deletion, and unauthorized event replay.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (10)

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill explicitly provides SQLite-backed persistence, offline queues, audit history, and JSON storage of message and event payloads, but it does not warn users that communication content and history will be retained. This can lead operators to use the skill for sensitive agent-to-agent traffic without understanding the privacy, retention, and compliance implications, increasing the chance of accidental exposure or improper handling of stored data.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The publish method writes event payload and metadata into persistent storage, which may include user or system data. In this code file there is no confirmation prompt, logging, or explanatory comment/docstring disclosing that event contents are stored.

Missing User Warnings

Low
Confidence
86% confidence
Finding
This markdown file describes features that store agent messages, session history, and offline queues durably in SQLite, which can affect user or system data privacy. Under the markdown-specific warning rule, the description should disclose that message contents and metadata may be persisted for audit, replay, and delayed delivery.

Unpinned Dependencies

Low
Category
Supply Chain
Content
],
  "license": "MIT",
  "dependencies": {
    "better-sqlite3": "^11.10.0"
  },
  "devDependencies": {
    "@types/better-sqlite3": "^7.6.13",
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"better-sqlite3": "^11.10.0"
  },
  "devDependencies": {
    "@types/better-sqlite3": "^7.6.13",
    "@types/node": "^24.0.0",
    "tsx": "^4.20.5",
    "typescript": "^5.8.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.13",
    "@types/node": "^24.0.0",
    "tsx": "^4.20.5",
    "typescript": "^5.8.2",
    "vitest": "^3.0.8"
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.13",
    "@types/node": "^24.0.0",
    "tsx": "^4.20.5",
    "typescript": "^5.8.2",
    "vitest": "^3.0.8"
  }
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.13",
    "@types/node": "^24.0.0",
    "tsx": "^4.20.5",
    "typescript": "^5.8.2",
    "vitest": "^3.0.8"
  }
}
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": "^24.0.0",
    "tsx": "^4.20.5",
    "typescript": "^5.8.2",
    "vitest": "^3.0.8"
  }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unverifiable Dependency: vitest has 3 known advisory(ies) (CVE-2026-47429 (When Vitest UI server is listening, arbitrary file can be read and executed); CVE-2026-84373 (Vitest: Path Traversal / Arbitrary File Read via @vitest/mocker Redirect Mock); CVE-2025-24964 (Vitest allows Remote Code Execution when accessing a malicious website while Vit)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

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

Static analysis

No suspicious patterns detected.