mirror of
https://github.com/apple/pkl.git
synced 2026-03-26 11:01:14 +01:00
Prevent --multiple-file-output-path writes from following symlinks outside the target directory (#1467)
This commit is contained in:
@@ -240,8 +240,7 @@ constructor(
|
||||
|
||||
for ((pathSpec, fileOutput) in output) {
|
||||
checkPathSpec(pathSpec)
|
||||
val resolvedPath = realOutputDir.resolve(pathSpec).normalize()
|
||||
val realPath = if (resolvedPath.exists()) resolvedPath.toRealPath() else resolvedPath
|
||||
val (realPath, resolvedPath) = realOutputDir.resolveRealPath(Path.of(pathSpec))
|
||||
if (!realPath.startsWith(realOutputDir)) {
|
||||
throw CliException(
|
||||
"Output file conflict: `output.files` entry `\"$pathSpec\"` in module `$moduleUri` resolves to file path `$realPath`, which is outside output directory `$realOutputDir`."
|
||||
@@ -269,4 +268,22 @@ constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves [rel] against this Path name-by-name. At each step, the real path is resolved if the
|
||||
* file exists. The normalized real path and normalized resolved path are returned. This has a
|
||||
* similar effect to `this.resolve(rel).toRealPath().normalize()`, but the real paths account for
|
||||
* symlinks in the middle of the relative path so the full path need not exist.
|
||||
*/
|
||||
private fun Path.resolveRealPath(rel: Path): Pair<Path, Path> {
|
||||
assert(!rel.isAbsolute)
|
||||
var resolved = this
|
||||
var real = this
|
||||
for (name in rel) {
|
||||
resolved = resolved.resolve(name)
|
||||
real = real.resolve(name)
|
||||
if (real.exists()) real = real.toRealPath()
|
||||
}
|
||||
return real.normalize() to resolved.normalize()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -931,6 +931,37 @@ result = someLib.x
|
||||
.hasMessageContaining("which is outside output directory")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledOnOs(OS.WINDOWS)
|
||||
fun `multiple file output throws if files are written outside the base path via symlink`() {
|
||||
val output = tempDir.resolve(".output").createDirectories()
|
||||
val outside = tempDir.resolve("outside").createDirectories()
|
||||
output.resolve("outside").createSymbolicLinkPointingTo(outside)
|
||||
|
||||
val moduleUri =
|
||||
writePklFile(
|
||||
"test.pkl",
|
||||
"""
|
||||
output {
|
||||
files {
|
||||
["outside/foo.txt"] {
|
||||
text = "bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent(),
|
||||
)
|
||||
val options =
|
||||
CliEvaluatorOptions(
|
||||
CliBaseOptions(sourceModules = listOf(moduleUri), workingDir = tempDir),
|
||||
multipleFileOutputPath = ".output",
|
||||
)
|
||||
assertThatCode { evalToConsole(options) }
|
||||
.hasMessageStartingWith("Output file conflict:")
|
||||
.hasMessageContaining("which is outside output directory")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `multiple file output throws if file path is a directory`() {
|
||||
tempDir.resolve(".output/myDir").createDirectories()
|
||||
|
||||
Reference in New Issue
Block a user