Loading...
Loading...
Compare original and translation side by side
@multiversx/sdk-dapp@multiversx/sdk-dapp// 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);
};// 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);
};| 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 |
| 检查项 | 风险 | 缓解措施 |
|---|---|---|
| 接收方地址验证 | 资金转入错误地址 | 与已知地址进行校验 |
| 数据载荷构造 | 恶意函数调用 | 白名单允许的操作 |
| 金额验证 | 转账金额错误 | 与用户确认转账金额 |
| Gas限制操纵 | 交易失败 | 使用合适的限制值 |
// 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);
};// 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);
};// 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// 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// 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
}
}
});// 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
}
}
});// 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}`
}
});// 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}`
}
});// 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 }} />;
};// 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 }} />;
};// 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
}
};// 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
}
};// 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://// 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://// 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);// 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);undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedContent-Security-Policy: default-src 'self'; script-src 'self'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-originContent-Security-Policy: default-src 'self'; script-src 'self'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin// 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');
}
});// 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');
}
});// 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('/');
};// 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('/');
};dangerouslySetInnerHTMLeval()dangerouslySetInnerHTMLeval()undefinedundefined| ID | Description | Location | Recommendation |
|---|---|---|---|
| C1 | ... | ... | ... |
| ID | Description | Location | Recommendation |
|---|---|---|---|
| C1 | ... | ... | ... |
undefinedundefined