Detection catalog

Every check Kitsune runs, grouped by signal layer. Convicting rules (coherence · automation · artifact) can label a session a bot; the rest corroborate.

143checks
97convicting
5layers

cross-layer 14

net.accept_encoding_vs_ua convictsUA claims a modern browser but Accept-Encoding omits Brotli (scripted client)
net.accept_lang_vs_navigator convictsHTTP Accept-Language contradicts the JS navigator.languages locale
net.ch_platform_header_vs_ua convictsHTTP Sec-CH-UA-Platform contradicts the JS UA platform
net.ch_ua_mobile_vs_ua convictsSec-CH-UA-Mobile form factor contradicts the UA's mobile-ness
net.ch_ua_version_vs_ua convictsSec-CH-UA brand version disagrees with the UA-string Chrome version
net.ch_ua_vs_ua_browser convictsHTTP Sec-CH-UA brand contradicts the JS UA browser
net.datacenter_origin_proxied convictsWebRTC reveals a datacenter machine hidden behind a non-datacenter connection
net.h2_vs_ua_browser convictsHTTP/2 (Akamai) fingerprint contradicts User-Agent browser
net.no_js_execution convictsPage request with a TLS fingerprint but no browser layer (no JS ran — scripted client)
net.sec_fetch_vs_ua convictsUA claims a modern browser but the request omits Sec-Fetch metadata headers
net.tls_grease_vs_ua convictsUA claims a GREASEing-engine browser (Chromium/Safari) but the TLS ClientHello has no GREASE
net.tls_pq_keyshare_vs_ua convictsUA claims current Chrome but the TLS handshake offers no post-quantum key share
net.tls_vs_ua_browser convictsJA4 browser family contradicts User-Agent browser
net.webrtc_ip_vs_observed corroboratesWebRTC-revealed public IP contradicts the observed connection IP (proxied bot)

network 20

net.ch_ua_no_grease_brand convictsChromium Sec-CH-UA brand list omits the GREASE brand (hardcoded header)
net.fake_declared_crawler convictsA UA declaring a known crawler (Googlebot/Bingbot/…) whose IP fails forward-confirmed reverse DNS
net.h2_continuation_flood convictsHTTP/2 CONTINUATION flood (CVE-2024-27316) on this connection
net.h2_control_flood convictsHTTP/2 control-frame flood (SETTINGS/PING — CVE-2019-9515/9512)
net.h2_header_order_vs_ua convictsChromium UA but the HTTP/2 regular-header order is not chromium-shaped (JA4H)
net.h2_madeyoureset convictsHTTP/2 MadeYouReset stream-reset coercion (CVE-2025-8671) on this connection
net.h2_rapid_reset convictsHTTP/2 rapid-reset flood (CVE-2023-44487) on this connection
net.h2_settings_vs_order convictsHTTP/2 SETTINGS-profile engine contradicts the pseudo-header-order engine
net.h2_unknown_vs_ua convictsModern-browser UA but the HTTP/2 stack matches no known browser engine
net.h2_unstable_within_session convictsHTTP/2 fingerprint (SETTINGS preface) changed within one session
net.h2_vs_tls_browser convictsHTTP/2 fingerprint browser contradicts the TLS (JA4) browser
net.ip_rotation_within_session convictsOne session egressed from many distinct IPs (rotating proxy pool)
net.ja4_tool_vs_ua convictsJA4 is a non-browser HTTP client but the User-Agent claims a browser
net.ja4_unstable_within_session convictsTLS engine (JA4_b cipher identity) changed within one session
net.tcp_os_vs_ua convictsTCP/IP-stack OS kernel contradicts the OS the User-Agent claims
net.tls_ext_order_static_within_session convictsChromium-JA4 session repeated one TLS extension order (no per-connection permutation)
net.tls_os_vs_tcp_os convictsJA4-implied OS contradicts TCP/IP-implied OS
net.ua_rotation_within_session convictsOne session sent multiple distinct User-Agent strings (mid-session UA rotation)
net.web_bot_auth_invalid convictsA request presents a Web Bot Auth (RFC 9421) signature that fails Ed25519 verification
net.web_bot_auth_nonce_replay convictsA valid Web Bot Auth (RFC 9421) signature reuses an in-window nonce — a captured-credential replay

browser 90

br.apple_ua_nonwebkit convictsA UA claiming Apple WebKit (Safari / any iOS browser) exposes a Blink-only structural API
br.audio_noise convictsAudioContext fingerprint differs across identical renders (per-render perturbation)
br.automation_globals convictsAutomation-framework global or webdriver DOM attribute present
br.brave_spoofed convictsnavigator.brave is present but faked (isBrave is not a native function)
br.canvas_geometry_noise convictsCanvas path geometry is non-deterministic (isPointInPath farbling, e.g. JShelter)
br.canvas_lie convictsCanvas/WebGL API tampering detected (getter override)
br.canvas_noise convictsSolid-fill canvas reads back perturbed (engine-level farbling, e.g. Brave)
br.canvas_worker_vs_main convictsCanvas pixel hash differs between the main thread and a Worker OffscreenCanvas
br.cdc_artifacts convictsSelenium / chromedriver automation artifacts in window/document
br.cdp_runtime_enabled convictsCDP Runtime.enable detected (prototype-chain Proxy ownKeys trap)
br.ch_he_headless convictsUA-Client-Hints high-entropy brand list reveals HeadlessChrome
br.ch_he_version_vs_ua convictsUA-CH high-entropy Chrome version contradicts the UA-string version
br.coalesced_untrusted convictsA coalesced pointer batch contains an untrusted (constructor-built) event — fabricated coalescing
br.csp_bypassed convictsPage CSP not enforced (setBypassCSP — an automation context, never a real browser)
br.domrect_invariant convictsgetBoundingClientRect is non-deterministic or disagrees with getClientRects — geometry shim
br.electron_process convictsRenderer exposes a Node process object — Electron / automation runtime
br.engine_feature_vs_ua convictsJS engine lacks an API its claimed Chrome version shipped — stale template
br.engine_stack_vs_ua convictsJS-engine stack API (Error.stackTraceLimit = V8) contradicts the UA engine
br.error_engine_vs_ua convictsJS-engine error-message format contradicts the UA engine
br.fingerprint_unstable_within_session convictsOne session presented divergent hardware-invariant browser fingerprints (re-randomising anti-detect browser)
br.firefox_ua_nongecko convictsA UA claiming Firefox lacks navigator.buildID — a Gecko-only surface, so it is not Gecko
br.font_mac_internal convictsmacOS internal dot-prefixed system fonts are web-measurable (never on a real Mac)
br.font_os_vs_ua convictsInstalled fonts imply an OS that contradicts the UA platform
br.headless_ua convictsUser-Agent advertises a headless browser
br.honeypot_interaction convictsInteracted with a honeypot bait element a human cannot reach (off-screen + aria-hidden + tabindex -1)
br.iframe_divergence convictsnavigator differs between the main document and an iframe
br.language_vs_languages convictsnavigator.language disagrees with navigator.languages[0] (spec-invariant violation)
br.languages_worker_vs_main convictsnavigator.languages differs between the main thread and a Web Worker
br.measuretext_offscreen_vs convictsCanvas measureText differs between main and OffscreenCanvas — main-thread font hook
br.mobile_no_touch convictsPhone/tablet UA reports no touch capability (maxTouchPoints 0)
br.native_invariant_violated convictsA built-in method claims native but violates native-function invariants
br.nav_property_spoofed convictsA prototype-inherited navigator property (pdfViewerEnabled/mimeTypes) is an own property
br.navplatform_vs_ua convictsnavigator.platform implies an OS that contradicts the UA platform
br.no_chrome_object convictsChrome User-Agent but window.chrome is absent
br.notification_getter_tampered convictsNotification.permission getter is non-native (faked to beat the notification floor)
br.oscpu_vs_ua convictsnavigator.oscpu implies an OS that contradicts the UA platform
br.permissions_anomaly convictsNotification.permission contradicts the Permissions API state
br.plugins_spoofed convictsnavigator.plugins is an own property (patched via defineProperty)
br.pointer_touch_incoherent convictsCSS coarse-pointer and navigator.maxTouchPoints disagree on touch
br.productsub_vs_ua convictsnavigator.productSub render engine contradicts the UA
br.readback_noise convictsAudioBuffer getChannelData and copyFromChannel disagree — readback noise shim
br.safari_ua_no_webkit_api convictsA UA claiming Safari lacks window.GestureEvent — a WebKit-only surface, so it is not WebKit
br.screen_impossible convictsAvailable screen area exceeds the physical screen (impossible geometry)
br.timezone_inconsistent convictsIANA timeZone and getTimezoneOffset() disagree (a real browser derives both from the OS)
br.timezone_offset_vs_intl convictsgetTimezoneOffset disagrees with the Intl IANA timezone's actual offset
br.timezone_worker_vs_main convictsTimezone differs between the main thread and a Web Worker
br.tostring_tampered convictsA native function's toString no longer reads [native code]
br.ua_platform_vs_ch_platform convictsnavigator/UA platform contradicts Sec-CH-UA-Platform
br.vendor_vs_ua convictsnavigator.vendor implies an engine that contradicts the UA
br.voice_os_vs_ua convictsInstalled TTS voices imply an OS that contradicts the UA platform
br.webdriver_getter_tampered convictsNavigator.prototype.webdriver getter is non-native (prototype patch)
br.webdriver_present convictsnavigator.webdriver is true
br.webdriver_spoofed convictsnavigator.webdriver is an own property (patched via defineProperty)
br.webgl_caps_worker_vs_main convictsWebGL capability vector (limits + extensions) differs between the main thread and a Worker OffscreenCanvas
br.webgl_getparameter_tampered convictsWebGLRenderingContext.getParameter is overridden (non-native toString)
br.webgl_os_vs_ua convictsWebGL renderer implies an OS that contradicts the UA platform
br.webgl_renderer_artifact convictsWebGL renderer string is an anti-detect spoofing placeholder, not a real driver
br.webgl_renderer_caps_mismatch convictsWebGL renderer names a high-end GPU but the hardware capabilities are below its floor (spoofed string)
br.webgl_worker_vs_main convictsWebGL GPU renderer differs between the main thread and a Worker OffscreenCanvas
br.webgpu_vendor_vs_webgl convictsWebGPU adapter GPU family contradicts the WebGL renderer (spoofed renderer on real hardware)
br.worker_constructor_tampered convictsWeb Worker / OffscreenCanvas constructor is not native (worker-realm spoof injection)
br.worker_divergence convictsnavigator differs between the main thread and a Web Worker
br.worker_source_rewritten convictsWeb Worker ran from a different source than the page passed (worker-scope spoof injection)
br.adblock_present corroboratesA content blocker hides the ad bait element (Camoufox default-bundles uBlock Origin)
br.audio_missing corroboratesNo OfflineAudioContext (audio stack absent)
br.codec_os_incoherent corroboratesNon-Linux UA cannot play H.264/AAC (proprietary codecs a real Windows/macOS has via OS)
br.color_depth_anomaly corroboratesscreen.colorDepth is not a real-display value (24/30/32)
br.devicepixelratio_anomaly corroborateswindow.devicePixelRatio is not a positive finite number
br.fingerprint_improbable corroboratesFingerprint joint (platform/gpu/screen/colour/cores) is improbable under the real-traffic prior
br.font_linux_leak corroboratesLinux metric-compatible fonts (Arimo/Cousine/Tinos) present under a non-Linux UA
br.hover_none_desktop corroboratesNon-mobile UA reports no hover capability (CSS hover:none)
br.languages_empty corroboratesnavigator.languages is empty
br.low_hardware_concurrency corroboratesnavigator.hardwareConcurrency implausibly low
br.macos_dpr1 corroboratesmacOS UA but devicePixelRatio is 1.0 (a modern Mac is Retina, dPR 2)
br.media_devices_empty corroboratesnavigator.mediaDevices.enumerateDevices() is empty (no audio/video hardware)
br.mimetypes_empty corroboratesnavigator.mimeTypes is empty
br.no_connection corroboratesChromium UA but navigator.connection is absent
br.no_devicememory corroboratesChromium UA but navigator.deviceMemory is undefined
br.no_pdfviewer corroboratesChromium UA but pdfViewerEnabled is false
br.no_plugins corroboratesDesktop browser reports zero plugins
br.platform_empty corroboratesnavigator.platform is empty
br.rfp_browser corroboratesresistFingerprinting signature (UTC + letterboxed window + 2-core, all at once)
br.screen_avail_invalid corroboratesscreen.availWidth/Height exceeds the physical screen size
br.screen_zero corroboratesScreen or outer window dimensions are zero
br.voices_empty corroboratesNo speech-synthesis voices (headless/container — a real desktop has OS TTS)
br.webgl2_missing corroboratesWebGL2 unsupported (headless software rendering)
br.webgl_not_angle corroboratesChromium UA but the WebGL renderer is not ANGLE-wrapped — renderer spoof
br.webgl_software corroboratesWebGL renderer is software (SwiftShader / llvmpipe / Mesa) — headless tell
br.webgpu_webgl_vs corroboratesWebGL renderer claims a hardware GPU but WebGPU exposes no real adapter (spoofed renderer)
br.webrtc_unavailable corroboratesWebRTC gathers no ICE candidates (disabled/blocked)

