Back to skill

Security audit

MuHaven RWA Portfolio

Security checks for vulnerabilities and agentic risk

Overview

The skill largely matches its finance-portfolio purpose, but it handles account JWTs and financial intents while relying on under-enforced and overstated backend and tool-integrity protections.

Install only if you trust MuHaven and can keep the backend URL at the default production value, or run in a runtime that truly enforces the declared network allowlist. Prefer read-only mode if you only need portfolio views, verify the broker daemon and account session are yours, and treat any request to change backend_url, dashboard_url, or broker_endpoint as security-sensitive.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
dist/index.cjs:18844
Finding

Configurable Backend URL Can Receive the Broker JWT

Content
View full analysis
ctrl.abort(), this.options.timeoutMs ...[truncated 4041 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
dist/index.cjs:19965
Finding

Tool-Description Integrity Verification Fails Open When Pin Data Is Missing

Content
View full analysis
[p.name, p.sha256]) ); const drift = []; for (const t of TOOL_DESCRIPTORS) { const live = hashToolDescriptor(t); const pin = pinnedMap.get(t.name); if (!pin || pin !== live) { drift.push({ name: t.name, live, pinned: pin ?? "" }); } } return { ok: drift.length === 0, drift }; } ``` ```js async function runMcpStdioCli(opts = {}) { const config2 = loadMcpConfig(); const pinned = await loadPinnedToolHashes(); const verify = verifyToolHashes(pinned); if (!verify.ok) { process.stderr.write( "tool-description hash drift detected — refusing to start. drift:\n" + JSON.stringify(verify.drift, null, 2) + "\n" ); process.exit(70); } // Server initialization continues when pinned is null. } ``` The package documentation claims that the pin file is present and enforced: ```md The skill only re-advertises the `mcp.toolset_subset` listed in this frontmatter; descriptor SHA-256 hashes are pinned in `tool-hashes.json` and verified on every skill load (mcp-context-protect ...[truncated 2620 chars]
Remediation
View remediation
", live: "", pinned: "" }] }; } // Continue strict descriptor verification. } ``` 3. Validate that: - Every exposed tool has exactly one pin - No duplicate tool names exist - No unknown or stale pins exist - Every hash is a correctly formatted SHA-256 value - The pinned set exactly matches the exposed tool subset 4. Do not suppress filesystem or JSON parsing errors for a mandatory security artifact. 5. Add a release test that unpacks the final tarball and confirms the presence and validity of `tool-hashes.json`. 6. Add a negative startup test proving that deletion or corruption of the file causes a non-zero exit. 7. Bind the tool subset, descriptor hashes, Skill version, and bundled MCP version into one signed manifest to reduce independent drift points. 8. Correct the documentation only after the packaged artifact demonstrably enforces the claimed fail-closed behavior. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Rogue AgentSelf-Modification, Session Persistence
Findings (24)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 34)May include surrounding context.

md
entry: dist/index.cjs

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · dist/index.cjs (reported line 1915)May include surrounding context.

js
function validateAsync() {
        const ruleErrs = gen2.let("ruleErrs", null);
        gen2.try(() => assignValid((0, codegen_1._)`await `), (e) => gen2.assign(valid, false).if((0, codegen_1._)`${e} instanceof ${it.ValidationError}`, () => gen2.assign(ruleErrs, (0, codegen_1._)`${e}.errors`), () => gen2.throw(e)));
        return ruleErrs;
      }
      function validateSync() {
        const validateErrs = (0, codegen_1._)`${validateRef}.errors`;

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · dist/index.js (reported line 1904)May include surrounding context.

js
function validateAsync() {
        const ruleErrs = gen2.let("ruleErrs", null);
        gen2.try(() => assignValid((0, codegen_1._)`await `), (e) => gen2.assign(valid, false).if((0, codegen_1._)`${e} instanceof ${it.ValidationError}`, () => gen2.assign(ruleErrs, (0, codegen_1._)`${e}.errors`), () => gen2.throw(e)));
        return ruleErrs;
      }
      function validateSync() {
        const validateErrs = (0, codegen_1._)`${validateRef}.errors`;

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · dist/index.cjs (reported line 3112)May include surrounding context.

js
nv.root.schema)
        return env;
      return void 0;
    }
  }
});

