Skip to content

security: harden loader — remove unverified load(), atomic verified install - #2

Merged
exo-doguhan merged 3 commits into
masterfrom
exo-doguhan/security-harden-native
Sep 1, 2026
Merged

security: harden loader — remove unverified load(), atomic verified install#2
exo-doguhan merged 3 commits into
masterfrom
exo-doguhan/security-harden-native

Conversation

@exo-doguhan

Copy link
Copy Markdown

Overview

Security hardening of the bundle loader, following up on the app-sec review of the dynamic dev-bundle feature. The goal is to make the library safe in isolation: it should never load a remote bundle without native integrity verification, and never leave unverified or partial bytes on disk.

  • Remove the unverified load() path entirely — iOS load:, Android load() + downloadToCache, the JS load/loadUnverified exports, and the BundlePrompt URL-typing UI. Loading a remote bundle without native SHA-256 verification is an unauthenticated RCE primitive; only loadVerified(url, sha256) remains.
  • Android: verify before install. Download to a temp file, hash it, and atomically promote it to the canonical verified-bundle.jsbundle (same-directory rename) only on a hash match; delete the temp on any mismatch or error. The canonical path never holds unverified or partial bytes (closes the verify-after-write TOCTOU).
  • iOS: 64 MB size cap (parity with Android) before the response is hashed, written, or loaded. iOS already verifies-before-write.
  • Tests + README/SECURITY.md updated to the verified-only design.

No version bump in this PR — the version will be bumped in a separate commit after merge so the bump is visible and decoupled from these changes.

Session-scoping (clearing the active/pending remote bundle on cold start) intentionally stays host-app-driven; it must run in the app's boot path before RN loads, which a RN native module can't reach. See SECURITY.md → Accepted residual risks.

Verification

  • JS: lint, typecheck, jest, verify-pack (tarball allowlist).
  • Android: JVM unit tests (VerifyAndInstallTest — promote-on-match, delete+no-target-on-mismatch, no-clobber, replace).
  • iOS: XCTest (rejects non-https / bad-length / invalid-hex; setBundleURL ordering).
  • End-to-end on Android against a CI-built dev bundle: consent gate → verified fetch + SHA-256 + atomic promote + process restart → remote bundle runs → cold restart returns to the local bundle (session-scoped).

exo-doguhan and others added 3 commits September 1, 2026 10:12
…nstall

- Remove the unverified load() path: iOS load:, Android load() + downloadToCache,
  the JS load/loadUnverified exports, and the BundlePrompt UI. Only loadVerified
  remains — loading a remote bundle without native SHA-256 verification is an
  unauthenticated RCE primitive.
- Android: download to a temp file, verify, then atomically promote it to the
  canonical verified-bundle.jsbundle only after the hash matches; delete the temp
  on mismatch or error. The canonical path never holds unverified/partial bytes
  (fixes verify-after-write TOCTOU).
- iOS: cap the downloaded bundle at 64 MB (parity with Android) before it is
  hashed, written, or loaded.
- Tests: drop the removed load() cases; add VerifyAndInstallTest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Remove unverified-load / BundlePrompt / Metro-tunnel docs and the load(url)
  API row.
- Fix stale native descriptions: iOS uses NSUserDefaults + loadSourceForBridge
  (not KVC bundleURL); Android downloads to temp then atomically promotes.
- SECURITY.md: drop obsolete mBundleLoader-reflection / KVC residual risks;
  document the removed unverified path, atomic verified install, size cap, and
  the host-driven session scoping.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review follow-up: the JS native-module mock no longer declares or stubs the
removed native `load` method, and the iOS mock-bridge comment now reflects
that the module writes the pending URL to NSUserDefaults and calls
[_bridge reload] only (it no longer sets a value on the bridge).
@exo-doguhan
exo-doguhan marked this pull request as ready for review September 1, 2026 11:11
@exo-doguhan
exo-doguhan merged commit be62c1c into master Sep 1, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant