deploy: POST /api/wallets + full swagger
This commit is contained in:
39
apps/api/src/lib/address-validators.ts
Normal file
39
apps/api/src/lib/address-validators.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Chain-specific address format validators.
|
||||
* НЕ заменяет реальную чеканку checksum — это первый barrier.
|
||||
*/
|
||||
import { ethers } from 'ethers';
|
||||
|
||||
const BTC_RE = /^(bc1[a-zA-HJ-NP-Z0-9]{25,62}|[13][a-km-zA-HJ-NP-Z1-9]{25,34})$/;
|
||||
const TRX_RE = /^T[1-9A-HJ-NP-Za-km-z]{33}$/;
|
||||
const SOL_RE = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
||||
|
||||
export type ChainCode = 'ETH' | 'BTC' | 'SOL' | 'TRX' | 'BSC';
|
||||
|
||||
export function isValidAddress(chain: ChainCode, address: string): boolean {
|
||||
if (typeof address !== 'string' || address.length === 0 || address.length > 256) return false;
|
||||
|
||||
switch (chain) {
|
||||
case 'BTC':
|
||||
return BTC_RE.test(address);
|
||||
case 'TRX':
|
||||
return TRX_RE.test(address);
|
||||
case 'ETH':
|
||||
case 'BSC':
|
||||
return ethers.utils.isAddress(address);
|
||||
case 'SOL':
|
||||
return SOL_RE.test(address);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isValidAmount(amount: string): boolean {
|
||||
if (typeof amount !== 'string' || amount.length === 0 || amount.length > 78) return false;
|
||||
if (!/^\d+$/.test(amount)) return false;
|
||||
try {
|
||||
return BigInt(amount) > 0n;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,25 @@ function getCallerInfo(): { file: string; line: number } {
|
||||
return { file: 'unknown', line: 0 };
|
||||
}
|
||||
|
||||
// Удаляем потенциально чувствительные значения из лог-сообщений.
|
||||
// Защита от случайной утечки Vault tokens / passwords / secrets через сообщения ошибок.
|
||||
const SENSITIVE_PATTERNS: { regex: RegExp; replace: string }[] = [
|
||||
{ regex: /(password|passwd|pwd)\s*[:=]\s*\S+/gi, replace: '$1=***' },
|
||||
{ regex: /(token|secret|api[_-]?key|auth)\s*[:=]\s*\S+/gi, replace: '$1=***' },
|
||||
{ regex: /\bhvs\.[A-Za-z0-9]+/g, replace: 'hvs.***' },
|
||||
{ regex: /\bs\.[A-Za-z0-9]{20,}/g, replace: 's.***' },
|
||||
{ regex: /Bearer\s+[A-Za-z0-9._-]+/gi, replace: 'Bearer ***' },
|
||||
{ regex: /Authorization:\s*\S+/gi, replace: 'Authorization: ***' },
|
||||
];
|
||||
|
||||
function sanitize(msg: string): string {
|
||||
let out = msg;
|
||||
for (const { regex, replace } of SENSITIVE_PATTERNS) {
|
||||
out = out.replace(regex, replace);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function log(level: string, message: string): void {
|
||||
if ((LEVELS[level] ?? 0) < MIN_LEVEL) return;
|
||||
|
||||
@@ -41,7 +60,7 @@ function log(level: string, message: string): void {
|
||||
file: caller.file,
|
||||
line: caller.line,
|
||||
trace_id: getTraceId(),
|
||||
message,
|
||||
message: sanitize(message),
|
||||
};
|
||||
process.stdout.write(JSON.stringify(entry) + '\n');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user