security: round 3 hardening (CSRF double-submit, TRX MITM, container hardening)
This commit is contained in:
@@ -85,8 +85,8 @@ async function getSwapQuote(req: Request, res: Response) {
|
||||
toDecimals: TOKEN_DECIMALS[to],
|
||||
});
|
||||
} catch (error) {
|
||||
const msg = error instanceof Error ? error.message : 'Failed to get BSC swap quote';
|
||||
res.status(502).json({ success: false, error: msg });
|
||||
console.error(`BSC quote failed: ${(error as any)?.stack || (error as any)?.message}`);
|
||||
res.status(502).json({ success: false, error: 'Failed to get BSC swap quote' });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,18 @@ async function buildSwapTx(req: Request, res: Response) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate amount + amountOutMin: positive integers > 0 (string-encoded BigInt).
|
||||
// "0" truthy bypass — без этого attacker может выставить amountOutMin=0 = 100% slippage
|
||||
// → sandwich attack осушает swap.
|
||||
if (!/^\d+$/.test(String(amount)) || BigInt(amount) <= 0n) {
|
||||
res.status(400).json({ success: false, error: 'Invalid amount (must be positive integer string)' });
|
||||
return;
|
||||
}
|
||||
if (!/^\d+$/.test(String(amountOutMin)) || BigInt(amountOutMin) <= 0n) {
|
||||
res.status(400).json({ success: false, error: 'Invalid amountOutMin (must be positive; 0 = unlimited slippage)' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const provider = new ethers.providers.StaticJsonRpcProvider(BSC_RPC, BSC_CHAIN_ID);
|
||||
const routerContract = new ethers.Contract(PANCAKE_ROUTER, ROUTER_ABI, provider);
|
||||
@@ -145,10 +157,12 @@ async function buildSwapTx(req: Request, res: Response) {
|
||||
);
|
||||
|
||||
if (currentAllowance.lt(ethers.BigNumber.from(amount))) {
|
||||
// Build approve tx
|
||||
// Exact-amount approve (НЕ MaxUint256!). Infinite approval = post-tx attack vector:
|
||||
// если router compromised или attacker узнаёт private key позже, attacker дренит
|
||||
// всё что approved. Approve только то что нужно сейчас.
|
||||
const approveData = tokenContract.interface.encodeFunctionData(
|
||||
'approve',
|
||||
[PANCAKE_ROUTER, ethers.constants.MaxUint256]
|
||||
[PANCAKE_ROUTER, amount]
|
||||
);
|
||||
|
||||
transactions.push({
|
||||
@@ -175,8 +189,8 @@ async function buildSwapTx(req: Request, res: Response) {
|
||||
|
||||
res.json({ success: true, transactions });
|
||||
} catch (error) {
|
||||
const msg = error instanceof Error ? error.message : 'Failed to build BSC swap';
|
||||
res.status(502).json({ success: false, error: msg });
|
||||
console.error(`BSC build failed: ${(error as any)?.stack || (error as any)?.message}`);
|
||||
res.status(502).json({ success: false, error: 'Failed to build BSC swap' });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user