{"record":{"id":"6d67c67e0ea691b7","repo":"apache/maven","slug":"supplied-relative-uri-escapes-baseurl","errorCode":null,"errorMessage":"Supplied relative URI escapes baseUrl","messagePattern":"Supplied relative URI escapes baseUrl","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultTransport.java","lineNumber":54,"sourceCode":"public class DefaultTransport implements Transport {\n    private final URI baseURI;\n    private final Transporter transporter;\n\n    public DefaultTransport(URI baseURI, Transporter transporter) {\n        this.baseURI = requireNonNull(baseURI);\n        this.transporter = requireNonNull(transporter);\n    }\n\n    @Override\n    public boolean get(URI relativeSource, Path target) {\n        requireNonNull(relativeSource, \"relativeSource is null\");\n        requireNonNull(target, \"target is null\");\n        if (relativeSource.isAbsolute()) {\n            throw new IllegalArgumentException(\"Supplied URI is not relative\");\n        }\n        URI source = baseURI.resolve(relativeSource);\n        if (!source.toASCIIString().startsWith(baseURI.toASCIIString())) {\n            throw new IllegalArgumentException(\"Supplied relative URI escapes baseUrl\");\n        }\n        GetTask getTask = new GetTask(source);\n        getTask.setDataPath(target);\n        try {\n            transporter.get(getTask);\n            return true;\n        } catch (Exception e) {\n            if (Transporter.ERROR_NOT_FOUND != transporter.classify(e)) {\n                throw new RuntimeException(e);\n            }\n            return false;\n        }\n    }\n\n    @Override\n    public Optional<byte[]> getBytes(URI relativeSource) {\n        try {\n            Path tempPath = null;","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/apache/maven/blob/e4093d4e120eac99d6bdce5ba67cace2f3085c97/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultTransport.java#L36-L72","documentation":"After resolving the supplied relative URI against baseURI, DefaultTransport verifies the result still starts with the base URI's ASCII string. '../' sequences that navigate above the repository root produce a resolved URI outside the base and are rejected with IllegalArgumentException. This is an explicit path-traversal guard so a transport bound to one repository cannot read from another location.","triggerScenarios":"transport.get(URI.create(\"../../../etc/passwd\"), path); transport.get(URI.create(\"../../other-repo/artifact.jar\"), path); any relative URI whose resolution with baseURI.resolve(...) no longer starts with baseURI.toASCIIString().","commonSituations":"Building relative paths by string concatenation that leaves leading '../' segments; artifact coordinates containing '..' parts; deliberately probing traversal behavior in security tests.","solutions":["Normalize the path and strip leading parent ('..') segments so it stays under the repository root","Compute relative paths with baseURI.relativize(absoluteURI) instead of manual concatenation","If the artifact genuinely lives in another repository, create a separate Transport for that repository's URL"],"exampleFix":"// before\nString rel = \"../../infra/libs/util-1.0.jar\";\ntransport.get(URI.create(rel), target);\n\n// after: resolve against the other repo's own transport\nTransport infra = transportProvider.transport(session, infraRepo);\ninfra.get(URI.create(\"libs/util-1.0.jar\"), target);","handlingStrategy":"validation","validationCode":"static URI safeRelative(URI base, URI rel) {\n    if (rel.isAbsolute()) throw new IllegalArgumentException(\"absolute URI: \" + rel);\n    URI resolved = base.resolve(rel);\n    if (!resolved.toASCIIString().startsWith(base.toASCIIString())) {\n        throw new IllegalArgumentException(\"path escapes repository base: \" + rel);\n    }\n    return rel;\n}\ntransport.get(safeRelative(baseURI, rel), target);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build relative paths from groupId/artifactId/version segments only","Reject or strip leading '..' segments in any user-supplied repository path","Prefer baseURI.relativize() over manual '../' math when computing relative locations"],"tags":["maven","resolver","transport","path-traversal","security","uri"],"backgroundTag":"path-traversal-blocked","analyzedSha":"e4093d4e120eac99d6bdce5ba67cace2f3085c97","analyzedAt":"2026-08-21T22:58:24.034Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}