// ../../node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/refs/data.json
var require_data = __commonJS({
  "../../node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/refs/data.json"(exports$1, module) {
    module.exports = {
      $id: "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#",
      description: "Meta-schema for $data reference (JSON AnySchema extension proposal)",
      type: "object",
      required: ["$data"],
      properties: {
        $data: {
          type: "string",
          anyOf: [{ format: "relative-json-pointer" }, { format: "json-pointer" }]
        }
      },
      additionalProperties: false
    };
  }
});

// ../../node_modules/.pnpm/fast-uri@3.1.0/node_modules/fast-uri/lib/utils.js
var require_utils = __commonJS({
  "../../node_modules/.pnpm/fast-uri@3.1.0/node_modules/fast-uri/lib/utils.js"(exports$1, module) {
    init_esm_shims();
    var isUUID = Re

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · dist/index.js (reported line 3101)May include surrounding context.

js
nv.root.schema)
        return env;
      return void 0;
    }
  }
});

// ../../node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/refs/data.json
var require_data = __commonJS({
  "../../node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/refs/data.json"(exports$1, module) {
    module.exports = {
      $id: "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#",
      description: "Meta-schema for $data reference (JSON AnySchema extension proposal)",
      type: "object",
      required: ["$data"],
      properties: {
        $data: {
          type: "string",
          anyOf: [{ format: "relative-json-pointer" }, { format: "json-pointer" }]
        }
      },
      additionalProperties: false
    };
  }
});

