Back to skill

Security audit

Olvid Channel

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real Olvid channel plugin, but it handles credentials, message attachments, and shutdown control in ways that should be reviewed before installation.

Review this before installing. Use only a local daemon or an HTTPS daemon you control, treat the Olvid client key as a secret that may be stored in OpenClaw configuration, and avoid enabling this in environments where untrusted senders can push large attachments until attachment limits and cleanup are addressed. Be aware that stopping the account may not fully stop message processing until the plugin process exits.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
src/setup.ts:20
Finding
Client Key Can Be Transmitted to an Arbitrary Plaintext HTTP Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `src/setup.ts:20-30`, `src/monitor.ts:25-27`, `src/tools.ts:45-54`, `openclaw.plugin.json:21-26` **Vulnerability Type**: Insufficient transport security and unrestricted credential destination **Risk Level**: High ### Vulnerable Code ```ts // src/setup.ts:20-30 validateInput: ({ accountId, input }) => { if (input.useEnv && accountId !== DEFAULT_ACCOUNT_ID) { return "Olvid env vars can only be used for the default account."; } const clientKey = input.botToken ?? input.token; const daemonUrl = input.url ?? input.httpUrl; if (!input.useEnv && (!clientKey || !daemonUrl)) { return "Olvid requires --bot-token and --http-url (or --use-env)."; } if (!daemonUrl?.startsWith("https://") && !daemonUrl?.startsWith("http://")) { return "Olvid --url must include a valid base URL."; } return null; }, ``` ```ts // src/monitor.ts:25-27 constructor(account: ResolvedOlvidAccount, opts: MonitorOlvidOpts, cfg: CoreConfig) { super({ serverUrl: account.daemonUrl, clientKey: account.clientKey }); this.opts = opts; ``` ```ts // src/tools.ts:45-54 export function getOlvidClient(olvidChannelAccountId?: string): OlvidClient { const runtime = getOlvidRuntime(); const config = runtime.config.loadConfig(); // Retrieve the configuration/credentials for the specific olvidChannelAccountId, or fallback to default let olvidAccount: ResolvedOlvidAccount = resolveOlvidAccount({cfg: config as CoreConfig, accountId: olvidChannelAccountId ?? "default"}); if (!olvidAccount || !olvidAccount.daemonUrl || !olvidAccount.clientKey) { olvidAccount = resolveOlvidAccount({cfg: config as CoreConfig, accountId: "default"}); } return new OlvidClient({clientKey: olvidAccount.clientKey, serverUrl: olvidAccount.daemonUrl}); } ``` ```json // openclaw.plugin.json:21-26 "properties": { "daemonUrl": { "type": "string", "default": "http://localhost:50051" }, "clientKey": { "type" ...[truncated 2569 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse the endpoint with the standard `URL` constructor instead of checking string prefixes. 2. Require `https:` for every non-loopback endpoint. 3. Permit plaintext HTTP only when the parsed hostname is a validated loopback address such as `localhost`, `127.0.0.0/8`, or `::1`. 4. Reject embedded username/password components, malformed ports, fragments, and unsupported protocols. 5. Consider an explicit daemon-host allowlist where deployments have a known endpoint. 6. Ensure TLS certificate validation remains enabled; consider certificate pinning for high-assurance deployments. 7. Warn users before changing an existing endpoint because the client key will be presented to the new destination. 8. Prefer environment variables or a platform secret store for the client key rather than ordinary configuration storage. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/monitor.ts:81
Finding
Inbound Attachments Are Saved in a Shared Persistent Temporary Directory Without Limits or Cleanup<![CDATA[ ## Vulnerability Details **File Location**: `src/monitor.ts:81-92` **Vulnerability Type**: Unsafe temporary-file handling and uncontrolled resource consumption **Risk Level**: Medium ### Vulnerable Code ```ts // src/monitor.ts:81-92 // download message attachments const attachmentsWithPaths: {attachment: datatypes.Attachment, path: string}[] = []; if (message.attachmentsCount > 0) { fs.mkdirSync("/tmp/olvid-attachments", {recursive: true}); for await (const attachment of this.attachmentList({filter: new datatypes.AttachmentFilter({messageId: message.id})})) { attachmentsWithPaths.push({ attachment: attachment, path: await attachment.save(this, "/tmp/olvid-attachments") }) } this.logger?.info(`downloaded ${attachmentsWithPaths.length} attachment(s)`) } ``` ### Technical Analysis Every inbound attachment is written beneath the fixed, predictable directory `/tmp/olvid-attachments`. The code does not: - Create a private directory for the current process or message. - Set restrictive permissions. - Enforce a maximum attachment count or aggregate byte limit. - Validate or generate destination filenames itself. - Remove downloaded attachments after agent processing. - Use a `finally` block to guarantee cleanup after failures. Attachments originate from remote Olvid participants and must therefore be treated as untrusted input. Persisting them in a shared predictable location can retain sensitive user data after processing. Repeated messages with large or numerous attachments can consume disk space. Depending on how the dependency chooses filenames, shared-directory reuse may also create collision or local interference risks. The directory is used to support the declared media functionality, but a global persistent directory and unbounded retention are not necessary for that function. ### Attack Path 1. An attacker who can send messages to an accepted Olvid discussion sends messages containing numerous or large attachments. ...[truncated 1351 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a private temporary directory with `fs.promises.mkdtemp()` for each message or processing operation. 2. Set directory permissions to `0700` and file permissions to `0600`. 3. Generate trusted random local filenames instead of relying on sender-controlled names. 4. Enforce maximum per-file size, attachment count, and aggregate message size before downloading. 5. Apply account-level and global storage quotas. 6. Check available storage and reject downloads that would exceed a safe threshold. 7. Remove all downloaded files and their temporary directory in a `finally` block after reply processing. 8. Add startup cleanup for abandoned directories left by crashes, with strict ownership and path validation. 9. Avoid following symbolic links and use exclusive file creation where supported. 10. Log rejected attachments without recording sensitive filenames or content. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/monitor.ts:209
Finding
Stopping an Account Does Not Terminate Its Monitor or Reconnection Loop<![CDATA[ ## Vulnerability Details **File Location**: `src/channel.ts:172-184`, `src/monitor.ts:209-247` **Vulnerability Type**: Broken lifecycle control and failure to honor cancellation **Risk Level**: Medium ### Vulnerable Code ```ts // src/channel.ts:172-184 return monitorOlvidProvider({ accountId: account.accountId, config: ctx.cfg as CoreConfig, runtime: ctx.runtime, abortSignal: ctx.abortSignal, logger: ctx.log, statusSink: (patch: Partial<ChannelAccountSnapshot>) => ctx.setStatus({ accountId: ctx.accountId, ...patch }), }); }, stopAccount: async (ctx) => { ctx.setStatus({ accountId: ctx.accountId, lastStopAt: Date.now() }); }, ``` ```ts // src/monitor.ts:209-247 let globalRunBot = false; export async function monitorOlvidProvider(opts: MonitorOlvidOpts = {}): Promise<void> { const core = getOlvidRuntime(); const cfg: CoreConfig = opts.config ?? (core.config.loadConfig() as CoreConfig); const account = resolveOlvidAccount({ cfg: cfg, accountId: opts.accountId }); if (!account.daemonUrl) { throw new Error(`Olvid daemon url not configured for account "${account.accountId}"`); } if (!account.clientKey) { throw new Error(`Olvid client key not configured for account "${account.accountId}"`); } globalRunBot = true; while (globalRunBot) { try { let bot = new OpenClawBot(account, opts, cfg); opts.statusSink?.({connected: true, lastConnectedAt: Date.now()}); await bot.waitForCallbacksEnd(); } catch (err) { opts.logger?.error(`olvid: ${err}`); opts.statusSink?.({ lastError: String(err), connected: false, lastDisconnect: {at: Date.now(), error: String(err)}, }); } opts.logger?.info("wait before reconnection try") await new Promise(resolve => setTimeout(resolve, 5_000)); } } ``` ### Technical Analysis The gateway passes an `AbortSignal` to `monitorOlvidProvider`, but the monitor never checks it or registers an abort handler. The `sto ...[truncated 1822 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Register an `abort` listener immediately when monitoring starts. 2. On cancellation, call the appropriate `OlvidClient.stop()` or connection-close method. 3. Check `opts.abortSignal?.aborted` before creating a client, after callbacks end, and before reconnecting. 4. Replace the fixed timeout with an abortable delay. 5. Maintain a separate controller or lifecycle state for each account rather than using a module-global boolean. 6. Have `stopAccount` signal cancellation and await complete monitor termination before reporting success. 7. Remove callback registrations and release resources during shutdown. 8. Add tests confirming that no inbound processing or reconnect attempt occurs after an account is stopped. ]]>

T08 · Insecure Dependencies

Note
Location
package.json:14
Finding
Publishing Script Executes an Unpinned Package Through npx<![CDATA[ ## Vulnerability Details **File Location**: `package.json:14` **Vulnerability Type**: Unpinned executable development dependency **Risk Level**: Low ### Vulnerable Code ```json // package.json:14 "publishSkill": "git branch --show-current | grep -qx 'clawhub' && npx clawhub login && npx clawhub publish `pwd` --version '0.1.0' --name 'Olvid Channel' --slug olvid-channel" ``` ### Technical Analysis The publishing script invokes `npx clawhub` without an exact version and without declaring `clawhub` as a locked development dependency. If the package is not already present locally, `npx` may retrieve and execute package code from the configured npm registry. This is not an automatic installation hook and does not affect normal plugin runtime unless a maintainer explicitly runs `npm run publishSkill`. Nevertheless, publishing commonly occurs in a privileged developer or CI environment containing registry credentials, source code, and release permissions. Executing an unpinned package at that point creates avoidable supply-chain exposure. The lockfile also contains a large transitive OpenClaw peer-dependency tree, including network and credential-provider packages. The audit found no project source that invokes those AWS credential providers. The concrete issue here is the explicit unpinned `npx` execution, not evidence that the lockfile itself deliberately exfiltrates credentials. ### Attack Path 1. A maintainer or CI runner checks out the expected `clawhub` branch. 2. The maintainer runs `npm run publishSkill`. 3. The branch check passes. 4. `npx` resolves `clawhub` from the current registry because no exact locked local executable is specified. 5. A compromised or unexpectedly changed package version executes with the maintainer or CI runner's permissions. 6. The executed package can access files, environment variables, network credentials, and publication tokens available to that process. 7. Those credentials could be stolen or used to publish a m ...[truncated 667 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add the audited publishing CLI to `devDependencies` using an exact version. 2. Commit the resulting lockfile update and verify its integrity metadata during CI. 3. Execute the locked local binary with `npm exec --offline -- clawhub` or an equivalent package-manager command that refuses network fallback. 4. Use dependency allowlisting and registry provenance verification in release workflows. 5. Run publishing in an isolated CI job with short-lived, minimally scoped credentials. 6. Pin action, container, and runtime versions used by the publication pipeline. 7. Require review when the publishing tool version or lockfile entry changes. ]]>
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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (16)

Known Vulnerable Dependency: @hono/node-server==1.19.9 — 3 advisory(ies): CVE-2026-39406 (@hono/node-server: Middleware bypass via repeated slashes in serveStatic); GHSA-frvp-7c67-39w9 (Node.js Adapter for Hono: Path traversal in `serve-static` on Windows via encode); CVE-2026-29087 (@hono/node-server has authorization bypass for protected static paths via encode)

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.

Known Vulnerable Dependency: undici==7.21.0 — 16 advisory(ies): CVE-2026-1525 (Undici has an HTTP Request/Response Smuggling issue); CVE-2026-6733 (undici vulnerable to HTTP response queue poisoning via keep-alive socket reuse); CVE-2026-1527 (Undici has CRLF Injection in undici via `upgrade` option) +13 more

High
Category
Supply Chain
Confidence
97% confidence
Finding
undici 7.21.0 appears in the lockfile as a concrete installed transitive dependency, including under the OpenClaw stack, and the listed advisories include request smuggling, response queue poisoning, and CRLF injection classes. Because this skill operates in a networked agent ecosystem and depends on OpenClaw and AI/network SDKs, vulnerable HTTP client behavior can affect outbound service-to-service communication and may be exploitable in realistic deployments.

Known Vulnerable Dependency: @mariozechner/pi-coding-agent==0.52.7 — 3 advisory(ies): CVE-2026-54326 (Pi Agent: Potential XSS in HTML session exports via Markdown URL sanitization by); CVE-2026-54328 (Pi Agent: Predictable temporary extension install paths allow local privilege es); CVE-2026-54327 (Pi Agent: Race condition in Pi auth.json writes could expose stored credentials)

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.

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
The second RP1 finding is the same underlying issue: `npx clawhub` is executed without a pinned version in a release script. Because `npx` can resolve and run remote package code, a compromised or changed upstream package could execute arbitrary code in the publisher's environment and affect credentials or published artifacts.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
Inbound message attachments are automatically persisted to a fixed directory under /tmp before any policy check, size/type validation, or explicit user approval. In a messaging integration, this increases exposure to malicious or sensitive files, can leave recoverable data on disk, and may enable disk-filling or downstream processing risks if other components trust these saved paths.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The skill reads a sensitive client key from an environment variable and then proceeds to use it during onboarding, which expands secret ingestion beyond explicit user entry. Although the code asks for confirmation, pulling long-lived credentials from process environment increases the chance of unintended secret exposure or persistence, especially in shared shells, CI environments, or inherited process contexts.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The client key is written into the configuration object without any user-facing warning that a sensitive credential will be persisted. If the configuration is stored in plaintext or in a broadly accessible location, this can expose the credential to other local users, backups, logs, or accidental source control commits.

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
60% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"description": "Olvid native channel for OpenClaw.",
  "type": "module",
  "dependencies": {
    "@olvid/bot-node": "^1.5.0",
    "zod": "^4.3.6"
  },
  "devDependencies": {
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"type": "module",
  "dependencies": {
    "@olvid/bot-node": "^1.5.0",
    "zod": "^4.3.6"
  },
  "devDependencies": {
    "typescript": "^5.9.3"
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"zod": "^4.3.6"
  },
  "devDependencies": {
    "typescript": "^5.9.3"
  },
  "peerDependencies": {
    "openclaw": "2026.2.6-3"
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The code reads `process.env.OLVID_CLIENT_KEY` and `process.env.OLVID_DAEMON_TARGET`, which are sensitive configuration values, but there is no confirmation prompt, logging, comment, or docstring disclosing that the skill will consume environment-provided credentials/settings. For code files, accessing sensitive environment variables without any visible disclosure matches the missing user warnings criterion.

Context-Inappropriate Capability

Low
Confidence
76% confidence
Finding
Reading process environment variables such as OLVID_DAEMON_TARGET and OLVID_CLIENT_KEY introduces access to host-provided configuration and secrets. For a skill described only as adding a native Olvid channel, that capability is not explicitly justified by the stated purpose, even though it may be convenient for onboarding.

Description-Behavior Mismatch

Low
Confidence
81% confidence
Finding
The manifest description says only 'Add a native Olvid channel in OpenClaw,' which suggests channel integration at a high level. In this file, the adapter not only enables the channel but also collects a sensitive client key from environment variables or user prompts and stores it into configuration, expanding the behavior into credential onboarding and persistence.

Missing User Warnings

Low
Confidence
82% confidence
Finding
This code sends user-provided text and optional attachment paths to an external Olvid client via `messageSendWithAttachmentsFiles` and `messageSend`, which is a network/data-transmission operation. While the function name suggests sending is its purpose, the file itself provides no user-facing confirmation, prompt, or explicit disclosure beyond a brief developer comment, so there is no visible warning at the point of transmission in this code file.

Static analysis

No suspicious patterns detected.