Back to skill

Security audit

@hashingcorp/hashbox-plugin

Security checks for vulnerabilities and agentic risk

Overview

The plugin can send HashBox notifications, but it also enables under-disclosed remote command handling and stores credentials in ways users should review before installing.

Install only if you trust the HashBox backend and are comfortable with notification contents leaving your environment. Treat hashbox_config.json as a secret file, do not commit it, and rotate the HB token if it may have been exposed. The publisher should make inbound command listening explicitly opt-in, avoid direct agent-message injection, store tokens in a protected secret store, move authentication out of URL query strings, and upgrade flagged dependencies.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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
Findings (4)

T01 · Skill Instruction Hijacking

Error
Location
src/index.ts:92
Finding
Remote Firestore Commands Are Injected Directly into the Agent Reasoning Loop<![CDATA[ ## Vulnerability Details **File Location**: `src/index.ts:92-96` **Vulnerability Type**: `T01: Skill Instruction Hijacking` **Risk Level**: Critical ### Vulnerable Code ```typescript unsubscribe = startCommandListener(db, config.userId, async (command) => { const message = `User command from HashBox App: ${command.payload.raw_instruction}`; await context.injectMessage(message); }); ``` ### Technical Analysis The plugin receives `raw_instruction` from a remote Firestore document and injects it directly into the agent's reasoning context through `context.injectMessage()`. The Firestore listener in `src/commandListener.ts` constructs command objects from document fields using TypeScript assertions, but it performs no runtime schema validation, cryptographic message verification, command allowlisting, or confirmation of the instruction's intended scope. Prefixing the value with `User command from HashBox App:` does not establish a security boundary or prevent the agent from interpreting the remaining text as authoritative instructions. This creates an explicit remote prompt-injection channel. The declared command types are also not enforced at runtime before the callback is invoked. ### Attack Path 1. An attacker obtains the ability to create or modify documents in the relevant user's `agent_commands` Firestore queue. This may occur through compromised HashBox credentials, a compromised backend, or overly permissive Firestore rules. 2. The attacker creates a document with: - The victim's `userId` - A `pending` status - A malicious `payload.raw_instruction` 3. The active `onSnapshot` listener receives the document. 4. The plugin marks the command as processing and invokes the command callback. 5. The callback embeds the attacker-controlled text in a message and calls `context.injectMessage()`. 6. The agent may interpret the text as a genuine user instruction and invoke tools or access resources available in the current agent session. # ...[truncated 622 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove direct injection of `raw_instruction` into the agent's reasoning context. 2. Convert inbound commands into narrowly scoped, typed operations with explicit handlers. 3. Enforce a runtime schema for every Firestore field, including: - Exact supported command types - Required and optional payload fields - Maximum string and collection sizes - Valid status transitions 4. Authenticate each command independently using a server-generated signature or equivalent integrity mechanism. 5. Bind commands to the expected user, installation, and agent instance. 6. Require explicit local user confirmation before any command that accesses data, invokes tools, or causes external side effects. 7. Treat remote text as untrusted data rather than as instructions. 8. Enforce restrictive Firestore security rules and test that users cannot write commands for other accounts. 9. Record auditable command provenance without logging sensitive command contents unnecessarily. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/index.ts:55
Finding
Undisclosed Inbound Command Listener Exceeds the Privileges Needed for Push Notifications<![CDATA[ ## Vulnerability Details **File Location**: `src/index.ts:55-96` **Vulnerability Type**: `T05: Unauthorized Access and Privilege Escalation` **Risk Level**: High ### Vulnerable Code ```typescript /** * Lifecycle: Initialize command listener. * * 1. Reads hashbox_config.json for token, customToken, userId * 2. If customToken + userId exist, authenticates with Firebase Client SDK * 3. Starts onSnapshot listener on agent_commands collection * 4. Injects incoming commands into the Agent's reasoning loop * * If customToken is expired, automatically refreshes via exchangeToken. * If no config or no customToken, silently skips (backward compatible). */ initialize: async (context: PluginContext) => { const config = await loadConfig(); if (!config?.customToken || !config?.userId) { // No custom token yet — plugin works in output-only mode return; } try { await initFirebaseClient(config.customToken); } catch { // Custom Token likely expired — refresh and retry try { const refreshed = await refreshCustomToken(config); if (!refreshed.customToken) return; await initFirebaseClient(refreshed.customToken); } catch { // Exchange also failed — skip listener, output-only mode return; } } const { getDb } = await import("./firebaseClient.js"); const db = getDb(); unsubscribe = startCommandListener(db, config.userId, async (command) => { const message = `User command from HashBox App: ${command.payload.raw_instruction}`; await context.injectMessage(message); }); }, ``` ### Technical Analysis The public documentation describes the Skill primarily as an outbound push-notification integration. It states that configuration saves an API token so the agent can send notifications. It does not clearly disclose that configuration also: - Sends the HB token to a token-exchange backend - Obtains Firebase authentication material - Authenticates to Firestore - Starts a real-time inbou ...[truncated 1725 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove inbound command listening from the outbound notification Skill. 2. If inbound control is required, implement it as a separate plugin or separately enabled feature. 3. Keep inbound listening disabled by default and require explicit, informed user consent. 4. Clearly document: - Every external endpoint - Credentials transmitted and stored - Firestore collections accessed - Inbound command behavior - Agent privileges that commands may exercise 5. Request credentials scoped only to notification delivery when inbound functionality is disabled. 6. Provide a visible status indicator and immediate revocation control for the listener. 7. Require local approval for consequential remote commands. 8. Apply restrictive server-side authorization and per-installation command scoping. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/setupHashBox.ts:61
Finding
Long-Lived API and Firebase Tokens Are Stored in a Plaintext Working-Directory File<![CDATA[ ## Vulnerability Details **File Location**: `src/setupHashBox.ts:61-64` **Vulnerability Type**: `T09: Insecure Skill Coding Practices` **Risk Level**: Medium ### Vulnerable Code ```typescript const config: HashBoxConfig = { token, customToken, userId }; const configPath = join(process.cwd(), CONFIG_FILENAME); await writeFile(configPath, JSON.stringify(config, null, 2), "utf-8"); ``` The same insecure persistence behavior is repeated during token refresh at `src/setupHashBox.ts:86-93`: ```typescript export async function refreshCustomToken( config: HashBoxConfig, ): Promise<HashBoxConfig> { const { customToken, userId } = await exchangeTokenWithBackend(config.token); const updated: HashBoxConfig = { ...config, customToken, userId }; const configPath = join(process.cwd(), CONFIG_FILENAME); await writeFile(configPath, JSON.stringify(updated, null, 2), "utf-8"); return updated; } ``` ### Technical Analysis The plugin stores the HB API token, Firebase custom token, and user identifier as readable JSON in `hashbox_config.json` under `process.cwd()`. No restrictive file mode is specified. The code also does not use an operating-system credential store, framework secret facility, private per-user configuration directory, atomic write procedure, or explicit source-control exclusion. Because the file is placed in the current working directory, it may be created inside a project repository or another shared location. Although the exact default permissions depend on the process umask and operating system, the implementation does not enforce confidentiality. ### Attack Path 1. The user configures the plugin with an HB token. 2. The plugin exchanges the HB token and writes all credentials to `hashbox_config.json`. 3. Another local account, process, backup system, development tool, or repository operation reads or copies the file. 4. The exposed token is used to impersonate the user against the HashBox backend or associated Firebase services. ...[truncated 594 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store long-lived credentials in an OS credential manager or the OpenClaw secret-management facility. 2. If file storage is unavoidable: - Use a private per-user configuration directory - Create files with mode `0600` - Verify directory ownership and permissions - Write updates atomically through a protected temporary file 3. Do not store Firebase custom tokens unless persistence is operationally necessary. 4. Add `hashbox_config.json` to distributed `.gitignore` guidance and document that it must never be committed. 5. Avoid placing secrets under `process.cwd()`. 6. Support token revocation and rotation. 7. Minimize stored values and separate non-sensitive configuration from credentials. 8. Detect and reject insecure existing configuration-file permissions where supported. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/pushToHashBox.ts:90
Finding
HB API Token Is Transmitted in a Webhook Query String<![CDATA[ ## Vulnerability Details **File Location**: `src/pushToHashBox.ts:90-100` **Vulnerability Type**: `T09: Insecure Skill Coding Practices` **Risk Level**: Medium ### Vulnerable Code ```typescript const url = `${WEBHOOK_BASE_URL}?token=${config.token}`; const request = buildRequest( payloadType, channelName, channelIcon, title, contentOrData ); const body = JSON.stringify(request); try { const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body, }); ``` ### Technical Analysis The HB API token is embedded in the URL query string. HTTPS protects the request in transit against ordinary passive interception, but it does not prevent the complete URL from being recorded by infrastructure. Query strings are commonly captured by: - Reverse-proxy and load-balancer access logs - Cloud request tracing - Application monitoring systems - Error reports and debugging output - Security appliances - Redirect destinations The request does not explicitly reject redirects. Consequently, redirect handling may broaden the set of systems exposed to the credential. Authentication data should not be placed in a URL when an authorization header is available. Sending notification contents to the declared webhook is consistent with the Skill's functionality; exposing the authentication token through the URL is not required. ### Attack Path 1. The plugin constructs a webhook URL containing the user's HB token. 2. The request URL is recorded by Cloud Run infrastructure, an intermediary, monitoring software, or application logs. 3. An attacker or unauthorized operator obtains access to those logs. 4. The attacker extracts the token from the query string. 5. The token is replayed against the webhook or token-exchange service, subject to the backend's accepted token scope. ### Impact Assessment A leaked token may allow unauthorized parties to: - Send notifications as the victim - Submit misleading or maliciou ...[truncated 290 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Transmit the token in an HTTP authorization header, for example: ```http Authorization: Bearer <token> ``` 2. Update the backend to reject credentials supplied through query parameters. 3. Disable redirects or validate every redirect target against an exact origin allowlist. 4. Redact authorization headers and tokens from application and infrastructure telemetry. 5. Rotate tokens previously used in query strings if production logging may have captured them. 6. Scope notification credentials to notification delivery only. 7. Consider short-lived, audience-bound access tokens to reduce replay risk. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (32)

Known Vulnerable Dependency: protobufjs==7.5.4 — 12 advisory(ies): CVE-2026-44294 (protobuf.js: Denial of service from crafted field names in generated code); CVE-2026-44293 (protobuf.js: Code injection through bytes field defaults in generated toObject c); CVE-2026-44289 (protobuf.js: Denial of service through unbounded protobuf recursion) +9 more

Critical
Category
Supply Chain
Confidence
93% confidence
Finding
protobufjs 7.5.4 is present with numerous serious advisories including denial of service and code-injection issues in generated code and parser behavior. Because protobufjs can process structured attacker-controlled data and generate runtime artifacts, a heavily vulnerable version meaningfully increases supply-chain and parsing risk even when pulled transitively.

Known Vulnerable Dependency: vitest==4.0.18 — 2 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)

Critical
Category
Supply Chain
Confidence
92% confidence
Finding
vitest 4.0.18 is a real vulnerable version with advisories for arbitrary file read and, when the UI server is enabled, file execution. Although this is dev-only, test runners are often used in CI and on developer workstations; a vulnerable test/UI service can expose sensitive files or enable code execution in those environments.

Known Vulnerable Dependency: websocket-driver==0.7.4 — 2 advisory(ies): CVE-2026-54490 (websocket-driver: Resource limit bypass via message compression); CVE-2026-54466 (websocket-driver: Message corruption via abuse of protocol length headers)

Critical
Category
Supply Chain
Confidence
90% confidence
Finding
websocket-driver 0.7.4 is present with critical advisories involving resource-limit bypass and message corruption via protocol header abuse. Since websocket-driver sits on a network boundary and can process attacker-controlled frames, exploitation could cause denial of service or destabilize services/components that rely on it.

Known Vulnerable Dependency: vitest==4.0.18 — 2 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)

Critical
Category
Supply Chain
Confidence
97% confidence
Finding
The package declares vitest 4.0.18, which is flagged with critical advisories including arbitrary file read and possible code execution when the Vitest UI server is exposed, as well as path traversal via mocker redirect behavior. Although Vitest is a devDependency, this remains dangerous in developer workstations, CI environments, or any workflow that runs the test tooling, especially for an agent/plugin project where contributors may execute repository scripts without isolating them.

Known Vulnerable Dependency: @grpc/grpc-js==1.9.15 — 2 advisory(ies): CVE-2026-48068 (@grpc/grpc-js: A malformed request can cause a server crash); CVE-2026-48069 (@grpc/grpc-js: An incoming malformed compressed message can cause a client or se)

High
Category
Supply Chain
Confidence
92% confidence
Finding
The lockfile pins @grpc/grpc-js 1.9.15, and the cited advisories describe malformed network inputs causing crashes in gRPC client/server handling. Even though this is a transitive dependency brought in through Firebase rather than obviously invoked directly here, shipping a known vulnerable version is still a real supply-chain risk if any affected gRPC paths are reachable.

Known Vulnerable Dependency: nanoid==3.3.11 — 3 advisory(ies): CVE-2026-67214 (nanoid: non-secure generators can loop indefinitely with negative size); CVE-2026-67213 (nanoid: custom generators can loop indefinitely when size is zero); CVE-2026-73086 (nanoid: Integer Overflow or Wraparound)

High
Category
Supply Chain
Confidence
84% confidence
Finding
nanoid 3.3.11 is included and the listed flaws can cause infinite loops or integer wraparound in non-secure/custom generator paths, producing denial-of-service conditions. Because nanoid is transitive and often used in tooling, reachability may be limited, but the version itself is still vulnerable.

Known Vulnerable Dependency: picomatch==4.0.3 — 2 advisory(ies): CVE-2026-33672 (Picomatch: Method Injection in POSIX Character Classes causes incorrect Glob Mat); CVE-2026-33671 (Picomatch has a ReDoS vulnerability via extglob quantifiers)

High
Category
Supply Chain
Confidence
88% confidence
Finding
picomatch 4.0.3 is a real vulnerable version with reported ReDoS and glob-matching manipulation issues. Since it is commonly used in tooling to process attacker-influenced paths or patterns, exploitation can cause excessive CPU consumption or incorrect file selection in development/build workflows.

Known Vulnerable Dependency: postcss==8.5.6 — 4 advisory(ies): CVE-2026-45623 (PostCSS: Arbitrary file read and information disclosure via attacker-controlled ); CVE-2026-69153 (PostCSS: incomplete fix of GHSA-6g55-p6wh-862q — attacker-controlled sourceMappi); CVE-2026-41305 (PostCSS has XSS via Unescaped </style> in its CSS Stringify Output) +1 more

High
Category
Supply Chain
Confidence
86% confidence
Finding
postcss 8.5.6 is present and the cited advisories include arbitrary file read, information disclosure, and XSS conditions in specific processing scenarios. Although this instance is dev-only via Vite, build and dev tooling often handles project files and browser-served assets, so vulnerable parsing/serialization can still be impactful in developer environments or CI.

Known Vulnerable Dependency: rollup==4.58.0 — 1 advisory(ies): CVE-2026-27606 (Rollup 4 has Arbitrary File Write via Path Traversal)

High
Category
Supply Chain
Confidence
84% confidence
Finding
rollup 4.58.0 is a known vulnerable version per the cited path traversal/arbitrary file write advisory. In this project it is a dev/build dependency, which reduces direct runtime exposure, but compromise of build tooling can still affect developer machines, CI runners, or generated artifacts.

Known Vulnerable Dependency: vite==7.3.1 — 5 advisory(ies): CVE-2026-39365 (Vite Vulnerable to Path Traversal in Optimized Deps `.map` Handling); CVE-2026-53571 (vite: `server.fs.deny` bypass on Windows alternate paths); CVE-2026-39363 (Vite Vulnerable to Arbitrary File Read via Vite Dev Server WebSocket) +2 more

High
Category
Supply Chain
Confidence
91% confidence
Finding
vite 7.3.1 is present with multiple advisories for path traversal, arbitrary file read, and dev-server bypass conditions. Because Vite commonly runs a network-accessible development server and serves project files, the skill context makes this more dangerous than a dormant library issue if developers run it locally or in shared environments.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The README states that the plugin connects to the HashBox iOS app via Firebase webhook for push notifications, but it does not clearly warn that notification content is transmitted to external infrastructure off-device. Because the documented payloads include articles, metrics, and audit findings, users may unknowingly send sensitive operational or security data to third-party services, creating privacy, compliance, and data exposure risks.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The README explicitly instructs users to save a HashBox API token locally but provides no warning about secure storage, scope, rotation, or the risks of storing long-lived credentials on disk. In an agent/plugin context, local token persistence can expose credentials to other tools, logs, backups, or compromised environments, enabling unauthorized notification sending or account misuse.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill instructs users to store an API token locally in `hashbox_config.json` but provides no warning about credential sensitivity, file permissions, or the risk of committing the file to source control. This can lead to token disclosure through local compromise, backups, logs, or accidental repository inclusion, enabling unauthorized use of the HashBox account.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill encourages sending arbitrary notification content and structured audit/metric data through a Firebase webhook without warning that this transmits data to a third-party service and potentially to a mobile device. Users may send sensitive operational, audit, or security information without understanding the privacy, retention, or exposure implications.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
The usage examples invoke `npx tsx` without pinning an explicit package version. `npx` may resolve and execute whatever version is available locally or fetch the latest package from the registry, which creates a supply-chain risk if a malicious or compromised release is published or if behavior changes unexpectedly.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
The dry-run usage example also relies on unpinned `npx tsx`, exposing the same supply-chain execution risk. Even though this is only documentation/comments in a test script, users commonly copy-paste such commands, so the unsafe pattern can directly influence real execution behavior.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The plugin is presented as a push-notification connector, but it also automatically initializes a Firebase-backed inbound command listener on registration whenever prior configuration exists. That creates an additional remote control channel that can receive and surface external instructions, expanding the trust boundary and attack surface beyond what the tool descriptions and plugin summary suggest.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
This function sends arbitrary article, metric, or audit content to a hardcoded external webhook along with an authentication token, creating a clear data egress path. Because the skill context provides no visible user consent, destination validation, or minimization of transmitted data, sensitive inputs could be exfiltrated to a third-party service without adequate transparency or control.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The code performs a POST of the full notification payload to an external service and includes the token in the URL query string, but there is no user-facing disclosure or indication of this transfer in the code path shown. Putting the token in the query string also increases exposure through logs, proxies, and monitoring systems, making unauthorized reuse more likely if infrastructure telemetry is accessible.

Intent-Code Divergence

Medium
Confidence
81% confidence
Finding
The docstring describes this function as configuring the plugin with a token and saving values to disk, but the earlier documentation for the exchanged token states it enables the plugin to authenticate as the user and listen to the agent_commands Firestore collection. That is a meaningful intent difference: the documented purpose here sounds like local configuration, while the actual effect is provisioning credentials for remote authenticated access.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The code writes both the original HB- token and the exchanged custom authentication token to a JSON file in the current working directory without any access control, encryption, permission hardening, or user warning. If another local user, process, repository sync, backup tool, or accidental commit exposes this file, an attacker may be able to reuse the tokens to impersonate the user or access associated backend resources.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The heading "错题本 / Error Log" introduces a Chinese term in the document without any stated user opt-in or justification for mixing locales. Under the language/locale policy, forcing or implicitly imposing a specific language without user choice can be a policy concern, even when partial English translation is present.

Known Vulnerable Dependency: @protobufjs/utf8==1.1.0 — 1 advisory(ies): CVE-2026-44288 (protobufjs has overlong UTF-8 decoding)

Low
Category
Supply Chain
Confidence
79% confidence
Finding
@protobufjs/utf8 1.1.0 is present and the advisory concerns overlong UTF-8 decoding, which can lead to incorrect input validation or parser discrepancies. In this lockfile it appears only as a low-severity transitive issue, with no evidence in the file itself that attacker-controlled protobuf text processing is exposed directly.

Known Vulnerable Dependency: @vitest/mocker==4.0.18 — 1 advisory(ies): CVE-2026-84373 (Vitest: Path Traversal / Arbitrary File Read via @vitest/mocker Redirect Mock)

Low
Category
Supply Chain
Confidence
83% confidence
Finding
@vitest/mocker 4.0.18 is a real vulnerable version per the cited path traversal/arbitrary file read advisory. However, it is a dev-only test dependency, so exploitation generally requires running the test tooling in a context where an attacker can influence mock redirect behavior, making production exposure lower.

Known Vulnerable Dependency: esbuild==0.27.3 — 1 advisory(ies): GHSA-g7r4-m6w7-qqqr (esbuild allows arbitrary file read when running the development server on Window)

Low
Category
Supply Chain
Confidence
81% confidence
Finding
esbuild 0.27.3 is present with a disclosed arbitrary file read issue affecting the development server on Windows. This is a real dependency risk, but in this lockfile esbuild is dev-only and the advisory scope is limited to development-server usage on affected platforms.