new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Expo & React Native · all subjects

security & secrets

28 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Path containment check must use trailing path.sep or dedicated helper, not bare startsWith

A containment check written as bare resolved.startsWith(base) is insufficient because a sibling directory sharing the prefix passes it. The check needs a trailing path.sep or a dedicated helper.

Security review scope for Expo SDK: command injection, injection into native files and HTML, path containment, dev server, credentials, native platform, CI workflows, dependencies

The Expo SDK security review process covers seven major attack vectors that derive from fixes in an internal May 2026 audit (tagged EXP-01 through EXP-67). Every change to packages/ and packages/@expo/cli reaches thousands of apps and developer machines. Reviewers trace exploit paths across TypeScript/native boundaries, flag new or newly widened vulnerabilities, and verify that fixes do not regress.

Command and subprocess construction security: preferred form is argv array through spawnAsync

When constructing commands in packages/@expo/** or tools/**, the preferred form is an argv array passed through spawnAsync. The SPM tooling under tools/** was converted to this pattern (fixes #45819, #45837). Do not use execSync, exec, or spawnSync with interpolated values that reach the shell. If a shell is genuinely required, every interpolated value needs explicit quoting. A finding is a new or newly widened interpolation whose value can come from app config, filename, device output, or user input. Pre-existing calls are not findings unless the diff widens them.

adb shell command construction must quote each token individually

adb shell re-parses the string on the device, so each token needs its own quoting when any token comes from app config, device output, or user arguments. See packages/@expo/cli/src/start/platforms/android/adb.ts for the pattern.

AppleScript and sh -c construction must use escapeString and shellQuote helpers

AppleScript or sh -c construction that bypasses the escapeString and shellQuote helpers in packages/@expo/osascript/src/index.ts is a security finding.

HTML injection in CLI middleware and router: escape every interpolated value

HTML built by string concatenation or a new dangerouslySetInnerHTML in @expo/cli middleware, packages/@expo/router-server/src/utils/html.ts, packages/@expo/metro-config/src/serializer/, or expo-router static rendering must escape every interpolated value. Asset URLs inserted into HTML attributes were the original miss (issue #45848).

Archive extraction must re-resolve entries and symlinks against output root

Archive extraction that writes an entry or follows a linkname symlink without re-resolving against the output root is a security finding. See packages/@expo/cli/src/utils/tar.ts for the pattern to match.

URI resolution in native modules must canonicalize and confine to scoped directories

content:// or file:// URI resolution in a native module must canonicalize the result and confine it to the app's scoped directories. Issues #45967, #45972, #45977 required this. See packages/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemPath.kt for the pattern.

Config-supplied paths must assert they resolve inside project or package root

A config-supplied path used as a filesystem root (EXPO_PUBLIC_FOLDER, routerRoot, template paths, devtools-plugin paths) must assert it resolves inside the project or package root.

Externally supplied identifiers as filesystem paths must be validated or encoded

An externally supplied identifier (contact id, EAS project id, package name) used directly as a filename or cache-path segment needs validation or encoding.

Dev server routes must include origin check or loopback check

A new dev-server route, websocket endpoint, or CDP handler under packages/@expo/cli/src/start/server/ must include an origin check (isMatchingOrigin, assertSameOrigin) or loopback check (isLocalSocket). The dev server is reachable from the browser and the LAN (issue #45863).

Client-driven loops must have throttle, message-size cap, concurrency limit, and timeout

A client-driven loop must include a throttle, message-size cap, concurrency limit, and timeout. Limits had to be retrofitted onto the network-debugging websocket (issue #45864).

expo-server and router-server route handlers must enforce method, header, and module lookup rules

New expo-server or router-server route handlers or RSC endpoints must not skip method and header enforcement, must not bypass the shared middleware chain, and must not fall back to looser module lookup on a miss (issues #45905, #45870, #45895).

URL and redirect handling must reject protocol-relative and absolute external targets

URL or redirect handling must reject protocol-relative (//#host) or absolute external targets before treating input as a same-site path (issue #45866). See packages/expo-server/src/utils/matchers.ts and packages/expo-router/src/link/href.ts.

Request-controlled headers must not be trusted for host resolution or cache keys unless constrained to known set

Trust must not be placed in request-controlled headers (Host, Forwarded, X-Forwarded-*, expo-platform) for host resolution, cache keys, or route selection unless the value is constrained to a known set (issues #48267, #45908).

Tokens, sessions, and private keys must be written with owner-only file mode

A token, session, or private key must be written with explicit owner-only file mode. Both state.json and the expo-updates private key defaulted to world-readable (issues #45873, #45880).

Credentialed fetch wrappers must check request target host against expected API origin

A credentialed fetch wrapper or auth header must not be attached without first checking the request's target host against the expected API origin (issue #45875).

Security-relevant configuration must use original-system-env accessors, not process.env

Security-relevant configuration in @expo/cli, @expo/env, or fingerprint must not be read through process.env, where a .env file could supply the value. This applies to API URLs, credential paths, and toolchain overrides. Original-system-env accessors exist for this (issues #45831, #45833).

expo-updates code signing changes must not relax verification or make signature checks conditional

Changes to expo-updates code signing (CertificateChain, CodeSigningConfiguration, SignatureHeaderInfo) or update-request header overrides must not relax verification, accept an unexpected chain shape, or make a signature check conditional.

Android manifest exports must use android:exported='false' plus android:grantUriPermissions convention

An Android manifest change that exports an Activity, Service, Provider or Receiver, or widens networkSecurityConfig or permits cleartext must be reported. The convention is android:exported='false' plus android:grantUriPermissions (issue #45357, #44558). Report what becomes reachable to other apps on the device. Do not treat prose explanation or PR body as clearing it.

Deep-link handling in expo-dev-launcher must not load arbitrary dev-server URL on cold launch

Deep-link or intent-extra handling in packages/expo-dev-launcher/ that loads an arbitrary dev-server URL on cold launch is a security finding.

Object merges and parser output must reject __proto__, constructor, and prototype computed keys

Object merges or parser output written with computed keys must reject __proto__, constructor and prototype. Plist parsing needed this guard (issue #45854).

CI workflow: github.event.* must not be interpolated directly into run blocks, use env: and quote

github.event.* values (issue titles, bodies, branch names, login values) are attacker-controlled and must not be interpolated directly into a run: block or inline script. Pass them through env: and quote them. See .github/workflows/issue-triage.yml for the pattern.

CI workflows: new pull_request_target, workflow_run, or issue_comment with secrets must gate on non-fork

A new pull_request_target, workflow_run, or issue_comment trigger, or a secrets.* reference in a job reachable from a fork PR is a security finding. Gate those jobs on non-fork (issue #45859).

Security-relevant dependencies with CVE history must not be widened below pinned floor

Do not add or widen version ranges for packages with known CVE history (tar, node-forge, form-data, undici, axios, postcss, lodash, react-server-dom) below the pinned floor already used elsewhere in the workspace. Treat any newly introduced package in that family the same way.

Do not flag theoretical vulnerabilities with no reachable path; trace the caller first

Do not report a theoretical vulnerability without tracing the caller. If the only caller passes a literal, stay silent.

Do not flag test and fixture code unless the unsafe helper is exported for real use

Test, fixture and example code that constructs deliberately unsafe input is not flagged unless the unsafe helper is exported for real use. Being called a fixture proves nothing — check the path and export list.

Dev-only paths gated behind __DEV__ or development-build check do not reach production apps

Dev-only paths correctly gated behind __DEV__ or a development-build check are not flagged because the risk cannot reach a production app.

Give your agent this brain