Skip to content

fix(security): allowlist browserstack.com for the CLI download URL override [DEVA11Y-479][DEVA11Y-488] - #50

Open
nmnishant-browserstack wants to merge 4 commits into
mainfrom
fix/DEVA11Y-479-DEVA11Y-488-override-host-allowlist
Open

nmnishant-browserstack wants to merge 4 commits into
mainfrom
fix/DEVA11Y-479-DEVA11Y-488-override-host-allowlist

Conversation

@nmnishant-browserstack

@nmnishant-browserstack nmnishant-browserstack commented Oct 22, 2026 •

Copy link
Copy Markdown

Why

The Security retest of DEVA11Y-479 and DEVA11Y-488 at 806f98cb found that the --download-url / BROWSERSTACK_A11Y_CLI_DOWNLOAD_URL override still accepted any HTTPS host. Anyone who can set that env var (CI env injection, a malicious PR workflow, a poisoned .env) could point the plugin at their own server. #46 closed the redirect and integrity gaps; this closes the remaining ones:

  1. Restrict the override to an allowlisted host.
  2. Remove the file:// copy branch in download().

What

54eba31 – override host allowlist

0126efa – reject . as a cache version

  • extractVersion() accepted a filename ending in -..zip as version ., which resolves to the cache root itself. Found while replaying the C-002 chain for DEVA11Y-486; it doesn't reopen that chain.

Tests

  • New tests/download-url-policy/run_tests.sh lifts parseOverride, isAllowedRedirectTarget and extractVersion verbatim from the plugin source and checks them against a case table. It needs no network and no credentials, and runs as a new download-url-policy job in spm-smoke-test.yml.

Testing

Policy suite: 30/30 pass. Against main before this change, 11 of the 23 URL cases fail, so the suite detects the gap.

Accepted Rejected
https://api.browserstack.com/sdk/v1/download_cli?… https://evil.example/cli.zip
https://sdk-assets.browserstack.com/… https://api.browserstack.com@evil.example/… (userinfo trick)
https://browserstack.com/… https://evilbrowserstack.com/…, https://browserstack.com.evil.example/…
HTTPS://API.BROWSERSTACK.COM/… (case) https://127.0.0.1/…, https://169.254.169.254/…
https://api.browserstack.com:443/… https://github.com/…, http://…, ftp://…, file://…, /tmp/cli.zip, https:///cli.zip

End to end: the plugin was built and run via tests/spm on macOS:

$ swift package … scan --download-url 'https://evil.example/cli.zip'
error: Unsupported host 'evil.example' in download URL. Only browserstack.com hosts are allowed.
$ swift package … scan --download-url 'https://api.browserstack.com@evil.example/cli.zip'
error: Unsupported host 'evil.example' in download URL. Only browserstack.com hosts are allowed.
$ swift package … scan --download-url 'file:///tmp/cli.zip'
error: Unsupported URL scheme 'file' in download URL. Only HTTPS is allowed.

The default download (no override) is unchanged: it downloads CLI 1.63.0 and passes the signature check.

Authenticated sanity scan (macOS, Xcode 26.3 / Swift 6.2.4, production BrowserStack account). The plugin was rebuilt from this branch with a clean tests/spm/.build, then tests/spm/scripts/run-a11y-scan.sh ran against the harness:

Check Result
Fresh CLI download (BROWSERSTACK_A11Y_CLI_FORCE_DOWNLOAD=1) through the changed path ✅ CLI 1.63.0, TeamIdentifier=YQ5FZQ855D
--non-strict scan ✅ exit 0 in 13s; authenticated and reported the 3 intentional issues in SampleViews.swift (imageview-element-content-label, interactable-element-content-label, switch-element-content-label)
Strict scan ✅ exit 1, so the issues fail the run as expected

Note for local runs: RUN_A11Y_SCAN=1 swift test hung here (~10 min, no scan process started), most likely because the test's inner swift package plugin waits on the .build lock held by the outer swift test under SwiftPM 6.2. The two scans above are the same assertions testA11yScanPluginRuns makes, run directly. CI (macos-14) is unaffected, and this PR doesn't touch that test.

After merge

Request a Security retest of DEVA11Y-479. DEVA11Y-488 closes on that result.

🤖 Generated with Claude Code

nmnishant and others added 2 commits October 22, 2026 15:28
…de [DEVA11Y-479][DEVA11Y-488]

parseOverride() now rejects any --download-url / BROWSERSTACK_A11Y_CLI_DOWNLOAD_URL
whose host is not browserstack.com or a subdomain, using the same policy PR #46
applies to redirect hops. The file:// branches in resolveOverrideArtifact() and
download() are removed, and download() refuses any URL outside the policy.

Adds tests/download-url-policy, which lifts parseOverride verbatim and checks
off-domain, userinfo, look-alike, IP, non-https, file:// and bare-path inputs.
Wired into spm-smoke-test.yml.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
extractVersion() accepted a redirect filename ending in '-..zip' as version
'.', which resolves to the cache root itself. Reject it, and cover the
version parser in tests/download-url-policy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Central YAML (base), Organization UI (inherited), Workspace UI (inherited); Enforced overrides: Organization
  • Review profile: ASSERTIVE
  • Plan: Enterprise
  • Run ID: 62ba9462-887d-43f3-908b-28f6526f6a3b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@nmnishant-browserstack
nmnishant-browserstack marked this pull request as ready for review October 22, 2026 10:35
@nmnishant-browserstack
nmnishant-browserstack requested a review from a team as a code owner October 22, 2026 10:35
nmnishant and others added 2 commits October 22, 2026 16:06
…-479]

Keeps the PR to the scope of the Security retest. The suite stays in
tests/download-url-policy and runs locally.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Restores the download-url-policy job so the override allowlist is checked
on every PR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants