Skip to main content

Module integrity_canary

Module integrity_canary 

Source
Expand description

JavaScript integrity trap canary probes.

§What is a “JavaScript integrity trap”?

Modern anti-bot vendors (Cloudflare, DataDome, PerimeterX, Akamai Bot Manager, Kasada) ship detection scripts that look for artefacts left by patched browser surfaces — places where a stealth framework rewrote a native prototype, getter, or accessor and the patch is detectable from the JavaScript side. Classic examples:

  • Object.getOwnPropertyDescriptor(Navigator.prototype, "webdriver") returning a data property instead of a native accessor.
  • Function.prototype.toString showing patch source code (e.g. "function webdriver() { [native code] }") when applied to a patched prototype method.
  • Performance.now returning suspiciously round / quantized values that betray deterministic jitter injection.

The traps come in two shapes:

  • Suspected — the surface shape is unusual but the signal is ambiguous (e.g. a non-native code toString on a polyfill that the user’s browser already shipped).
  • Confirmed — the surface shape is only achievable via a stealth framework patch on a real browser (e.g. webdriver is a data property on Navigator.prototype).

§What this module provides

  1. A stable IntegrityProbe catalogue with weighted risk contributions and per-probe mitigation hints (see probes::all_probes).
  2. A pure-Rust scoring pipeline (report::IntegrityRiskScore) that turns a set of probes::ProbeFinding records into an aggregate score and a documented Suspected vs Confirmed classification.
  3. A trend-detection seam (trend::CanaryTrendObservation) that future canary infrastructure (T84) can subscribe to without modifying the probe set.

§Probe catalogue

The default probe set (see probes::all_probes) covers eight surfaces:

ProbeDefault weightWhat it checks
probes::IntegrityProbeId::WebDriverDescriptorNative0.20Navigator.prototype.webdriver accessor shape
probes::IntegrityProbeId::FunctionToStringNative0.18Function.prototype.toString reports [native code] for patched natives
probes::IntegrityProbeId::ErrorToStringNative0.08(function(){}).toString() reports [native code]
probes::IntegrityProbeId::IntlDateTimeFormatNative0.10Intl.DateTimeFormat.prototype.format is native
probes::IntegrityProbeId::RegExpTestNative0.08RegExp.prototype.test is native
probes::IntegrityProbeId::CanvasGetImageDataNative0.10CanvasRenderingContext2D.prototype.getImageData is native
probes::IntegrityProbeId::PerformanceNowResolution0.14performance.now() resolution is plausible (not quantized)
probes::IntegrityProbeId::ProxyTrapObservable0.12Proxy traps on patched natives do not leak surface state

§Feature flag

This module is default-on and is always compiled as part of the stygian-browser crate. The probe set and scoring pipeline are pure Rust with no I/O so they are safely callable in deterministic tests without booting Chrome.

§Integration with the existing diagnostic payload

The canary report attaches additively to crate::diagnostic::DiagnosticReport via crate::diagnostic::DiagnosticReport::with_integrity_canary (added in this task) so downstream automation can consume the finding set without breaking the legacy schema.

§Reuse of the canary trend pipeline (T84)

T84 will add a stealth canary hard-gate. This module exposes trend::CanaryTrendObservation as the stable seam that future canary infrastructure can consume without changing probe definitions: each observation carries the normalized risk score and a deterministic signature string so two reports with the same findings produce byte-identical trend entries.

§Example

use stygian_browser::integrity_canary::{
    IntegrityCanaryReport, IntegrityProbe, IntegrityRiskClassification,
};

// Simulate a probe set where two probes fired with confirmed traps.
let finding_a = IntegrityProbe::confirmed_finding(
    "webdriver_descriptor_native",
    0.20,
    "Navigator.prototype.webdriver is a data property (should be an accessor)",
);
let finding_b = IntegrityProbe::confirmed_finding(
    "performance_now_resolution",
    0.14,
    "performance.now() values are quantized to 0.1 ms (timing-noise injection)",
);

let report = IntegrityCanaryReport::from_findings(vec![finding_a, finding_b]);
assert!(report.score.value() > 0.0);
assert!(matches!(
    report.score.classification(),
    IntegrityRiskClassification::Confirmed | IntegrityRiskClassification::Suspected
));
assert_eq!(report.findings.len(), 2);

Structs§

CanaryTrendObservation
Deterministic, JSON-stable trend observation built from an IntegrityCanaryReport.
IntegrityCanaryPolicy
Configurable thresholds for the canary risk bands.
IntegrityCanaryReport
Aggregate integrity canary report.
IntegrityProbe
Single integrity probe definition: stable id, weight, JS evaluation script, description, and mitigation hint.
IntegrityRiskScore
Aggregate integrity risk score in [0.0, 1.0].
ProbeFinding
Captured result of a single integrity probe evaluation.

Enums§

IntegrityProbeId
Stable identifier for a built-in integrity probe.
IntegrityProbeOutcome
Severity outcome of an integrity probe.
IntegrityRiskClassification
Aggregate risk classification.
TrendSeverity
Coarse trend severity band.

Constants§

RISK_CONFIRMED_THRESHOLD_DEFAULT
Default lower bound of the Confirmed risk band.
RISK_SUSPECTED_THRESHOLD_DEFAULT
Default lower bound of the Suspected risk band.

Functions§

all_probes
Return the full built-in integrity probe catalogue.
probe_by_id
Look up a probe by its stable identifier.