Skip to main content

AgentVault Security Audit Report

Version: v1.0.0
Date: February 12, 2026
Scope: Internal security review of AgentVault codebase


Executive Summary​

This security audit identified 23 findings across 7 security domains. The codebase demonstrates generally good security practices with strong encryption implementations, but has several areas requiring attention.

SeverityCountDescription
Critical0No critical vulnerabilities
High2Require immediate attention
Medium9Should be addressed soon
Low8Consider fixing
Info4Positive findings / documentation

Overall Assessment: AgentVault is suitable for v1.0 release with documented limitations. High-severity findings should be addressed in v1.1.


Methodology​

This audit reviewed:

  • 100+ TypeScript files (~15,000 lines of code)
  • All CLI command handlers
  • Core library modules (deployment, wallet, security, canister)
  • External integrations (Bittensor, Arweave, multi-chain wallets)
  • Dependencies in package.json

Security domains covered:

  1. Authentication & Authorization
  2. Cryptographic Operations
  3. Secrets Management
  4. Input Validation
  5. Network Security
  6. File Operations
  7. Dependencies

1. Authentication & Authorization​

Finding 1.1: No Hardcoded Credentials Found​

Severity: Informational (Positive)
Files: Various test files

No hardcoded production credentials found. Test files use clearly fake values like 0xabcdef1234....

Recommendation: Continue this practice; consider adding automated scanning for credential leaks.


Finding 1.2: Local Approval System Lacks Cryptographic Verification​

Severity: Medium
File: src/security/multisig.ts:47-58

The multisig approval system uses auditToken which is described as "NOT a cryptographic signature":

export interface ApprovalSignature {
signer: string;
/** Audit token - NOT a cryptographic signature */
auditToken: string;
timestamp: Date;
comment?: string;
}

Risk: Token is just a SHA-256 hash that can be forged by anyone who knows request parameters.

Mitigation: Documentation clearly states this is for audit trail only.

Recommendation: For production multisig, use VetKeys canister-based threshold signatures.


Finding 1.3: Anonymous Agent for Local Development​

Severity: Low
File: src/canister/actor.ts:302-309

createAnonymousAgent creates an unauthenticated agent for local development:

export function createAnonymousAgent(host?: string): HttpAgent {
const defaultHost = process.env.ICP_LOCAL_URL || 'http://localhost:4943';
return new HttpAgent({ host: host ?? defaultHost });
}

Risk: Could be accidentally used in production.

Mitigation: Only used for local development; mainnet requires authenticated agent.

Recommendation: Add environment check to prevent production use.


2. Cryptographic Operations​

Finding 2.1: Strong Encryption Implementation​

Severity: Informational (Positive)
File: src/canister/encryption.ts

Encryption module implements AES-256-GCM and ChaCha20-Poly1305 with proper:

  • Random IV generation (12 bytes for GCM)
  • Authentication tags
  • Timing-safe HMAC comparison
  • PBKDF2 key derivation with 100,000 iterations

Assessment: Implementation follows best practices.


Finding 2.2: Seed Phrase Retained in Memory​

Severity: High
File: src/security/vetkeys.ts:59-70, 311-336

Seed phrases are passed directly to key derivation and stored in returned objects:

return {
type: 'threshold',
key: derivedKey.key,
method: derivedKey.method,
seedPhrase, // <-- Seed phrase included in return object!
...
};

Risk: Seed phrases persist in memory longer than necessary, increasing exposure to memory dumps.

Recommendation:

  1. Clear seed phrase from memory after key derivation
  2. Never return seed phrase in derived key object
  3. Use Buffer.fill(0) to zero memory
// Recommended fix
const seedBuffer = Buffer.from(seedPhrase, 'utf8');
// ... derive key ...
seedBuffer.fill(0); // Clear from memory

Finding 2.3: Weak Secret Sharing Implementation​

Severity: Medium
File: src/security/vetkeys.ts:180-220

Secret sharing is not true Shamir's Secret Sharing:

private generateParticipantSecret(seedPhrase: string, participantIndex: number): string {
const secretBytes = Buffer.from(seedPhrase, 'utf8');
const participantSuffix = Buffer.concat([Buffer.from([participantIndex]), secretBytes]);
return participantSuffix.toString('hex');
}

Risk: Shares are derived, not cryptographically split. Any single share can derive others.

Mitigation: Documentation notes this is not production-ready VetKeys.

Recommendation: Use proper SSS library like shamir-secret-sharing or integrate actual VetKeys canister.


Finding 2.4: Encryption Key Not Persisted​

Severity: Medium
File: src/wallet/vetkeys-adapter.ts:71-111

encryptSecret generates a random key but returns only encrypted data:

async encryptSecret(secret: string, transactionId?: string): Promise<EncryptedSecret> {
const key = crypto.randomBytes(32); // Key generated
// ... encryption ...
return { id, ciphertext, iv, tag, createdAt }; // Key not returned!
}

