Browser Sniffing via Navigator
ID |
javascript.navigator_browser_detection |
Severity |
low |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Code Smell |
Language |
JavaScript |
Tags |
best-practice, browser |
Description
Reports reads of navigator.userAgent, navigator.appVersion, navigator.platform or navigator.vendor. All four are historic browser-sniffing entry points: their values can be spoofed by users, lied about by vendors, and they encode hard-coded assumptions about specific browsers that break new releases and devices.
Only the property access itself is reported; downstream substring or regex checks against the value are out of scope. The intent is to surface the access so that it can be replaced with capability-based feature detection.
Rationale
// Bad — implementations differ, user agents lie, regexes rot.
function isMobile() {
return /Mobi|Android/i.test(navigator.userAgent);
}
function isChrome() {
return navigator.vendor.indexOf("Google") !== -1;
}
Feature detection asks the runtime "do you support what I need?" instead of "are you a browser I know?". The first question is forward-compatible; the second one ages badly.
Remediation
Replace the sniff with a capability check. A few common substitutions:
// Touch input / pointer behaviour
const isTouchPrimary = matchMedia("(pointer: coarse)").matches;
// Geolocation availability
const hasGeolocation = "geolocation" in navigator;
// Service worker support
const canCacheOffline = "serviceWorker" in navigator;
// CSS feature
const canGap = CSS.supports("gap: 1rem");
When you must keep a navigator-driven branch (for example, to work around a documented bug in one specific engine), isolate it behind a single, well-named helper and add a comment explaining the bug and the bug-tracker link.