Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: browserstack/browserstack-local-php
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: browserstack/browserstack-local-php
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: locsec/WI-30fccf3b
Choose a head ref
Checking mergeability… Don’t worry, you can still create the pull request.
  • 3 commits
  • 8 files changed
  • 2 contributors

Commits on Jul 30, 2026

  1. LOC-6740: validate TLS chain and verify the binary before granting exec

    download_binary() disabled certificate-chain validation
    (CURLOPT_SSL_VERIFYPEER => false), ignored cURL and HTTP errors, and
    chmod 0755'd whatever bytes came back. An on-path attacker who redirected
    s3.amazonaws.com could substitute the Local binary and get code execution
    as the developer or CI user that later calls Local::start().
    
    - Enforce CURLOPT_SSL_VERIFYPEER and CURLOPT_SSL_VERIFYHOST => 2.
    - Restrict the transfer and any redirect to HTTPS so a redirect cannot
      downgrade to plaintext.
    - Raise LocalException on cURL failure or a non-2xx status instead of
      storing the response body.
    - Add verify_binary(): the file must be >= 1 MiB and carry the platform's
      executable magic (Mach-O / PE / ELF). Verified against the real
      darwin-x64, .exe, linux-x64, linux-ia32 and linux-arm64 artifacts on
      both s3.amazonaws.com and local-downloads.browserstack.com. The
      download is deliberately not executed to test it.
    - Grant 0755 only after verification passes, and delete the file on
      failure so a later run cannot pick up and execute an unverified
      download. A cached binary is verified too.
    - Bound the download with connect/total timeouts and retry 3 times.
    - platform_url() private -> protected so tests can point the download at a
      fixture without patching installed source.
    
    Note: this closes the transport-authenticity gap. Cryptographic integrity
    verification (a published SHA-256 per artifact) still needs a channel to
    publish the digests on -- see the ticket for that follow-up.
    
    Refs LOC-6740 (HackerOne #3695294)
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    07souravkunda and claude committed Jul 30, 2026
    Configuration menu
    Copy the full SHA
    b46601b View commit details
    Browse the repository at this point in the history
  2. LOC-6740: add PHP CI, commit the PoC harness, clear the stat cache

    Review round 1 (blocking): the new regression tests were unexecuted, nothing
    in the repo ran them, and the manual verification handoff lived only in a
    private session folder with an unresolvable path.
    
    - .github/workflows/php.yml: php:7.4-cli job on push/PR. `php -l` over every
      lib/ and tests/ file runs first and on its own, so a syntax error fails the
      build even if the suite cannot boot; then composer install and
      `phpunit --exclude-group network`.
    - tests/manual/php-poc.sh: the end-to-end MITM harness now travels with the
      repo instead of a session folder. Locates the checkout itself (no argument,
      no placeholder path), and rewrites platform_url() by sed so it works against
      both the pre-fix (private) and post-fix (protected) shape. Verified: the
      rewrite replaces all 4 URLs in both shapes with no leftovers.
    - composer.json: phpunit 4.6.* -> 4.8.*. 4.8 is the last 4.x and the first to
      support PHP 7, keeping the PHPUnit_Framework_TestCase API both test files
      use. 4.6 could not run on any PHP the CI image offers.
    - lib/LocalBinary.php: clearstatcache() in verify_binary() and
      make_executable(). The retry loop stats the same path up to 3 times, so a
      cached size from a failed attempt must not be able to reject the genuine
      binary downloaded by the next one.
    - lib/LocalBinary.php: nosemgrep annotations on both unlink() calls. The
      basename is fixed by dest_binary_name(), so there is no user-controlled path
      component and no traversal.
    - README.md: "Upgrading" section naming the new behaviour, the literal
      `cURL error 60` string users will see, and the trust-store remedy. The
      break needs to be documented before the release tag delivers it.
    
    Refs LOC-6740
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    07souravkunda and claude committed Jul 30, 2026
    Configuration menu
    Copy the full SHA
    d0f1b91 View commit details
    Browse the repository at this point in the history
  3. LOC-6740: move tests to phpunit 9.6 so CI can actually install and ru…

    …n them
    
    The CI job added in the previous commit went red at dependency install:
    Composer 2.10 refuses to install phpunit 4.8.* because every 4.8.x release
    carries published security advisories (PKSA-z3gr-8qht-p93v,
    PKSA-w57n-mhp6-c9sd). Suppressing advisories via policy.advisories.ignore-id
    is not an acceptable trade on a security fix, so the test dependency moves
    forward instead.
    
    The `php -l` step DID pass on that run — all five lib/ and tests/ files,
    including the 145 new lines in LocalBinary.php.
    
    - composer.json: phpunit 4.8.* -> ^9.6. require-dev only; the library's own
      runtime floor stays php >= 5.3.19 and is unaffected.
    - tests/: PHPUnit_Framework_TestCase -> PHPUnit\Framework\TestCase,
      setUp/tearDown given the `: void` signature phpunit 9 requires, and
      assertContains() on strings -> assertStringContainsString() (removed in 9).
    - tests/LocalTest.php: tagged test_isRunning, test_checkPid and
      test_multiple_binary `@group network`. All three call Local::start(), which
      downloads and runs the real binary and needs BROWSERSTACK_ACCESS_KEY, so
      they can never pass on a credential-less runner. --exclude-group network
      now excludes them, which is what makes the CI job meaningfully green
      rather than green-because-skipped.
    - phpunit.xml: phpunit 9 schema, bootstrap=vendor/autoload.php, and a
      Test.php suffix on the directory so tests/manual/ is not scanned.
    - workflow: install git + unzip before Composer. Neither is in the official
      php image and ext-zip is not built in, so Composer has no way to unpack
      packages without one of them.
    
    Refs LOC-6740
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    07souravkunda and claude committed Jul 30, 2026
    Configuration menu
    Copy the full SHA
    803abb6 View commit details
    Browse the repository at this point in the history
Loading