diff --git a/.env.example b/.env.example index 86b7fc1e20..3bc9575414 100644 --- a/.env.example +++ b/.env.example @@ -80,3 +80,7 @@ KEYCLOAK_CLIENT_SECRET= KEYCLOAK_BASE_URL= KEYCLOAK_USERNAME= KEYCLOAK_PASSWORD= + +# Comma-separated file extensions (optional; defaults in config/files.php) +# ALLOWED_EXTENSIONS=pdf,doc,docx,txt,jpg,png +# DANGEROUS_EXTENSIONS=zip,rar,7z,tar diff --git a/config/files.php b/config/files.php index d95f373047..23f99063ea 100644 --- a/config/files.php +++ b/config/files.php @@ -1,5 +1,38 @@ strtolower(trim($extension)), + explode(',', $value) + ))); +}; + +$defaultAllowedExtensions = [ + // Documents + 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', + 'txt', 'csv', + + // Images + 'jpg', 'jpeg', 'png', 'gif', 'svg', + + // Audio + 'mp3', + + // Video + 'mp4', 'webm', +]; + +$defaultDangerousExtensions = [ + 'zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz', 'lzma', + 'cab', 'ar', 'iso', 'dmg', 'pkg', 'deb', 'rpm', +]; + return [ /* |-------------------------------------------------------------------------- @@ -19,22 +52,13 @@ | List of file extensions that are allowed to be uploaded. | Only files with these extensions will be accepted. | Archive formats (.zip, .rar, .tar, .7z) are explicitly NOT allowed for security. + | Override with comma-separated ALLOWED_EXTENSIONS in .env (optional). | */ - 'allowed_extensions' => [ - // Documents - 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', - 'txt', 'csv', - - // Images - 'jpg', 'jpeg', 'png', 'gif', 'svg', - - // Audio - 'mp3', - - // Video - 'mp4', 'webm', - ], + 'allowed_extensions' => $parseCommaSeparatedExtensions( + 'ALLOWED_EXTENSIONS', + $defaultAllowedExtensions + ), /* |-------------------------------------------------------------------------- | Extension to MIME Type Mapping @@ -116,12 +140,13 @@ | Archive formats (.zip, .rar, .tar, .7z, .gz, etc.) are explicitly | NOT allowed for security reasons. These file types can contain | malicious content and are blocked by default. + | Override with comma-separated DANGEROUS_EXTENSIONS in .env (optional). | */ - 'dangerous_extensions' => [ - 'zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz', 'lzma', - 'cab', 'ar', 'iso', 'dmg', 'pkg', 'deb', 'rpm', - ], + 'dangerous_extensions' => $parseCommaSeparatedExtensions( + 'DANGEROUS_EXTENSIONS', + $defaultDangerousExtensions + ), /* |-------------------------------------------------------------------------- diff --git a/tests/unit/FilesConfigTest.php b/tests/unit/FilesConfigTest.php new file mode 100644 index 0000000000..a338d84a2f --- /dev/null +++ b/tests/unit/FilesConfigTest.php @@ -0,0 +1,85 @@ +unsetEnv(self::ENV_ALLOWED); + $this->unsetEnv(self::ENV_DANGEROUS); + + parent::tearDown(); + } + + public function test_allowed_extensions_use_defaults_when_env_unset(): void + { + $this->unsetEnv(self::ENV_ALLOWED); + + $config = $this->loadFilesConfig(); + + $this->assertContains('pdf', $config['allowed_extensions']); + $this->assertContains('mp4', $config['allowed_extensions']); + } + + public function test_allowed_extensions_parsed_from_env(): void + { + $this->setEnv(self::ENV_ALLOWED, 'pdf, custom ,TXT'); + + $config = $this->loadFilesConfig(); + + $this->assertSame(['pdf', 'custom', 'txt'], $config['allowed_extensions']); + } + + public function test_dangerous_extensions_parsed_from_env(): void + { + $this->setEnv(self::ENV_DANGEROUS, 'zip,rar'); + + $config = $this->loadFilesConfig(); + + $this->assertSame(['zip', 'rar'], $config['dangerous_extensions']); + } + + public function test_dangerous_extensions_defaults_when_env_empty(): void + { + $this->unsetEnv(self::ENV_DANGEROUS); + + $config = $this->loadFilesConfig(); + + $this->assertContains('zip', $config['dangerous_extensions']); + $this->assertContains('7z', $config['dangerous_extensions']); + } + + private function loadFilesConfig(): array + { + return include __DIR__ . '/../../config/files.php'; // NOSONAR: load fresh config after changing environment variables. + } + + private function setEnv(string $key, string $value): void + { + putenv("{$key}={$value}"); + $_ENV[$key] = $value; + $_SERVER[$key] = $value; + } + + private function unsetEnv(string $key): void + { + putenv($key); + unset($_ENV[$key], $_SERVER[$key]); + } +}