# Security Audit Report
## Tangem Android Wallet — Source Code Review
---
| | |
|---|---|
| **Subject** | `tangem-app-android` — Tangem cryptocurrency wallet for Android |
| **Upstream** | `https://github.com/tangem/tangem-app-android` |
| **Commit reviewed** | `358144ee61a` (branch `master`) — confirmed identical to upstream `master` (§6.5) |
| **Report date** | 17 August 2026 |
| **Revised** | 17 August 2026 — §3.1, §3.6, O-3 corrected; §6.5 resolved. See "Revisions" below |
| **Companion report** | [APKANALYSIS.md](./APKANALYSIS.md) — binary comparison against Tangem's official v6.1.2 release APK |
| **Review type** | Static source code review (manual, targeted) |
| **Codebase size** | ~292 Gradle modules · 7,719 Kotlin files · 641 XML · 5 Java |
| **Objective** | Detect malware, backdoors, information leaks, obfuscated code, and remote code-download behaviour |
---
## 1. Executive Summary
**The reviewed source code is clean.** No malicious code, backdoor, data-exfiltration channel,
obfuscated logic, or remote code-download ("dropper") capability was identified.
Beyond the absence of malicious code, the codebase demonstrates **deliberate, consistent defensive
engineering** of a standard appropriate to software that custodies private keys. Several controls
found here are ones that trojanised or negligent applications characteristically lack: JavaScript
is disabled in WebViews with no JavaScript bridge present at all, certificate validation is left
entirely intact, analytics identifiers are cryptographically hashed rather than transmitted raw,
and the application manifest actively *removes* location and microphone permissions that bundled
third-party SDKs attempt to introduce.
Ten categories of malicious or leak-prone behaviour were examined. All ten returned negative.
Three informational observations are recorded in §5 — none is a vulnerability, and none affects
the verdict.
### Findings Summary
| Severity | Count | Detail |
|---|---|---|
| **Critical** | **0** | — |
| **High** | **0** | — |
| **Medium** | **0** | — |
| **Low** | **0** | — |
| Informational | 3 | Operational notes for downstream forks (§5) |
### Verdict
> **CLEAN.** Nothing in the reviewed first-party source behaves maliciously or leaks sensitive
> data. The security posture is materially better than typical for the category.
Readers should note the scope boundaries in §6 — most importantly that Tangem's own SDKs are
consumed as **pre-built binary artifacts** and were therefore outside the reach of this source
review.
### Revisions (17 August 2026)
This report was amended after a build of the source was produced and compared against Tangem's
official release APK. **Three corrections narrow claims that were stated too broadly**; one closes a
limitation. All should be read before relying on the original text:
| Section | Change |
|---|---|
| §3.1 | "No native code" holds for the repository, **not** for the built application, which bundles 14 native libraries — including those performing key derivation, signing, and at-rest encryption |
| §3.6 | The nine permissions listed are the *source manifest* set. The **merged** manifest contains 23. "Permissions minimised" overstated the position |
| O-3 | A clean checkout **does not build at all** — the `ds-tokens` submodule is unresolvable outside Tangem. The prior text said it compiles |
| §6.5 | Commit provenance is now **verified** against upstream, closing a limitation rather than adding one |
None of these alters the §1 verdict on the source. §3.1 and §3.6 do mean the shipped application has
a larger native-code and permission surface than the original report conveyed.
---
## 2. Methodology
The review targeted the behaviours that distinguish malicious software from ordinary application
code, rather than attempting an exhaustive line-by-line reading of 7,719 files. Each question
below was resolved by exhaustive pattern analysis across the full tree, followed by manual reading
of every candidate hit in context.
| # | Question put to the code | Result |
|---|---|---|
| 1 | Can it load or execute code obtained at runtime? | **No** |
| 2 | Can it execute shell commands or spawn processes? | **No** |
| 3 | Does the *repository* contain native binaries or opaque blobs? | **No** — but the built APK bundles 14 native libraries from binary dependencies; see §3.1 |
| 4 | Does key material reach logs, storage, or the network? | **No** |
| 5 | Does it contact hidden or hostile endpoints? | **No** |
| 6 | Are live credentials committed to the repository? | **No** |
| 7 | Can the build system pull hostile artifacts? | **No** |
| 8 | Does telemetry leak personally identifying data? | **No** — pseudonymised |
| 9 | Is transport security weakened anywhere? | **No** — hardened |
| 10 | Are permissions over-broad or components exposed? | **No** — minimised |
A finding was only cleared once the underlying code was read and understood in context. Two
pattern classes produce **misleading automated results** and are documented explicitly in §4 so
that any repeat or automated review does not raise a false alarm.
---
## 3. Detailed Findings
### 3.1 Runtime Code Loading and Dropper Behaviour — CLEAN
No occurrence of `DexClassLoader`, `PathClassLoader`, `BaseDexClassLoader`,
`InMemoryDexClassLoader`, or any `dalvik.system` API exists in the repository. The application
cannot load code it did not ship with.
**Process execution.** Exactly one match tree-wide:
```kotlin
// app/src/main/java/com/tangem/tap/common/finisher/AndroidAppFinisher.kt:25
Runtime.getRuntime().exit(0)
```
This terminates the application's own process. It executes nothing.
**Native code — repository.** No `.so` libraries, no JNI, and no NDK build inputs are committed to
the tree. Only two `.jar` files exist:
| File | Size | Assessment |
|---|---|---|
| `gradle/wrapper/gradle-wrapper.jar` | 55.6 KB | Standard Gradle 8.14.1 wrapper |
| `app/libs/dexprotector-annotations.jar` | 3.5 KB | Compile-time annotations only (§3.9) |
**Native code — built application.** *(Correction, 17 Aug 2026.)* The statement above describes the
repository, not the artifact. A build of this source bundles **14 native libraries**, several of
them security-critical:
```
libTrustWalletCore.so · libsecp256k1-jni.so · libTrezorCrypto.so · libargon2.so
libblst.so · libsqlcipher.so · libuniffi_yttrium.so · libtoolChecker.so
libtensorflowlite_jni.so · libbarhopper_v3.so · libjnidispatch.so
libimage_processing_util_jni.so · libandroidx.graphics.path.so · libsurface_util_jni.so
```
Key derivation and signing (`libTrustWalletCore`, `libsecp256k1-jni`, `libTrezorCrypto`), key
stretching (`libargon2`), and at-rest database encryption (`libsqlcipher`) are all performed in
native code. These arrive through the pre-built Maven dependencies excluded from this review by §6.1
and were therefore **not** examined. Tangem's own release build adds `libdexprotector.so` and
`libalice.so` on top. See [APKANALYSIS.md](./APKANALYSIS.md) §4.2.
**Reflection.** Searches for `Class.forName`, `getDeclaredMethod`, `getDeclaredField`, and
`setAccessible` returned **zero** genuine results. There is no reflective dispatch to hide
behaviour behind.
### 3.2 Obfuscation and Encoded Payloads — CLEAN
A scan for long opaque literals (≥200 characters of Base64-like text) returned three hits, all in
`libs/visa/`:
- `TangemPaymentAccountRegistry.java`
- `TangemBridgeProcessor.java`
- `TangemPaymentAccount.java`
Each declares `public static final String BINARY = "6080604052…"`. The `6080604052` prefix is the
standard **EVM (Solidity) contract constructor prologue**. These are Web3j-generated smart-contract
wrappers carrying on-chain deployment bytecode — an expected artifact of the Visa payment-account
integration, not concealed program logic.
All eight `Base64.decode` call sites serve declared cryptographic purposes — AES/RSA key and IV
decoding in `RainCryptoUtil`, `DefaultAuthNonceDecryptor`, and `SetVisaPinCodeUseCase`, plus swap
transaction decoding. None reconstructs an executable payload.
### 3.3 Private Key and Seed Phrase Handling — CLEAN
This is the highest-consequence area for a wallet. It is handled correctly.
**Key material never reaches logs.** Every logging call (`Log.*`, `Timber.*`, `println`) was
cross-referenced against *mnemonic, seed, privateKey, passphrase, secret, accessCode, passcode*.
**No true positives.** The only matches were UI-state fields on onboarding screens
(`dialog.dismissButtonText`, `dialog.dismissWarningColor`) — incidental matches on the substring
"dismiss", carrying no secret data.
**Key material never reaches the network.** The sole endpoint whose name suggests otherwise is
`v1/seedphrase-notification/{wallet_id}`. Its complete payload is:
```kotlin
data class SeedPhraseNotificationDTO(val status: Status)
enum class Status { NOT_NEEDED, NOTIFIED, DECLINED, CONFIRMED, REJECTED, ACCEPTED }
```
A single enum recording whether the user was reminded to back up their phrase. No phrase material
is transmitted.
**Export is authentication-gated.** `ExportSeedPhraseUseCase` delegates to
`DefaultHotWalletAccessor.exportSeedPhrase()`, which routes through `hotSdkRequest()` — a path
requiring password or biometric unlock (`HotAuth.Password` / `HotAuth.Biometry`) before the SDK
releases the mnemonic. Wrong-password retry and biometric lockout are handled explicitly.
**Exported phrases have exactly two consumers**, both user-initiated display flows:
- `features/hot-wallet/impl/…/viewphrase/model/ViewPhraseModel.kt` — user views their own backup
- `features/onboarding-v2/impl/…/upgradewallet/model/MultiWalletUpgradeWalletModel.kt`
Neither transmits, persists, nor logs the value.
**Supporting controls.** `clearContextualUnlock()` and `clearAllContextualUnlock()` purge unlocked
wallet handles from memory; `LockUserWalletsTimer` auto-locks wallets on a timer via WorkManager.
### 3.4 Network Endpoints — CLEAN
Every hardcoded host in the tree was enumerated and classified. All fall into legitimate
categories:
| Category | Examples |
|---|---|
| Public blockchain RPC | Ankr, publicnode, dRPC, thirdweb, Infura, QuikNode, GetBlock, Arbitrum, Base, Solana, Tron, Kaspa, Polkadot |
| Tangem infrastructure | `api.tangem.com`, `express.tangem.com`, `buy.tangem.com` |
| Payment / staking partners | MoonPay, Mercuryo, P2P.org, StakeKit, Paera |
| Documentation / schema | `schemas.android.com`, `developer.android.com`, EIP references |
No unexplained endpoint, dead-drop host, dynamic-DNS domain, or hardcoded IP address was found.
### 3.5 Transport Security — HARDENED
`app/src/main/res/xml/network_security_config.xml`:
```xml
```
- Cleartext HTTP disabled application-wide.
- **System trust anchors only** — user-installed CAs are excluded, resisting casual interception
proxies.
- No `debug-overrides` block.
Searches for `X509TrustManager`, `checkServerTrusted`, `HostnameVerifier`, `ALLOW_ALL`, and
`SSLContext` returned **zero** results. No certificate-validation bypass exists anywhere in the
repository — a common weakness that is simply absent here.
**WebView.** `core/ui/…/webview/WebViewExt.kt` sets `javaScriptEnabled = false`. There is no
`addJavascriptInterface` or `@JavascriptInterface` anywhere in the tree. The JavaScript-bridge
attack surface does not exist by construction.
### 3.6 Permissions and Exported Components — RESTRAINED IN SOURCE, WIDER AS SHIPPED
Permission set declared in the **hand-written manifests** (`app/src/main/`, `app/src/huawei/`):
`INTERNET` · `ACCESS_NETWORK_STATE` · `CAMERA` (QR scanning) · `NFC` (card scanning) ·
`USE_BIOMETRIC` · `VIBRATE` · `WAKE_LOCK` · `HIDE_OVERLAY_WINDOWS` · `AD_ID`
Nothing there exceeds what a wallet requires.
**Correction (17 Aug 2026).** An earlier revision presented the nine above as the complete set and
listed external storage and `RECEIVE_BOOT_COMPLETED` among permissions that were *absent*. That was
wrong. The nine describe the source manifests; the **merged** manifest in the built APK contains
**23**. The additional fourteen, contributed by manifest merging from bundled third-party SDKs:
```
READ_EXTERNAL_STORAGE · WRITE_EXTERNAL_STORAGE · WRITE_SETTINGS
RECEIVE_BOOT_COMPLETED · FOREGROUND_SERVICE · USE_FINGERPRINT · POST_NOTIFICATIONS
BIND_GET_INSTALL_REFERRER_SERVICE · ACCESS_ADSERVICES_ATTRIBUTION
ACCESS_ADSERVICES_AD_ID · c2dm.RECEIVE · DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION
com.samsung.android.mapsagent.permission.READ_APP_INFO
com.huawei.appmarket.service.commondata.permission.GET_COMMON_DATA
```
`WRITE_SETTINGS` and `RECEIVE_BOOT_COMPLETED` are broader than a wallet needs and are not explained
by first-party code; they warrant enquiry with the SDK vendors that introduce them. This merged set
is **identical** in Tangem's official release APK and in a build from this source — see
[APKANALYSIS.md](./APKANALYSIS.md) §4.1 and D-1.
**Still genuinely absent** from the merged manifest: SMS, contacts, call log, location, microphone,
accessibility services, `SYSTEM_ALERT_WINDOW`, and `REQUEST_INSTALL_PACKAGES`.
Two measures are worth drawing out, as both are affirmative hardening rather than mere restraint:
**1. Overlay defence.** `HIDE_OVERLAY_WINDOWS` is requested to defeat tapjacking — a known vector
for hijacking wallet transaction-confirmation dialogs.
**2. Permission stripping.** The manifest actively removes permissions that bundled third-party
SDKs would otherwise introduce through manifest merging:
```xml
```
Denying location and microphone access to advertising and analytics SDKs is a privacy measure that
argues directly against hostile intent. **This control was verified in the shipped artifact:**
neither location nor `RECORD_AUDIO` appears in the merged manifest of Tangem's official release APK
(APKANALYSIS.md §4.1). The stripping is effective, not merely declared.
**Exported components.** Exactly one: `MainActivity`, necessarily exported as the launcher. Its
intent filters are tightly scoped — the `LAUNCHER` category, NFC NDEF discovery restricted to
`tangem.com` / `www.tangem.com` / `app.tangem.com` under `/ndef` path prefixes, and the `wc:` and
`tangem://wc` WalletConnect schemes.
The single declared service, `TangemPushNotificationService` (Firebase Cloud Messaging), is
`exported="false"`. There are **no broadcast receivers, no boot receivers, and no content
providers**. The only `WorkManager` job in the application is `LockUserWalletsTimer` — which
*locks* wallets.
### 3.7 Telemetry and Personal Data — CLEAN (pseudonymised)
Analytics and marketing SDKs present: Firebase Analytics/Crashlytics, Amplitude, AppsFlyer,
CustomerIO, SurveySparrow. This is conventional commercial telemetry, declared openly in the build
configuration.
The user identifier transmitted to these services is **hashed, never raw**
(`app/…/analytics/DefaultTrackingContextProxy.kt`):
```kotlin
private fun calculateUserIdHash(userWalletId: UserWalletId?): String? {
return userWalletId?.value
?.calculateSha256()
?.toHexString()
}
```
Every `setUserProperties` call site — cold wallet, hot wallet, and scan response — routes through
this function. Accompanying properties are non-identifying product facts (`batchId`, `productType`,
`firmwareVersion`).
No device-identifier harvesting was found in any analytics path: no `Settings.Secure.ANDROID_ID`,
no IMEI, no MAC address, no subscriber ID.
### 3.8 Local Diagnostic Logging — CLEAN
`NetworkLogsSaveInterceptor` records HTTP traffic to a local `AppLogsStore`, supporting the in-app
"contact support with logs" feature. The implementation shows care:
- URLs pass through `SensitiveUrlMasker`, replacing configured secrets with `******`. The masker
sorts candidates by **descending length**, so a shorter value that is a prefix of a longer one
cannot mask first and leave a partial secret exposed — a considered detail.
- Sensitive endpoints are excluded from body logging entirely, via `restrictedForLogURLs`
(`api.stakek.it/v1/yields/enabled`) and `restrictedForLogHosts` (`us.paera.com`).
- Response bodies beyond ~2 MB are skipped; binary bodies are omitted.
**Egress is user-initiated only.** `DefaultFeedbackRepository` exposes `getLogFile()` /
`getZipLogFile()` and passes the file to `EmailSender` as an attachment, opening the platform email
intent. Logs are **never** uploaded automatically or silently. See Observation **O-1**.
### 3.9 Build System and Supply Chain — CLEAN
**Repositories** — all official, no typosquats or unexpected mirrors:
`gradlePluginPortal()` · `google()` (content-filtered to `androidx` / `com.android` / `com.google`)
· `mavenCentral()` · `developer.huawei.com/repo` (official, for the Huawei flavour) ·
`maven.pkg.github.com/tangem/*` (first-party)
`settings.gradle.kts` enforces:
```kotlin
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
```
This **prevents any individual module from silently introducing its own repository** — a
meaningful supply-chain control, and one many projects omit.
**No build-time code execution or fetching.** All `*.gradle.kts` and `*.gradle` files were searched
for `exec {`, `URL(`, `download`, `curl`, `wget`, and `HttpURLConnection`. **Zero results.** The
build does not download or run anything beyond declared Maven dependencies.
**Gradle wrapper** — `distributionUrl` resolves to the official
`https://services.gradle.org/distributions/gradle-8.14.1-bin.zip`.
```
gradle-wrapper.jar SHA-256: 3dc39ad650d40f6c029bd8ff605c6d95865d657dbfdeacdb079db0ddfffedf9f
Size: 55,616 bytes
```
Verify this against the official Gradle 8.14.1 wrapper when reproducing the build.
**DexProtector.** The `com.dexprotector.rtc.RtcStatus` reference in
`DefaultDeviceSecurityInfoProvider` is Licel's commercial application-hardening product, integrated
for **runtime root, emulator, and tamper detection**. Its presence is a defensive control, not
concealment: the mock build flavour transparently substitutes
`MockAwareDeviceSecurityInfoProvider`, which reports a clean device.
### 3.10 Committed Credentials — CLEAN
`app/src/main/assets/tangem-app-config/config_prod.json` and `config_dev.json` declare the full key
schema — Amplitude, AppsFlyer, Blockchair, BlockCypher, Infura, QuikNode, GetBlock, MoonPay,
Mercuryo, NowNodes, TonCenter and others — with **every value set to the literal string
`PLACEHOLDER`**. No live credentials are committed.
The only committed keystore is a dummy debug keystore:
```properties
store_password=android
key_alias=dummy
key_password=android
```
These are the well-known Android debug defaults. The keystore cannot sign a release build. See
Observation **O-3**.
---
## 4. Expected False Positives
Two pattern classes in this repository will alarm automated scanners and reviewers working from
grep output alone. Both are benign. They are documented here so that any subsequent review reaches
the same conclusion without re-investigating.
### FP-1 — `loadDex*` is *Decentralised Exchange*, not Android DEX
A search for `loadDex` returns numerous hits across `features/swap/`:
```
loadDexSwapData · loadDexSwapDataNoFee · loadDexSwapFee · DexSwapFeeCalculator · ResolvedFlow.DexLike
```
**DEX** here means **D**ecentralised **EX**change — token-swap business logic. These functions load
swap quotes and fee data from exchange providers. They have no relationship to Android DEX
bytecode or class loading. Confirmed by reading `SwapInteractorImpl.kt` and the surrounding swap
domain.
### FP-2 — Hostile hostnames are security *test fixtures*
The following appear as string literals in the tree:
```
faketangem.com · fake.tangem.com · tangem.com.attacker.com
buy.tangem.com.attacker.com · evil-spoofed.example · fake.surveysparrow.com
```
Every one is a **negative test case** asserting `expected = false`, located in:
- `common/src/testDebug/…/uri/ExternalUrlValidatorTest.kt`
- `data/wallet-connect/src/test/…/DefaultWcPairUseCaseTest.kt`
Their presence is **evidence of a deliberate anti-spoofing URL validator** and a WalletConnect
proposal-spoofing defence, verified against realistic attack strings. These are security controls
under test, not indicators of compromise.
---
## 5. Observations
Operational notes for anyone forking or deploying this application. **None is a vulnerability**,
and none affects the verdict in §1.
### O-1 — Support logs are financially sensitive
When diagnostic logging is active, full JSON request and response bodies are written to local
application storage. URLs are masked and specific hosts excluded (§3.8), but bodies may still
contain wallet addresses, balances, and transaction details. The data never leaves the device
unless the user deliberately emails it to support.
**Recommendation:** treat a user-submitted support-log attachment as sensitive financial data in
your support process — restrict access, and set a retention limit.
### O-2 — Third-party telemetry ships enabled by default
Five analytics and marketing SDKs (Firebase, Amplitude, AppsFlyer, CustomerIO, SurveySparrow) and
the `AD_ID` permission are present in the Google flavour. This is disclosed and the user identifier
is pseudonymised (§3.7).
**Recommendation:** a privacy-focused fork may wish to remove Amplitude, AppsFlyer, CustomerIO, and
SurveySparrow, and drop the `AD_ID` permission. The Huawei flavour already differs in SDK
composition.
### O-3 — A clean checkout does not build, and does not fully function once it does
*(Revised 17 Aug 2026 after an actual build was attempted. The previous text stated a clean checkout
compiles; it does not.)*
**The build fails outright.** `core/ui/ds-tokens` is registered in the tree as a submodule gitlink
(commit `a59c6a36`), but **no `.gitmodules` file exists anywhere in the repository's 16,087-commit
history**. There is no URL from which the submodule can be resolved. `:core:ui:verifyDesignTokens`
runs during `preBuild` and aborts before any module compiles, emitting the instruction
`git submodule update --init --recursive` — which cannot succeed outside Tangem's own environment.
The task only *verifies*: it hashes the submodule's JSON and SVG sources and compares the digest
against a committed `.tokens-hash`. It generates nothing, and the 380 generated files it guards are
all committed. Building with `-x :core:ui:verifyDesignTokens` therefore produces a functionally
complete APK, at the cost of losing the assurance that the committed generated sources still
correspond to the design tokens they derive from.
**Once building, integrations remain partly inert.** All configuration values are `PLACEHOLDER`
(§3.10) and are compiled into the APK at build time by `GenerateEnvironmentConfigTask`. No runtime
check detects the sentinel, so the application transmits `PLACEHOLDER` as a live credential and
receives authentication failures.
The practical effect is uneven. `providers_order.json` lists RPC providers per chain as either
`public` (no key) or `private` (keyed), and the SDK falls through the list in order. Of 97 chains,
**87 retain at least one public provider and will synchronise without any key**. The 10 that do not
are:
```
bitcoin · ethereum · solana · cardano · litecoin
dogecoin · bitcoin-cash · dash · the-open-network · chia
```
— that is, the chains most users actually hold. Fiat prices and the swap aggregator additionally
depend on Tangem-issued credentials that cannot be self-provisioned.
**Consequence for downstream forks:** a `PLACEHOLDER` build presents as a working wallet while
silently failing to report balances on the major chains. A user scanning a funded card may see a
zero balance and conclude their funds are lost. Such a build should not be distributed to
non-technical users, and any release of one should say so explicitly.
Publishing a source tree without private keys or internal submodules is correct practice, not a
defect — but the resulting checkout is neither buildable nor safely usable as-is, and that should be
stated plainly rather than left to be discovered.
---
## 6. Scope and Limitations
These boundaries are stated plainly so that no broader assurance is inferred than was actually
obtained.
1. **First-party source only.** Tangem's own SDKs — `com.tangem:blockchain` (Blockchain SDK),
`tangem-sdk-kotlin` (Card SDK), `tangem-hot-sdk-kotlin` (Hot Wallet SDK), `wallet-core`, `vico`,
and `web3j` — are resolved as **pre-built binary artifacts** from GitHub Packages at build time
and were **not** reviewed. This is a material limitation: the hot-wallet SDK performs the actual
mnemonic encryption and at-rest storage. Assurance over those components requires a separate
review of their own repositories.
2. **Third-party Maven dependencies were not individually reviewed**, although the repository set
from which they are drawn is constrained and official (§3.9).
3. **No dynamic analysis was performed.** This is a static review. No APK was executed,
instrumented, decompiled, or observed on a network. Runtime behaviour was inferred from source.
4. **No cryptographic correctness review.** This review establishes that key material is not
*leaked*. It does not assess whether the encryption schemes protecting that material at rest are
correctly chosen or parameterised.
5. **Commit provenance — resolved.** *(Updated 17 Aug 2026.)* An earlier revision recorded this as
an open limitation. It has since been closed: `git ls-remote` confirms that `master` at
`github.com/tangem/tangem-app-android` is `358144ee61a9f65e0f39578e8c943226abb5d996` — the exact
commit reviewed — and `git diff` against it is empty. The reviewed tree is the published upstream
tree, unmodified.
The `Updated on 2026-08-14` subject line appears on **all 16,087 commits**, not merely recent
ones, so it is an artefact of how the upstream repository is published rather than evidence of
rewriting. Per-change authorship and history remain unavailable for inspection, so the *content*
of individual upstream changes could not be reviewed; the *identity* of the tree is now
established.
6. **Point-in-time.** Findings apply to commit `358144ee61a` only.
---
## 7. Conclusion
The reviewed source is **clean**. No malware, backdoor, dropper, exfiltration channel, or
intentionally obfuscated logic was identified across ten categories of examination.
The codebase further exhibits positive security engineering that would be unusual in a trojanised
or negligent application:
- JavaScript disabled in WebViews, with no JavaScript bridge present at all
- Certificate validation fully intact; cleartext traffic disabled; user-installed CAs excluded
- Location and microphone permissions actively stripped from third-party SDK manifests
- Analytics identifiers SHA-256 hashed rather than transmitted raw
- A dedicated external-URL anti-spoofing validator, tested against realistic attack strings
- Tapjacking/overlay protection via `HIDE_OVERLAY_WINDOWS`
- Seed-phrase export gated behind biometric or password authentication
- Automatic wallet locking on an inactivity timer
- `FAIL_ON_PROJECT_REPOS` constraining the build supply chain
- Commercial runtime tamper and root detection
Subject to the scope boundaries in §6 — in particular the unreviewed binary SDKs and the native
libraries they contribute (§3.1) — **nothing in the reviewed first-party source is an obstacle to
building and distributing this application.**
That is a narrower statement than "safe to distribute", and deliberately so. A build produced from
this source alone carries `PLACEHOLDER` credentials and will not report balances on the ten most
widely held chains while otherwise presenting as a functioning wallet (O-3). Anyone distributing
such a build must say so explicitly. Assurance over the binary that Tangem itself ships is a
separate question, addressed — and largely left open — in [APKANALYSIS.md](./APKANALYSIS.md).
---
*Static source review of commit `358144ee61a`. Findings reflect that commit only; re-audit after
merging upstream changes.*