Skip to content

Modernize: PHP 8.2+, Flysystem 3, GitHub Actions, and security/correctness fixes - #142

Open
tarikmanoar wants to merge 6 commits into
masbug:2.xfrom
tarikmanoar:feature/modernization
Open

Modernize: PHP 8.2+, Flysystem 3, GitHub Actions, and security/correctness fixes#142
tarikmanoar wants to merge 6 commits into
masbug:2.xfrom
tarikmanoar:feature/modernization

Conversation

@tarikmanoar

Copy link
Copy Markdown

Summary

This PR modernizes the package for current PHP/Flysystem and fixes several
correctness & security bugs surfaced by an adversarial code audit. It is
framework-agnostic — no Laravel/Illuminate coupling is introduced.

⚠️ Breaking changes (PHP/dependency floors), so this is really a 3.x-line
change. I targeted 2.x since it's the default branch — happy to retarget to a
3.x branch if you prefer.

Security fixes

  • readStream() token leakage: the OAuth bearer token (and cookies) were sent
    to whatever host a download redirect pointed to. They're now only sent to
    verified Google-owned hosts, over a connection with TLS peer verification, with
    the socket connection guarded. Includes unit regression tests for the host check
    (suffix/prefix spoofing, lookalikes).
  • Query-language injection: file names interpolated into Drive query literals
    (name = '…') are now escaped.

Correctness fixes

  • Reading metadata of a Drive shortcut crashed with a fatal "property on null"
    (shortcutDetails was never requested by the GET fetch); now requested + guarded.
  • copy() could call getRawVisibility() on null when the source was resolved
    purely from the path cache.
  • delete() swallowed all throwables and reported success on real failures; it now
    only treats a missing path as a no-op success and surfaces genuine API errors.
  • move() could report false success when the post-update fetch returned no file.
  • Fixed a broken for-loop condition in the path-cache builder (compared int to
    array); hardened several null/false cases; writeData() now forwards the
    underlying error as the exception cause.

Modernization

  • Requires PHP ^8.2; drops EOL Guzzle 6 / psr7 v1; requires
    league/flysystem ^3.0, guzzlehttp/guzzle ^7.5, guzzlehttp/psr7 ^2.4.
  • Replaced dead Travis/Scrutinizer/StyleCI with GitHub Actions: test matrix
    (PHP 8.2/8.3/8.4 × prefer-lowest/prefer-stable), PHPStan (level 5), and
    Pint. Added Dependabot.
  • Migrated dev tooling to PHPUnit 10/11; reorganized tests into Unit and
    Integration suites; added a credential-free unit test suite so the core
    logic is finally covered in CI (the existing integration tests still skip without
    credentials).
  • Removed the global define('DEBUG_ME') debug scaffolding; added native parameter
    and return types throughout; fixed typos and stale comments.
  • Added CHANGELOG.md, CONTRIBUTING.md, SECURITY.md; refreshed README badges
    and added a requirements matrix.

Verification

composer validate, PHPStan, Pint, and the unit suite all pass locally on PHP 8.5;
the integration suite is skipped (no credentials), as before.

I'm happy to split this into smaller PRs (e.g. CI-only, then fixes) if that's
easier to review.

tarikmanoar and others added 5 commits June 21, 2026 16:08
…+ Pint

- Remove dead CI configs (.travis.yml, .scrutinizer.yml, .styleci.yml)
- Add GitHub Actions: test matrix (PHP 8.2/8.3/8.4 x lowest/stable),
  PHPStan static analysis, and Pint code-style checks
- Add Dependabot for composer + github-actions updates
- Add .editorconfig, phpstan.neon (level 5), pint.json (laravel preset)
- Refresh .gitignore for modern tool caches

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Bump floor to PHP ^8.2; drop EOL Guzzle 6 and psr7 v1
- Require league/flysystem ^3, google/apiclient ^2.15, guzzle ^7.5, psr7 ^2.4
- Move dev tooling to PHPUnit ^10.5|^11, add phpstan + pint
- Add ext-fileinfo (used by FinfoMimeTypeDetector)
- Add composer scripts (test, analyse, format) and PSR-4 test autoloading
- Migrate phpunit.xml.dist to the PHPUnit 10/11 schema
- Restructure tests into Unit and Integration suites; namespace the
  live integration test under Masbug\Flysystem\Tests\Integration

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…folding

