mirror of
https://github.com/apple/pkl.git
synced 2026-07-08 05:55:13 +02:00
Reject relative package URIs (#1683)
This commit is contained in:
@@ -45,6 +45,7 @@ import java.util.stream.StreamSupport;
|
|||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
import org.graalvm.collections.EconomicMap;
|
import org.graalvm.collections.EconomicMap;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
import org.pkl.core.PklBugException;
|
||||||
import org.pkl.core.SecurityManager;
|
import org.pkl.core.SecurityManager;
|
||||||
import org.pkl.core.SecurityManagerException;
|
import org.pkl.core.SecurityManagerException;
|
||||||
import org.pkl.core.http.HttpClient;
|
import org.pkl.core.http.HttpClient;
|
||||||
@@ -456,10 +457,17 @@ final class PackageResolvers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Path getRelativePath(PackageUri uri) {
|
private Path getRelativePath(PackageUri uri) {
|
||||||
return Path.of(
|
var relativePath =
|
||||||
CACHE_DIR_PREFIX,
|
Path.of(
|
||||||
IoUtils.encodePath(uri.getUri().getAuthority()),
|
CACHE_DIR_PREFIX,
|
||||||
getEffectivePackageUriPath(uri));
|
IoUtils.encodePath(uri.getUri().getAuthority()),
|
||||||
|
getEffectivePackageUriPath(uri));
|
||||||
|
// ensure the derived path cannot escape the cache directory
|
||||||
|
var resolved = cacheDir.resolve(relativePath).normalize();
|
||||||
|
if (!resolved.startsWith(cacheDir.normalize())) {
|
||||||
|
throw new PklBugException("Package URI escapes the cache directory");
|
||||||
|
}
|
||||||
|
return relativePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getLastSegmentName(PackageUri packageUri) {
|
private String getLastSegmentName(PackageUri packageUri) {
|
||||||
|
|||||||
@@ -61,6 +61,13 @@ public final class PackageUri {
|
|||||||
throw new URISyntaxException(
|
throw new URISyntaxException(
|
||||||
uri.toString(), ErrorMessages.create("missingPathInPackageUri", uri));
|
uri.toString(), ErrorMessages.create("missingPathInPackageUri", uri));
|
||||||
}
|
}
|
||||||
|
// reject `..` segments, percent-encoded or not
|
||||||
|
for (var segment : path.split("/", -1)) {
|
||||||
|
if (segment.equals("..")) {
|
||||||
|
throw new URISyntaxException(
|
||||||
|
uri.toString(), ErrorMessages.create("invalidRelativePathInPackageUri"));
|
||||||
|
}
|
||||||
|
}
|
||||||
var versionIdx = path.lastIndexOf('@');
|
var versionIdx = path.lastIndexOf('@');
|
||||||
if (versionIdx == -1) {
|
if (versionIdx == -1) {
|
||||||
throw new URISyntaxException(
|
throw new URISyntaxException(
|
||||||
|
|||||||
@@ -861,6 +861,9 @@ Package URIs must have an authority component.\n\
|
|||||||
\n\
|
\n\
|
||||||
For example, `example.com` in URI `project://example.com/my/package@1.0.0`.
|
For example, `example.com` in URI `project://example.com/my/package@1.0.0`.
|
||||||
|
|
||||||
|
invalidRelativePathInPackageUri=\
|
||||||
|
Package URIs cannot contain `..` segments in the URI's "path" component.
|
||||||
|
|
||||||
unexpectedChecksumInPackageUri=\
|
unexpectedChecksumInPackageUri=\
|
||||||
Did not expect to find a checksum component in this package URI.
|
Did not expect to find a checksum component in this package URI.
|
||||||
|
|
||||||
|
|||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
// relative path in package URI
|
||||||
|
res = import("package://localhost:0/%2e%2e/birds@0.5.0#/catalog/Bird.pkl")
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
–– Pkl Error ––
|
||||||
|
Module URI `package://localhost:0/%2e%2e/birds@0.5.0#/catalog/Bird.pkl` has invalid syntax.
|
||||||
|
|
||||||
|
x | res = import("package://localhost:0/%2e%2e/birds@0.5.0#/catalog/Bird.pkl")
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
at badImport12.error#res (file:///$snippetsDir/input/packages/badImport12.error.pkl)
|
||||||
|
|
||||||
|
Package URIs cannot contain `..` segments in the URI's "path" component.
|
||||||
|
|
||||||
|
xxx | renderer.renderDocument(value)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
at pkl.base#Module.output.text (pkl:base)
|
||||||
|
|
||||||
|
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||||
|
^^^^
|
||||||
|
at pkl.base#Module.output.bytes (pkl:base)
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.pkl.core.packages
|
||||||
|
|
||||||
|
import java.net.URISyntaxException
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.assertj.core.api.Assertions.assertThatCode
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.assertThrows
|
||||||
|
|
||||||
|
class PackageUriTest {
|
||||||
|
@Test
|
||||||
|
fun `rejects percent-encoded dot-dot path segments`() {
|
||||||
|
val err =
|
||||||
|
assertThrows<URISyntaxException> {
|
||||||
|
PackageUri("package://attacker.com/%2e%2e/legit.example.com/legit@1.2.3")
|
||||||
|
}
|
||||||
|
assertThat(err).hasMessageContaining("..")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `rejects literal dot-dot path segments`() {
|
||||||
|
assertThrows<URISyntaxException> { PackageUri("package://attacker.com/../legit@1.2.3") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `rejects trailing dot-dot segment`() {
|
||||||
|
assertThrows<URISyntaxException> { PackageUri("package://attacker.com/foo@1.2.3/%2e%2e") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `accepts a valid package URI`() {
|
||||||
|
assertThatCode { PackageUri("package://example.com/my/package@1.0.0") }
|
||||||
|
.doesNotThrowAnyException()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `does not reject path segments that merely contain dots`() {
|
||||||
|
assertThatCode { PackageUri("package://example.com/my..pkg/..foo/bar..@1.0.0") }
|
||||||
|
.doesNotThrowAnyException()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user