diff --git a/src/Services/FileStorage.php b/src/Services/FileStorage.php new file mode 100644 index 0000000..ae77699 --- /dev/null +++ b/src/Services/FileStorage.php @@ -0,0 +1,106 @@ +basePath = realpath(__DIR__ . '/../../data'); + } else { + $this->basePath = realpath($basePath); + } + + if (!$this->basePath || !is_dir($this->basePath)) { + throw new Exception("Invalid base path for FileStorage."); + } + } + + /** + * Safely resolves and validates a path within the base directory. + * Prevents path traversal attacks. + */ + private function resolvePath(string $path): string + { + $fullPath = $this->basePath . DIRECTORY_SEPARATOR . ltrim($path, DIRECTORY_SEPARATOR); + $realPath = realpath(dirname($fullPath)); + + if ($realPath === false || strpos($realPath, $this->basePath) !== 0) { + throw new Exception("Security Alert: Path traversal attempt detected or invalid path: " . $path); + } + + return $fullPath; + } + + /** + * Saves an array as a JSON file. + */ + public function put(string $path, array $data): bool + { + $fullPath = $this->resolvePath($path); + + $json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); + + if (file_put_contents($fullPath, $json) === false) { + return false; + } + + return true; + } + + /** + * Reads a JSON file and returns it as an associative array. + */ + public function get(string $path): ?array + { + $fullPath = $this->resolvePath($path); + + if (!file_exists($fullPath)) { + return null; + } + + try { + $content = file_get_contents($fullPath); + if ($content === false) { + return null; + } + + return json_decode($content, true, 512, JSON_THROW_ON_ERROR); + } catch (Exception $e) { + return null; + } + } + + /** + * Checks if a file exists. + */ + public function exists(string $path): bool + { + try { + $fullPath = $this->resolvePath($path); + return file_exists($fullPath); + } catch (Exception $e) { + return false; + } + } + + /** + * Deletes a file. + */ + public function delete(string $path): bool + { + $fullPath = $this->resolvePath($path); + + if (file_exists($fullPath)) { + return unlink($fullPath); + } + + return false; + } +}