Risk: Data is unrecoverable without external key management.

Recommendation: Document key management requirements or return encrypted key.


3. Secrets Management​

Finding 3.1: Environment Variable Usage​

Severity: Low
Files: Multiple

API keys and RPC URLs read from environment variables:

  • ETHEREUM_RPC_URL, INFURA_API_KEY, ETHERSCAN_API_KEY
  • SOLANA_RPC_URL, POLKADOT_RPC_URL
  • ICP_LOCAL_URL, ICP_MAINNET_URL

Mitigation: .gitignore includes .env* files.

Recommendation: Document required environment variables; consider secrets manager for production.


Finding 3.2: Private Keys in WalletData Object​

Severity: Medium
File: src/wallet/types.ts:23-46

WalletData stores sensitive fields:

export interface WalletData {
privateKey?: string; // Sensitive!
mnemonic?: string; // Sensitive!
...
}

Risk: Wallet data serialized to CBOR and stored locally unencrypted.

Recommendation: Encrypt sensitive fields at rest with user-provided password.


Finding 3.3: Wallet Storage Path Predictability​

Severity: Low
File: src/wallet/wallet-storage.ts:27-62

Wallet files stored at predictable paths: ~/.agentvault/wallets/{agentId}/{walletId}.wallet

Mitigation: Directory permissions default to user-only.

Recommendation: Ensure ~/.agentvault has restrictive permissions (0700).


Finding 3.4: Console Logging of Secret IDs​

Severity: Medium
File: src/security/vetkeys.ts:452, 459, 497

Debug logging includes secret-related IDs:

console.log('Encrypted secret stored on canister:', secretId);
console.warn(`Failed to store encrypted secret on canister: ${message}`);

Risk: Information leakage in production logs.

Recommendation: Guard with debug flag or remove.


4. Input Validation​

Finding 4.1: Limited Canister ID Validation​

Severity: Medium
File: src/deployment/icpClient.ts:323-328

Canister ID validation uses regex instead of proper Principal parsing:

const principalPattern = /^[a-z0-9]{5}(-[a-z0-9]{3,5})+$/;
if (!principalPattern.test(canisterId)) {
throw new Error(`Invalid canister ID format: ${canisterId}`);
}

Recommendation: Use @dfinity/principal Principal.fromText() for proper validation.


Finding 4.2: No Agent ID Path Validation​

Severity: Medium
File: src/wallet/wallet-storage.ts:39-45

agentId used directly in file paths:

export function getAgentWalletDir(agentId: string): string {
return path.join(baseDir, agentId); // No validation!
}

Risk: Path traversal if agentId contains .. or /.

Recommendation:

function sanitizePathPart(part: string): string {
if (part.includes('..') || part.includes('/') || part.includes('\\')) {
throw new Error('Invalid path component');
}
return part;
}

Finding 4.3: Limited Backup Validation​

Severity: Low
File: cli/commands/wallet-import.ts:100-114

Backup validation checks structure but not content validity.

Recommendation: Add validation for wallet address formats, private key lengths.


Finding 4.4: Proper Agent Name Validation​

Severity: Informational (Positive)
File: cli/commands/init.ts:42-50

Agent names properly validated:

if (!/^[a-z0-9-]+$/.test(input)) {
return 'Agent name must be lowercase alphanumeric with hyphens only';
}

Assessment: Good pattern to apply to other inputs.


5. Network Security​

Finding 5.1: Dynamic Code Execution for Module Loading​

Severity: High
Files: src/inference/bittensor-client.ts:92-104, src/archival/arweave-client.ts:81-93

Uses new Function() to dynamically import modules:

