Route parameters that carry non-serializable values — functions, class instances, Date objects, circular references — silently corrupt navigation state when React Navigation serializes the stack for deep linking, state persistence, or web URL synchronization. On top of serialization breakage, passing unsanitized user input directly as route parameters (from a text input or deep link) is an OWASP A03 / CWE-20 injection surface: a crafted parameter value can overwrite screen state or trigger unexpected navigation behavior. The Mobile Offline & Storage audit (mobile-offline-storage) documents the parallel risk in storage serialization — the same class of defect.
Medium because non-serializable params break navigation state persistence and deep linking, while unsanitized user-sourced params introduce an injection vector per CWE-20 and OWASP A03.
Pass only JSON-primitive values in navigation.navigate() — IDs, strings, booleans — and fetch or reconstruct complex objects in the destination screen.
// Correct: pass the ID, fetch the object in the destination
navigation.navigate('ProductDetail', { productId: '123' })
// Wrong: passing the object directly (breaks serialization)
// navigation.navigate('ProductDetail', { product: fullProductObject })
// In the destination screen, validate before use
function ProductDetailScreen() {
const { productId } = useRoute().params
useEffect(() => {
if (typeof productId !== 'string' || !/^\d+$/.test(productId)) return
fetchProduct(productId).then(setProduct)
}, [productId])
}
For user-sourced string params (search queries, names entered in forms), sanitize by stripping or encoding control characters before passing to navigate(). Never pass the raw output of a text input as a route param without at least a trim() and type assertion.
ID: mobile-navigation-linking.state-management.param-serialization
Severity: medium
What to look for: Examine how route parameters are passed between screens. Look for parameter passing via navigation.navigate(), especially if parameters come from user input or deep links. Check whether parameters are properly serialized and deserialized without risk of code injection or unsafe data structures.
Pass criteria: Count all navigation.navigate() calls that pass parameters. Parameters must be JSON-serializable primitives (strings, numbers, booleans) or simple objects. No more than 0 navigation calls should pass function references, class instances, or non-serializable data. At least 1 validation or sanitization pattern must exist for user-sourced parameters.
Fail criteria: Parameters include non-serializable data (functions, Date objects without serialization). User input is passed directly as parameters without sanitization. Do NOT pass when route params contain objects with methods or circular references.
Skip (N/A) when: The app does not pass parameters between screens (0 parameterized navigate calls).
Cross-reference: The Mobile Offline & Storage audit (mobile-offline-storage) covers data serialization patterns that parallel safe route parameter handling.
Detail on fail: "Parameters are passed with unserializable objects (functions, class instances)" or "User input is passed directly as route params without sanitization"
Remediation: Keep route parameters simple and JSON-serializable:
// Good: pass IDs and use them to fetch data in the destination screen
navigation.navigate('ProductDetail', { productId: '123' })
// Avoid: passing complex objects or non-serializable data
// DON'T do this:
// navigation.navigate('ProductDetail', { product: productObject })
// navigation.navigate('Product', { fetchData: () => {...} })
// In the destination screen
function ProductDetailScreen() {
const route = useRoute()
const { productId } = route.params
const [product, setProduct] = useState(null)
useEffect(() => {
// Validate and fetch
if (productId && /^\d+$/.test(productId)) {
fetchProduct(productId).then(setProduct)
}
}, [productId])
// ... render
}