Bug fixes (surfaced by an adversarial multi-dimension audit):
- Shortcut metadata crash: add shortcutDetails to FETCHFIELDS_GET and guard
  the shortcut branch in normaliseObject() so broken/unfetched shortcuts raise
  UnableToReadFile instead of a fatal "property on null" error
- copy(): fetch the source object explicitly before reading its visibility
  (it could be resolved purely from the path cache and be absent from the
  object cache, causing getRawVisibility() on null)
- delete(): only treat a missing path as a successful no-op; genuine API
  failures now surface as UnableToDeleteFile instead of being swallowed
- move(): guard the post-update fetch and throw UnableToMoveFile when it does
  not yield a DriveFile (previously reported false success)
- writeData(): forward the underlying throwable as the exception cause
- hasDir(): handle getMetadata() returning false
- cachePaths(): fix broken for-loop condition (compared int to array)
- Escape file names interpolated into Google Drive query literals (prevents
  query-language injection via crafted names)

Security:
- readStream(): only send the OAuth bearer token and cookies to trusted Google
  hosts, enforce HTTPS + verified TLS, and guard the socket connection so the
  access token can never leak to a third-party redirect target

Hardening & modernization:
- Remove the global define('DEBUG_ME') and all leftover echo debug blocks
- Add native param/return types and union types across the public API
- Reuse a shared FinfoMimeTypeDetector instead of instantiating per upload
- StreamableUpload: strict_types, typed properties, strict comparisons,
  int chunk size, nullable size sentinel, drop the unused boundary property
- Fix typos and misleading comments

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a Unit test suite (runnable in CI without Google credentials) covering:
- escapeQueryValue() incl. query-injection break-out attempts
- normalizeDirname(), dirname(), extension-based guessMimeType()
- trusted-host checks (security regression tests for the readStream token-leak
  fix: suffix/prefix spoofing and lookalike domains must be rejected)
- splitPath(), splitFileExtension(), sanitizeFilename(), indexString()
- cleanOptParameters() global/per-operation merging

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- README: replace dead Travis/StyleCI badges with GitHub Actions + PHP-version
  badges; add a version/requirements compatibility matrix and updated install
  instructions for the 3.x (PHP 8.2+, Flysystem v3) line
- Add CHANGELOG.md (3.0.0), CONTRIBUTING.md and SECURITY.md
- .gitattributes: drop references to removed files and export-ignore the new
  dev tooling; normalize line endings
- .gitignore: ignore .env and modern tool caches

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 21, 2026 10:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Modernizes the Flysystem Google Drive adapter to current PHP/Flysystem tooling while addressing multiple audit-driven security and correctness issues (notably around download redirects/token handling, query escaping, and shortcut metadata handling). This is a major-version-style update (PHP/dependency floor raises) and also introduces CI + static analysis to keep the new behavior covered.

Changes:

  • Upgrade runtime/dependency floors (PHP ^8.2, Flysystem ^3, Guzzle 7/psr7 v2) and add modern dev tooling (PHPUnit 10/11, PHPStan, Pint, GitHub Actions, Dependabot).
  • Security/correctness fixes in core adapter logic (trusted-host enforcement for streamed downloads, query-literal escaping, shortcut metadata handling, copy/move/delete edge cases).
  • Introduce a credential-free Unit test suite plus reorganized Integration tests.

Reviewed changes

