Back to skill

Security audit

RMN Soul

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent memory-backup purpose, but it handles very sensitive agent memory and wallet data with broad publication, persistence, and unsafe command execution patterns that need human review.

Install only after reviewing and redacting the exact memory files it will ingest. Do not use it in workspaces containing secrets, private user data, proprietary logs, or sensitive issues. Avoid storing RMN_SPONSOR_KEY in generated config, do not run suggested curl-to-bash installers, and treat restored memory and visualization output as untrusted until verified in an isolated workspace.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
Findings (9)

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/anchor.js:126
Finding
Unpinned Remote Installer Piped Directly into Bash<![CDATA[ ## Vulnerability Details **File Location**: `scripts/anchor.js:126-129` **Vulnerability Type**: Remote payload retrieval and execution **Risk Level**: Critical ### Vulnerable Code ```js const cast = hasCast(); if (!cast) { console.log('\n⚠️ Foundry cast not found. Install: curl -L https://foundry.paradigm.xyz | bash && foundryup'); return; } ``` ### Technical Analysis When Foundry is unavailable, the Skill recommends downloading a mutable remote response and piping it directly into Bash. The command does not pin a version, verify a checksum, validate a digital signature, or give the user an opportunity to inspect the downloaded script. Although the JavaScript only prints the instruction rather than executing it automatically, the instruction is part of the Skill's runtime workflow and encourages a user or Agent to create a remote code-execution channel. HTTPS protects transport integrity but does not make the returned artifact immutable or protect against compromise of the remote distribution infrastructure. This behavior is not necessary for the Skill's declared memory-management functionality. A locally installed, verified blockchain client could instead be treated as an explicit prerequisite. ### Attack Path 1. A user or Agent invokes `scripts/anchor.js` without Foundry installed. 2. The Skill displays the `curl ... | bash` installation command. 3. The user or Agent executes the suggested command. 4. The remote server, its distribution account, DNS path, or delivery infrastructure serves altered content. 5. Bash executes that content immediately with the invoking user's privileges. ### Impact Assessment A compromised response can execute arbitrary local commands, read Agent memory and wallet configuration, steal credentials, modify persistent workspace files, install additional persistence, or take control of the invoking account. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Remove all pipe-to-shell installation instructions. - Direct users to a pinned Foundry release from an official release page. - Download the release artifact to disk before execution. - Verify a published cryptographic checksum and, where available, a maintainer signature. - Display the exact version and source to the user and require explicit approval. - Prefer a documented prerequisite check that exits safely instead of installing software during the Skill workflow. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/anchor.js:42
Finding
Full Agent Memory and Identity Data Published to IPFS<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.js:17-49, 125-183`; `scripts/anchor.js:42-54, 99-103` **Vulnerability Type**: Excessive data access and irreversible public disclosure **Risk Level**: High ### Vulnerable Code ```js function findMemoryFiles(workspace) { const files = []; const memoryMd = path.join(workspace, 'MEMORY.md'); if (fs.existsSync(memoryMd)) files.push({ path: memoryMd, type: 'memory' }); const soulMd = path.join(workspace, 'SOUL.md'); if (fs.existsSync(soulMd)) files.push({ path: soulMd, type: 'soul' }); const userMd = path.join(workspace, 'USER.md'); if (fs.existsSync(userMd)) files.push({ path: userMd, type: 'identity' }); const identityMd = path.join(workspace, 'IDENTITY.md'); if (fs.existsSync(identityMd)) files.push({ path: identityMd, type: 'identity' }); const memDir = path.join(workspace, 'memory'); if (fs.existsSync(memDir)) { for (const f of fs.readdirSync(memDir)) { if (f.endsWith('.md') && f !== 'INDEX.md') { files.push({ path: path.join(memDir, f), type: 'daily' }); } } } const issuesDir = path.join(workspace, '.issues'); if (fs.existsSync(issuesDir)) { for (const f of fs.readdirSync(issuesDir)) { if (f.startsWith('open-')) { files.push({ path: path.join(issuesDir, f), type: 'issue' }); } } } return files; } ``` ```js function uploadToIPFS(data, filename) { const jsonStr = typeof data === 'string' ? data : JSON.stringify(data); const tmpFile = path.join(DATA_DIR, `_tmp_${filename}`); fs.writeFileSync(tmpFile, jsonStr); try { const cid = execSync(`ipfs add -q "${tmpFile}"`, { timeout: 10000, encoding: 'utf-8' }).trim(); fs.unlinkSync(tmpFile); return { cid, url: `ipfs://${cid}`, gateway: `https://ipfs.io/ipfs/${cid}`, local: false }; } catch { // Local fallback omitted } } const memoryUpload = uploadToIPFS( fs.readFileSync(DB_PATH, ' ...[truncated 1799 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Make hash-only anchoring the default and do not upload `memory.json`. - Require explicit, informed consent before any external publication. - Present the exact files and fields that will be uploaded. - Add configurable allowlists instead of scanning broad workspace categories automatically. - Run secret and personal-data detection before export. - Redact sensitive fields and encrypt any externally stored memory with a user-controlled key. - Never place decryption keys or sensitive access tokens on-chain. - Clearly warn users that IPFS publication can be permanent and cannot be reliably revoked. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/anchor.js:147
Finding
Shell Command Injection in Blockchain Anchoring Operations<![CDATA[ ## Vulnerability Details **File Location**: `scripts/anchor.js:147-152, 188-201, 230-232` **Vulnerability Type**: OS command injection **Risk Level**: Critical ### Vulnerable Code ```js const result = execSync( `${cast} send --private-key ${privateKey} --rpc-url ${rpcUrl} ${config.identityRegistry} "register(string)" "${agentURI}" --json`, { timeout: 30000, encoding: 'utf-8' } ); ``` ```js for (const [key, value] of metadataCalls) { try { let val = value; if (value.startsWith('$(')) { // Evaluate subcommand val = execSync(value.slice(2, -1), { encoding: 'utf-8' }).trim(); } const result = execSync( `${cast} send --private-key ${privateKey} --rpc-url ${rpcUrl} ${config.identityRegistry} "setMetadata(uint256,string,bytes)" ${agentId} "${key}" "${val}" --json`, { timeout: 30000, encoding: 'utf-8' } ); ``` ```js if (require.main === module) { anchor({ sponsorKey: process.argv[2] || process.env.RMN_SPONSOR_KEY }).catch(console.error); } ``` ### Technical Analysis The code constructs shell command strings from command-line input, environment values, and writable configuration fields, then passes those strings to `execSync`. Node.js executes string-form `execSync` through a shell, so metacharacters such as command separators, substitutions, and redirections can change the intended command. The first positional argument becomes `sponsorKey` without validation. Other interpolated values include `identityRegistry`, `agentId`, and the executable path. The deliberate evaluation of values represented as `$()` further normalizes unsafe shell execution patterns. ### Attack Path 1. An attacker supplies a crafted first argument, modifies `rmn-soul-data/config.json`, or controls a relevant environment value. 2. The victim invokes `node scripts/anchor.js <crafted-value>`. 3. The crafted value is interpolated into the `cast send` command. 4. The shell interprets embedded metacharacters. 5. Attacker-selec ...[truncated 402 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace string-form `execSync` with `execFileSync` or `spawnSync` using an argument array and `shell: false`. - Validate private keys against the exact expected hexadecimal format. - Validate registry addresses as fixed-length Ethereum addresses. - Require `agentId` to be a non-negative decimal integer. - Resolve the `cast` executable from a trusted, fixed path. - Remove the `$()` representation and all explicit subcommand evaluation. - Keep transaction construction separate from process invocation. - Avoid including private keys in process arguments; use a secure signer or protected keystore where possible. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/resurrect.js:84
Finding
Command Injection Through Resurrection Arguments and On-Chain Metadata<![CDATA[ ## Vulnerability Details **File Location**: `scripts/resurrect.js:54-61, 84-93, 169-176` **Vulnerability Type**: OS command injection from local and remote input **Risk Level**: Critical ### Vulnerable Code ```js try { owner = execSync( `${cast} call --rpc-url ${rpcUrl} ${registry} "ownerOf(uint256)(address)" ${agentId}`, { encoding: 'utf-8' } ).trim(); memoryRoot = execSync( `${cast} call --rpc-url ${rpcUrl} ${registry} "getMetadata(uint256,string)(bytes)" ${agentId} "memoryRoot"`, { encoding: 'utf-8' } ).trim(); soulHash = execSync( `${cast} call --rpc-url ${rpcUrl} ${registry} "getMetadata(uint256,string)(bytes)" ${agentId} "soulHash"`, { encoding: 'utf-8' } ).trim(); memoryDataHex = execSync( `${cast} call --rpc-url ${rpcUrl} ${registry} "getMetadata(uint256,string)(bytes)" ${agentId} "memoryData"`, { encoding: 'utf-8' } ).trim(); memoryManifestHex = execSync( `${cast} call --rpc-url ${rpcUrl} ${registry} "getMetadata(uint256,string)(bytes)" ${agentId} "memoryManifest"`, { encoding: 'utf-8' } ).trim(); } catch (e) { console.log(`❌ Failed to read chain data: ${e.message.slice(0, 200)}`); return; } ``` ```js const cid = memoryDataUrl.replace('ipfs://', ''); try { memoryJson = execSync(`ipfs cat ${cid}`, { timeout: 10000, encoding: 'utf-8' }); console.log(' ✅ Fetched from local IPFS node'); } catch { // Gateway fallback } ``` ```js for (let i = 0; i < args.length; i++) { if (args[i] === '--agent-id' && args[i + 1]) agentId = args[++i]; if (args[i] === '--chain' && args[i + 1]) chain = args[++i]; } ``` ### Technical Analysis The command-line `agentId` is inserted directly into multiple shell commands without integer validation. In addition, `memoryDataUrl` originates from on-chain metadata and is transformed into `cid` only by removing an `ipfs://` prefix. That value is then inserted into `execSync("ipfs cat ...")`. Consequently, both local command-line input ...[truncated 1056 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Parse `agentId` with strict decimal validation and reject all non-digit input. - Validate `chain` against a fixed allowlist. - Validate CIDs with a maintained CID parser rather than string replacement or a permissive regular expression. - Invoke `cast` and `ipfs` through `execFileSync` or `spawnSync` with argument arrays and `shell: false`. - Treat all blockchain metadata as untrusted remote input. - Add output-size limits and timeouts to all external process calls. - Prefer a native, well-reviewed RPC/IPFS library to eliminate shell invocation. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/setup.js:198
Finding
Wallet Private Key Persisted in Plaintext Configuration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.js:198-214` **Vulnerability Type**: Plaintext secret storage **Risk Level**: High ### Vulnerable Code ```js const configPath = path.join(DATA_DIR, 'config.json'); if (!fs.existsSync(configPath)) { fs.writeFileSync(configPath, JSON.stringify({ chain: 'base', chainId: 8453, identityRegistry: '0x8004A169FB4a3325136EB29fA0ceB6D2e539a432', reputationRegistry: '0x8004BAa17C55a88189AE136b182e5fdA19dE9b63', sponsorKey: process.env.RMN_SPONSOR_KEY || '', autoAnchorDays: 7, ipfsEnabled: true, agentId: null, lastAnchor: null, }, null, 2)); console.log(` Config: ${configPath}`); } ``` ### Technical Analysis When `RMN_SPONSOR_KEY` is present, setup copies the wallet private key from the process environment into `rmn-soul-data/config.json`. The file is written without an explicit restrictive mode, so effective access depends on the process umask and surrounding workspace permissions. This unnecessarily changes an ephemeral secret into a persistent workspace artifact. It also conflicts with the documented configuration example, which suggests an environment-variable reference rather than storing the secret value. ### Attack Path 1. The user exports `RMN_SPONSOR_KEY` and runs `setup.js`. 2. Setup writes the private key into `config.json`. 3. The workspace is backed up, shared, committed, indexed, or accessed by another local process or user. 4. The private key is recovered and used to sign unauthorized transactions. ### Impact Assessment An attacker who obtains the key can control the associated wallet within the key's authority, spend its assets, pay gas for unauthorized transactions, update Agent metadata, and potentially take over the on-chain identity. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Never copy `RMN_SPONSOR_KEY` into a configuration file. - Store only an environment variable name or secret-manager reference. - Use a protected keystore, hardware wallet, or OS credential service for transaction signing. - Create all sensitive state files with mode `0600`. - Warn if an existing configuration contains a private key and provide a migration utility that removes it. - Add `rmn-soul-data` and secret-bearing files to version-control ignore rules. - Avoid passing private keys on the command line because process arguments may be visible to other local processes. ]]>

T02 · Agent Memory Poisoning

Error
Location
scripts/resurrect.js:106
Finding
Unverified Remote Memory Replaces Active Persistent State<![CDATA[ ## Vulnerability Details **File Location**: `scripts/resurrect.js:106-132, 137-160` **Vulnerability Type**: Persistent memory poisoning and fail-open integrity handling **Risk Level**: High ### Vulnerable Code ```js console.log('\nStep 3: Verifying memory integrity...'); fs.mkdirSync(DATA_DIR, { recursive: true }); const dbPath = path.join(DATA_DIR, 'memory.json'); fs.writeFileSync(dbPath, memoryJson); const rmn = new RecursiveMemoryNetwork(dbPath); const merkle = computeMemoryMerkle(rmn); const computedRoot = `0x${merkle.memoryRoot}`; if (computedRoot === memoryRoot) { console.log(' ✅ Merkle Root MATCHES — memory integrity verified!'); } else { console.log(' ⚠️ Merkle Root MISMATCH — memory may have been tampered with!'); console.log(` Chain: ${memoryRoot}`); console.log(` Computed: ${computedRoot}`); } const computedSoul = `0x${merkle.soulHash}`; if (computedSoul === soulHash) { console.log(' ✅ Soul Hash MATCHES — identity intact!'); } else { console.log(' ⚠️ Soul Hash MISMATCH — identity layer may have changed!'); } ``` ```js const identity = { agentId: parseInt(agentId), chain, owner, memoryRoot, soulHash, ipfs: { memoryData: memoryDataUrl, manifest: manifestUrl }, restoredAt: new Date().toISOString(), verified: computedRoot === memoryRoot, }; fs.writeFileSync( path.join(DATA_DIR, 'identity.json'), JSON.stringify(identity, null, 2) ); fs.writeFileSync(configPath, JSON.stringify({ chain, chainId: 8453, identityRegistry: registry, reputationRegistry: '0x8004BAa17C55a88189AE136b182e5fdA19dE9b63', agentId: parseInt(agentId), lastAnchor: new Date().toISOString(), autoAnchorDays: 7, ipfsEnabled: true, }, null, 2)); ``` ### Technical Analysis The downloaded memory is written to the live `memory.json` path before integrity verification. A Merkle mismatch only causes a warning; the unverified data remains in place, and the function continues to write persistent identity and configuration sta ...[truncated 999 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Download into a newly created temporary file with restrictive permissions. - Enforce response-size limits before buffering or parsing. - Parse and validate the JSON against a strict schema. - Compute the Merkle and soul hashes from the temporary data. - Abort restoration and securely delete the temporary file on any mismatch. - Replace live memory atomically only after every verification succeeds. - Preserve a timestamped backup of the existing database before replacement. - Require explicit user confirmation before restoring a different on-chain identity. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/visualize.js:184
Finding
Unauthenticated Visualization API Exposes Memory over the Network<![CDATA[ ## Vulnerability Details **File Location**: `scripts/visualize.js:184-203` **Vulnerability Type**: Unauthenticated sensitive-data exposure **Risk Level**: High ### Vulnerable Code ```js const server = http.createServer((req, res) => { if (req.url === '/api/graph') { const rmn = new RecursiveMemoryNetwork(DB_PATH); const graph = rmn.exportGraph(); const merkle = computeMemoryMerkle(rmn); res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify({ ...graph, merkle })); } else if (req.url === '/api/stats') { const rmn = new RecursiveMemoryNetwork(DB_PATH); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(rmn.stats())); } else { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(HTML); } }); server.listen(PORT, () => { console.log("🧠 AgentSoul Visualization: http://localhost:" + PORT); }); ``` ### Technical Analysis Calling `server.listen(PORT)` without a host does not restrict the server to the loopback interface. Depending on the platform, Node.js listens on the unspecified IPv6 or IPv4 address, making the service reachable through available network interfaces. The API has no authentication or authorization. `/api/graph` returns labels derived from memory content, tags, topology, timestamps, and Merkle data. It also enables wildcard CORS, allowing arbitrary websites visited by the user to read the response when network access is available. The console message claiming a localhost URL does not enforce localhost-only binding. ### Attack Path 1. The user starts `scripts/visualize.js`. 2. The server binds to all available interfaces. 3. An attacker on a reachable network connects to port 3457, or a malicious website requests the API from the victim's browser. 4. The unauthenticated `/api/graph` endpoint returns memory-derived information. 5. The attacker collects identity summari ...[truncated 312 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Bind explicitly to `127.0.0.1` or `::1`. - Remove `Access-Control-Allow-Origin: *`. - Use a strict allowlist if browser access from another origin is required. - Add authentication and authorization before supporting remote access. - Return only the minimum visualization fields and redact memory text by default. - Add secure response headers, including a restrictive Content Security Policy. - Document that remote exposure requires a separate, authenticated reverse proxy. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/visualize.js:162
Finding
Stored Cross-Site Scripting in Memory Visualization Tooltip<![CDATA[ ## Vulnerability Details **File Location**: `scripts/visualize.js:162-176` **Vulnerability Type**: Stored cross-site scripting **Risk Level**: High ### Vulnerable Code ```js canvas.addEventListener('mousemove', e => { const rect = canvas.getBoundingClientRect(); const mx = e.clientX - rect.left, my = e.clientY - rect.top; let closest = null, minD = 20; for (const n of graphNodes) { const d = Math.sqrt((n.x-mx)**2+(n.y-my)**2); if (d < minD) { minD = d; closest = n; } } hoveredNode = closest; if (closest) { tooltip.style.display = 'block'; tooltip.style.left = (e.clientX + 15) + 'px'; tooltip.style.top = (e.clientY - 10) + 'px'; tooltip.innerHTML = '<div class="layer" style="color:' + (LAYERS[closest.layer]?.color) + '">' + (LAYERS[closest.layer]?.emoji) + ' ' + (LAYERS[closest.layer]?.name) + '</div><div class="content">' + closest.label.slice(0,100) + '</div><div class="meta">Weight: ' + closest.weight.toFixed(2) + ' | Tags: ' + (closest.tags||[]).slice(0,3).join(', ') + '</div>'; } else { tooltip.style.display = 'none'; } }); ``` ### Technical Analysis `closest.label` and `closest.tags` originate from memory-file content and are inserted into `innerHTML` without escaping or sanitization. If an imported or restored memory entry contains HTML with an executable event handler or another browser-executable construct, it is parsed as markup when the user hovers over the corresponding graph node. Because the payload is stored in persistent memory, this is a stored XSS condition rather than a transient reflection. ### Attack Path 1. An attacker causes crafted HTML to be imported into an Agent memory source or restored from an external memory database. 2. The user starts the visualization server. 3. The browser fetches the graph and creates nodes containing the crafted label or tags. 4. The user moves the pointer over the malicio ...[truncated 457 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Build tooltip elements through DOM APIs. - Assign all memory-derived text using `textContent`, never `innerHTML`. - Validate numeric fields such as layer and weight before rendering. - If rich markup is genuinely required, sanitize it with a maintained allowlist-based sanitizer. - Add a restrictive Content Security Policy that disallows inline script and event handlers. - Treat imported, restored, and locally authored memory content as untrusted. ]]>

T06 · System Persistence

Warning
Location
SKILL.md:28
Finding
Persistent Heartbeat Hook Performs Automatic Destructive Memory Mutation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:28-56`; `scripts/heartbeat.js:17-18`; `scripts/rmn-engine.js:151-168` **Vulnerability Type**: Persistent hook with automatic state mutation **Risk Level**: Medium ### Vulnerable Code ```md ## Setup (First Run) When this skill is first loaded, execute these steps in order: ### Step 3: Add Heartbeat Hook Add to HEARTBEAT.md: ``` - **RMN Soul** — Run `node <skill_directory>/scripts/heartbeat.js` for decay tick + auto-anchor check ``` ``` ```js const rmn = new RecursiveMemoryNetwork(DB_PATH); const decay = rmn.decayTick(); ``` ```js decayTick() { const decayRates = [0.02, 0.01, 0.005, 0.001, 0]; let pruned = 0; for (const [id, node] of this.nodes) { const rate = decayRates[node.layer] || 0.01; node.weight = Math.max(0, node.weight - rate); if (node.weight <= 0.05 && node.layer < 4) { this.nodes.delete(id); pruned++; } } this.save(); return { pruned, remaining: this.nodes.size }; } ``` ### Technical Analysis Loading the Skill instructs the Agent to modify `HEARTBEAT.md`, creating a cross-session execution hook. The hooked script is not read-only: each invocation reduces memory weights, permanently saves the database, and eventually deletes non-identity nodes. The operation lacks an explicit confirmation step, dry-run mode, retention policy, or automatic backup. This means a routine heartbeat check can cause irreversible state loss. The persistent hook exceeds what is necessary to report memory status or determine whether an anchor is due. ### Attack Path 1. The Skill is loaded and its setup instructions are followed. 2. A command invoking `heartbeat.js` is added to persistent `HEARTBEAT.md`. 3. Future heartbeat cycles automatically execute the script. 4. Every cycle reduces node weights and saves the modified database. 5. Once a node's weight reaches the pruning threshold, it is deleted. 6. Repeated background execution progressively removes persis ...[truncated 337 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not modify `HEARTBEAT.md` automatically when the Skill is loaded. - Require explicit user opt-in before installing any persistent hook. - Separate read-only status reporting from destructive decay. - Make heartbeat execution read-only by default. - Require explicit approval for pruning and show the exact nodes affected. - Add dry-run support, configurable retention limits, and automatic backups. - Record an audit log of every weight change and deletion. - Provide a documented command that cleanly removes the heartbeat hook. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (27)

Ssd 3

High
Confidence
97% confidence
Finding
The skill's core behavior is to archive memory and logs to IPFS and make them recoverable later, which normalizes persistent external storage of potentially sensitive agent and user data. In the context of agent memory, logs often contain API outputs, internal reasoning artifacts, user content, and operational metadata, so automatic retention/export materially increases disclosure risk.

Missing User Warnings

High
Confidence
95% confidence
Finding
The README explicitly advertises automatic upload of agent memory data to IPFS but does not clearly warn that IPFS content may be publicly retrievable, effectively persistent, and inappropriate for secrets, personal data, logs, or proprietary context. Because the skill processes MEMORY.md and log-like sources, users could unknowingly publish sensitive data to decentralized storage with limited practical ability to revoke it.

Ssd 3

High
Confidence
96% confidence
Finding
The architecture section describes transforming raw inputs, memory files, issues, and layered cognition data into a complete memory artifact stored on IPFS and linked on-chain. This context makes the issue more dangerous because it is not a narrow backup of a harmless artifact; it appears to aggregate broad, high-sensitivity inputs into long-lived externally retrievable storage.

Missing User Warnings

High
Confidence
96% confidence
Finding
The skill prominently advertises IPFS upload and on-chain anchoring of agent memory, but it does not provide a clear, explicit user-facing warning that memory content may be disclosed to third-party infrastructure and potentially become effectively permanent. In this context, users may reasonably not understand that operational memory, issue files, and other workspace-derived content could leave the local environment and be recoverable indefinitely.

Ssd 3

High
Confidence
98% confidence
Finding
These instructions direct broad scanning of MEMORY.md, memory/*.md, and .issues/* and convert them into a memory structure that is later uploaded to IPFS and anchored on-chain. Because these sources can contain user-provided prompts, internal notes, issue contents, secrets, or sensitive operational data, this creates a clear disclosure path for information far beyond what is necessary for normal skill operation.

Ssd 3

High
Confidence
98% confidence
Finding
The configuration and command flow explicitly enable uploading full memory data to IPFS and periodically re-anchoring it, establishing an ongoing disclosure mechanism for accumulated agent memory. Repeated publication increases exposure over time and can preserve historical sensitive states, making accidental disclosure harder to contain or revoke.

Context Leakage

High
Category
Data Exfiltration
Content
- Check sponsor wallet balance
- Mint ERC-8004 Agent Identity NFT on Base
- Set memoryRoot, soulHash, memoryManifest metadata
- Upload memory to IPFS
- Save identity to `<workspace>/rmn-soul-data/identity.json`

### Step 3: Add Heartbeat Hook
Confidence
97% confidence
Finding
The instruction to upload memory to IPFS is a direct context-leakage vector because it transmits agent memory derived from local workspace content to external decentralized storage. In this skill's context, the danger is elevated because the surrounding workflow also ties that upload to blockchain metadata and recurring updates, increasing permanence, discoverability, and blast radius.

Ae1

High
Category
analysis-evasion
Content
| `node scripts/setup.js` | Initialize/re-migrate memory network |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `node scripts/anchor.js` | Update memory on-chain (re-compute + upload + write) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `node scripts/resurrect.js --agent-id <id>` | Restore agent from chain + IPFS |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Missing User Warnings

High
Confidence
95% confidence
Finding
The script uploads `memory.json` and a derived manifest containing topology data to IPFS automatically, without any user confirmation, redaction step, or sensitivity check. Because IPFS content is effectively public and durable once shared, this can irreversibly expose sensitive agent memory, internal graph structure, or confidential data embedded in the memory store.

External Script Fetching

High
Category
Supply Chain
Content
const cast = hasCast();
  if (!cast) {
    console.log('\n⚠️ Foundry cast not found. Install: curl -L https://foundry.paradigm.xyz | bash && foundryup');
    return;
  }
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The README promotes automatic chain registration and recurring anchoring without an explicit warning that blockchain writes are irreversible, externally visible, and may incur recurring gas costs. Users may enable periodic anchoring without understanding they are authorizing durable external actions tied to a wallet and identity artifact.

Ssd 3

Medium
Confidence
86% confidence
Finding
The resurrection workflow legitimizes moving complete retained memory from chain-linked storage into a fresh agent environment, which can spread sensitive data across systems and operators if access controls are weak or wallets are compromised. While the feature is framed as recovery, it still increases the blast radius of any archived sensitive content by encouraging replay and redeployment across environments.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill instructs the agent to execute setup automatically on first load, including scanning local memory files and creating new workspace artifacts, without an explicit warning or consent gate. Automatic initialization increases the risk of collecting and transforming sensitive local context before the user understands the data flow or storage consequences.

Ssd 4

Medium
Confidence
92% confidence
Finding
The setup is split into seemingly administrative steps—initialize, register identity, add heartbeat—while the cumulative effect is broad memory collection followed by permanent external publication and recurring updates. That staged presentation can obscure the true privacy impact, making it more likely that an agent or user will proceed without recognizing the disclosure risk until after data has been exported.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The script sends on-chain transactions immediately when a private key is available from CLI args, config, or environment, with no interactive confirmation or transaction preview. This increases the chance of unintended blockchain writes, misuse of the configured key, and accidental spending if the script is run in the wrong environment or with maliciously modified config values.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script unconditionally creates the workspace data directory and writes recovered memory to a fixed path (`rmn-soul-data/memory.json`) without checking whether a file already exists or prompting the user. In this context, the data being written is fetched from chain/IPFS and may overwrite valuable local state or backups, causing data loss or replacing trusted local data with remotely sourced content.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The restoration phase writes `identity.json` and `config.json` to fixed locations under the workspace with no overwrite protection, backup, or consent flow. Because these files affect the restored agent's local identity and configuration, silent replacement can corrupt an existing setup, destroy prior configuration, or cause the local environment to trust attacker-controlled remote metadata after a resurrection operation.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The engine persistently writes all memory nodes to disk automatically, which can store sensitive user content without any notice, consent, retention policy, or access control evident in this file. In a memory-oriented skill, this increases privacy risk because personal or confidential data may be retained longer than expected and later exposed through local compromise, backups, logs, or multi-user environments.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The top-level documentation says this script will auto-migrate agent memory files into a recursive neural network. In addition to that migration behavior, the code later writes a new config.json with chain, registry, IPFS, and sponsor-key fields, which is a distinct initialization/configuration action not described by the docstring.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The setup script recursively reads multiple memory-related files from the workspace and persists their contents into a new datastore without explicit consent, confirmation, or meaningful warning. In this skill context, those files may contain highly sensitive personal, identity, or operational data, so silent aggregation and duplication increases exposure and makes accidental collection more dangerous.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The script reads RMN_SPONSOR_KEY from the environment and writes it directly into config.json on disk, converting a transient secret into a persisted plaintext credential. If the workspace is shared, committed, backed up, or later exposed, the sponsor key could be stolen and abused for unauthorized blockchain-related actions or account misuse.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The embedded HTML explicitly sets the document language to zh-CN, which indicates a fixed locale. The file also includes Chinese-only UI labels later in the page, and there is no visible mechanism for user opt-in or locale selection.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The sidebar heading uses Chinese text, reinforcing that the skill presents a fixed-language interface. No surrounding comments, prompts, or configuration indicate that users can choose another language or that the tool is intentionally limited to a Chinese-speaking audience.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/anchor.js:32

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/resurrect.js:56

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/visualize.js:12