// ../../node_modules/.pnpm/fast-uri@3.1.0/node_modules/fast-uri/lib/utils.js
var require_utils = __commonJS({
  "../../node_modules/.pnpm/fast-uri@3.1.0/node_modules/fast-uri/lib/utils.js"(exports$1, module) {
    init_esm_shims();
    var isUUID = Re

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · config.json (reported line 68)May include surrounding context.

json
"stdio": "manifest_only"
    },
    "secrets": {
      "storage": "os_keychain",
      "deny_paste_ui": true,
      "references": [
        {

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · config.json (reported line 117)May include surrounding context.

json
"stdio": "manifest_only"
    },
    "secrets": {
      "storage": "os_keychain",
      "deny_paste_ui": true,
      "references": [
        {

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · manifest.json (reported line 63)May include surrounding context.

json
"stdio": "manifest_only"
    },
    "secrets": {
      "storage": "os_keychain",
      "deny_paste_ui": true,
      "references": [
        {

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · SECURITY.md (reported line 56)May include surrounding context.

md
2. **Tool-description hash pinning** — `@muhaven/mcp`'s
   `verify-tool-hashes --check` runs at server startup and exits non-zero
   on descriptor drift (MCPoison-style attacks fail-closed).
3. **Broker JWT-scope check** — write paths require a JWT scoped to
   `mcp.propose.*`. The broker daemon's `signHash` is the only route to
   a UserOp signature; the JWT is bound to the user's account.
4. **Three-tier confirmation** — `position.*` + `policy.pause` emit

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The statement that the skill 'does not read or write any path on the operator's system' is contradicted by the earlier requirement that it call a local broker over a socket or named-pipe path such as ~/.muhaven/broker.sock. Accessing that IPC endpoint is local filesystem/path interaction, so the documentation presents a stronger no-filesystem-access claim than the behavior described elsewhere in the same file.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The checklist claims that non-MuHaven egress is blocked by the runtime, but earlier comments explicitly state that under host_native fallback in staging the network allowlist is only advisory and not kernel-enforced. This creates a documentation-to-enforcement gap that can mislead reviewers or users into trusting a stronger boundary than actually exists in some deployments.

Content

No source excerpt is available for this finding.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill grants unrestricted tool access without appropriate constraints. An agent with unfettered tool access can perform arbitrary actions including file modification, network requests, and code execution.

Content

Scanner excerpt · dist/index.cjs (reported line 16465)May include surrounding context.

js
* Controls when tools are used:
   * - "auto": Model decides whether to use tools (default)
   * - "required": Model MUST use at least one tool before completing
   * - "none": Model MUST NOT use any tools
   */
  mode: _enum(["auto", "required", "none"]).optional()
});

Unrestricted Tool Access

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill grants unrestricted tool access without appropriate constraints. An agent with unfettered tool access can perform arbitrary actions including file modification, network requests, and code execution.

Content

Scanner excerpt · dist/index.js (reported line 16454)May include surrounding context.

js
* Controls when tools are used:
   * - "auto": Model decides whether to use tools (default)
   * - "required": Model MUST use at least one tool before completing
   * - "none": Model MUST NOT use any tools
   */
  mode: _enum(["auto", "required", "none"]).optional()
});

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The comment states the OpenClaw skill 'never advertises' issuer flows, but the file still embeds the issuer tool descriptors and handler wiring in shared registries at L19268-L19304 and L19885-L19905, only filtering them later in selectOpenClawSubsetRegistry at L20136-L20148. That is an intent/documentation mismatch because the codebase does include and wire those capabilities, even if they are later excluded from the exposed subset.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The statement 'The skill cannot prompt you to paste a token' overstates what this config guarantees. The policy denies paste UI, environment blocks, and clipboard I/O, but that does not inherently prove the skill can never ask a user for a token through other interaction paths; the claim is stronger than the demonstrated control.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The inline comment at L20130-L20144 says the bundled OpenClaw skill 'never advertises' issuer and governance tools. However, the module exports TOOLSET_EXCLUDED at L20176, which contains those excluded tool names, so the code still exposes their identifiers to consumers even if they are not callable. This is a documentation-to-code contradiction about what is exposed by the skill bundle.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 31)May include surrounding context.

json
"CHANGELOG.md"
  ],
  "devDependencies": {
    "@types/node": "^22.0.0",
    "rimraf": "^5.0.0",
    "tsup": "^8.5.1",
    "tsx": "^4.21.0",

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 32)May include surrounding context.

json
],
  "devDependencies": {
    "@types/node": "^22.0.0",
    "rimraf": "^5.0.0",
    "tsup": "^8.5.1",
    "tsx": "^4.21.0",
    "typescript": "^5.7.0",

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
71% confidence
Finding

The unpinned tsup dependency creates avoidable supply-chain uncertainty because semver ranges allow different versions to be installed over time, including potentially vulnerable or compromised releases. In this case the risk is somewhat elevated because a separate finding notes a known advisory for tsup, and the manifest alone does not let defenders verify whether installs are safe.

Content

Scanner excerpt · package.json (reported line 33)May include surrounding context.

json
"devDependencies": {
    "@types/node": "^22.0.0",
    "rimraf": "^5.0.0",
    "tsup": "^8.5.1",
    "tsx": "^4.21.0",
    "typescript": "^5.7.0",
    "vitest": "^3.0.0",

Unverifiable Dependency: tsup has 1 known advisory(ies) (CVE-2024-53384 (tsup DOM Clobbering vulnerability)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
79% confidence
Finding

This is a valid supply-chain risk because tsup has a referenced advisory and the manifest's semver range prevents verification of whether an installed version is affected. The danger is primarily to developers and CI systems building the skill rather than end users, but compromised build environments can still taint released artifacts.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 34)May include surrounding context.

json
"@types/node": "^22.0.0",
    "rimraf": "^5.0.0",
    "tsup": "^8.5.1",
    "tsx": "^4.21.0",
    "typescript": "^5.7.0",
    "vitest": "^3.0.0",
    "@muhaven/mcp": "0.1.5"

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 35)May include surrounding context.

json
"rimraf": "^5.0.0",
    "tsup": "^8.5.1",
    "tsx": "^4.21.0",
    "typescript": "^5.7.0",
    "vitest": "^3.0.0",
    "@muhaven/mcp": "0.1.5"
  },

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
75% confidence
Finding

Leaving vitest unpinned creates supply-chain ambiguity and makes it difficult to determine whether installed versions are exposed to known advisories. Although vitest is a devDependency, some listed issues involve file read or code execution in developer or CI contexts, so version drift can still create meaningful risk during testing workflows.

Content

Scanner excerpt · package.json (reported line 36)May include surrounding context.

json
"tsup": "^8.5.1",
    "tsx": "^4.21.0",
    "typescript": "^5.7.0",
    "vitest": "^3.0.0",
    "@muhaven/mcp": "0.1.5"
  },
  "engines": {

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
83% confidence
Finding

This is a true vulnerability signal because vitest has multiple advisories involving arbitrary file read and possible code execution, and the manifest does not pin a version that can be checked against fixed releases. Even though Vitest is development-only, those impacts are serious in local development or CI environments where secrets, repository contents, and signing credentials may be present.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
dist/index.cjs:2966

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
dist/index.js:2955