private async importAxios(): Promise<any> {
const dynamicImport = new Function('modulePath', 'return import(modulePath)');
const axiosModule = await dynamicImport('axios');

Risk: Dynamic code execution pattern, though module path is hardcoded.

Recommendation: Replace with standard ESM dynamic imports:

const axiosModule = await import('axios');

Finding 5.2: External API Calls Without Rate Limiting​

Severity: Low
File: src/wallet/providers/cketh-provider.ts:240-290

Etherscan API calls have no client-side rate limiting.

Recommendation: Implement rate limiting for external APIs.


Finding 5.3: HTTP for Local Development​

Severity: Low
File: src/canister/actor.ts:303

Local development uses HTTP:

const defaultHost = process.env.ICP_LOCAL_URL || 'http://localhost:4943';

Mitigation: Only for local development.

Recommendation: Document that HTTP should never be used for production.


Finding 5.4: Shell Command Execution​

Severity: Medium
File: src/deployment/icpClient.ts:108-155

Shell commands executed via execa with user-influenced parameters:

await execa('dfx', ['canister', 'create', '--all', '--network', this.config.network]);

Mitigation: Uses execa which escapes arguments properly.

Recommendation: Validate network parameter against whitelist.


6. File Operations​

Finding 6.1: Path Traversal Risk in Multiple Locations​

Severity: Medium
Files: Multiple

Multiple functions use path.join() with user-provided identifiers:

FileFunctionParameter
src/wallet/wallet-storage.tsgetAgentWalletDir()agentId
src/security/multisig.tsgetApprovalFilePath()id
src/backup/backup.tscreateBackup()agentName
src/metrics/metrics.tsgetMetricsFilePath()canisterId

Recommendation: Add validation for all path inputs (see Finding 4.2).


Finding 6.2: Non-Atomic File Writes​

Severity: Low
File: src/wallet/wallet-storage.ts:101

Wallet files written without atomic operations:

fs.writeFileSync(walletPath, Buffer.from(serialized));

Risk: Data corruption if process interrupted during write.

Recommendation: Use atomic writes:

const tempPath = `${walletPath}.tmp`;
fs.writeFileSync(tempPath, Buffer.from(serialized));
fs.renameSync(tempPath, walletPath);

Finding 6.3: Backup File Permissions​

Severity: Low
File: src/backup/backup.ts:15-16

Backups stored in ~/.agentvault/backups/ which may be world-readable.

Recommendation: Explicitly set directory permissions to 0700.


7. Dependencies​

Finding 7.1: Dependencies Current​

Severity: Informational (Positive)
File: package.json

Key dependencies are current:

PackageVersionStatus
@dfinity/agent^3.4.3Current
ethers^6.16.0Current
@solana/web3.js^1.98.4Current
@polkadot/api^16.5.4Current
bip39^3.1.0Current

Assessment: No vulnerable dependency patterns detected.


Finding 7.2: Optional Dependencies​

Severity: Low
Files: src/inference/bittensor-client.ts, src/archival/arweave-client.ts

axios and arweave are optional dependencies loaded dynamically.

Mitigation: Graceful error handling when not installed.

Recommendation: Document optional dependencies in README.


Summary Table​

IDSeverityDescriptionStatus
1.1InfoNo hardcoded credentialsOK
1.2MediumNon-cryptographic multisigDocumented
1.3LowAnonymous local agentExpected
2.1InfoStrong encryptionOK
2.2HighSeed phrase in memoryNeeds Fix
2.3MediumWeak SSS implementationDocumented
2.4MediumLost encryption keyNeeds Doc
3.1LowEnv var usageOK
3.2MediumUnencrypted wallet storageNeeds Review
3.3LowPredictable storage pathOK
3.4MediumDebug logging of secretsNeeds Fix
4.1MediumWeak canister ID validationNeeds Fix
4.2MediumNo agent ID validationNeeds Fix
4.3LowLimited backup validationOK
4.4InfoGood agent name validationOK
5.1HighDynamic Function executionNeeds Fix
5.2LowNo rate limitingOK
5.3LowHTTP for local devExpected
5.4MediumShell command executionReview
6.1MediumPath traversal riskNeeds Fix
6.2LowNon-atomic writesOK
6.3LowBackup permissionsReview
7.1InfoDependencies currentOK
7.2LowOptional deps handlingOK

Priority Recommendations​

Critical (Fix Immediately)​

  1. Finding 2.2: Remove seed phrase from returned objects in VetKeys
  2. Finding 5.1: Replace new Function() with standard ESM imports

High Priority (Fix in v1.1)​

  1. Finding 6.1/4.2: Add path validation for all user-provided identifiers
  2. Finding 3.4: Remove debug logging of secret-related information
  3. Finding 4.1: Use proper Principal validation for canister IDs

Medium Priority (Plan to Fix)​

  1. Finding 3.2: Encrypt wallet data at rest with user password
  2. Finding 2.4: Document key management for VetKeys adapter
  3. Finding 2.3: Implement true Shamir's Secret Sharing or use proper library
  4. Finding 5.4: Validate shell command parameters before execution

Low Priority (Consider)​

  1. Set explicit file permissions on sensitive directories
  2. Implement atomic file writes for critical data
  3. Add rate limiting to external API calls

Appendix: Quick Fix Examples​

Path Validation Helper​

export function sanitizePathPart(part: string): string {
if (!part || part.includes('..') || part.includes('/') || part.includes('\\') || part.includes('\0')) {
throw new Error('Invalid path component');
}
return part;
}

Principal Validation​

import { Principal } from '@dfinity/principal';

export function validateCanisterId(canisterId: string): Principal {
try {
return Principal.fromText(canisterId);
} catch {
throw new Error(`Invalid canister ID: ${canisterId}`);
}
}

Memory-Safe Seed Handling​

function deriveKey(seedPhrase: string): DerivedKey {
const seedBuffer = Buffer.from(seedPhrase, 'utf8');
try {
const key = deriveFromSeed(seedBuffer);
return { key, method: 'pbkdf2' };
} finally {
seedBuffer.fill(0); // Always clear
}
}

Report Generated: February 12, 2026
Auditor: Internal Security Review
Next Review: Recommended after v1.1 release