Skip to content

Trigger Detection

CSD validation requires real browser-side JavaScript activity. The canonical Node.js 22 runner uses Chrome DevTools Protocol (CDP), enables Page, Runtime, Network, and Log/console collection, and writes a sanitized receipt. Immediate browser success and eventual CSD telemetry are separate facts.

1. Start a dedicated Chrome debugging profile

Section titled “1. Start a dedicated Chrome debugging profile”
Terminal window
open -na "Google Chrome" --args \
--remote-debugging-port=9222 \
--user-data-dir="$TMPDIR/csd-demo-chrome"

The runner creates and disposes an isolated browser context/page. It does not close Chrome.

Terminal window
node scripts/csd-traffic.mjs --list

The list comes from scripts/lib/csd-scenarios.mjs and reports exactly these stable names:

login-credential-skimmer, registration-harvester, payment-overlay-card-skimmer, obfuscated-loader, multi-cdn-injection, tag-manager-hijack, multi-channel-exfiltration, high-volume-domain-exfiltration, form-overlay, keylogger-simulation, and maximum-detection.

An execution requires exactly one selector form: repeat --scenario for one or more selected scenarios, or use --all. No scenario runs implicitly.

Terminal window
mkdir -p .artifacts/csd
node scripts/csd-traffic.mjs \
--scenario login-credential-skimmer \
--cdp-endpoint http://127.0.0.1:9222 \
--timeout 30s \
--settle 10s \
--receipt .artifacts/csd/login-credential-skimmer.json

A focused multi-scenario run repeats --scenario:

Terminal window
node scripts/csd-traffic.mjs \
--scenario login-credential-skimmer \
--scenario payment-overlay-card-skimmer \
--receipt .artifacts/csd/focused.json

Run the composed coverage scenario after a focused run is healthy:

Terminal window
node scripts/csd-traffic.mjs \
--scenario maximum-detection \
--receipt .artifacts/csd/maximum-detection.json

Use --all only when all 11 independent entries are required:

Terminal window
node scripts/csd-traffic.mjs \
--all \
--receipt .artifacts/csd/all-scenarios.json

--timeout bounds execution and navigation. --settle is only a browser-event collection period; it is not a product-detection delay. A custom target requires --target plus an exact repeatable --allow-host; unsafe schemes, hosts, ports, redirects, and final origins fail closed.

For pipelines, --receipt - sends only JSON to stdout and human logs to stderr:

Terminal window
node scripts/csd-traffic.mjs \
--scenario maximum-detection \
--receipt - \
| jq '{schema_version, run_id, requested_scenarios, success, eventual_csd_evidence}'

For a file receipt, inspect the stable evidence groups without assuming a fixed platform latency:

Terminal window
jq '{
schema_version,
run_id,
started_at,
ended_at,
duration_ms,
tool,
requested_scenarios,
target,
allowlist,
cdp_endpoint,
scenarios: [.scenarios[] | {
name, target, status, immediate_evidence, dom_cleanup, console, network,
protected_document, instrumentation, cleanup, error, success, eventual_csd_evidence
}],
success,
caveats,
error,
eventual_csd_evidence
}' .artifacts/csd/maximum-detection.json

Require the exact authorized top-frame origin, protected-page readiness, __imp_apg__, required scenario assertions, real Network terminal outcomes, sanitized Log/console evidence, and complete DOM/target/context/listener cleanup. The high-volume scenario must show five script attempts plus two POST attempts. Keylogger evidence contains aggregate event and periodic flush/POST counts only. Overlay evidence includes geometry/removal, and payment-overlay evidence confirms masked display-only content.

A requested receipt is written atomically even when execution or cleanup fails. The receipt preserves the primary error in error and reports runner cleanup failures in cleanup.errors. Candidate failures and timed-out outcomes stay visible and are never relabeled as successful loads.

Terminal window
node scripts/csd-traffic.mjs \
--print-script maximum-detection \
> .artifacts/csd/maximum-detection.browser.js

Open the documented route, review the generated file, and paste it into DevTools Console. --print-script is the only manual payload source. Record visible Console and Network outcomes; manual execution cannot supply the automated receipt or cleanup proof.

The generator has no embedded API client and does not read platform credentials. Only after the browser receipt passes, separately use read-only xcsh_api operations. Scope them to the receipt’s start/end window, protected origin, and exact reviewed hosts from Network evidence. Label each result OBSERVED, NOT_OBSERVED, PENDING, or ERROR, and keep it under the separate eventual_csd_evidence concept. Pending, empty, or failed correlation does not change a successful immediate-browser result.

EvidenceWhat it establishesWhat it does not establish
Receipt and sanitized Log/console markersThe canonical payload ran in the accepted document.CSD classification or causation.
Network terminal outcomesWhich reviewed destinations were attempted and what the browser observed.That every candidate loaded or appeared in CSD.
Original-field and aggregate key countsField access and event handling occurred without retaining values.Captured credentials, key content, or guaranteed classification.
Overlay geometry/removalThe synthetic overlay existed and cleanup removed it.A guaranteed standalone injected-form signal.
__imp_apg__ and dip evidenceInstrumentation existed and, when observed, emitted browser traffic.A specific eventual risk label.
Separate CSD API resultsRecords returned during the queried window.Deterministic latency, complete coverage, or causal attribution.

