Agent Skills: MultiversX dApp Auditor

Audit frontend dApp components for security vulnerabilities in wallet integration and transaction handling. Use when reviewing React/TypeScript dApps using sdk-dapp, or assessing client-side security.

UncategorizedID: multiversx/mx-ai-skills/multiversx-dapp-audit

Install this agent skill to your local

pnpm dlx add-skill https://github.com/multiversx/mx-ai-skills/tree/HEAD/skills/multiversx-dapp-audit

Skill Files

Browse the full folder contents for multiversx-dapp-audit.

Download Skill

Loading file tree…

skills/multiversx-dapp-audit/SKILL.md

Skill Metadata

Name
multiversx-dapp-audit
Description
Audit frontend dApp components for security vulnerabilities in wallet integration and transaction handling. Use when reviewing React/TypeScript dApps using sdk-dapp, or assessing client-side security.

MultiversX dApp Auditor

Audit the frontend components of MultiversX applications built with @multiversx/sdk-dapp. This skill focuses on client-side security, transaction construction, and wallet integration vulnerabilities.

When to Use

  • Reviewing React/TypeScript dApp code
  • Auditing wallet connection implementations
  • Assessing transaction signing security
  • Checking for XSS and data exposure vulnerabilities
  • Validating frontend-backend security boundaries

1. Transaction Construction Security

The Threat Model

The frontend constructs transaction payloads that users sign. Vulnerabilities here can trick users into signing malicious transactions.

Payload Manipulation

// VULNERABLE: User input directly in transaction data
const sendTransaction = async (userInput: string) => {
    const tx = new Transaction({
        receiver: Address.newFromBech32(recipientAddress),
        data: Buffer.from(userInput),  // Attacker controls data!
        // ...
    });
    await signAndSend(tx);
};

// SECURE: Validate and sanitize all inputs
const sendTransaction = async (functionName: string, args: string[]) => {
    // Whitelist allowed functions
    const allowedFunctions = ['stake', 'unstake', 'claim'];
    if (!allowedFunctions.includes(functionName)) {
        throw new Error('Invalid function');
    }

    // Validate arguments
    const sanitizedArgs = args.map(arg => validateArgument(arg));

    const tx = new Transaction({
        receiver: contractAddress,
        data: Buffer.from(`${functionName}@${sanitizedArgs.join('@')}`),
        // ...
    });
    await signAndSend(tx);
};

Critical Checks

| Check | Risk | Mitigation | |-------|------|------------| | Receiver address validation | Funds sent to wrong address | Validate against known addresses | | Data payload construction | Malicious function calls | Whitelist allowed operations | | Amount validation | Incorrect value transfers | Confirm amounts with user | | Gas limit manipulation | Transaction failures | Use appropriate limits |

2. Signing Security

Blind Signing Risks

Users may sign transactions without understanding the content:

// DANGEROUS: Signing opaque data
const signMessage = async (data: string) => {
    const hash = keccak256(data);
    return await wallet.signMessage(hash);  // User sees only hash!
};

// SECURE: Show clear message to user
const signMessage = async (message: string) => {
    // Display message to user before signing
    const confirmed = await showConfirmationDialog({
        title: 'Sign Message',
        content: `You are signing: "${message}"`,
        warning: 'Only sign messages you understand'
    });

    if (!confirmed) throw new Error('User rejected');
    return await wallet.signMessage(message);
};

Transaction Preview Requirements

Before signing, users should see:

  • Recipient address (with verification if known)
  • Amount being transferred
  • Token type (EGLD, ESDT, NFT)
  • Function being called (if smart contract interaction)
  • Gas cost estimate

3. Sensitive Data Handling

Private Key Security

// CRITICAL VULNERABILITY: Never do this
localStorage.setItem('privateKey', wallet.privateKey);
localStorage.setItem('mnemonic', wallet.mnemonic);
sessionStorage.setItem('seed', wallet.seed);

// Check for these patterns in code review:
// - Any storage of private keys, mnemonics, or seeds
// - Logging of sensitive data
// - Sending sensitive data to APIs

Secure Patterns

// CORRECT: Use sdk-dapp's secure session management
import { initApp } from '@multiversx/sdk-dapp/out/methods/initApp/initApp';

initApp({
    storage: {
        getStorageCallback: () => sessionStorage  // Session only, not persistent
    },
    dAppConfig: {
        nativeAuth: {
            expirySeconds: 3600  // Short-lived tokens
        }
    }
});

Access Token Security

// VULNERABLE: Token exposed in URL
window.location.href = `https://api.example.com?accessToken=${token}`;

// VULNERABLE: Token in console
console.log('Auth token:', accessToken);

