Documentation

Environment Integrity

Reference guides for release workflows, command-line usage, cross-file protections, and the desktop app.

Inside the Docs

Practical guides for real release work.

How-to guides Start with release sequencing and command-line usage, then move into feature-specific references.
Advanced protection Browse cross-file controls like Replace Globals and Protect Members when a build spans multiple scripts.

Environment Integrity

  • browser runtime module, npm package jso-protector/runtime/environment-integrity
  • detecting instrumented builtins and automated analysis harnesses

Integrity hashing answers “has my code been altered?” and third-party script inventory answers “where is my page talking to?”. This module answers a third question: has the JavaScript environment been instrumented underneath my code? A tracing harness usually replaces a handful of builtins — Function.prototype.call, Object.defineProperty, JSON.parse — so it can watch arguments and return values without touching your bundle at all. Bundle hashing cannot see that, because the bundle is untouched.

What it costs an attacker, and what it does not

These checks raise the cost of automated analysis. They do not make anything irreversible, and they are bypassable by an attacker who controls the runtime — every check here is itself JavaScript running in the attacker’s browser, so a sufficiently patient attacker can neutralise it. Use them to make casual and scripted analysis expensive and noisy, not as a guarantee. Pair them with runtime defense response actions so a detection produces telemetry and a reaction rather than only a log line.

The three checks

hookedBuiltins
Compares each watched builtin against the [native code] shape that Function.prototype.toString produces for an un-replaced native. Cheap, synchronous, needs no DOM.
crossRealm
Builds a throwaway same-origin iframe as a pristine realm and diffs your builtins against it. Catches a hook that also spoofed its own toString, which defeats hookedBuiltins on its own.
automation
Looks for driver and headless markers: navigator.webdriver, PhantomJS and Selenium leftovers, and ChromeDriver-family cdc_ properties.
watch
The list of builtins the first two checks inspect. Defaults to Function.prototype.toString, call, apply, bind, Object.defineProperty, Object.getOwnPropertyDescriptor, JSON.parse, and JSON.stringify.

Usage

The policy is pure data until you call inspect(), so you can construct it at startup and probe on a timer, on a route change, or before a sensitive operation. The report is shaped to drop straight into a countermeasure policy with no adapter.

const ei = require("jso-protector/runtime/environment-integrity");
const cm = require("jso-protector/runtime/countermeasures");

const probe  = ei.createEnvironmentIntegrityPolicy({
  checks: ["hookedBuiltins", "crossRealm", "automation"],
  watch:  ["Function.prototype.call", "JSON.parse"]
});
const policy = cm.createCountermeasurePolicy({ onTamper: "log" });

const report = probe.inspect(window);
if (!report.ok) {
  policy.react(window, report.violation);
}

inspect() is read-only apart from the iframe the crossRealm check creates and then removes. Because window is a parameter rather than an ambient global, the module unit-tests against a stub with no DOM.

Reading the report

Severity is deliberately graded, and automation findings are capped at signal — never high — because navigator.webdriver is a one-line patch and a tuned headless browser leaves none of these markers. Treat automation findings as signals, never as proof.

high
A watched builtin is not reporting as native, i.e. it has been replaced or wrapped.
signal
A heuristic automation marker is present. Evadable; corroborate before acting harshly.
unavailable
A check could not run — for example a Content Security Policy frame-src rule blocked the probe iframe.
complete
False when any check reported unavailable. Could not look is not the same as nothing found, so test this alongside ok.

ok means “found nothing actionable”: no high and no signal findings. An unavailable check does not by itself clear the report, so a caller that reads only ok still learns from complete that something could not be verified.

Two defaults worth knowing

console.* is not watched by default. Analytics, logging, and error-reporting libraries legitimately wrap console methods, so watching them produces false positives on ordinary production pages. Opt in explicitly with watch: ["console.log"] only if your build has no such library.

An unreliable stringifier reports once, not eight times. Every hookedBuiltins result depends on Function.prototype.toString, so it is probed first. If it misreports itself, the module emits a single toStringUnreliable finding and stops, rather than flagging every watched builtin and burying the one finding that matters. crossRealm is the check that can still get a truthful answer in that state.

Where results appear

The report.violation payload uses the same { reason, src, findings } shape as the rest of the runtime suite, with reason set to environment-hooked, automation-signal, or environment-unverified. Pass it to a countermeasure policy to reuse the existing response actions and beacon channel described in Runtime Defense — there is no separate collector or dashboard to configure.

Try this in the online obfuscator

Paste your own code and see this option applied, or compare plans for larger projects and the desktop app.

Try It Free See Pricing