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.8 / 19.1.9 / 19.0.8 (recurring server-function 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
117k/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, and since 2026-07 npm's own publish-time malware scan — 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
5k/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 5k · jail-monkey 117k
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 family RECURS — treat the floor as a moving target, not a one-time patch: GHSA-wx67-qw84-cm4g (High, CVSS 7.5, 2026-07, verified vs the advisory) is another server-function DoS (crafted HTTP requests → OOM / excessive CPU) affecting react-server-dom-webpack/parcel/turbopack 19.0.0–19.0.7, 19.1.0–19.1.8 and 19.2.0–19.2.7, patched in 19.0.8 / 19.1.9 / 19.2.8. Same scoping as before — no server, no RSC-capable bundler, no exposure. 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. PLATFORM-SIDE FIX (2026-07-16 back-audit, verified vs the GitHub changelog): actions/checkout v7 ships safer pull_request_target defaults — closing the exact 'Pwn Request' door the TanStack postmortem reading documents; pin checkout ≥7 in CI. REGISTRY-SIDE LAYER (2026-07-28, verified vs the GitHub changelog): npm now scans every newly published package for malware BEFORE it becomes installable — default and non-negotiable, with three outcomes (publish, hold for manual review, block, with an appeal path) and a typical 5–15 minute publish delay. Packages with legitimate security-relevant capability must now declare it as DUAL-USE: a `contentPolicy` field in package.json plus a text-only DISCLOSURE file describing the functionality and its intended use; the declaration must persist across versions and publishing requires 2FA. This is the first defense in the chain that runs on the REGISTRY rather than in your install or CI — it raises the floor against the Shai-Hulud/axios-RAT family but catches only "the malware we can detect", so it stacks with, and does not replace, install-script blocking, install-age gating and egress control. Maintainers: budget the delay into release pipelines (this repo publishes via OIDC trusted publishing). FIRST-PARTY UMBRELLA (2026-08-07, verified vs the GitHub post): "Disrupting supply chain attacks on npm and GitHub Actions" (github.blog, 2026-07-28) is the one link that bundles the whole 2026 program — staged publishing (May), npm v12 install-script defaults (June), 72-hour read-only lock on high-impact accounts after email changes or 2FA-recovery use (June), Actions hardening (checkout defaults, workflow-execution policies, read-only cache for untrusted workflows), and a 3-day default Dependabot cooldown (July) — install-age gating is now a PLATFORM default, not just a local countermeasure. See reading. LIVE COUNTEREXAMPLE (2026-08-07, verified vs the Aikido incident report): the keyv compromise (2026-08-04) — maintainer GitHub account takeover → poisoned versions published WITH VALID GitHub-Actions provenance → preinstall-hook dropper (downloads Bun, harvests npm/GitHub/AWS/ Vault/Stripe/Slack credentials) → self-replicating worm: 444 packages / 1,381 versions / ~2B combined monthly installs within a day. Receipts for two standing claims at once: provenance alone is not enough (the signatures were valid), and registry-side scanning catches only what it can detect (the worm shipped a week after publish-time scanning went default, 2026-07-28). Install-script blocking + install-age gating remain the layers that bite. Incident is ongoing — promote to a reading when a definitive postmortem lands.
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 DEFENSE half of this entry's Flight-protocol readings, which are otherwise all attack postmortems. Walks the protocol as an attack surface — a line-delimited format with its own type system, its own reference resolution, and its own rules for reconstructing executable behavior on the client — then works through the CVSS 10.0 React2Shell mechanics (CVE-2025-55182) to a RANKED set of defenses, from strict schema validation of everything crossing the boundary to CSRF hardening. Read when you ship RSC and need practice, not just incident history.
The first-party umbrella over this entry's whole supply-chain thread: GitHub's 2026 program in one post — npm staged publishing, install-script restrictions (npm v12), 72-hour read-only account lock after email/2FA-recovery changes, Actions hardening (checkout defaults, workflow-execution policies, read-only cache), an outbound-network-firewall technical preview, and a 3-day default Dependabot cooldown. The platform-side complement to the Shai-Hulud and TanStack postmortems' local mitigations.
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.