Skip to content

security: validate PERCY_CLI_API to loopback (PER-8913/8917/8918/8919, mitigates 8914)#239

Open
Shivanshu-07 wants to merge 1 commit into
masterfrom
security/PER-8913-8919-validate-cli-api
Open

security: validate PERCY_CLI_API to loopback (PER-8913/8917/8918/8919, mitigates 8914)#239
Shivanshu-07 wants to merge 1 commit into
masterfrom
security/PER-8913-8919-validate-cli-api

Conversation

@Shivanshu-07

@Shivanshu-07 Shivanshu-07 commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Five High runtime findings in @percy/selenium-python (deadline 2026-06-22) share one root: an unvalidated PERCY_CLI_API.

Ticket CWE Finding
PER-8919 CWE-918 SSRF via env-var-controlled Percy CLI base URL
PER-8913 CWE-306 Session-type auth gate bypassed via attacker-controlled CLI URL
PER-8917 CWE-494 Externally-fetched JS injected into browser without integrity verification
PER-8918 CWE-79 Unverified script injected into cross-origin iframe via execute_script
PER-8914 CWE-79 LRU-cached poisoned DOM script persists for the process lifetime (mitigated)

Root cause + fix

PERCY_CLI_API was read from the env with no validation and used as (a) the base for all outbound calls, (b) the source of the @percy/dom script injected/executed in the page (and into cross-origin iframes), and (c) the origin of the healthcheck that drives the session-type auth gate. A hostile value is therefore SSRF + CLI-fetched-JS RCE + auth-gate bypass.

Added _resolve_cli_api_address(): loopback-only (localhost/127.0.0.1/::1) by default; a remote host is allowed only over HTTPS with an explicit PERCY_ALLOW_REMOTE_CLI_API opt-in; otherwise it warns and falls back to http://localhost:5338.

Because the injected DOM script and the cached script can now only come from a loopback CLI, this single fix closes 8919/8913/8917/8918 and mitigates 8914 (the lru-cached script is now trusted-source).

Verification

  • Validator logic unit-verified (loopback allow; remote + 169.254.169.254 reject; https opt-in). Added a TestResolveCliApiAddress unit class.
  • Option payloads are already JSON-encoded via json.dumps, so they aren't string-interpolated into execute_script.

Closes PER-8913, PER-8917, PER-8918, PER-8919; mitigates PER-8914. (The 06-22 chain tickets for this repo close once their linked components are resolved.)

🤖 Generated with Claude Code

…, mitigates 8914)

PERCY_CLI_API was read from the environment with no validation and used as the
base for every outbound call, as the source of the @percy/dom script injected
and executed in the browser (incl. into cross-origin iframes), and its
healthcheck response drives the session-type auth gate. An attacker-controlled
value enables SSRF, CLI-fetched-JS code execution, and an auth-gate bypass.

Add _resolve_cli_api_address(): loopback-only by default (localhost/127.0.0.1/
::1); a remote host is allowed only over HTTPS with an explicit
PERCY_ALLOW_REMOTE_CLI_API opt-in, otherwise warn and fall back to
http://localhost:5338.

This one root fix covers:
  PER-8919 (CWE-918 SSRF), PER-8913 (CWE-306 session-gate bypass via attacker
  CLI URL), PER-8917 (CWE-494 externally-fetched JS without integrity),
  PER-8918 (CWE-79 unverified script injected into cross-origin iframe — source
  now loopback-only), and mitigates PER-8914 (the lru-cached DOM script can now
  only be fetched from the trusted local CLI).

Adds unit tests for the validator (loopback allow, remote/link-local reject,
https opt-in). Note: option payloads are already JSON-encoded via json.dumps,
so they are not string-interpolated into execute_script.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Shivanshu-07 Shivanshu-07 requested a review from a team as a code owner June 15, 2026 06:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant