CoolCMS API

AttachmentFilePolicy
in package

FinalYes

Policy for advert attachments (Bazaar / Comcat), which are written to the public webroot (`www/modules/{bazaar,comcat}/files/items/...`) with an extension taken from the file name.

  • Extensions are an allowlist, not a deny-list: advert attachments are a narrow, known case (documents, spreadsheets, images, archives, media). The file manager deny-list (FileSystemRepository) fits uploads/, which holds unknown legitimate types; this case has no such need.
  • An empty extension is rejected. A double extension (shell.php.pdf) is harmless because the file is stored under a random name with the single last extension.
  • The content is read (finfo), not the upload header: active content (HTML, SVG, XML, PHP, JS) is rejected even under an innocent extension, since the web server or browser may render it by content.

IMPORTANT: the authoritative check is the Api call (self::rejectionReason()) before any write to disk. The dropzones (self::ALLOWED_EXTENSIONS) only give immediate feedback and can be bypassed; without the Api check a .php file would land in a directory the root www/.htaccess serves directly.

Table of Contents

Constants

ALLOWED_EXTENSIONS  : array<string|int, mixed> = [ // documents 'pdf', 'txt', 'rtf', 'doc', 'doc...
Extensions without the dot, lowercase.
REASON_CONTENT  : string = 'content'
REASON_EXTENSION  : string = 'extension'
ACTIVE_CONTENT_MIME  : array<string|int, mixed> = ['text/html', 'application/xhtml+xml', 'image/s...
MIME types detected from the content that must not appear in the public area whatever the extension. Prefix matches (`text/x-php`, `application/x-httpd-php`) are handled by {@see self::isActiveContent()}.

Methods

extensionOf()  : string
isAllowedExtension()  : bool
rejectionReason()  : string|null
Returns `null` when the file may be stored, otherwise the rejection reason ({@see self::REASON_EXTENSION} / {@see self::REASON_CONTENT}).
detectMime()  : string
isActiveContent()  : bool

Constants

ALLOWED_EXTENSIONS

Extensions without the dot, lowercase.

public array<string|int, mixed> ALLOWED_EXTENSIONS = [ // documents 'pdf', 'txt', 'rtf', 'doc', 'docx', 'odt', // spreadsheets and presentations 'xls', 'xlsx', 'ods', 'csv', 'ppt', 'pptx', 'odp', // raster images (SVG is active content) 'jpg', 'jpeg', 'png', 'gif', 'webp', 'heic', // archives 'zip', 'rar', '7z', // media 'mp3', 'mp4', 'mov', ]

ACTIVE_CONTENT_MIME

MIME types detected from the content that must not appear in the public area whatever the extension. Prefix matches (`text/x-php`, `application/x-httpd-php`) are handled by {@see self::isActiveContent()}.

private array<string|int, mixed> ACTIVE_CONTENT_MIME = ['text/html', 'application/xhtml+xml', 'image/svg+xml', 'text/xml', 'application/xml', 'text/javascript', 'application/javascript', 'application/x-javascript', 'application/x-shockwave-flash']

Methods

extensionOf()

public static extensionOf(string $fileName) : string
Parameters
$fileName : string
Return values
string

isAllowedExtension()

public static isAllowedExtension(string $extension) : bool
Parameters
$extension : string
Return values
bool

rejectionReason()

Returns `null` when the file may be stored, otherwise the rejection reason ({@see self::REASON_EXTENSION} / {@see self::REASON_CONTENT}).

public static rejectionReason(string $fileName, string $content) : string|null
Parameters
$fileName : string
$content : string
Return values
string|null

detectMime()

private static detectMime(string $content) : string
Parameters
$content : string
Return values
string

isActiveContent()

private static isActiveContent(string $mime) : bool
Parameters
$mime : string
Return values
bool
On this page

Search results