Copilot reviewed 24 out of 25 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/GoogleDriveAdapter.php Core adapter modernization plus security/correctness fixes (redirect handling, query escaping, shortcut metadata, delete/move/copy hardening).
src/StreamableUpload.php Adds strict types and stronger typing/logic cleanup for resumable/multipart upload handling.
composer.json Raises platform/dependency floors; adds dev tools and Composer scripts.
phpunit.xml.dist Migrates to PHPUnit 10/11 config and splits Unit vs Integration suites.
phpstan.neon Adds PHPStan config and targeted ignores for Google client magic properties.
pint.json Adds Pint configuration for code style enforcement.
.github/workflows/tests.yml Adds CI test matrix (PHP 8.2–8.4, prefer-lowest/prefer-stable).
.github/workflows/static-analysis.yml Adds PHPStan workflow.
.github/workflows/code-style.yml Adds Pint workflow.
.github/dependabot.yml Enables Dependabot updates for Composer and GitHub Actions.
tests/Unit/TrustedHostTest.php Regression tests for trusted-host check to prevent token leakage on redirects.
tests/Unit/StaticHelpersTest.php Unit tests for query escaping, dirname normalization, and MIME guessing helpers.
tests/Unit/ExposedGoogleDriveAdapter.php Test-only subclass exposing protected pure-logic helpers.
tests/Unit/AdapterLogicTest.php Unit tests for adapter pure logic (path splitting, sanitization, param merging).
tests/Integration/GoogleDriveAdapterTest.php Updates integration tests for new namespace/config and credential discovery.
README.md Updates badges and documents version requirements/installation guidance.
SECURITY.md Adds a security policy and disclosure guidance.
CONTRIBUTING.md Adds contributor workflow, quality gates, and testing guidance.
CHANGELOG.md Adds changelog documenting 3.0.0 breaking changes and fixes.
.gitignore Updates ignores for new tooling caches and local environment files.
.gitattributes Sets default LF handling and updates export-ignore list for dist packaging.
.editorconfig Adds editor configuration for consistent formatting.
.travis.yml Removes legacy CI config.
.styleci.yml Removes legacy StyleCI config.
.scrutinizer.yml Removes legacy Scrutinizer config.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/StreamableUpload.php
Comment thread src/StreamableUpload.php Outdated
- fetchResumeUri(): only send a content-length header when the request body
  size is known; getSize() may return null for streams of unknown size, which
  would otherwise produce an invalid header value
- resume(): add the missing string parameter type to $resumeUri (getResumeUri()
  already promises a string)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread README.md
## Installation

- For **Flysystem V2/V3** or **Laravel >= 9.x.x**
- For **PHP >= 8.2** / **Flysystem V3** / **Laravel 9.x – 13.x**

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this part.
This package doesn't use Laravel dependencies, so why put a limit?
I think that just Laravel >= 9.x.x more accurate.

That way it wouldn't be necessary to change it every time Laravel releases a new version.

Another question: does the current version not work on Laravel 13?
Because I see you set a limit of 12.x.

Comment thread README.md
| Package version | PHP | Flysystem | Laravel |
|-----------------|------------|-----------|----------------|
| `^3.0` | `>= 8.2` | `^3.0` | `9.x` – `13.x` |
| `^2.0` | `7.2 – 8.x`| `^2.1`/`^3.0` | `9.x` – `12.x` |

@erikn69 erikn69 Jun 23, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does the current version not work on Laravel 13?

Yes, it works, Tested on 13.x, look at laravel-google-drive-ext-demo.

This package doesn't use Laravel dependencies, so why put a limit?
I think that just Laravel >= 9.x.x more accurate.

That's correct, it depends on Flysystem. Laravel handles compatibility, and as long as Flysystem doesn't release a major version, it will be compatible with future versions of Laravel.

Breaking changes (PHP/dependency floors), so this is really a 3.x-line
change

Updating dependency floors is not a breaking change; those that do not meet the requirements will simply not receive the changes, nothing will be broken.

Flysystem 2 was an incomplete release; it didn't last long and everyone immediately migrated to V3. It can be removed without any problems.


on:
push:
branches: [ "2.x", "3.x", "master", "main" ]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"master", "main" ??

those branches do not exist

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.

4 participants