Unvalidated deep link parameters are the mobile equivalent of SQL injection — an attacker crafts a URL like myapp://product?id=../../admin and your app executes database queries or navigation logic against that raw string. OWASP A03 (Injection) and CWE-20 (Improper Input Validation) both target exactly this pattern. A product ID that arrives as "../config" and lands directly in a fetch call exposes data that the user was never meant to see. Beyond security, invalid parameters crash screens and generate unhandled exceptions that are reported to production crash tracking.
Critical because unvalidated deep link parameters are user-controlled input that flows directly into data-fetching and navigation logic, enabling injection and unauthorized data access.
Validate every parameter from route.params at the screen boundary before it touches any data-fetching or navigation call. Use a Zod schema at the entry point so validation is explicit and type-safe:
import { z } from 'zod'
import { useRoute } from '@react-navigation/native'
const ParamsSchema = z.object({
id: z.string().regex(/^\d+$/, 'ID must be numeric'),
})
function ProductDetailScreen() {
const route = useRoute()
const parsed = ParamsSchema.safeParse(route.params)
if (!parsed.success) {
return <ErrorScreen message="Invalid product ID" />
}
const productId = parseInt(parsed.data.id, 10)
// safe to fetch
}
Apply this pattern to every screen that has a :param segment in its deep link definition — not just the ones you think are sensitive.
ID: mobile-navigation-linking.deep-linking.param-validation
Severity: critical
What to look for: Examine screens that receive deep link parameters (via route.params). Check whether parameters are validated, type-checked, or sanitized before use. Look for uses of route.params without validation, or direct DOM/data access with user-provided IDs.
Pass criteria: Count all screens that receive deep link parameters via route.params. Screens receiving deep link parameters must validate their type and format before use. At least 1 validation pattern (type check, regex test, or schema validation) must exist per parameterized screen. Numeric IDs are checked to be numeric, string IDs are sanitized.
Fail criteria: Parameters from deep links are used directly without validation (e.g., const userId = route.params.id followed by database query without checking if id is a valid number). No more than 0 parameterized screens should lack validation.
Skip (N/A) when: The app has no deep links with dynamic parameters (0 URL patterns with :param segments).
Detail on fail: Describe validation gaps. Example: "ProductDetail screen accesses route.params.id directly in a data fetch without validating it's a valid product ID" or "No type checking on deep link parameters"
Remediation: Validate all deep link parameters before use. In your screens:
function ProductDetailScreen() {
const route = useRoute()
const { id } = route.params
// Validate before use
if (!id || typeof id !== 'string' || !/^\d+$/.test(id)) {
return <ErrorScreen message="Invalid product ID" />
}
// Now safe to use
const productId = parseInt(id, 10)
// ... fetch and render
}
For more complex validation, consider using a library like Zod:
const ParamsSchema = z.object({ id: z.string().regex(/^\d+$/) })
const parsed = ParamsSchema.safeParse(route.params)
if (!parsed.success) {
return <ErrorScreen message="Invalid parameters" />
}