- Home
- Client-Side Defense
- Demo
- Phase 3 — Mitigate
Phase 3 — Mitigate
Phase 3 creates a before/after proof of CSD mitigation. Re-run the authorized simulation over HTTPS to establish a baseline, mitigate only script domains observed in that baseline and current telemetry, then repeat the same run. Phase 2 has one entry gate: DET-3 must PASS; DET-1 and DET-4 may remain PENDING.
Step 1: Confirm No Active Mitigations (Baseline)
Section titled “Step 1: Confirm No Active Mitigations (Baseline)”Before capturing the “before” snapshot, use the Phase 1 CSD_API_SESSION_FILE, ledger_outcome, and ledger_record helpers in the same shell. Normalize the current API URL, derive its tenant identity, resolve the ledger path, and validate the exact API URL, tenant, namespace, and domain before any request:
case "${XCSH_API_URL:-}" in https://*) ;; *) exit 1 ;; esacwhile [ "${XCSH_API_URL%/}" != "$XCSH_API_URL" ]; do XCSH_API_URL=${XCSH_API_URL%/}doneXCSH_TENANT_HOSTNAME=${XCSH_API_URL#https://}case "$XCSH_TENANT_HOSTNAME" in ''|*/*|*:*|*[!A-Za-z0-9.-]*) exit 1 ;; esacXCSH_TENANT_IDENTITY=${XCSH_TENANT_HOSTNAME%%.*}[ -n "$XCSH_TENANT_IDENTITY" ] || exit 1case "${CSD_API_SESSION_FILE:-.csd-api-session.json}" in /*) CSD_API_SESSION_FILE=${CSD_API_SESSION_FILE:-.csd-api-session.json} ;; *) CSD_API_SESSION_FILE="$PWD/${CSD_API_SESSION_FILE:-.csd-api-session.json}" ;;esacCSD_API_SESSION_DIR=$(dirname -- "$CSD_API_SESSION_FILE")CSD_API_SESSION_NAME=$(basename -- "$CSD_API_SESSION_FILE")[ -d "$CSD_API_SESSION_DIR" ] && [ "$CSD_API_SESSION_NAME" != . ] && [ "$CSD_API_SESSION_NAME" != .. ] || exit 1CSD_API_SESSION_DIR=$(cd "$CSD_API_SESSION_DIR" && pwd -P) || exit 1CSD_API_SESSION_FILE="$CSD_API_SESSION_DIR/$CSD_API_SESSION_NAME"export CSD_API_SESSION_FILE CSD_API_SESSION_DIR XCSH_API_URL XCSH_TENANT_HOSTNAME XCSH_TENANT_IDENTITY
jq -e --arg api_url "$XCSH_API_URL" --arg tenant_hostname "$XCSH_TENANT_HOSTNAME" \ --arg tenant_identity "$XCSH_TENANT_IDENTITY" --arg ns "$XCSH_NAMESPACE" \ --arg domain "$XCSH_DOMAINNAME" --arg protected_name "$XCSH_LB_NAME" \ --arg pool_name "$XCSH_ORIGIN_POOL" --arg lb_name "$XCSH_LB_NAME" \ --arg hc_name "${XCSH_HC_NAME:-}" ' (keys | sort) == ["environment","outcomes","ownership_mode","resources","schema_version","session_id"] and .schema_version == 1 and .ownership_mode == "api" and (.session_id | type == "string" and test("^[0-9]{8}T[0-9]{6}Z$")) and .environment == { api_url: $api_url, tenant_hostname: $tenant_hostname, tenant_identity: $tenant_identity, namespace: $ns, domain: $domain } and (.resources | type == "array") and (all(.resources[]; (keys | sort) == ["kind","name","namespace","spec","status"] and (.kind | IN("namespace","protected_domain","healthcheck","origin_pool","http_loadbalancer","mitigated_domain")) and .namespace == $ns and (.name | type == "string" and test("^[a-z]([a-z0-9-]{0,62}[a-z0-9])?$")) and (.status | IN("created","pre-existing","unknown")) and ( (.kind == "namespace" and .name == $ns) or (.kind == "protected_domain" and .name == $protected_name) or (.kind == "origin_pool" and .name == $pool_name) or (.kind == "http_loadbalancer" and .name == $lb_name) or (.kind == "healthcheck" and $hc_name != "" and .name == $hc_name) or (.kind == "mitigated_domain") ) and (.spec | type == "object") )) and ([.resources[] | [.kind,.namespace,.name] | join("\u0000")] | length) == ([.resources[] | [.kind,.namespace,.name] | join("\u0000")] | unique | length) and ([.resources[] | select(.status == "unknown")] | length == 0) and (.outcomes | type == "array") and (all(.outcomes[]; (keys | sort) == ["kind","name","namespace","operation","result"] and (.operation | IN("GET","POST","PUT")) and (.kind | IN("namespace","protected_domain","healthcheck","origin_pool","http_loadbalancer","mitigated_domain")) and .namespace == $ns and (.name | type == "string" and test("^[a-z]([a-z0-9-]{0,62}[a-z0-9])?$")) and ( (.kind == "namespace" and .name == $ns) or (.kind == "protected_domain" and .name == $protected_name) or (.kind == "origin_pool" and .name == $pool_name) or (.kind == "http_loadbalancer" and .name == $lb_name) or (.kind == "healthcheck" and $hc_name != "" and .name == $hc_name) or (.kind == "mitigated_domain") ) and (.result | IN("match","absent","created","replaced","conflict","unknown")) )) and ([.outcomes[] | select(.result == "unknown")] | length == 0)' "$CSD_API_SESSION_FILE" >/dev/null || { echo "STOP: mitigation ledger is corrupt, mismatched, duplicated, or has unknown ownership" exit 1}List mitigated domains. If the count is not 0, stop and use Phase 4 to remove only mitigation entries recorded as created by their originating session. Baseline cleanup must never guess names or delete unrecorded resources.
curl -s \ -H "Authorization: APIToken xXCSH_API_TOKENx" \ "xXCSH_API_URLx/api/shape/csd/namespaces/xXCSH_NAMESPACEx/mitigated_domains" \ | jq '{count: (.items | length)}'Confirm the Phase 2 Entry Gate
Section titled “Confirm the Phase 2 Entry Gate”curl -s \ -H "Authorization: APIToken xXCSH_API_TOKENx" \ "xXCSH_API_URLx/api/shape/csd/namespaces/xXCSH_NAMESPACEx/detected_domains" \ | jq '{total_domains: .domain_summary.totalDomains.count, domains: [.domains_list[]? | {domain, category}]}'Evidence
Section titled “Evidence”| Check | Expected | Status |
|---|---|---|
| Mitigated domain count | 0 (clean baseline) | PASS if 0; otherwise reconcile owned entries before continuing |
| DET-3 | At least one synthetic external destination from the Phase 2 run appears in /detected_domains | Required to proceed |
| Slower checks | DET-1 and DET-4 | PENDING is allowed |
Step 2: Run Attack — Before Mitigation
Section titled “Step 2: Run Attack — Before Mitigation”Re-run the combined simulation with no mitigations active to capture a fresh baseline. This is the “before” snapshot: a record of which candidate browser requests were observed before CSD mitigation was applied.
AI-Automated Execution
Section titled “AI-Automated Execution”AI assistants with browser automation tools run the same Phase 2 simulation against the protected HTTPS application:
- Navigate with initScript — use a clean context, then navigate to
https://$XCSH_DOMAINNAME/#/loginwith the Phase 2 initScript. - Dismiss Welcome Banner — press
Escapeif it appears. - Wait for completion — allow the candidate requests to settle; ten seconds is a common lab observation, not a product guarantee.
- Capture baseline evidence — record console completion and the browser Network results for every candidate script and fetch request. This observed set defines eligible mitigation targets.
Manual Execution
Section titled “Manual Execution”Operators without browser automation tools run the simulation manually using the same procedure as Phase 2 — Step 8: Attack Simulation:
- Navigate to
https://xXCSH_DOMAINNAMEx/#/login - Enter synthetic markers in the Email and Password fields (do not submit)
- Open DevTools and select the Console and Network tabs
- Run the Combined Detection Script from Trigger Detection
- Record the script and fetch requests actually observed; do not assume all candidates completed
Evidence — Before Mitigation
Section titled “Evidence — Before Mitigation”| Check | Expected (Before Mitigation) | Status |
|---|---|---|
| Protected application | Loaded over HTTPS | PASS / FAIL |
| HTTP redirect | http://xXCSH_DOMAINNAMEx/ returns 301; do not execute the simulation over HTTP | PASS / FAIL |
| Candidate script requests | Record the observed subset and result for each | PASS / FAIL |
| Synthetic fetch requests | Record the observed browser result for each | PASS / FAIL |
| Console output | [CSD Demo] Simulation complete | PASS / FAIL |
The browser Network record is the baseline execution evidence. Preserve the exact observed script-domain subset for Step 3.
Step 3: Apply Mitigations
Section titled “Step 3: Apply Mitigations”Mitigate only script domains that satisfy both conditions: they were observed in the Step 2 browser Network baseline, and they appear in the current /detected_domains response. Do not require all four CDN candidates and do not add synthetic fetch destinations merely because the harness attempted them.
Prepare a reviewed tab-separated list containing only that intersection. Each resource name must be a DNS label and each hostname remains in spec.mitigated_domain:
cat >"$CSD_API_SESSION_DIR/observed-mitigation-targets.tsv" <<'TARGETS'# Include only rows supported by this run's browser and API evidence.# cdn-jsdelivr-net<TAB>cdn.jsdelivr.netTARGETSReview the file before continuing. An empty target list blocks mitigation because there is no observed script-domain baseline to compare.
For each reviewed pair, GET the exact DNS-label name first, then append exactly one ledger outcome before processing the next pair. A successful POST is created; an exact GET or 409 is pre-existing; timeout, malformed response, conflict, or ambiguous 5xx is unknown and stops the phase. A 409 is never evidence that this session created the object.
mitigate_domain() { name=$1 domain=$2 base="$XCSH_API_URL/api/shape/csd/namespaces/$XCSH_NAMESPACE/mitigated_domains" spec=$(jq -cn --arg domain "$domain" '{mitigated_domain:$domain}') || return 1 body=$(mktemp) || return 1
code=$(curl -sS -o "$body" -w '%{http_code}' \ -H "Authorization: APIToken xXCSH_API_TOKENx" \ "$base/$name") || code=000
case "$code" in 200) if jq -e --arg name "$name" --arg ns "$XCSH_NAMESPACE" --arg domain "$domain" ' .metadata.name == $name and .metadata.namespace == $ns and .spec.mitigated_domain == $domain ' "$body" >/dev/null; then ledger_outcome GET mitigated_domain "$XCSH_NAMESPACE" "$name" match || return 1 ledger_record mitigated_domain "$XCSH_NAMESPACE" "$name" pre-existing "$spec" || return 1 else ledger_outcome GET mitigated_domain "$XCSH_NAMESPACE" "$name" unknown || return 1 ledger_record mitigated_domain "$XCSH_NAMESPACE" "$name" unknown "$spec" || return 1 rm -f "$body" return 1 fi ;; 404) ledger_outcome GET mitigated_domain "$XCSH_NAMESPACE" "$name" absent || return 1 code=$(curl -sS -o "$body" -w '%{http_code}' -X POST \ -H "Authorization: APIToken xXCSH_API_TOKENx" \ -H "Content-Type: application/json" \ -d "$(jq -cn --arg name "$name" --arg ns "$XCSH_NAMESPACE" --arg domain "$domain" \ '{metadata:{name:$name,namespace:$ns},spec:{mitigated_domain:$domain}}')" \ "$base") || code=000 case "$code" in 200|201) if jq -e --arg name "$name" --arg ns "$XCSH_NAMESPACE" --arg domain "$domain" ' .metadata.name == $name and .metadata.namespace == $ns and .spec.mitigated_domain == $domain ' "$body" >/dev/null; then ledger_outcome POST mitigated_domain "$XCSH_NAMESPACE" "$name" created || return 1 ledger_record mitigated_domain "$XCSH_NAMESPACE" "$name" created "$spec" || return 1 else ledger_outcome POST mitigated_domain "$XCSH_NAMESPACE" "$name" unknown || return 1 ledger_record mitigated_domain "$XCSH_NAMESPACE" "$name" unknown "$spec" || return 1 rm -f "$body" return 1 fi ;; 409) ledger_outcome POST mitigated_domain "$XCSH_NAMESPACE" "$name" conflict || return 1 code=$(curl -sS -o "$body" -w '%{http_code}' \ -H "Authorization: APIToken xXCSH_API_TOKENx" \ "$base/$name") || code=000 if [ "$code" = 200 ] && jq -e --arg name "$name" --arg ns "$XCSH_NAMESPACE" \ --arg domain "$domain" ' .metadata.name == $name and .metadata.namespace == $ns and .spec.mitigated_domain == $domain ' "$body" >/dev/null; then ledger_outcome GET mitigated_domain "$XCSH_NAMESPACE" "$name" match || return 1 ledger_record mitigated_domain "$XCSH_NAMESPACE" "$name" pre-existing "$spec" || return 1 else ledger_outcome GET mitigated_domain "$XCSH_NAMESPACE" "$name" unknown || return 1 ledger_record mitigated_domain "$XCSH_NAMESPACE" "$name" unknown "$spec" || return 1 rm -f "$body" return 1 fi ;; *) ledger_outcome POST mitigated_domain "$XCSH_NAMESPACE" "$name" unknown || return 1 ledger_record mitigated_domain "$XCSH_NAMESPACE" "$name" unknown "$spec" || return 1 rm -f "$body" return 1 ;; esac ;; *) ledger_outcome GET mitigated_domain "$XCSH_NAMESPACE" "$name" unknown || return 1 ledger_record mitigated_domain "$XCSH_NAMESPACE" "$name" unknown "$spec" || return 1 rm -f "$body" return 1 ;; esac rm -f "$body"}
target_count=0while IFS="$(printf '\t')" read -r name domain; do case "$name" in ''|'#'*) continue ;; esac [ -n "$domain" ] || { echo "STOP: malformed mitigation target"; exit 1; } mitigate_domain "$name" "$domain" || exit 1 target_count=$((target_count + 1))done <"$CSD_API_SESSION_DIR/observed-mitigation-targets.tsv"[ "$target_count" -gt 0 ] || { echo "STOP: no observed mitigation targets"; exit 1; }Do not retry a missing or failed item by POSTing blindly: the ledger’s uniqueness rule prevents rewriting ownership history. Resolve the unknown entry first and start a new reviewed session if necessary.
Step 4: Verify Mitigations Applied
Section titled “Step 4: Verify Mitigations Applied”List all mitigated domains and reconcile the reviewed targets against the collection and ledger:
curl -s \ -H "Authorization: APIToken xXCSH_API_TOKENx" \ "xXCSH_API_URLx/api/shape/csd/namespaces/xXCSH_NAMESPACEx/mitigated_domains" \ | jq '{count: (.items | length), items: [.items[]? | {name: .metadata.name, domain: .spec.mitigated_domain}]}'Mitigation Ledger Evidence
Section titled “Mitigation Ledger Evidence”| Check | Expected | Status |
|---|---|---|
| Reviewed targets | Exactly the non-empty observed intersection from Step 3 | PASS / FAIL |
| Ledger outcomes | One unique entry per reviewed target; each created or pre-existing; none unknown | PASS / FAIL |
| Created ownership | Only a 200/201 POST is created; every 409 is pre-existing | PASS / FAIL |
Step 5: Run Attack — After Mitigation
Section titled “Step 5: Run Attack — After Mitigation”Re-run the exact same HTTPS simulation and compare browser Network behavior for the reviewed target domains. The simulation script is unchanged; only mitigation configuration differs.
After-Mitigation AI Execution
Section titled “After-Mitigation AI Execution”- Use a clean browser context and navigate to
https://$XCSH_DOMAINNAME/#/loginwith the Phase 2 initScript. - Dismiss the Welcome Banner if present.
- Allow requests to settle using the same observation method as the baseline.
- Capture console completion and Network results for every reviewed target domain.
After-Mitigation Manual Execution
Section titled “After-Mitigation Manual Execution”- Navigate to
https://xXCSH_DOMAINNAMEx/#/login. - Enter the same synthetic markers used in the baseline.
- Open DevTools and preserve the Network log.
- Run the same Combined Detection Script.
- Compare each reviewed target’s browser request with the Step 2 baseline.
Evidence — After Mitigation
Section titled “Evidence — After Mitigation”| Check | Expected (After Mitigation) | Status |
|---|---|---|
| Reviewed script domains | At least one browser-observed request outcome differs from its successful baseline in a way consistent with enforcement | PASS / PENDING / FAIL |
| Unreviewed domains | No mitigation claim | INFO |
| Synthetic fetch requests | Record observed results; they are not mitigation targets in this procedure | INFO |
| API configuration | Reviewed domains appear in the mitigated-domain collection and ledger | PASS / FAIL |
| Console output | [CSD Demo] Simulation complete | PASS / FAIL |
Browser Network behavior is the enforcement proof. API counters and list membership are supporting configuration evidence only.
Step 6: Before vs After Comparison
Section titled “Step 6: Before vs After Comparison”Compare only the reviewed domains observed in the Step 2 baseline:
| Signal | Before Mitigation | After Mitigation |
|---|---|---|
| Reviewed script-domain request | Record actual browser result | Record actual browser result and whether enforcement is observable |
| Synthetic fetch request | Record actual browser result | Record actual browser result; informational only |
| Mitigated-domain API | Baseline state | Reviewed targets present; configuration evidence only |
Verify Post-Mitigation Configuration Data
Section titled “Verify Post-Mitigation Configuration Data”curl -s \ -H "Authorization: APIToken xXCSH_API_TOKENx" \ "xXCSH_API_URLx/api/shape/csd/namespaces/xXCSH_NAMESPACEx/detected_domains" \ | jq '{summary: {total: .domain_summary.totalDomains.count, mitigated: .domain_summary.mitigatedDomains.count, action_needed: .domain_summary.actionNeededCount.count}, domains: [.domains_list[]? | {domain, status, category}]}'NOW=$(date +%s)START=$(( NOW - 86400 ))curl -s -X POST \ -H "Authorization: APIToken xXCSH_API_TOKENx" \ -H "Content-Type: application/json" \ -d "{\"startTime\": \"$START\", \"endTime\": \"$NOW\"}" \ "xXCSH_API_URLx/api/shape/csd/namespaces/xXCSH_NAMESPACEx/scripts" \ | jq '{total: (.scripts | length), scripts: [.scripts[]? | {script_name, risk_level}]}'These API results may lag and are informational after configuration is applied. They do not override browser enforcement evidence.
Phase 3 Evidence Summary
Section titled “Phase 3 Evidence Summary”| Check | Expected | Status |
|---|---|---|
| Phase 2 entry gate | DET-3 PASS; DET-1/DET-4 may be PENDING | PASS / FAIL |
| HTTPS baseline | Browser evidence captured for the actual observed domain subset | PASS / FAIL |
| Mitigations applied | One ledgered result per reviewed observed target | PASS / FAIL |
| Configuration confirmed | Reviewed targets present in collection/API data | PASS / FAIL |
| Browser enforcement | Observable before/after change for at least one reviewed script domain | PASS / PENDING / FAIL |
| HTTP behavior | Used only to verify 301 redirect | PASS / FAIL |
Phase 3 complete. Proceed to Phase 4 — Teardown when you are ready to remove all deployment objects.