A deep white-box security assessment of the Veyrnox Wallet repository (self-custody cryptocurrency wallet for iOS/Android/web, ~200k LOC plus [edge-compute-platform] and [serverless-backend]) identified 15 confirmed vulnerabilities — 3 High, 10 Medium, 2 Low — each reproduced with a working proof of concept, root-caused to source, and remediated with a tested in-repo fix.
Overall risk posture: Elevated, with two items requiring immediate operational action.
Immediate operational actions (cannot be fixed by code alone):
Live third-party production credentials are public. The [payment-partner] production partner API key and secret sit in plaintext in the repository's operations guide and were verified live against [payment-partner]'s production API. Separately, the production HMAC signing secret for the [security-advisor-service] service remains recoverable from public git history and was never rotated. Both must be rotated immediately; the committed literals have been redacted in the working tree.
The referral backend's deployed state must be migrated and redeployed. The unauthenticated revenue-forgery fix ships as SQL migrations plus a proxy allowlist change; until operators run the migrations and redeploy the Pages Functions and webhook, the production endpoint accepts forged revenue rows that manufacture paid-tier status and real [subscription-vendor] entitlement grants.
Key findings:
Complete seed-theft chain on Android (pre-fix): an injected script inside the wallet WebView could retrieve the raw vault data-encryption key through the ungated biometric-cache bridge, read the encrypted vault blob through an ungated third-party storage plugin, decrypt it with the publicly derivable parameters, and broadcast a mainnet drain — no PIN, no prompt, and no exfiltration channel required.
Coercion-resistance (the product's flagship property) had three breaks: a plaintext version field distinguished every real hidden wallet from the 256 chaff slots in a forensic dump; panic wipe left [wallet-protocol] session records containing the wallet's address while reporting "clean"; and decoy/hidden sessions both leaked crash reports naming hidden-wallet components and rendered the real user's NFT collection to third-party CDNs.
Five distinct pre-sign security-gate gaps allowed signing or broadcasting with re-authentication, second-factor, spend-limit, or runtime-integrity checks skipped — including one where a single link click (?demo=1) persistently disarmed every human-confirmation layer for a real unlocked vault in shipped non-release builds.
Unauthenticated financial forgery: any anonymous client could insert arbitrary referral revenue rows (the proxy forwards the service-role key), flip [internal-function] eligibility, and trigger a real promotional entitlement grant; the referral earnings ledger was similarly readable without authentication.
Business impact: potential total loss of user funds on compromised Android devices (pre-fix), destruction of the deniability guarantee the product is sold on, forged revenue/entitlement records with direct payout-liability implications, and third-party account takeover potential from exposed production credentials.
Mitigating context: the vault cryptography itself (Argon2id, hardware-bound KEK, AES-256-GCM, AAD-bound blobs) fully matched its documented invariants, and the UI layer, deeplink/QR/postMessage entry surfaces, CORS/SSRF posture of the API proxies, and the [wallet-protocol] documented controls all withstood targeted attack — every candidate there was closed with a named, mostly dynamically-exercised control.
Technical Analysis
Severity model: CVSS v3.1 vectors derived from the demonstrated attack, calibrated per finding; 15 reports filed (3 High, 10 Medium, 2 Low), every one dynamically reproduced or, for the single device-dependent chain, executed at bridge-fidelity with the native links traced line-by-line.
1. Exposed production credentials (High — CVSS 7.2, 8.3). The [payment-partner] production partner key/secret sat in plaintext in the operations guide at HEAD and were verified live: the committed pair authenticated to [payment-partner]'s production token endpoint and received a 7-day partner access token. The [advisor] HMAC signing secret remains recoverable from public git history; the repository's own scrub commit states it "remains live in prod," and it is the sole authenticator for the [advisor-chat] API. Both are CWE-798 in an anonymously readable repository. Literals redacted in-tree; rotation is the real fix.
2. Unauthenticated referral revenue forgery → entitlement grants (High — CVSS 7.5). The Pages Function RPC proxy forwards the [backend-provider] service-role key for allowlisted functions; record_attribution was allowlisted with no caller authentication and no purchase proof. Reproduced against a full local mirror (real migrations, real [database-api], real functions running under [edge-cli]/[js-runtime]): one forged request inserted a revenue row, flipped [internal-function] eligibility, and drove the genuine bonus function to issue a real [subscription-vendor] promotional entitlement request. A schema drift (plan CHECK constraint never widened past legacy values) simultaneously guaranteed that only attacker-forged legacy plans could ever insert — honest purchases fail silently. Companion finding: the referral earnings ledger was readable unauthenticated.
3. Android seed-theft chain (Medium — CVSS 6.7, chained). Composing three weaknesses, an injected script could: pull the raw 32-byte vault DEK via the biometric-cache bridge (no native integrity gate, unlike every sibling plugin); read the encrypted vault blob via the third-party [third-party-plugin]'s ungated internalGetItem; decrypt locally with the publicly derivable AAD recipe; and broadcast a mainnet drain to the CSP-allowlisted public RPC — full seed recovery with no PIN entry, no prompt, and no exfiltration channel. The chain also proved the control case: the password-factor release alone does not decrypt (the hardware factor stays enclave-bound) — the DEK fast-path slot is the crown-jewel release.
4. Coercion-resistance breaks (Medium — CVSS 4.0–5.1). (a) The stealth chaff pool stamps plaintext envelope v:1 while real hidden wallets are written v:2; a forensic dump identifies every real hidden wallet with one field read (regression from a version bump that missed the chaff writer). (b) Panic wipe destroys the vault and residue databases but not the [wallet-protocol] SDK's own IndexedDB, leaving session records embedding the wallet's EVM address while the wipe report says "clean." (c) Decoy/hidden sessions leak [third-party-service] crash reports whose component stacks name hidden-wallet surfaces (the only gate ran at init, before any unlock could exist). (d) Three NFT pages read the device-global store ungated, rendering the real user's collection to third-party CDNs from inside a decoy session.
5. Pre-sign gate gaps (Medium/Low — CVSS 2.9–4.4). Five distinct call-site failures against an otherwise sound, fail-closed gate core: the token-approval revoke path reached the real signing key and broadcast with no gate at all; the [wallet-protocol] send path enforced the second factor only in Action-Password mode, silently skipping biometric/passkey users; the message-signing page gated on a up-to-60-seconds-stale runtime-integrity sample; the send screen keyed its exemptions off the raw demo flag rather than demoActive, so one ?demo=1 link click persistently disarmed re-auth, second factor, spend limits, and risk screening for a real unlocked vault (shipped builds compile the flag branch because the store/canary lanes never set [build-flag]); and the WC approval modal rendered ERC-20 transfers as "0 ETH," hiding the actual token amount and recipient — a full-balance drain displayed as a zero-value send.
Systemic themes: (1) Gate-at-every-new-surface is the recurring failure mode — the pure policy core is correct; call sites forget it (three separate instances this assessment, matching the project's own prior audit H-1 class). (2) Init-time vs. runtime-state checks — two deniability leaks shared the same shape: a control evaluated when the protected state could not yet exist. (3) Ship-path/build-wiring drift — the release-only guard existed but no shipped build path invoked it. (4) Third-party bridge plugins inherit trust without gates — hardening first-party plugins left the vault ciphertext one ungated bridge call away.
Verified clean (named controls, most dynamically exercised): vault cryptography (Argon2id, HKDF(H‖C) KEK, AES-256-GCM, AAD-bound versioned blobs, bounded KDF params, zeroization), backups (commitment-authenticated, KDF-pinned, no substitution path), UI render layer (no XSS sinks; icon/news URL allowlists), deeplink/postMessage/QR entry surfaces (strict allowlists, 54-test harness), API proxy CORS/SSRF/rate-limit posture, [wallet-protocol]'s documented controls (chainId binding, param-order resolution, gas clamp, session expiry), and RASP verdict freshness (native-sourced, fail-closed, one-way BLOCK latch).
Methodology
Engagement type: White-box source-assisted assessment of repository VEYRNOX/veyrnox (branch main). No deployed URL was in scope; all dynamic validation ran against locally executed code (Vite dev/build output, vitest+[test-lib] harnesses, a local [database]+[database-api]+[edge-cli]+[js-runtime] mirror of the production backend, and compiled bundle inspection of both build variants).
Framework alignment: OWASP WSTG (authentication, session, access control, input handling, business logic), OWASP MASVS (mobile storage, platform interaction, resilience) for the [mobile-framework]/native layer, with threat-model-driven prioritization per PTES.
Activities performed:
- Shared threat model derived from the repository's documented security invariants (key confinement, deniability, fail-closed behavior, untrusted backend) and amended as testing disproved or refined it.
- Structural mapping: [static-analysis-tool] and [static-analysis-tool] passes over 559 files cataloguing 336 sinks (storage, parse/decode, IPC, network, crypto, file), all entry points (15 HTTP handlers, 101 routes, deeplink funnels, the [mobile-framework] bridge), and the wallet-core dependency graph.
- Static triage: one scoped [static-analysis-tool] pass (5 rulesets), [secret-scanner] and [secret-scanner] over content and full 2,353-commit history, [fs-scanner] filesystem secret/misconfig scan, and a 20-category manual sink sweep — cross-referenced against the project's own prior audit corpus to avoid re-reporting known-fixed issues.
- Deep manual review of the crypto core (keystore, vault, duress/panic/stealth, backups, sign-gate), every backend function, the native Android/iOS layers, and the [wallet-protocol] provider.
- Dynamic validation: every Medium-or-above finding was reproduced at runtime — [test-lib]/[test-lib] harnesses against the real components and real cryptography, a full local backend mirror replaying the actual migrations and running the actual functions, and compiled-bundle inspection proving which code paths ship in which build variant.
- Attack chaining: all plausible cross-finding combinations were evaluated; one materially compounding chain was executed end-to-end and reported; the remainder collapsed against named controls or were ruled out as unrelated (documented).
- Remediation: each report carries an inline patch, applied and re-verified in the working tree with regression tests; the full test suite (6,000+ tests) passes on the patched tree.
- Coverage discipline: 134 surface/risk entries recorded — 24 reported, 48 clean after testing, 61 ruled out with named controls, 1 not applicable; zero items left open.
Constraints: no physical Android/iOS device or emulator was available, so native-layer findings rest on complete source traces plus bridge-fidelity harnesses rather than on-device execution; production deployment state (Pages build flags, environment secrets) could not be probed and is flagged where it affects remediation.
Recommendations
Immediate (this week)
Rotate the exposed production credentials: replace the [payment-partner] production partner key/secret via the partner dashboard and the [advisor] HMAC signing secret across the worker and both [backend-provider] projects in one coordinated flip; treat both historical values as permanently public. The in-repo literals have been redacted — commit that alongside the rotation. History rewrite is optional and only after rotation.
Deploy the referral-backend fixes: run the three changed SQL migrations (api-security-hardening, ai-referral-attribution-plan-family, base schema) and redeploy the Pages Functions and [internal-webhook]. Until then, record_attribution remains forgery-capable in production.
Verify the production web build sets [build-flag]=1 in the [hosting-platform] project configuration (outside this repository). Without it, the ?demo=1 flag branch ships to real users; the CI store lanes are already corrected in-tree.
Audit the referral ledger before paying referrers: every referral_attributions row created after the plan-family migration is either forged or stale — do not pay from it until reviewed.
Merge and ship the applied fixes (all in the working tree with green suites): the bridge integrity gates — extended to the third-party SecureStorage vault-blob read path, not just first-party plugins — should be treated as the highest-priority code change.
Short-term (next month) 6. Enforce a repo-wide invariant by CI: fail the build when any withPrivateKey*/sign primitive gains a call site without a fresh-probe presignGate composition — three of the five gate gaps were exactly this class. 7. Sweep every remaining consumer of device-global stores for the deniability enabled-gate + blocked-mutation pattern (the NFT fix shows the shape); keep the CSP-parity and chaff-uniformity tests as build-breaking regression pins. 8. Add a native WebView network policy separating app-initiated RPC from page-initiated traffic — CSP connect-src permits in-WebView broadcast of signed drains, so it is not an anti-exfiltration control. 9. Compile-verify the Kotlin bridge gates on a real toolchain (compileDebugKotlin + the plugin's JVM suite) and run the bridge PoC on a device to confirm the native gate blocks the DEK release path.
Medium-term (next quarter) 10. Add the one-block chaff-version-uniformity assertion to the shipped stealth test suite and the WC message-size cap at 64–256 KB to the hardening backlog already landed. 11. Revisit the documented-accepted residuals with product owners: the duress/panic localStorage configuration markers and the fast-path 30-second biometric window are conscious trade-offs — record the decisions in the threat model. 12. Commission the outstanding independent audits the README itself flags (hardware-KEK layer and native RASP), now informed by the bridge-gate findings above.
Retest & validation: re-run this assessment's regression harnesses (chaff PoC, kill-chain bridge test, referral-forgery local mirror, send-gate demo-disarm suite, WC transfer-display tests, NFT deniability tests, CSP-parity guard) after the rotations and the production redeploy. Rotation is confirmed closed only when the committed [payment-partner] pair stops returning tokens from the refresh endpoint and the [advisor] verifier rejects signatures made with the historical secret.