Repository navigation
fix(security): keep launcher CLI downloads on https and browserstack.com [DEVA11Y-473] - #49
Conversation
…com [DEVA11Y-473] The launchers downloaded the CLI with `curl -L` and never checked where the redirect landed, so it could go to any host, over plain http. On Linux nothing verifies the binary's signature, and the risk Security accepted on DEVA11Y-473 assumes HTTPS from BrowserStack end to end. - Add `--proto =https --proto-redir =https` to the download curl call. - Refuse an archive whose final URL (url_effective, already captured) is not https on browserstack.com or a subdomain: delete the zip and exit 2, as a failed signature check does, before anything is extracted. - Add a `url` mode to tests/launcher-signature/run_tests.sh and a CI job for it. - Reword the README paragraph so it matches the launcher code. - Regenerate the launchers' self-update .sha256 sidecars. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
… checks [DEVA11Y-473] The DEVA11Y-484 suite serves its fixtures from plain http on 127.0.0.1 through a curl shim. The launchers' new `--proto '=https'` refused every fixture, and the host check would refuse the 127.0.0.1 url_effective, so 39 cases failed in "extraction-guard / shell launchers". - For a swapped URL, the shim widens --proto/--proto-redir to '=http,https' and reports the production redirect target as url_effective. download_binary itself is still unmodified. - The harness loads is_allowed_download_url, and its extraction step greps for both transport checks. Removing either one fails the suite (verified by mutation). - Cases 10 and 11 turn each shim relaxation off (SHIM_KEEP_PROTO, SHIM_REAL_URL_EFFECTIVE). The launcher must then refuse the plain-http download (exit 1) and the off-domain download (exit 2), leaving nothing on disk. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nmnishant-browserstack
left a comment
There was a problem hiding this comment.
LGTM. The https-only flags and the url_effective host check are identical across bash, zsh and fish, and they run before extraction and the signature check. The refused cases (userinfo, port, path, query and fragment tricks) all hold. The sidecars verify, and the url and extraction-guard suites pass locally (54/54, 99/99).
Non-blocking:
- This conflicts with #50 in
spm-smoke-test.yml(both add a job at the same spot), so whichever merges second needs a rebase. - curl's exit-1 branch always prints "redirected to a non-HTTPS URL", but curl exits 1 for any unsupported protocol.
Why
Security's follow-up on DEVA11Y-473, after #46 merged. The launchers (
scripts/*/cli.sh) download the CLI withcurl -L, and nothing checks where the redirect lands. A redirect can go to any host, including plainhttp://. On macOS the signature check (#47) covers that. On Linux nothing verifies the binary, and the risk Security accepted on DEVA11Y-473 assumes HTTPS from BrowserStack end to end. Security asked for:--proto =https --proto-redir =httpson that curl callurl_effectivethe launchers already captureWhat
Launchers (the change is identical in bash, zsh and fish):
--proto '=https' --proto-redir '=https'. curl refuses any non-https request or redirect hop and exits 1, and the launcher reportsBrowserStack CLI download was redirected to a non-HTTPS URL; refusing it.is_allowed_download_url: the final URL must behttps://with hostbrowserstack.comor a subdomain of it. The check is case-insensitive and ignores a port or a trailing dot. It reads the host after stripping userinfo, sohttps://browserstack.com@evil.example/is refused. Look-alikes such asbrowserstack.com.evil.exampleandevilbrowserstack.comare refused too.download_binaryruns that check right after curl succeeds, before the size check or any extraction. If the check fails, the zip is deleted and the launcher exits 2, as a failed signature check does. On a 304,url_effectiveis still the redirect target, so a cached zip is held to the same rule..sha256sidecars are regenerated.Tests/CI:
tests/launcher-signature/run_tests.sh urlliftsis_allowed_download_urlverbatim from each launcher and runs 6 allowed and 12 hostile URLs through it. It needs no network. The new job, Launcher download URL allowlist, runs it onubuntu-latest. It isn't a required check.Extraction-guard suite (DEVA11Y-484): the suite serves its fixtures over plain http on 127.0.0.1 through
_shim/curl, so the new checks refused every fixture. For a swapped URL, the shim now widens--proto/--proto-redirto=http,httpsand reports the production redirect target asurl_effective.download_binaryitself is still unmodified. The harness loadsis_allowed_download_urland greps for both checks; I confirmed by mutation that removing them turns the suite red. New cases 10 and 11 turn each relaxation off: the launcher must then refuse the http download (exit 1) and the off-domain download (exit 2), leaving nothing on disk. The suite now passes 99/99.README: the launcher paragraph now states the download rules for the launchers directly, rather than "the same check".
Out of scope:
self-updatedownloads the launcher fromraw.githubusercontent.com(a different origin, covered by its own checksum sidecar), so I didn't change it.Testing (macOS arm64, curl 8.7.1, real network)
Today the live endpoint redirects once, to
https://sdk-assets.browserstack.com/binary-<os>-<arch>-1.63.0.zip, for macOS and Linux on both arm64 and x64, so real downloads pass the host check.run_tests.sh urlrun_tests.sh matrix(existing signature suite, with the reworkedextract)bash -non all launchers;shasum -con the sidecarsdownload_binaryend to end, live endpoint, empty cachecurl: (1) Protocol "http" disabled (in redirect), rc=1, nothing left on diskexample.comdownload came from https://example.com/, not https on browserstack.com, rc=2, zip deletedThe end-to-end runs lifted
download_binaryand its helpers verbatim fromscripts/bash/cli.shand used a scratch cache directory. For the two redirect cases, only the endpoint URL was replaced, with an httpbin redirector.🤖 Generated with Claude Code