octobercms/october · error · SystemException

Invalid image file name.

Error message

Invalid image file name.

What it means

SystemException thrown at the top of cropImage() (MediaManager.php:1916) when basename($imageSrcPath) contains '..', '/', or '\\'. This is a path-traversal guard: the crop API is only allowed to operate on a bare filename inside the current crop session directory, never on a path or a traversal sequence.

Source

Thrown at modules/media/widgets/MediaManager.php:1916

     * @param string $imageSrcPath
     * @param array $selectionData
     * @param string $cropSessionKey
     * @param string $path
     * @return array
     */
    protected function cropImage($imageSrcPath, $selectionData, $cropSessionKey, $path)
    {
        $originalFileName = basename($path);

        $path = rtrim(dirname($path), '/').'/';
        $fileName = basename($imageSrcPath);

        if (
            strpos($fileName, '..') !== false ||
            strpos($fileName, '/') !== false ||
            strpos($fileName, '\\') !== false
        ) {
            throw new SystemException('Invalid image file name.');
        }

        $selectionParams = ['x', 'y', 'w', 'h'];

        foreach ($selectionParams as $paramName) {
            if (!array_key_exists($paramName, $selectionData)) {
                throw new SystemException('Invalid selection data.');
            }

            if (!is_numeric($selectionData[$paramName])) {
                throw new SystemException('Invalid selection data.');
            }

            $selectionData[$paramName] = (int) $selectionData[$paramName];
        }

        $sessionDirectoryPath = $this->getCropSessionDirPath($cropSessionKey);
        $fullSessionDirectoryPath = temp_path($sessionDirectoryPath);

View on GitHub (pinned to b608633a7e)

Solutions

  1. In custom clients, post only the file name portion: image = imageSrcPath.split('/').pop().
  2. Do not attempt to bypass the guard — stage the file via the popup open step first, then crop by bare filename.
  3. For pentest findings, treat this as the control working as designed; ensure the endpoint stays behind media permission checks.

Example fix

// before
data: { image: '/uploads/tmp/crop/original.jpg', ... }

// after
data: { image: 'original.jpg', ... }
Defensive patterns

Strategy: validation

Validate before calling

const name = String(imageSrc).split('/').pop();
if (name.includes('..') || name.includes('/') || name.includes('\\')) {
    throw new Error('image must be a bare filename');
}
$.request('crop', { data: { image: name, ... } });

Type guard

function isBareFileName(name) {
    return typeof name === 'string'
        && !name.includes('/')
        && !name.includes('\\')
        && !name.includes('..');
}

Try / catch

try {
    $this->cropImage($src, $selection, $key, $path);
} catch (\System\Classes\SystemException $e) {
    if (strpos($e->getMessage(), 'Invalid image file name') !== false) {
        // treat as a client bug or probe: log and reject, never retry
        \Log::warning('Rejected crop source path', ['src' => $src]);
    }
    throw $e;
}

Prevention

When it happens

Trigger: Posting image='../../storage/app/...' , image='sub/dir/original.png', or any absolute path as the crop source. Legit stock clients always send the plain staged filename ('original.jpg' / 'resized-W-H.jpg'), so this almost always indicates a hand-crafted request or a broken custom client.

Common situations: Security scanners probing the crop endpoint; custom crop UI that echoes back a full URL or DOM path instead of the staged file name; middleware/tests that forward the whole src URL.

Related errors


AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21). Data as JSON: /api/errors/f82cd75b43b826df. Report an issue: GitHub.