GHSA-grm4-wm43-9jh5

Suggest an improvement
Source
https://github.com/advisories/GHSA-grm4-wm43-9jh5
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/08/GHSA-grm4-wm43-9jh5/GHSA-grm4-wm43-9jh5.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-grm4-wm43-9jh5
Aliases
Published
2026-08-06T19:49:59Z
Modified
2026-08-06T20:00:15Z
Severity
  • 3.1 (Low) CVSS_V3 - CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N CVSS Calculator
Summary
Contao: Possible path traversal in job download URIs
Details

Summary

An authenticated backend user who can access one job can request an attachment identifier containing ../ segments and make the job attachment download endpoint read a file from another job directory inside var/job-attachments.

The controller authorizes only the jobUuid route parameter. The later attachment lookup joins that authorized job UUID with the attacker-controlled identifier, then passes the combined path to the virtual filesystem. VirtualFilesystem::resolve() canonicalizes the whole path and only rejects paths that escape the filesystem mount, so authorized-job/../victim-job/debug_log.csv becomes victim-job/debug_log.csv.

This is a cross-job authorization bypass for known job attachment paths. It is not a practical brute-force against unknown jobs because job directories are UUID v4 values.

Root Cause

JobsController::downloadJobAttachment() checks access to the route jobUuid before loading the attachment:

$job = $this->jobs->getByUuid($jobUuid);

if (!$job || !$this->jobs->hasAccess($job)) {
    throw $this->createNotFoundException();
}

$attachment = $this->jobs->getAttachment($jobUuid, $identifier);

Jobs::getAttachment() then resolves a path built from the authorized job UUID and the attacker-controlled identifier:

$fileItem = $this->jobAttachmentsStorage->get($this->getAttachmentIdentifier($job, $identifier));
return $job->getUuid().'/'.$identifier;

VirtualFilesystem::resolve() canonicalizes the combined path. It rejects absolute paths and paths that start with .., but it does not preserve the authorized job directory as a boundary:

$path = Path::canonicalize($location);

if (str_starts_with($path, '..')) {
    throw new \OutOfBoundsException(...);
}

return Path::join($this->prefix, $path);

Therefore:

<authorized-job>/../<victim-job>/debug_log.csv

canonicalizes to:

<victim-job>/debug_log.csv

which remains inside the job-attachments filesystem mount and is accepted.

Recommended Fix

Treat the attachment identifier as a filename, not a path:

  • Reject /, \, NUL, and dot-segment components in identifier.
  • Add a route requirement that prevents slashes in {identifier} if nested attachment paths are not intended.
  • After resolving, assert the canonical relative path starts with <authorized-job-uuid>/ before returning a FilesystemItem.
  • Apply the same identifier validation in Jobs::addAttachment() so future producers/extensions cannot write outside the owning job directory.

Impact

A low-privileged backend user can read another job's attachment if they know or obtain the target job UUID and attachment filename. Built-in crawler jobs attach CSV logs such as debug_log.csv, broken-link-checker_log.csv, and search-index_log.csv, which can contain crawled URLs, referring URLs, tags, and error messages.

Database specific
{
    "cwe_ids":  [
        "CWE-22"
    ],
    "github_reviewed":  true,
    "github_reviewed_at":  "2026-08-06T19:49:59Z",
    "nvd_published_at":  "2026-07-31T20:16:52Z",
    "severity":  "LOW"
}
References

Affected packages

Packagist / contao/contao

Package

Name
contao/contao
Purl
pkg:composer/contao/contao

Affected ranges

Type
ECOSYSTEM
Events
Introduced
5.7.0
Fixed
5.7.7

Affected versions

5.*
5.7.0
5.7.1
5.7.2
5.7.3
5.7.4
5.7.5
5.7.6

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/08/GHSA-grm4-wm43-9jh5/GHSA-grm4-wm43-9jh5.json"

Packagist / contao/core-bundle

Package

Name
contao/core-bundle
Purl
pkg:composer/contao/core-bundle

Affected ranges

Type
ECOSYSTEM
Events
Introduced
5.7.0
Fixed
5.7.7

Affected versions

5.*
5.7.0
5.7.1
5.7.2
5.7.3
5.7.4
5.7.5
5.7.6

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/08/GHSA-grm4-wm43-9jh5/GHSA-grm4-wm43-9jh5.json"