// SECURE: Token in Authorization header, never logged
fetch(apiUrl, {
    headers: {
        'Authorization': `Bearer ${accessToken}`
    }
});

4. XSS Prevention

User-Generated Content

// VULNERABLE: Direct HTML injection
const UserProfile = ({ bio }: { bio: string }) => {
    return <div dangerouslySetInnerHTML={{ __html: bio }} />;  // XSS!
};

// SECURE: React's default escaping
const UserProfile = ({ bio }: { bio: string }) => {
    return <div>{bio}</div>;  // Automatically escaped
};

// If HTML is necessary, sanitize first
import DOMPurify from 'dompurify';

const UserProfile = ({ bio }: { bio: string }) => {
    const sanitized = DOMPurify.sanitize(bio);
    return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
};

URL Handling

// VULNERABLE: Unvalidated redirect
const handleCallback = () => {
    const returnUrl = new URLSearchParams(window.location.search).get('returnUrl');
    window.location.href = returnUrl!;  // Open redirect!
};

// SECURE: Validate redirect URL
const handleCallback = () => {
    const returnUrl = new URLSearchParams(window.location.search).get('returnUrl');
    const allowed = ['/', '/dashboard', '/profile'];
    if (allowed.includes(returnUrl || '')) {
        window.location.href = returnUrl!;
    } else {
        window.location.href = '/';  // Default safe redirect
    }
};

5. API Communication Security

HTTPS Enforcement

// VULNERABLE: HTTP connection
const API_URL = 'http://api.example.com';  // Insecure!

// SECURE: Always HTTPS
const API_URL = 'https://api.example.com';

// Verify in code: all API URLs must use https://

Request/Response Validation

// VULNERABLE: Trusting API response blindly
const balance = await fetch('/api/balance').then(r => r.json());
displayBalance(balance.amount);  // What if API is compromised?

// SECURE: Validate response structure
const response = await fetch('/api/balance').then(r => r.json());
if (typeof response.amount !== 'string' || !/^\d+$/.test(response.amount)) {
    throw new Error('Invalid balance response');
}
displayBalance(response.amount);

6. Audit Tools and Techniques

Network Traffic Analysis

# Use browser DevTools Network tab to inspect:
# - All API requests and responses
# - Transaction data being sent
# - Headers (especially Authorization)
# - WebSocket messages

# Or use proxy tools:
# - Burp Suite
# - mitmproxy
# - Charles Proxy

Code Review Patterns

# Search for dangerous patterns
grep -r "localStorage" src/
grep -r "dangerouslySetInnerHTML" src/
grep -r "eval(" src/
grep -r "privateKey\|mnemonic\|seed" src/
grep -r "console.log" src/  # Check for sensitive data logging

Browser Security Headers Check

Content-Security-Policy: default-src 'self'; script-src 'self'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin

7. Authentication Flow Audit

Wallet Connection

// Verify wallet connection flow:
// 1. User initiates connection
// 2. Wallet provider prompts for approval
// 3. Only public address shared with dApp
// 4. No private data transmitted

// Check UnlockPanelManager usage
const unlockPanelManager = UnlockPanelManager.init({
    loginHandler: () => {
        // Verify: no sensitive data handling here
        navigate('/dashboard');
    }
});

Session Management

// Verify session security:
// - Token expiration enforced
// - Logout clears all session data
// - No persistent sensitive storage

const handleLogout = async () => {
    const provider = getAccountProvider();
    await provider.logout();
    // Verify: session storage cleared
    sessionStorage.clear();
    navigate('/');
};

8. Audit Checklist

Transaction Security

  • [ ] All transaction data validated before signing
  • [ ] User shown clear transaction preview
  • [ ] Recipient addresses validated
  • [ ] Amount confirmations for large transfers
  • [ ] Gas limits appropriate for operation

Data Security

  • [ ] No private keys in any storage
  • [ ] No sensitive data in console logs
  • [ ] Access tokens not exposed in URLs
  • [ ] HTTPS enforced for all API calls

XSS Prevention

  • [ ] No dangerouslySetInnerHTML with user input
  • [ ] No eval() with user input
  • [ ] URL redirects validated
  • [ ] Content-Security-Policy headers present

Session Security

  • [ ] Token expiration enforced
  • [ ] Logout clears all session data
  • [ ] Protected routes properly guarded
  • [ ] Re-authentication for sensitive operations

9. Report Template

# dApp Security Audit Report

## Scope
- Application: [name]
- Version: [version]
- Files reviewed: [count]

## Findings

### Critical
| ID | Description | Location | Recommendation |
|----|-------------|----------|----------------|
| C1 | ... | ... | ... |

### High
...

### Medium
...

### Informational
...

## Recommendations Summary
1. [Priority recommendation]
2. ...

## Conclusion
[Overall assessment]