diff --git a/routes/composer.php b/routes/composer.php index 155d1ce..aaf20bc 100644 --- a/routes/composer.php +++ b/routes/composer.php @@ -27,7 +27,7 @@ // Dist archive download Route::get('dists/{vendor}/{package}/{version}/{reference}.zip', [DistController::class, 'download']) ->name('composer.dist.download') - ->where(['vendor' => '[a-z0-9_.-]+', 'package' => '[a-z0-9_.-]+']); + ->where(['vendor' => '[a-z0-9_.-]+', 'package' => '[a-z0-9_.-]+', 'version' => '[a-zA-Z0-9_./-]+', 'reference' => '[a-f0-9]+']); // Download notification endpoint Route::post('notify-batch', NotifyBatchController::class) diff --git a/tests/Feature/Composer/DistDownloadTest.php b/tests/Feature/Composer/DistDownloadTest.php index ded8b1e..318445e 100644 --- a/tests/Feature/Composer/DistDownloadTest.php +++ b/tests/Feature/Composer/DistDownloadTest.php @@ -107,6 +107,29 @@ function distGet(string $uri, string $token): TestResponse expect($response->headers->get('ETag'))->toBe('"abc123def456"'); }); +it('downloads a dist archive for a branch version with slashes', function () { + $package = Package::factory() + ->for($this->organization, 'organization') + ->create(['name' => 'acme/test-package']); + + $distPath = 'acme/acme/test-package/dev-feat_ISSUE-123-my-feature_abc123def456.zip'; + Storage::disk('local')->put($distPath, 'fake-zip-content'); + + PackageVersion::factory() + ->for($package) + ->create([ + 'version' => 'dev-feat/ISSUE-123-my-feature', + 'source_reference' => 'abc123def456', + 'dist_url' => url('/acme/dists/acme/test-package/dev-feat/ISSUE-123-my-feature/abc123def456.zip'), + 'dist_path' => $distPath, + 'dist_shasum' => sha1('fake-zip-content'), + ]); + + $response = distGet('/acme/dists/acme/test-package/dev-feat/ISSUE-123-my-feature/abc123def456.zip', $this->plainToken); + + $response->assertOk(); +}); + it('requires authentication for dist download', function () { $response = test()->getJson('/acme/dists/acme/test-package/1.0.0/abc123.zip');