Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ import javax.net.ssl.SSLPeerUnverifiedException
/**
* Certificate pinning for React Native's shared OkHttp client (Iteration 1 - NewDot).
*
* On Android, `react-native-blob-util` (authenticated attachment/receipt downloads) and React Native
* networking (including `fetch()`) route through `OkHttpClientProvider.getOkHttpClient()`. Installing
* an [OkHttpClientProvider] factory with an OkHttp [CertificatePinner] here pins that traffic.
* On Android, `react-native-blob-util` (authenticated attachment/receipt downloads), Fresco, and
* other React Native networking consumers route through `OkHttpClientProvider.getOkHttpClient()`.
* Installing an [OkHttpClientProvider] factory with an OkHttp [CertificatePinner] here pins that
* traffic. JavaScript `fetch()` uses NitroFetch/Cronet and is pinned separately by the
* `react-native-nitro-fetch` patch.
*
* Additional networking channels are also monitored:
* - **HttpURLConnection**: A wrapping [javax.net.ssl.HostnameVerifier] validates pins after the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.multidex.MultiDexApplication
import com.expensify.chat.bootsplash.BootSplashPackage
import com.expensify.chat.navbar.NavBarManagerPackage
import com.expensify.chat.shortcutManagerModule.ShortcutManagerPackage
import com.margelo.nitro.nitrofetch.AutoPrefetcher
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
Expand Down Expand Up @@ -79,6 +80,12 @@ class MainApplication : MultiDexApplication(), ReactApplication {
// Install certificate pinning for React Native's shared OkHttp client (covers fetch(),
// react-native-blob-util, etc.). Must run before any networking starts.
CertificatePinning.install()
// This is the entrypoint for prefetching with `react-native-nitro-fetch`.
try {
AutoPrefetcher.prefetchOnStart(this)
Comment on lines +83 to +85

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Start the auto-prefetcher on iOS too

This initializes cold-start prefetching only in Android's MainApplication; the inspected ios/AppDelegate.swift proceeds through factory.startReactNative(...) without invoking the corresponding native Nitro auto-prefetch entrypoint. Consequently, registrations created by prefetchOnAppStart() cannot run before the JavaScript bundle loads on iOS, so the startup prefetch feature is ineffective for that entire platform.

Useful? React with 👍 / 👎.

} catch (_: Throwable) {
System.err.println("Error initializing Nitro `AutoPrefetcher`")
}

loadReactNative(this)

Expand Down
2 changes: 1 addition & 1 deletion config/certificatePinning/pins.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"_comment": "Canonical source of truth for SSL certificate pins (Iteration 1 - NewDot). Regenerate with scripts/generateCertificatePins.sh. Each domain pins the leaf SPKI hash (primary) and its issuing intermediate CA SPKI hash (durable backup that survives leaf rotation). Hashes are base64-encoded SHA-256 of the Subject Public Key Info. Native code (Android network_security_config_enforce.xml, Android OkHttpClientFactory, iOS TrustKit) MUST be kept in sync with this file - the generation script prints the values for each target.",
"_comment": "Canonical source of truth for SSL certificate pins (Iteration 1 - NewDot). Regenerate with scripts/generateCertificatePins.sh. Each domain pins the leaf SPKI hash (primary) and its issuing intermediate CA SPKI hash (durable backup that survives leaf rotation). Hashes are base64-encoded SHA-256 of the Subject Public Key Info. Native code (Android network_security_config_enforce.xml, Android OkHttpClientFactory, Android NitroFetch Cronet, iOS TrustKit) MUST be kept in sync with this file - the generation script prints the values for each target.",
"_lastGenerated": "2026-06-03",
"enforcePinning": false,
"domains": {
Expand Down
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -1075,6 +1075,7 @@
"NitroFetch",
"Prefetch",
"Prefetcher",
"Autoprefetch",
"knip",
"lottiefiles",
"jsitooling",
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* @format
*/
// import of polyfills should always be first
import './src/polyfills/NitroFetch';
import './src/polyfills/PromiseWithResolvers';
import './src/polyfills/requestIdleCallback';
import {AppRegistry} from 'react-native';
Expand Down
40 changes: 37 additions & 3 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,37 @@ PODS:
- nanopb/encode (= 3.30910.0)
- nanopb/decode (3.30910.0)
- nanopb/encode (3.30910.0)
- NitroModules (0.35.0):
- NitroFetch (1.5.4):
- boost
- DoubleConversion
- fast_float
- fmt
- glog
- hermes-engine
- NitroModules
- RCT-Folly
- RCT-Folly/Fabric
- RCTRequired
- RCTTypeSafety
- React-callinvoker
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- NitroModules (0.36.3):
- boost
- DoubleConversion
- fast_float
Expand Down Expand Up @@ -4336,6 +4366,7 @@ DEPENDENCIES:
- GzipSwift
- hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
- lottie-react-native (from `../node_modules/lottie-react-native`)
- NitroFetch (from `../node_modules/react-native-nitro-fetch`)
- NitroModules (from `../node_modules/react-native-nitro-modules`)
- "onfido-react-native-sdk (from `../node_modules/@onfido/react-native-sdk`)"
- "pusher-websocket-react-native (from `../node_modules/@pusher/pusher-websocket-react-native`)"
Expand Down Expand Up @@ -4566,6 +4597,8 @@ EXTERNAL SOURCES:
:tag: hermes-v250829098.0.10
lottie-react-native:
:path: "../node_modules/lottie-react-native"
NitroFetch:
:path: "../node_modules/react-native-nitro-fetch"
NitroModules:
:path: "../node_modules/react-native-nitro-modules"
onfido-react-native-sdk:
Expand Down Expand Up @@ -4817,7 +4850,7 @@ SPEC CHECKSUMS:
DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb
EXConstants: 1c400bb9969f4c9e5ab324553138b74ae47b9efe
expensify-react-native-background-task: 03c640e1f5649692d058cba48c0a138f024a6dd3
ExpensifyNitroUtils: 86109fe1ab88351ed63ffe11b760d537c70019d7
ExpensifyNitroUtils: b7225649e39a31eab926dc8551c9c64e15b98d47
Expo: 844d649bc6228d8fa118f29984565b58745bc1d8
ExpoAsset: c2e7b5ba1fe75be683282d6264e38fd7cd8defbb
ExpoAudio: 7774082d316ceecc2b26efeb10480bea2fa4d67e
Expand Down Expand Up @@ -4864,7 +4897,8 @@ SPEC CHECKSUMS:
MapboxCoreMaps: de1a4461d0ab5d65d907f06e8d82fdbe06bb3213
MapboxMaps: 22340ae0e0d47b2e4761019b1d6c569ab04e088d
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
NitroModules: f8c2cc3025e4550aee15ff77c525622bf98e774a
NitroFetch: 4941ca1e41fc72ebdad33c50c85fee59a3dd6871
NitroModules: 1961fff19baf1f4a5221dd2fea7d4de95941f9e4
NWWebSocket: b4741420f1976e1dff4da3edad00c401e4f1d769
Onfido: 65454f91d10758193c857fd149417f6efbea84c5
onfido-react-native-sdk: bb8cfd9198e2e97978461d969497d18b37e45ca7
Expand Down
10 changes: 10 additions & 0 deletions jest/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,16 @@ jest.mock('react-native-nitro-sqlite', () => ({
open: jest.fn(),
}));

jest.mock('react-native-nitro-fetch', () => ({
__esModule: true,
fetch: (...args: Parameters<typeof fetch>) => globalThis.fetch(...args),
prefetchOnAppStart: jest.fn(() => Promise.resolve()),
registerTokenRefresh: jest.fn(),
clearTokenRefresh: jest.fn(),
removeFromAutoPrefetch: jest.fn(() => Promise.resolve()),
removeAllFromAutoprefetch: jest.fn(() => Promise.resolve()),
}));

jest.mock('@shopify/react-native-skia', () => ({
useFont: jest.fn(() => null),
matchFont: jest.fn(() => null),
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading