Secrets in Keychain/secure-store (never AsyncStorage); harden the supply chain (block install scripts, gate install-age); device trust via server-side attestation; route per-diff secret/injection checks to the review skill.
device-trust requirement → server-side Play Integrity / DeviceCheck (jail-monkey is client-side only, defeatable)
web XSS surface → Trusted Types + sanitized dangerouslySetInnerHTML
RSC/Server Functions on web → patch React to 19.2.5+ (DoS CVEs); does not affect RN
want a cheap CI gate for RN-specific security footguns → rnsec scan (young — treat findings as leads, route confirmations to the review skill)
Options & tradeoffs
the field considered — and why each one isn’t the default here
option
tradeoff
evidence
jail-monkey (v3)
client-side jailbreak/root + mock-location + debug detection; v3 adds New-Arch support; NOT a substitute for server-side Play Integrity / DeviceCheck
120k/wk
react-native-keychain / expo-secure-store
store tokens/secrets in Keychain/Keystore, not AsyncStorage (see RB-E-STORAGE)
dependency CVE scanning
npm/pnpm audit; route depth to the review skill's security phase
supply-chain hardening
install-script blocking (npm v12 default), install-age gating (min-release-age / npmMinimalAgeGate), provenance — provenance alone is not enough
Trusted Types (web)
React integrates the browser Trusted Types API for XSS prevention; dangerouslySetInnerHTML needs an explicit policy
rnsec (RN/Expo security scanner)
zero-config static scanner — 63 rules across storage/network/auth/secrets/crypto, CI-ready (exit codes, PR comments, JSON/HTML reports); young + single-author (~500★, v1.3) — a cheap CI layer, not a substitute for per-diff review
6k/wk
DeepSec (Vercel Labs)
open-source AI security-scan harness — agents trace data flows on YOUR infra with YOUR Claude/Codex subscription; produces severity-rated findings (NOT auto-fix PRs), ~10–20% false-positive rate per Vercel; framework-agnostic, new
npm weekly downloads (from the corpus's last signals run): rnsec 6k · jail-monkey 120k
Verified notes
DURABLE SCOPING FACT: the React Server Components DoS family (CVE-2026-23864, CVSS 7.5; React2DoS CVE-2026-23869) and the Dec-2025 RSC source-exposure/RCE issues are SERVER-SIDE (react-server-dom-*; Next.js App Router) and do NOT affect React Native; patched across React 19.0.5 / 19.1.6 / 19.2.5. The 2025–26 SUPPLY-CHAIN wave (Shai-Hulud worm, the @tanstack/* npm compromise, axios RAT) is the bigger practical risk — hence npm v12 blocking install scripts by default + install-age gating. Live CVEs belong in a security feed, not this encyclopedia; for per-diff secret/injection/dependency checks defer to the review skill. PROVENANCE (2026-07-09): rnsec was surfaced by Native Weekly — whose author ALSO authors rnsec — so it was independently vetted before inclusion (repo checked: ~500★, 63 rules, CI integrations, active v1.3 2026-04). Kept with the young/single-author caveat; watch whether it earns adoption. INCIDENT LEDGER UPDATE (2026-07-10): the Vercel April-2026 incident (verified vs Vercel's bulletin) — a compromised third-party AI tool (Context.ai) led through an employee's Google Workspace to decryption of customers' plaintext-decryptable "non-sensitive" env vars; npm packages unaffected. Lessons: mark platform env vars SENSITIVE, and treat AI dev-tools as supply-chain surface. The TanStack npm compromise now has its first-party postmortem (reading). HARDENING TOOLKIT ADDITION (2026-07-10, verified vs the nuqs post): npm STAGED PUBLISHING (draft → 2FA-gated validate → finalize) + local tarball-hash reproduction — the maintainer-side countermeasure to the TanStack-style CI-compromise chain ("assume GitHub itself is compromised"); joins install-script blocking and install-age gating as the third supply-chain layer.
Canonical reading
Editorial annotations on why each piece matters — the articles themselves are the originals; read them there.
An RCE postmortem on CVE-2025-55182 in React's Flight protocol (thenable abuse, prototype-chain exploitation), with durable lessons on runtime-vs-build-time type safety and lenient-parsing attack surface. Canonical RSC security case study.
A DoS deep-dive (CVE-2026-23870) in the RSC Flight deserializer, with the generalizable lesson that dissolving a network boundary into developer ergonomics still owes a threat model (applies to tRPC/GraphQL/WebSockets too).
A first-party postmortem of the Shai-Hulud worm (preinstall-script credential exfiltration) with concrete mitigations — disable npm install scripts, pnpm 10, OIDC publishing. The supply-chain reading this entry's own note calls for; applies to any React/RN delivery pipeline.
First-party postmortem of the incident this entry's note cites: 84 versions across 42 @tanstack/* packages, via a pull_request_target 'Pwn Request' → pnpm store-cache poisoning → OIDC token extraction from the runner. The CI/CD-side attack chain — a different door than the install-script family covered by the Shai-Hulud and NodeSource readings.
The architectural half of this entry's RSC stance: compiler-generated server references are reachable via direct POST, so auth/authz must live INSIDE every server function — plus the rolling-deploy skew failure mode (generated function IDs drift across deploys and break old clients). Treat stable server functions as formal API boundaries. (Site is a JS shell; content verified via its RSS feed.)
The nuance behind this entry's supply-chain note: npm v12's default block on preinstall/install/postinstall closes the INSTALL-TIME door but leaves the EXECUTION-TIME door open. Attackers already moved the payload into the module body as a top-level side effect (runs on first require), or ship a binding.gyp so npm's implicit node-gyp rebuild fires anyway (the June-2026 Miasma campaign). Removing a trigger isn't removing the capability — so layer defenses: Node's --permission model (--allow-net / --allow-child-process / --allow-fs-write to cap what running code may DO), CI egress allow-listing (Harden-Runner), and container/seccomp isolation. Install-script blocking raises the floor; it is not the ceiling.
Depth (in-domain rules) is owned by the review skill — this entry is selection breadth.
The full explanation
The reviewed long-form essay behind this entry — the why, not a how-to. Also on GitHub.
About app security — boundaries, secrets, and supply chain (React & React Native)
Diataxis: Explanation. This page builds understanding of where React/RN security risk
actually lives — durable posture, not a live CVE feed. Per-diff secret/injection/dependency
checking is owned by the review skill; live advisories belong in a security feed. Read this
for the why.
The one question that organises everything: which side of which boundary?
Most security confusion in the React world dissolves once you ask where the boundary is —
because risk, and responsibility, sit on a specific side of it:
Client vs server. The scariest React-specific CVEs of 2025–26 — the RSC / Server-Function
DoS family (e.g. CVE-2026-23864, CVSS 7.5; React2DoS) and the Dec-2025 RSC source-exposure /
RCE issues — are server-side (react-server-dom-*, Next.js App Router). They are patched
across React 19.0.5 / 19.1.6 / 19.2.5 and do not affect React Native. Conflating "React had
a critical CVE" with "my RN app is exposed" is the most common scoping error.
Client vs server, again (device trust). Anything the client asserts about itself — "not
jailbroken," "real device" — is defeatable, because the attacker owns the client. Client-side
checks (jail-monkey) are signal, not enforcement; trust must be established server-side
(Play Integrity / DeviceCheck attestation).
Your code vs the supply chain. The bigger practical risk in 2025–26 wasn't your code at
all — it was the supply chain: the Shai-Hulud worm, the @tanstack/* npm compromise, the
axios RAT. That's why npm v12 now blocks install scripts by default.
So the durable posture isn't "scan for CVEs" — it's know which boundary a risk lives on and put
the control on the right side.
The default, and why
Secrets in Keychain / secure-store (never AsyncStorage); harden the supply chain (block
install scripts, gate install-age); device trust via server-side attestation; route per-diff
secret/injection/dependency checks to the review skill.
Each clause follows from a boundary. Secrets on a device belong in the OS secure enclave, not in
plaintext key-value (RB-E-STORAGE). The supply chain is now the highest-probability attack path,
so install-script blocking + install-age gating + provenance are baseline (provenance alone is not
enough). Device trust is meaningless client-side, so it must be server-verified. And live
per-change checks (a leaked key, an injection sink, a vulnerable dep in this PR) are a per-diff
concern — owned by the review skill, not by a static encyclopedia.
The landscape, and when each control matters
Keychain / secure-store (react-native-keychain / expo-secure-store) — the only correct home
for tokens/secrets on device. AsyncStorage is plaintext; using it for secrets is a recurring
finding (RB-E-STORAGE).
Supply-chain hardening — install-script blocking (npm v12 default), install-age gating
(minimumReleaseAge / min-release-age), and provenance. The response to the worm/compromise wave;
the highest-leverage dependency controls — but install-script blocking is a floor, not a ceiling
(malware shifts to import-time execution; see failure modes).
Device trust (jail-monkey v3 + server attestation) — jail-monkey does client-side
jailbreak/root/mock-location/debug detection (v3 adds New-Arch support), but it is not a
substitute for server-side Play Integrity / DeviceCheck. Use it as a hint; enforce on the server.
Trusted Types (web) — React integrates the browser Trusted Types API for XSS prevention;
dangerouslySetInnerHTML needs an explicit policy. The web injection-surface control.
Dependency CVE scanning — npm/pnpm audit and friends; route the depth (triage, per-diff)
to the review skill's security phase rather than treating the encyclopedia as the scanner.
Tradeoffs and failure modes to name out loud
Mis-scoping server CVEs to RN. The RSC DoS/RCE family is server-side; patch your React on
the web, but don't panic your React Native app over it (RB-E-REACT-CORE, RB-E-META-FRAMEWORKS).
Trusting the client. Shipping device-trust or entitlement decisions that the client can flip.
If the attacker owns the runtime, the check must live on the server.
Secrets in AsyncStorage. Plaintext tokens one adb backup away. Keychain/secure-store, always.
Provenance as a complete answer. Provenance proves origin, not safety; pair it with install-
script blocking and age-gating.
Install-script blocking as a complete answer. npm v12's default block closes the install-time
door, but malware has already moved to the execution-time door — the payload as a top-level
module side effect that runs on first require, or a binding.gyp that fires npm's implicit
node-gyp rebuild (the June-2026 Miasma campaign). Removing a trigger isn't removing the
capability; layer runtime constraints on top — Node's --permission model (cap net / child-process
/ fs-write), CI egress allow-listing, container/seccomp isolation.
Treating this page as a CVE feed. Specific live advisories go stale; they belong in a feed +
the review skill, while this entry holds the durable posture.
How it interacts with the rest of the stack
React core / meta-frameworks (RB-E-REACT-CORE, RB-E-META-FRAMEWORKS). The RSC/Server-
Function server surface is where the React-specific CVEs live; patch cadence is part of choosing
a web framework. None of it reaches React Native.
Storage (RB-E-STORAGE). Secrets → Keychain/secure-store, not AsyncStorage; the two entries
share that rule.
DX (RB-E-DX). Supply-chain hardening (install-script blocking, dependency automation) lives
in the same CI/dependency-hygiene loop.
Per-diff depth (review). Secret/injection/XSS/auth/dependency checks on an actual change
set are owned by the review skill — this page is posture, that skill is enforcement.
In one paragraph
Security clarity comes from asking which side of which boundary a risk lives on: the scary
React CVEs (RSC DoS/RCE) are server-side and don't touch React Native; device-trust checks
are meaningless on the client and must be server-verified; and the biggest practical danger is
the supply chain, not your code (hence install-script blocking + age-gating). Keep secrets in
Keychain/secure-store, use Trusted Types for web XSS, and route live per-diff
secret/injection/dependency checks to the review skill — this entry is durable posture, not a
CVE feed.