behavioral 15

bh.trace_replay_within_session convictsOne session replayed an identical pointer trajectory across page loads (record-and-replay bot)
bh.action_cadence_deliberative corroboratesMetronomic inter-action cadence at LLM-inference latency
bh.click_without_trajectory corroboratesTrusted mouse click with no pointer trajectory in the session (teleport-click)
bh.input_entropy_floor corroboratesMouse-movement entropy below the human floor
bh.input_via_paste corroboratesForm value injected with no keystroke or trusted paste (programmatic input)
bh.keystroke_entropy_floor corroboratesKeystroke timing entropy below the human floor
bh.keystroke_interval_floor corroboratesMedian inter-keystroke interval below the human cadence floor
bh.mobile_keystroke_interval_floor corroboratesMobile session typing faster than any human thumb cadence
bh.no_input_before_action corroboratesAction (submit/nav) with zero pointer events
bh.path_too_straight corroboratesMouse path is unnaturally straight (start->end ~= total length)
bh.power_law_violation corroboratesMouse motion violates the 2/3 power law (speed independent of curvature — synthetic path)
bh.scroll_teleport corroboratesProgrammatic scroll jump with no wheel/scroll-key input
bh.synthetic_no_coalesced corroboratesLong pointer stream never coalesces (CDP-injected input, not hardware)
bh.touch_uniform_velocity corroboratesTouch-swipe velocity too uniform for a human finger (mobile)
bh.uniform_velocity corroboratesMouse moves at near-constant speed (low velocity CV)

reputation 4

rep.abuse_listed corroboratesSource IP is on an abuse/threat reputation list
rep.datacenter_asn corroboratesSource IP belongs to a datacenter / hosting ASN
rep.known_proxy_exit corroboratesSource IP is a known VPN / proxy / Tor exit
rep.webrtc_origin_datacenter corroboratesWebRTC-leaked real origin IP belongs to a datacenter / hosting ASN