The Attack Script Library contains the exact 11-scenario behavior and outcome matrix.

F5 documents that Page Tamper takes snapshots of HTTP headers and can raise ClientSideDefenseHttpHeaderModified and ClientSideDefenseHttpHeaderCompromised alerts. Collection spans multiple browser sessions and sequential cycles, and the baseline moves as observations accumulate. See About Client-Side Defense and Configure Client-Side Defense.

Those official sources do not publish an exact list of monitored headers. The following entries are experiment candidates, not a supported-coverage statement. Feature-Policy, when present, is a distinct legacy response header and must not be treated as evidence for Permissions-Policy.

Use only these classifications for the dedicated endpoint experiment:

  • COMPROMISED — an exact ClientSideDefenseHttpHeaderCompromised record matches the namespace, selected header, /csd-page-tamper/payment, and experiment window.
  • MODIFIED_ONLY — an exact ClientSideDefenseHttpHeaderModified record matches the same fields, but no matching Compromised record arrives within the valid window.
  • NO_ALERT_WITHIN_WINDOW — the complete traffic and telemetry chain is valid, but neither exact alert arrives within the bounded window. This does not prove unsupported behavior.
  • INVALID_TEST — the baseline, cohort, telemetry, correlation, recovery, cleanup, readiness, or no-drift evidence chain is incomplete.

On 2026-09-23, a tenant ClientSideDefenseHttpHeaderModified alert identified x-content-type-options, x-frame-options, and cache-control, reported modification=Added, and included the exact protected path /. This proves response-header detection for that event only. It does not prove the Compromised trigger and does not establish a complete supported-header list.

The campaign from 2026-09-24T20:22:00.476Z through 2026-09-24T20:30:11.388Z produced 108 of 108 fresh browser sessions with HTTP 200, six simultaneous global response-header value changes, an injected CSD script, and a CSD dip POST. Restoration completed at 2026-09-24T21:40:51Z; workstation and worker checks agreed on canonical headers, the load balancer remained ready with a valid certificate, Terraform reported no changes, and no worker resources remained.

That campaign is INVALID TEST for Compromised-trigger conclusions. The load balancer changed all sessions together, so it provided neither an unchanged baseline cohort nor a simultaneous comparison cohort. Alert review also began with current alerts rather than alert history. Later searches across active, inactive, unprocessed, silenced, and inhibited states returned no matching Modified or Compromised record, but that bounded negative observation cannot establish per-header behavior or unsupported coverage.

The controlled hypothesis uses the same URL for both cohorts: /csd-page-tamper/payment. Control browsers receive the canonical synthetic payment page with all 12 candidate headers. Alternating tampered browsers send X-CSD-Page-Tamper: <header-id> and receive the identical page with exactly the selected header absent; ordinary requests remain unchanged. Individual experiments do not mutate the F5 Distributed Cloud load balancer or origin pool.

Bootstrap first proves the baseline from workstation and worker browsers, including all 12 exact headers, the five empty synthetic fields, CSD injection, and dip telemetry. Each header then runs in a separate window with control reinforcement, alternating control/tampered pairs, exact current and history alert correlation, and control-only recovery. X-Content-Type-Options is the canary: if it does not reach COMPROMISED, recover and stop the suite for diagnosis. A result is not successful until recovery, cleanup, application health, load-balancer readiness, and final Terraform no drift also pass. This method remains an unproven hypothesis until live evidence satisfies those gates.

The dedicated endpoint was deployed by the exact saved Terraform plan (5 add, 1 change, 1 destroy for a task-definition replacement) with zero F5 Distributed Cloud mutations. Bootstrap run dd91ea9b-3da3-4fc8-b24b-c32cdba2e863 ran from 2026-09-25T21:40:23.373Z to 2026-09-25T22:42:18.638Z. It returned alerts=[] and proved the endpoint and root healthy, all 12 canonical candidate headers exact, all five synthetic payment fields empty, the CSD script present, both ALB target groups healthy, load-balancer state VIRTUAL_HOST_READY, certificate state CertificateValid, and Terraform no drift.

The valid X-Content-Type-Options canary run 69f30bd0-6dc2-4721-bb21-cae159032af2 ran from 2026-09-25T23:21:44.908Z to 2026-09-26T00:38:11.806Z. It completed 12 of 12 controls and 20 of 20 mixed pairs with valid telemetry. Neither an exact Modified nor Compromised alert correlated; alerts=[], and the only correct outcome is NO_ALERT_WITHIN_WINDOW. Post-run current alerts returned zero and alert history returned an empty array.

Recovery completed with 10 control pairs, successful probes, readiness, cleanup, and Terraform no drift. Because the canary did not reach COMPROMISED, the mandatory stop rule applied: the remaining candidate headers were not run by design. This result validates the dedicated endpoint and controlled test chain, but it does not validate the Compromised hypothesis, prove unsupported behavior, or establish a monitored-header list.