From a38418ca737821e060751aa9cb3ade4f6299b597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= Date: Tue, 17 Jan 2023 21:36:58 -0500 Subject: [PATCH 1/3] Append partial hash --- src/ComposerFileStorage.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ComposerFileStorage.php b/src/ComposerFileStorage.php index 41602c8..90575fa 100644 --- a/src/ComposerFileStorage.php +++ b/src/ComposerFileStorage.php @@ -51,9 +51,16 @@ public static function basePath(Config $config): string */ public static function create(string $url, Config $config): self { + $escapedUrl = preg_replace('/[^[:alnum:]\.]/', '-', $url); + // Append a partial hash of the unescaped URL, to prevent URLs like + // `https://www.site.coop.info/packages` from colliding with + // `https://www.site.coop/info/packages`, whilst keeping the directory + // names easily distinguishable. + $hashedUrl = hash('sha256', $url); + $basePath = implode(DIRECTORY_SEPARATOR, [ static::basePath($config), - preg_replace('/[^[:alnum:]\.]/', '-', $url), + $escapedUrl . '-' . substr($hashedUrl, 0, 8), ]); return new static($basePath); } From 9f80fa2797b707ff60d8c1e12bae87d1eb70687e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= Date: Wed, 18 Jan 2023 09:33:07 -0500 Subject: [PATCH 2/3] Add public static method to make this easier --- src/ComposerFileStorage.php | 25 ++++++++++++++++++++----- tests/ComposerFileStorageTest.php | 16 ++++++++++++++-- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/ComposerFileStorage.php b/src/ComposerFileStorage.php index 90575fa..c36858c 100644 --- a/src/ComposerFileStorage.php +++ b/src/ComposerFileStorage.php @@ -50,6 +50,25 @@ public static function basePath(Config $config): string * The storage object. */ public static function create(string $url, Config $config): self + { + $basePath = implode(DIRECTORY_SEPARATOR, [ + static::basePath($config), + static::escapeUrl($url), + ]); + return new static($basePath); + } + + /** + * Converts a repository URL to a unique directory name. + * + * @param string $url + * A repository URL. + * + * @return string + * A version of the URL suitable to use as the name of the persistent + * storage directory. + */ + public static function escapeUrl(string $url): string { $escapedUrl = preg_replace('/[^[:alnum:]\.]/', '-', $url); // Append a partial hash of the unescaped URL, to prevent URLs like @@ -58,10 +77,6 @@ public static function create(string $url, Config $config): self // names easily distinguishable. $hashedUrl = hash('sha256', $url); - $basePath = implode(DIRECTORY_SEPARATOR, [ - static::basePath($config), - $escapedUrl . '-' . substr($hashedUrl, 0, 8), - ]); - return new static($basePath); + return $escapedUrl . '-' . substr($hashedUrl, 0, 8); } } diff --git a/tests/ComposerFileStorageTest.php b/tests/ComposerFileStorageTest.php index b1208d1..2330793 100644 --- a/tests/ComposerFileStorageTest.php +++ b/tests/ComposerFileStorageTest.php @@ -50,23 +50,35 @@ public function testBasePath(): void $this->assertSame($expectedPath, ComposerFileStorage::basePath($config)); } + /** + * @covers ::escapeUrl + */ + public function testEscapeUrl(): void + { + $url = 'https://example.net/packages'; + $hash = hash('sha256', $url); + $this->assertSame('https---example.net-packages-' . substr($hash, 0, 8), ComposerFileStorage::escapeUrl($url)); + } + /** * @covers ::__construct * @covers ::create * * @depends testBasePath + * @depends testEscapeUrl */ public function testCreate(): void { + $url = 'https://example.net/packages'; $config = new Config(); $basePath = implode(DIRECTORY_SEPARATOR, [ ComposerFileStorage::basePath($config), - 'https---example.net-packages', + ComposerFileStorage::escapeUrl($url), ]); $this->assertDirectoryDoesNotExist($basePath); - ComposerFileStorage::create('https://example.net/packages', $config); + ComposerFileStorage::create($url, $config); $this->assertDirectoryExists($basePath); } } From 3bfba8c80812d6ed2de787522cf6fa95be132eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= Date: Wed, 18 Jan 2023 09:39:53 -0500 Subject: [PATCH 3/3] do not codify actual uniqueness logic --- tests/ComposerFileStorageTest.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/ComposerFileStorageTest.php b/tests/ComposerFileStorageTest.php index 2330793..711f0ee 100644 --- a/tests/ComposerFileStorageTest.php +++ b/tests/ComposerFileStorageTest.php @@ -55,9 +55,14 @@ public function testBasePath(): void */ public function testEscapeUrl(): void { - $url = 'https://example.net/packages'; - $hash = hash('sha256', $url); - $this->assertSame('https---example.net-packages-' . substr($hash, 0, 8), ComposerFileStorage::escapeUrl($url)); + // Ensure that two very similar URLs are converted into unique, but + // readable, directory names. + $url1 = ComposerFileStorage::escapeUrl('https://site.coop/info/packages'); + $url2 = ComposerFileStorage::escapeUrl('https://site.coop.info/packages'); + + $this->assertNotSame($url1, $url2); + $this->assertMatchesRegularExpression('/^https---site\.coop-info-packages-[a-z0-9]{8}$/', $url1); + $this->assertMatchesRegularExpression('/^https---site\.coop\.info-packages-[a-z0-9]{8}$/', $url2); } /**