Skip to content

security: validate env-controlled inputs + harden release pipeline (PER-8514–8518)#209

Open
Shivanshu-07 wants to merge 1 commit into
masterfrom
security/PER-8514-8518-hardening
Open

security: validate env-controlled inputs + harden release pipeline (PER-8514–8518)#209
Shivanshu-07 wants to merge 1 commit into
masterfrom
security/PER-8514-8518-hardening

Conversation

@Shivanshu-07

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

Copy link
Copy Markdown

Summary

Resolves five High-severity AppSec findings (deadline 2026-06-15) in @percy/appium-python. These five also form the basis of the C-003…C-006 attack chains (PER-8532–8535), which collapse once the components are fixed.

Ticket CWE Finding
PER-8516 CWE-306 Credential-bearing POSTs sent to env-controlled host without auth
PER-8518 CWE-918 SSRF via env-var-controlled Percy CLI API URL
PER-8517 CWE-22 Path traversal via PERCY_TMP_DIR → arbitrary file write
PER-8514 CWE-829 Release workflow uses tag-pinned actions / write-all token
PER-8515 CWE-829 Unpinned twine enables publishing-tool substitution

Changes

percy/lib/cli_wrapper.py (PER-8516 + PER-8518): PERCY_CLI_API was interpolated directly into requests.get/post targets. Because post_poa_screenshots forwards capabilities (which may carry the BrowserStack accessKey), a hostile value enabled both SSRF and credential exfiltration. Added _resolve_cli_api_address():

  • loopback (localhost / 127.0.0.1 / ::1) allowed over http (the normal case);
  • a remote host allowed only over https:// and with PERCY_ALLOW_REMOTE_CLI_API=true;
  • anything else → warn and fall back to http://localhost:5338.

percy/providers/generic_provider.py (PER-8517): PERCY_TMP_DIR flowed straight into Path().mkdir() / tempfile.mkstemp(). Added _resolve_tmp_dir() which realpaths the value and requires it to stay within an approved temp root (system tmpdir, /tmp, /var/tmp); otherwise it falls back to mkdtemp().

.github/workflows/release.yml (PER-8514): SHA-pinned actions/checkout, actions/setup-python, actions/cache; added permissions: contents: read.

development.txt (PER-8515): pinned twine==5.1.1.

Verification

  • Added unit tests for both validators (loopback allow, remote reject, link-local SSRF target reject, https opt-in; path-traversal + .. rejection).
  • Full suite: 140 tests passing (python -m unittest discover).
  • pylint on both changed modules: 10.00/10.
  • release.yml parses cleanly.

Closes PER-8514, PER-8515, PER-8516, PER-8517, PER-8518. Mitigates PER-8532, PER-8533, PER-8534*, PER-8535* (chains).

* The credential cross-leak / fail-open aspects of C-005/C-006 (cache scoping, exception handling) are separate hardening items and not fully covered here; this PR removes the env-var-controlled SSRF/traversal links those chains depend on.

🤖 Generated with Claude Code

…ER-8514–8518)

Resolves five High-severity AppSec findings (and the C-003..C-006 attack
chains built on them):

PER-8516 (CWE-306) & PER-8518 (CWE-918) — PERCY_CLI_API was used verbatim as
the target for credential-bearing POSTs (capabilities can carry the
BrowserStack accessKey) and healthcheck GETs, enabling SSRF and credential
exfiltration to an attacker-controlled host. Add _resolve_cli_api_address():
loopback-only by default; a remote host is allowed only over HTTPS with an
explicit PERCY_ALLOW_REMOTE_CLI_API opt-in. Otherwise we warn and fall back
to http://localhost:5338.

PER-8517 (CWE-22) — PERCY_TMP_DIR was passed straight to Path().mkdir() and
tempfile.mkstemp(), allowing arbitrary file writes via path traversal. Add
_resolve_tmp_dir(): realpath the value and require it to stay within an
approved temp root (system tmpdir, /tmp, /var/tmp); fall back to mkdtemp()
otherwise.

PER-8514 (CWE-829) — release.yml pinned actions to mutable tags and ran with
write-all GITHUB_TOKEN. Pin checkout/setup-python/cache to commit SHAs and
add permissions: contents: read.

PER-8515 (CWE-829) — twine was unpinned in development.txt, allowing a
publishing-tool substitution. Pin to twine==5.1.1.

Adds unit tests for both validators. Full suite: 140 passing; pylint 10/10.

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 12, 2026 17:00
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