Back to skill

Security audit

X1 Vault Memory

Security checks for vulnerabilities and agentic risk

Overview

The skill broadly matches its backup-and-restore purpose, but it has a misleading dry-run command and broad restore/heartbeat behavior that can upload data, sign transactions, or overwrite agent state unexpectedly.

Review this before installing. Use only a dedicated low-balance wallet and a scoped Pinata token, keep .env and wallet.json out of source control, and do not rely on --dry-run until it is fixed. Avoid enabling heartbeat cron unless you accept unattended restores that may overwrite agent files, and update or audit the dependency set before restoring archives into a real workspace.

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

T09 · Insecure Skill Coding Practices

Error
Location
src/backup.js:77
Finding
Documented dry-run mode performs a real external upload and blockchain transaction<![CDATA[ ## Vulnerability Details **File Location**: `src/backup.js:77-181`; conflicting behavior documented at `SKILL.md:243-247` **Vulnerability Type**: Missing argument handling and misleading safety control **Risk Level**: High ### Vulnerable Code The documentation represents the command as side-effect-free: ```text ### Dry Run ```bash node src/backup.js --dry-run ``` Shows which files would be backed up without uploading or spending tokens. ``` However, the backup implementation does not inspect `process.argv` or otherwise handle `--dry-run`. It always uploads the encrypted backup and attempts to sign and submit a blockchain transaction: ```js async function createBackup() { const tempFiles = []; const entry = { timestamp: new Date().toISOString() }; try { // Create a temporary tar.gz archive const archivePath = path.resolve(__dirname, 'backup.tar.gz'); const tarFiles = filesToBackup.map(f => path.resolve(__dirname, '..', f)); const cwd = path.resolve(__dirname, '../..'); await tar.c( { gzip: true, file: archivePath, cwd, }, [...filesToBackup, 'memory'] ); tempFiles.push(archivePath); // Generate SHA-256 hash of archive before encryption const archiveBuffer = fs.readFileSync(archivePath); const checksum = crypto.createHash('sha256').update(archiveBuffer).digest('hex'); const checksumPath = path.resolve(__dirname, 'checksum.txt'); fs.writeFileSync(checksumPath, checksum); tempFiles.push(checksumPath); console.log('Archive checksum:', checksum); // Create payload with archive + checksum const payloadPath = path.resolve(__dirname, 'payload.tar'); await tar.c( { file: payloadPath, cwd: __dirname, }, ['backup.tar.gz', 'checksum.txt'] ); tempFiles.push(payloadPath); // Load wallet secret key const walletPath = path.resolve(__dirname, '../..', 'x1_vault_cli', 'wallet.json'); if ( ...[truncated 4325 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse command-line arguments before creating temporary files, reading the wallet, or making network requests. 2. When `--dry-run` is present, resolve and validate the intended source paths, print the exact files that would be included, and return immediately. 3. Ensure dry-run mode does not: - Read the wallet file. - Read `PINATA_JWT`. - Call `uploadToIPFS`. - Call `anchorCID`. - Modify `vault-log.json`. 4. Require a separate explicit confirmation option for real uploads if the command is likely to be invoked autonomously. 5. Add automated tests with mocked upload and anchoring functions. Assert that both mocks have zero calls in dry-run mode. 6. Update documentation only after the implementation has a tested, side-effect-free dry-run path. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/backup.js:116
Finding
Wallet generated by the documented setup is incompatible with runtime wallet parsing<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:171-184`, `README.md:99-111`, `src/backup.js:116-126`, `src/restore.js:118-140`, and `src/anchor.js:31-34` **Vulnerability Type**: Security-critical configuration format mismatch **Risk Level**: Medium ### Vulnerable Code The documented wallet-generation command writes a top-level JSON array: ```js node -e " const { Keypair } = require('@solana/web3.js'); const fs = require('fs'); const kp = Keypair.generate(); fs.writeFileSync('x1_vault_cli/wallet.json', JSON.stringify([...kp.secretKey])); console.log('Wallet created:', kp.publicKey.toBase58()); console.log('Save the secretKey JSON array to x1_vault_cli/wallet.json'); " ``` The backup implementation instead expects an object with a `secretKey` property: ```js // Load wallet secret key const walletPath = path.resolve(__dirname, '../..', 'x1_vault_cli', 'wallet.json'); if (!fs.existsSync(walletPath)) { throw new Error(`Wallet not found at ${walletPath}`); } const wallet = JSON.parse(fs.readFileSync(walletPath, 'utf8')); const secretKey = Buffer.from(wallet.secretKey); // Generate random salt and derive key using PBKDF2 const salt = crypto.randomBytes(SALT_SIZE); const key = deriveKey(secretKey, salt); ``` The same incompatible expectation is present in restoration: ```js // Load wallet secret key const walletPath = path.resolve(__dirname, '../..', 'x1_vault_cli', 'wallet.json'); if (!fs.existsSync(walletPath)) { throw new Error(`Wallet not found at ${walletPath}`); } const wallet = JSON.parse(fs.readFileSync(walletPath, 'utf8')); const secretKey = Buffer.from(wallet.secretKey); // Read encrypted file const data = fs.readFileSync(encryptedPath); // Extract components: salt + iv + ciphertext + authTag if (data.length < SALT_SIZE + IV_SIZE + AUTH_TAG_SIZE + 1) { throw new Error('Encrypted file too small to be valid'); } const salt = data.slice(0, SALT_SIZE); const iv = data.slice(SALT_SIZE, SALT_SIZE + IV_SIZE); const authTag = data.sli ...[truncated 2429 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define one canonical wallet schema and use it consistently in documentation and code. 2. For compatibility, explicitly accept both supported Solana keypair representations: ```js const parsed = JSON.parse(fs.readFileSync(walletPath, 'utf8')); const bytes = Array.isArray(parsed) ? parsed : parsed.secretKey; if ( !Array.isArray(bytes) || bytes.length !== 64 || !bytes.every(value => Number.isInteger(value) && value >= 0 && value <= 255) ) { throw new Error('Invalid wallet format: expected a 64-byte secret-key array'); } ``` 3. Centralize wallet loading in one audited helper used by backup, restore, and anchoring. 4. Validate the wallet before collecting files, downloading backups, or initiating network activity. 5. Generate the wallet file with owner-only permissions such as mode `0600`. 6. Add a setup self-test that derives and displays only the public key, then verifies encryption and decryption using a local test payload. 7. Add automated tests covering valid arrays, valid object wrappers, malformed JSON, missing properties, incorrect lengths, and out-of-range values. 8. Correct all setup and troubleshooting documentation to match the selected format. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/heartbeat.js:20
Finding
Scheduled heartbeat can overwrite healthy Agent state based on weak integrity checks<![CDATA[ ## Vulnerability Details **File Location**: `src/heartbeat.js:20-35` and `src/heartbeat.js:49-105`; scheduling documented at `SKILL.md:228-240` and `README.md:165-181` **Vulnerability Type**: Unsafe unattended restoration and overly broad overwrite scope **Risk Level**: Medium ### Vulnerable Code The heartbeat considers only file size and directory entry count: ```js function getFileSize(filePath) { try { const stats = fs.statSync(filePath); return stats.size; } catch { return 0; } } function getDirSize(dirPath) { try { const files = fs.readdirSync(dirPath); return files.length; } catch { return 0; } } ``` Any detected issue triggers a full restore from the last locally logged CID: ```js async function heartbeat() { console.log('Running X1 Vault heartbeat check...\n'); let issues = []; // Check SOUL.md const soulSize = getFileSize(SOUL_PATH); console.log(`SOUL.md: ${soulSize} bytes`); if (soulSize < 10) { issues.push('SOUL.md missing or too small (< 10 bytes)'); } // Check memory/ directory const memoryFiles = getDirSize(MEMORY_DIR); console.log(`memory/ directory: ${memoryFiles} files`); if (memoryFiles === 0) { issues.push('memory/ directory empty'); } // Report status console.log('\n--- Heartbeat Status ---'); if (issues.length === 0) { console.log('✓ All checks passed — memory files healthy'); console.log('No action needed.'); return; } // Issues found — attempt auto-restore console.log('✗ Issues detected:'); issues.forEach(issue => console.log(` - ${issue}`)); console.log('\nAttempting auto-restore from latest backup...'); const latestCID = getLatestCID(); if (!latestCID) { console.error('ERROR: No backups found in vault-log.json'); console.error('Cannot auto-restore — manual intervention required'); process.exit(1); } // Validate CID before using in shell command if (!validateCID(latestCID)) { ...[truncated 4407 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Separate monitoring from remediation. Make heartbeat report failures by default and require an explicit `--auto-restore` option for unattended changes. 2. Replace size and entry-count checks with authenticated baseline metadata, such as expected file types, approved paths, cryptographic hashes, and version information. 3. Distinguish missing files, empty files, permission failures, and transient filesystem errors instead of treating all exceptions as corruption. 4. Restore only the affected resource: - Use `--only SOUL.md` when only `SOUL.md` fails. - Use `--only memory/` when only memory fails. 5. Never overwrite unrelated healthy files during automated recovery. 6. Verify backup age, CID format, log structure, and expected source before initiating restoration. 7. Keep `vault-log.json` owner-writable only and consider authenticating its entries. 8. Create a local pre-restore snapshot or stage restored files in a separate directory, compare them, and atomically replace only approved targets. 9. Add a lock to prevent overlapping cron executions. 10. Document that cron grants the heartbeat recurring write access to Agent state, and recommend running it under a dedicated least-privileged account with logs stored in a user-writable protected directory rather than requiring `/var/log` privileges. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
Findings (34)

Known Vulnerable Dependency: tar==7.5.9 — 8 advisory(ies): CVE-2026-59873 (node-tar: Decompression/parse DoS via unlimited input); CVE-2026-59874 (node-tar: Negative tar entry size causes infinite loop in archive replace); CVE-2026-31802 (node-tar Symlink Path Traversal via Drive-Relative Linkpath) +5 more

Critical
Category
Supply Chain
Confidence
98% confidence
Finding
tar 7.5.9 has multiple advisories including path traversal and denial-of-service classes of issues. This is especially dangerous in a backup/restore skill because restore flows commonly extract archives, and attacker-controlled archives could overwrite files outside the intended destination or hang/crash the process.

Known Vulnerable Dependency: tar==7.5.9 — 8 advisory(ies): CVE-2026-59873 (node-tar: Decompression/parse DoS via unlimited input); CVE-2026-59874 (node-tar: Negative tar entry size causes infinite loop in archive replace); CVE-2026-31802 (node-tar Symlink Path Traversal via Drive-Relative Linkpath) +5 more

Critical
Category
Supply Chain
Confidence
98% confidence
Finding
The package declares tar version 7.5.9, which is identified as having multiple known vulnerabilities including denial-of-service and path traversal issues. In a backup/restore skill that likely handles archives for memory export and import, a vulnerable tar library is especially dangerous because attacker-controlled archives could trigger filesystem overwrite, unsafe extraction, or service disruption during restore operations.

Credential Access

High
Category
Privilege Escalation
Content
2. Add environment variables to your `.env` file:
```bash
echo "PINATA_JWT=your_token_here" >> ~/openclaw/.env
echo "X1_RPC_URL=https://rpc.mainnet.x1.xyz" >> ~/openclaw/.env
```
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
2. Add environment variables to your `.env` file:
```bash
echo "PINATA_JWT=your_token_here" >> ~/openclaw/.env
echo "X1_RPC_URL=https://rpc.mainnet.x1.xyz" >> ~/openclaw/.env
```
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The README advertises encryption, restore capability, and blockchain anchoring, but the finding indicates those controls may not actually exist while network credential use does. That creates a false sense of security around confidentiality and recovery, which is especially risky because the skill is marketed for backing up highly sensitive agent memory.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The README advertises encryption, restore capability, and blockchain anchoring, but the finding indicates those controls may not actually exist while network credential use does. That creates a false sense of security around confidentiality and recovery, which is especially risky because the skill is marketed for backing up highly sensitive agent memory.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The README advertises encryption, restore capability, and blockchain anchoring, but the finding indicates those controls may not actually exist while network credential use does. That creates a false sense of security around confidentiality and recovery, which is especially risky because the skill is marketed for backing up highly sensitive agent memory.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The README advertises encryption, restore capability, and blockchain anchoring, but the finding indicates those controls may not actually exist while network credential use does. That creates a false sense of security around confidentiality and recovery, which is especially risky because the skill is marketed for backing up highly sensitive agent memory.

External Script Fetching

High
Category
Supply Chain
Content
### New in v1.1.2

- ✅ Security fixes: removed "curl | sh" Solana CLI install suggestion
- ✅ Declared environment variables clearly in skill metadata
- ✅ Added opt-in note for heartbeat auto-restore
- Updated package.json with required env vars in config section
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Credential Access

High
Category
Privilege Escalation
Content
### 2. Configure environment variables

**Option A: Using a .env file (recommended for Docker and production)**

Create a `.env` file in your project or workspace root:
Confidence
89% confidence
Finding
The skill instructs users to store a sensitive API credential in environment configuration, which is normal operationally but still represents credential handling risk in a workspace-oriented tool. In this context, the danger is amplified because the skill also deals with backups of sensitive memory and may run in shared containers or repositories where env secrets are often exposed inadvertently.

Known Vulnerable Dependency: form-data==4.0.5 — 1 advisory(ies): CVE-2026-12143 (form-data: CRLF injection in form-data via unescaped multipart field names and f)

High
Category
Supply Chain
Confidence
95% confidence
Finding
form-data 4.0.5 is flagged for CRLF injection via unescaped multipart field names/filenames. This skill’s purpose includes backing up data to external services such as IPFS, so if untrusted metadata is ever inserted into multipart requests, an attacker may be able to smuggle or alter HTTP multipart content and potentially affect upstream services or request interpretation.

Known Vulnerable Dependency: ws==8.19.0 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
89% confidence
Finding
ws 8.19.0 is reported vulnerable to both memory disclosure and memory-exhaustion denial of service. Even though it is transitive, WebSocket-capable blockchain/RPC libraries may expose the process to attacker-influenced network traffic, which makes these issues relevant in a networked backup/anchoring skill.

Known Vulnerable Dependency: ws==7.5.10 — 1 advisory(ies): CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
90% confidence
Finding
ws 7.5.10 is also flagged for memory-exhaustion DoS from fragmented data chunks. Because this older ws version is pulled in through jayson and may be used in JSON-RPC/WebSocket communication, a remote peer could potentially consume excessive memory and disrupt availability.

Known Vulnerable Dependency: form-data==4.0.5 — 1 advisory(ies): CVE-2026-12143 (form-data: CRLF injection in form-data via unescaped multipart field names and f)

High
Category
Supply Chain
Confidence
93% confidence
Finding
The resolved form-data version is flagged for CRLF injection, which can allow manipulation of multipart request structure when attacker-influenced field names or values are included. This skill appears to back up encrypted memory to external services such as IPFS/Pinata, so malformed multipart construction could lead to request smuggling, header injection, or unintended upload behavior if any upload metadata is user-controlled.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The README promotes backing up sensitive agent identity and memory data to IPFS and anchoring the CID on-chain, but it does not clearly warn that this transmits data off-device and creates persistent metadata exposure. Even with encryption, observers can still learn that a backup occurred, correlate wallet activity, and retain the encrypted blob indefinitely for future cryptanalysis or key compromise scenarios.

Persistent Context Injection

Medium
Category
Memory Poisoning
Content
```

5. Tell your agent about the skill:
> "You have a new skill called x1-vault-memory. You can backup your memory with node x1-vault-memory/src/backup.js and restore with node x1-vault-memory/src/restore.js CID. Save this to your memory."

---
Confidence
95% confidence
Finding
The instruction to tell the agent about the skill and 'Save this to your memory' encourages persistent context injection into the agent's long-term memory. That can cause the agent to retain executable operational instructions and tool invocation patterns beyond the user's immediate intent, increasing the chance of unintended future execution or misuse if memory is later relied upon automatically.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill declares required environment variables and a wallet file but does not declare any explicit tool scope or permissions boundary. In a skill that handles secrets, filesystem access, and likely networked backup operations, missing scope declarations weakens least-privilege controls and makes the actual capability surface harder for operators to review.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The README recommends placing a long-lived Pinata JWT in a project or workspace .env file but does not warn about accidental git commits, container image inclusion, or log/exposure risks. Because this token enables IPFS upload access, leakage could allow unauthorized use of the account and access to related stored content or billing resources.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The code reads a wallet secret key directly from a file path and immediately reconstructs a signing key without any user-facing consent, warning, or restriction on what file may be accessed. In an agent-skill context, this is security-sensitive because wallet credentials enable on-chain signing, and silent credential access increases the risk of unintended key use or abuse if the skill is invoked on a host containing real wallets.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The backup routine reads a blockchain wallet secret from a local wallet file and uses it as input material for backup encryption. This unnecessarily couples backup confidentiality to an unrelated high-value credential, increasing blast radius: compromise, rotation, format changes, or accidental exposure of the wallet secret can directly affect backup security and may also encourage broader secret reuse across trust domains. In this skill context, handling agent memory backups makes the issue more dangerous because the archived files likely contain sensitive identity and memory data, while the wallet key is also highly sensitive.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
The manifest describes restoring agent memory from IPFS with encryption and X1 CID anchoring, but this restore file additionally accesses a local wallet file and loads its secret key material. While decryption needs key material, the manifest does not indicate that restore depends on reading a blockchain wallet secret from local disk, which is a sensitive capability beyond the plainly stated restore function.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The restore flow loads sensitive credential material from wallet.json and uses wallet.secretKey for decryption. While comments describe the operation for developers, there is no user-facing disclosure that sensitive key material will be accessed during execution.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The restore operation extracts attacker-controlled archive contents directly into the workspace without confirmation, overwrite protection, or path safety checks. Even though the encrypted payload must decrypt successfully, anyone able to produce a valid backup for this key can overwrite source files or plant malicious files, and tar extraction can become especially dangerous if entry paths or links are not strictly validated.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This code uploads data to a third-party remote service (Pinata/IPFS) with no built-in consent, disclosure, or policy checks in the function itself. In a memory-backup skill, that means potentially sensitive agent state could be exfiltrated off-host and made persistently retrievable by CID, so misuse by a caller or unexpected invocation can create a confidentiality risk even if the code is functioning as designed.

Known Vulnerable Dependency: bn.js==5.2.2 — 1 advisory(ies): CVE-2026-2739 (bn.js affected by an infinite loop)

Low
Category
Supply Chain
Confidence
72% confidence
Finding
The lockfile pins bn.js 5.2.2, which is reported as vulnerable to an infinite-loop condition. In this skill, bn.js is only a transitive dependency of Solana-related libraries, so exploitation would likely require attacker-controlled inputs to specific big-number parsing or processing paths, making this lower risk but still a real supply-chain issue.

Static analysis

No suspicious patterns detected.