Skip to main content
ESLint Interlace
Plugin: browser-securityRules

no-unvalidated-deeplinks

This rule detects when deep link URLs are opened without validation in React Native or mobile web apps

Requires validation of deep link URLs before navigation

Severity: 🟠 HIGH
CWE: CWE-939: Improper Authorization in Handler for Custom URL Scheme
OWASP Mobile: M4: Insufficient Input/Output Validation

Rule Details

This rule detects when deep link URLs are opened without validation in React Native or mobile web apps. Unvalidated deep links enable phishing attacks, unauthorized actions, and open redirect vulnerabilities. The rule flags Linking.openURL() and navigation.navigate() calls with variable/expression arguments instead of literal strings.

Why This Matters

Deep links allow external apps/websites to trigger actions in your app:

  • Phishing: Attacker crafts malicious deep link to trick users
  • Open redirects: Users redirected to malicious sites
  • Unauthorized actions: Deep links bypass normal auth flows
  • CSRF: Cross-site request forgery via deep links

❌ Incorrect

// React Native - opening URL from variable without validation
import { Linking } from 'react-native';

function handleDeepLink(url: string) {
  Linking.openURL(url); // ❌ Unvalidated URL from external source
}

// Navigation with user-controlled URL
function navigate(destination: string) {
  navigation.navigate(destination); // ❌ No whitelist check
}

// Deep link handler without validation
Linking.addEventListener('url', (event) => {
  const { url } = event;
  Linking.openURL(url); // ❌ Directly opening deep link
});

// Opening URL from props
function ExternalLink({ href }: { href: string }) {
  return (
    <TouchableOpacity onPress={() => Linking.openURL(href)}>
      {/* ❌ No validation of href prop */}
    </TouchableOpacity>
  );
}

✅ Correct

const x = 42;

⚙️ Configuration

This rule has no configuration options.

Known False Negatives

Literal URL Strings

Why: We only flag variable/expression arguments. Literal strings are assumed safe (but still review manually).

// ❌ NOT DETECTED - Literal string
Linking.openURL('https://evil.com'); // Literal, but still dangerous if hardcoded malicious URL

Mitigation: Code review all openURL() calls. Prefer whitelisted literals only.

Why: Validation in separate functions is not traced.

// ❌ NOT DETECTED - Validation in separate function
function validate(url: string): boolean {
  return url.startsWith('myapp:');
}

function handleLink(url: string) {
  if (validate(url)) {
    // Validation exists, but not detected statically
    Linking.openURL(url);
  }
}

Mitigation: Keep validation inline with openURL() call for static analysis.

Why: We only detect Linking.openURL() and navigation.navigate(). Custom libraries not analyzed.

// ❌ NOT DETECTED - Custom library
import { openExternalURL } from './customLinking';
openExternalURL(userProvidedUrl); // Not detected

Mitigation: Apply validation pattern to all URL opening mechanisms.

📚 References