diff --git a/Neos.Fusion/Classes/Core/Cache/ParserCache.php b/Neos.Fusion/Classes/Core/Cache/ParserCache.php index 08f2a82b21a..e5a8bb166c2 100644 --- a/Neos.Fusion/Classes/Core/Cache/ParserCache.php +++ b/Neos.Fusion/Classes/Core/Cache/ParserCache.php @@ -58,7 +58,12 @@ public function cacheForFusionFile(?string $contextPathAndFilename, \Closure $ge if (str_contains($contextPathAndFilename, '://')) { $contextPathAndFilename = $this->getAbsolutePathForPackageRessourceUri($contextPathAndFilename); } - $identifier = $this->getCacheIdentifierForFile($contextPathAndFilename); + $fusionFileRealPath = realpath($contextPathAndFilename); + if ($fusionFileRealPath === false) { + // should not happen as the file would not been able to be read in the first place. + throw new \RuntimeException("Couldn't resolve realpath for: '$contextPathAndFilename'", 1705409467); + } + $identifier = $this->getCacheIdentifierForAbsoluteUnixStyleFilePathWithoutDirectoryTraversal($fusionFileRealPath); return $this->cacheForIdentifier($identifier, $generateValueToCache); } diff --git a/Neos.Fusion/Classes/Core/Cache/ParserCacheFlusher.php b/Neos.Fusion/Classes/Core/Cache/ParserCacheFlusher.php index 9d02094ebd0..04a4b0c13ac 100644 --- a/Neos.Fusion/Classes/Core/Cache/ParserCacheFlusher.php +++ b/Neos.Fusion/Classes/Core/Cache/ParserCacheFlusher.php @@ -50,7 +50,10 @@ public function flushPartialCacheOnFileChanges($fileMonitorIdentifier, array $ch $identifiersToFlush = []; foreach ($changedFiles as $changedFile => $status) { - $identifiersToFlush[] = $this->getCacheIdentifierForFile($changedFile); + // flow already returns absolute file paths from the file monitor, so we don't have to call realpath. + // attempting to use realpath can even result in an error as the file might be removed and thus no realpath can be resolved via file system. + // https://github.com/neos/neos-development-collection/pull/4509 + $identifiersToFlush[] = $this->getCacheIdentifierForAbsoluteUnixStyleFilePathWithoutDirectoryTraversal($changedFile); } if ($identifiersToFlush !== []) { diff --git a/Neos.Fusion/Classes/Core/Cache/ParserCacheIdentifierTrait.php b/Neos.Fusion/Classes/Core/Cache/ParserCacheIdentifierTrait.php index 86fcf9d6a0a..23fd8a76b36 100644 --- a/Neos.Fusion/Classes/Core/Cache/ParserCacheIdentifierTrait.php +++ b/Neos.Fusion/Classes/Core/Cache/ParserCacheIdentifierTrait.php @@ -19,7 +19,7 @@ trait ParserCacheIdentifierTrait { /** - * creates a comparable hash of the dsl type and content to be used as cache identifier + * Creates a comparable hash of the dsl type and content to be used as cache identifier */ private function getCacheIdentifierForDslCode(string $identifier, string $code): string { @@ -27,18 +27,24 @@ private function getCacheIdentifierForDslCode(string $identifier, string $code): } /** - * creates a comparable hash of the absolute, resolved $fusionFileName + * Creates a comparable hash of the absolute-unix-style-file-path-without-directory-traversal * - * @throws \InvalidArgumentException + * something like + * - /Users/marc/Code/neos-project/Packages/Neos + * + * its crucial that the path + * - is absolute + * - the path separator is in unix style: forward-slash / + * - doesn't contain directory traversal /../ or /./ + * + * to be absolutely sure the path matches the criteria, {@see realpath} can be used. + * + * if the path does not match the criteria, a different hash as expected will be generated and caching will break. */ - private function getCacheIdentifierForFile(string $fusionFileName): string - { - $realPath = realpath($fusionFileName); - if ($realPath === false) { - throw new \InvalidArgumentException("Couldn't resolve realpath for: '$fusionFileName'"); - } - - $realFusionFilePathWithoutRoot = str_replace(FLOW_PATH_ROOT, '', $realPath); - return 'file_' . md5($realFusionFilePathWithoutRoot); + private function getCacheIdentifierForAbsoluteUnixStyleFilePathWithoutDirectoryTraversal( + string $absoluteUnixStyleFilePathWithoutDirectoryTraversal + ): string { + $filePathWithoutRoot = str_replace(FLOW_PATH_ROOT, '', $absoluteUnixStyleFilePathWithoutDirectoryTraversal); + return 'file_' . md5($filePathWithoutRoot); } }