skip to content
Back to
Home Bounties Research Advisories CodeQL Wall of Fame Get Involved Events
April 1, 2022

GHSL-2022-002_GHSL-2022-003: Partial path traversal in Apache James Server - CVE-2022-22931

Jaroslav Lobacevski

Coordinated Disclosure Timeline


Partial path traversal allows to break out of expected folder and access another user’s mailbox.


Apache James Server

Tested Version




Issue 1: Path traversal in (GHSL-2022-002)

validateWithinFolder validates if this.rootFolder path starts with the expected maildirRoot. getCanonicalPath transforms the path into a canonical form preventing such attack types as .. in path segments. It also gets rid of the terminating slash symbol in case of directory path.

public MaildirFolder validateWithinFolder(File maildirRoot) throws MailboxNotFoundException {
    try {
        if (!rootFolder.getCanonicalPath().startsWith(maildirRoot.getCanonicalPath())) {
            throw new MailboxNotFoundException(rootFolder.getCanonicalPath() + " jail breaks out of " + maildirRoot.getCanonicalPath());
    } catch (IOException e) {
        throw new RuntimeException(e);
    return this;

If the result of maildirRoot.getCanonicalPath() is not slash terminated it allows for partial path traversal.
Consider "/var/store/maildir/domain/admin".startsWith("/var/store/maildir/domain/adm"). The check is bypassed although it is not the adm directory. The terminating slash may be removed in various places. On Linux println(new File("/var/")) returns /var, but println(new File("/var", "/")) - /var/, however println(new File("/var", "/").getCanonicalPath()) - /var.

The validateWithinFolder function is used for example in loadMailbox where it is checked if the mailboxPath is under expected user mail directory, which is typically in the following form ../var/store/maildir/%domain/%user.

MaildirFolder folder = new MaildirFolder(getFolderName(mailboxPath), mailboxPath, locker)
    .validateWithinFolder(new File(userRoot(session.getUser())));


The partial path traversal allows the user named a to load mailboxes of all users whose name starts from a.

Issue 2: Path traversal in (GHSL-2022-003)

private void enforceRoot(File file) throws StorageException {
    try {
        if (!file.getCanonicalPath().startsWith(root.getCanonicalPath())) {
            throw new StorageException(new IllegalStateException("Path traversal attempted"));
    } catch (IOException e) {
        throw new StorageException(e);

enforceRoot similarly to validateWithinFolder doesn’t take into account that root may not be terminated with the slash even though it is hardcoded with it:

private static final String SIEVE_ROOT = FileSystem.FILE_PROTOCOL + "sieve/"


The partial path traversal allows allow one user to read scripts of the other.



These issues were discovered and reported by GHSL team member @JarLob (Jaroslav Lobačevski).


You can contact the GHSL team at, please include a reference to GHSL-2022-002 or GHSL-2022-003 in any communication regarding these issues.