Threat Model

Last edited: 2026-06-08

This document describes the threat model for the Pkl project. It is intended to help security researchers and reporters understand what we consider to be within scope and assess whether a potential vulnerability is relevant to Pkl’s security goals. For information on how to report a vulnerability, see Security.

What we are working on

Pkl is a programming language meant for generating configuration data. It is either a binary executable that runs on a machine, or a library that gets embedded into another runtime.

The Pkl language is:

  • Idempotent (the same input always produces the same output)

  • Declarative

  • Short-lived (a program is designed to run to completion)

    • Note: the language is turing complete. We address the halting problem via timeouts.

  • Lazy (only expressions in the critical path of the program’s output are evaluated)

Alongside the main runtime, we also work on the suite of tooling that supports the language. The tooling includes:

  • Evaluator clients and bindings, like pkl-swift and pkl-go, rules_pkl, and pkl-spring.

  • IDE tooling and libraries, like pkl-intellij, pkl-vscode, and pkl-lsp.

  • Meta tooling for Pkl, like pkl-parser, tree-sitter-pkl, pkl-doc, and pkl-textmate.

  • Libraries written in Pkl, like pkl-pantry and pkl-k8s.

  • Libraries that extend Pkl’s I/O capabilities, in pkl-readers.

The tools and languages are available as open-source repositories on GitHub. The list grows over time, and is available here: https://github.com/apple/pkl#pkl-github-repositories.

In addition to our open-source repositories, we also maintain:

  • https://pkl-lang.org - This website.

  • https://pkg.pkl-lang.org - A redirect server used for Pkl packages.

Glossary

Allowlist

A list of regular expression patterns configured by the Evaluator Client that controls which URIs the Language Runtime may access. Separate allowlists exist for modules (import) and resources (read).

Cache directory

A local directory (default: ~/.pkl/cache) where the runtime stores downloaded package contents. Configurable via --cache-dir.

CLI

The pkl command-line executable. A standalone binary that acts as an Evaluator Client.

Evaluator Client

Software that configures evaluator settings and invokes the Language Runtime. Either the pkl CLI or a language binding library (Java, Go, Swift, Kotlin, etc.).

External Reader

A process that handles a custom URI scheme on behalf of the Language Runtime. Registered by the Evaluator Client; communicates with the runtime via a message-passing protocol over stdio.

I/O

Input/Output — any operation that reads from or writes to an external resource (filesystem, network, environment variables, etc.).

Language Runtime

The Pkl evaluator. Receives an entrypoint module and evaluator settings, evaluates the module, and returns output.

Module

A single Pkl source file (.pkl). The basic unit of Pkl code.

Package

A versioned, addressable collection of Pkl modules, identified by a URI (e.g., package://pkg.pkl-lang.org/pkl/pkl-config@1.0.0). Package contents are integrity-verified by SHA-256 checksum (see Package Integrity).

pkg.pkl-lang.org

A redirect server operated by the Pkl team that resolves package: URIs to their canonical hosting location (e.g., GitHub Releases).

pkl-lsp

The Pkl Language Server, implementing the Language Server Protocol. Used by IDE plugins to provide editor features such as completions and diagnostics.

pkl-pantry

A collection of first-party Pkl packages maintained by the Pkl team.

pkl-readers

A library of first-party external reader implementations that extend Pkl’s built-in I/O with custom URI schemes.

PklProject

A file that defines project-level configuration for a Pkl codebase, including package metadata, package dependencies, and evaluator settings.

PklProject.deps.json

The dependency list, generated by pkl project resolve. Contains resolved versions and SHA-256 checksums for all declared package dependencies.

Root directory

A filesystem boundary configured via --root-dir that restricts file: reads to files within that path.

It does not affect package caching, external readers, or CLI behaviors such as loading of settings.pkl, PklProject/PklProject.deps.json, and multiple file output.

System Description

The Pkl system is composed of the following:

Components

Language Runtime

The Pkl language runtime itself. Inert until invoked; only evaluates the entrypoint and its transitive dependencies.

Evaluator Client

Configures evaluator settings and invokes the runtime. Either a standalone CLI (pkl) or as a library (Java, Go, Swift, Kotlin, Gradle, Bazel).

Pkl Libraries

Libraries written in Pkl, executed within the language runtime. They inherit the I/O permissions of the enclosing evaluation.

IDE Tooling

Plugins and tooling that run within the context of a user’s source code editor. Operates with the user’s full filesystem access and may invoke the Language Runtime.

External Actors

User

A person or system that invokes the Evaluator Client. Includes developers running the CLI directly, CI/CD pipelines, and applications embedding a Pkl library.

Package Author

An entity that publishes a Pkl package to a registry. Distinct from a User because they supply modules that other evaluations may transitively import.

Package Server

An external system that hosts and serves packages consumed by Pkl (e.g., pkg.pkl-lang.org, or an arbitrary HTTPS endpoint).

External Resource

Any external data source read by Pkl via read or import (e.g., HTTPS endpoints, local filesystem, environment variables). Not a human actor, but a source of untrusted input.

Publish Pipelines

The GitHub Actions pipelines that publish artifacts.

The following diagram demonstrates the flow of a common use-case, where the CLI is used to produce configuration output. This demonstrates two commands executed by the User, producing different results.

pkl cli sequence diagram

Boundaries

Execution-time

  • User → Evaluator Client: The User configures what the runtime is permitted to do — allowed modules, readers, and external resources. All runtime I/O permissions are established here; Pkl code cannot exceed them.

  • Evaluator Client → Language Runtime: The client invokes the runtime with a fixed set of evaluator settings. The runtime must enforce those settings; Pkl code must not be able to override or escape them.

  • Language Runtime → External Resource: During evaluation, the runtime may read external resources via read or import, gated by evaluator settings.

  • Pkl module → Pkl module: A module that imports another module cannot grant it more trust than it has itself. For example, a module imported from HTTPS cannot transitively import a module from the local filesystem.

Package resolution

  • Language Runtime / IDE Tooling → Package Server: When fetching a package, the runtime or IDE tooling contacts a package server over HTTPS. pkg.pkl-lang.org is a redirect server — it does not serve packages directly, but redirects to the canonical host (e.g., GitHub Releases). Package integrity is verified by checksum regardless of which server ultimately serves the content.

Supply chain

  • Publish Pipelines → Registries: GitHub Actions pipelines hold the credentials and signing keys used to publish artifacts to Maven Central, GitHub Releases, and NPM. Compromise of the pipeline or its credentials could result in malicious artifacts published under the Pkl team’s identity.

IDE tooling

  • IDE Tooling → Language Runtime: The IntelliJ plugin and pkl-lsp invoke the Language Runtime to evaluate Pkl files open in the editor. This runs with the user’s full filesystem access and operates on potentially untrusted files (e.g., from third-party repositories).

Pkl’s I/O Permission Model

The Language Runtime enforces I/O permissions set by the Evaluator Client before evaluation begins. No Pkl code can modify these settings at runtime.

Allowlists

The Evaluator Client configures two allowlists:

  • Allowed modules - regular expression patterns controlling which modules may be imported via import.

  • Allowed resources - regular expression patterns controlling which resources may be read via read.

If a read or import expression resolves to a URI that does not match the corresponding allowlist, the runtime throws an error rather than performing I/O.

URI Schemes

Pkl identifies all resources and modules by URI. The scheme determines what kind of I/O is performed.

The Pkl evaluator provides the following schemes baked-in:

Scheme Description

file: and jar:file:

Local filesystem access. Controlled by the allowed modules/resources allowlists.

http: / https:

Remote resource fetched over HTTP(S). Controlled by the allowed modules/resources allowlists.

package: / projectpackage:

A Pkl package, resolved either via HTTPS or file system. Integrity is verified by checksum (see Package Integrity).

pkl:

Built-in Pkl standard library modules. Always available; not subject to allowlist restrictions.

env:

Environment variable. Controlled by the allowed resources allowlist.

prop:

External property. Controlled by the allowed resources allowlist.

modulepath:

A set of directories or zip files, as specified by the modulepath setting. Controlled by the allowed modules/resources allowlists.

In addition to these, Pkl can understand other schemes by:

  • Custom readers - a custom reader tells Pkl what its scheme is, and how it performs I/O. The custom reader can either be implemented directly in the host runtime, or as an external process.

  • (JVM only) Generic URL - When Pkl is run in the JVM, it can read generic URLs that Java’s java.net.URL API understands.

File system reads

In addition to the allowlist controls, the import and read operators can only read files that exist inside the configured --root-dir.

By default, the CLI does not set a root dir, and the entire file system is available to read.

One exception to this rule is: package imports will read files from within the --cache-dir.

File system writes

Pkl can write to the specified --cache-dir, or defaulting to ~/.pkl/cache, unless the --no-cache option is specified.

Outside the language runtime, our tooling also writes files to the file system.

For example, the CLI writes to the file system based on the command and flags set, as documented by https://pkl-lang.org/main/current/pkl-cli/index.html#usage.

Custom Readers

The Evaluator Client may register custom readers that handle custom URI schemes not natively supported by the runtime or overload built-in URI schemes.

A custom reader can either be:

  • A direct API call from Pkl to the host language, when Pkl is embedded.

  • An external reader, an external process that is spawned.

A compromised custom reader can run malicious code within the permissions of the OS process.

Package Integrity

When downloading a package, the runtime verifies the package’s contents against a SHA-256 checksum. This happens in one of three ways:

  1. The package is a remote project dependency, and its checksum is stored inside the PklProject.deps.json file.

    When the project is resolved, the checksum is observed and recorded. This implies "trust on first use" semantics.

  2. The package is a remote project dependency, and its checksum is declared inside the PklProject file.

    When the project is resolved, the package’s checksum is verified.

    PklProject
    amends "pkl:Project"
    
    dependencies {
      ["pkg"] {
        uri = "package://example.com/pkg@1.0.0"
        checksums {
          sha256 = "abc123"
        }
      }
    }
  3. The package is a direct import, and its checksum is inside the import URI string. For example:

    import "package://example.com/pkg@1.0.0::sha256:abc123#/myMod.pkl"

This verification is independent of which server served the package, providing integrity guarantees even if the package server is compromised or the connection is intercepted.

The runtime does not verify the checksum of packages inside the cache dir; this type of attack is out of scope.

One exception to this is direct package imports that lack a sha256 checksum segment.

The following import does not verify package integrity:

import "package://example.com/pkg@1.0.0#/myModule.pkl"

Transitivity

Allowlists apply transitively. A module imported from HTTPS may only read resources permitted by the enclosing evaluation’s allowlist — it cannot grant access to resources outside that allowlist. For example, a remote module cannot read from the local filesystem unless file: URIs are explicitly permitted.

Assets

Pkl publications are distributed to the following locations:

  • Maven Central

    • Pkl’s Java libraries

    • Pkl’s CLIs

    • pkl-lsp

  • GitHub Releases

    • Pkl’s CLIs

    • pkl-lsp

    • IntelliJ plugin

    • VSCode plugin

    • Pkl packages (libraries written in Pkl)

  • NPM

    • Pkl’s JavaScript libraries, including tree-sitter-pkl

Publishing happens using GitHub Actions.

Security Goals

  • Pkl cannot perform I/O outside what is explicitly allowed.

  • Pkl code cannot modify or exceed the permissions established by the Evaluator Client.

  • Pkl cannot execute arbitrary system commands or load native code.

  • Pkl will run to completion, or time out.

  • The CLI only writes to filesystem locations as documented for each command.

  • Published artifacts are authentic.

  • Imported package dependencies are authentic.

  • Sandboxing flags allow un-trusted Pkl code to be executed safely.

Attacker Profiles

Remote Attacker

An external party with no special access. Can interact with public-facing services, submit Pkl code to any publicly-accessible evaluator, and craft malicious inputs.

Malicious Module Author

An attacker who can supply Pkl modules that run within other users' evaluations, either by publishing a package or contributing to a dependency. Their code runs with the I/O permissions of the enclosing evaluation.

Network Attacker

An attacker who can intercept traffic between the Pkl runtime and external services. A passive network attacker can observe but not modify traffic (blocked by TLS). An active network attacker can perform DNS hijacking or man-in-the-middle if they can compromise TLS.

Compromised Insider

An attacker who has obtained the Pkl team’s CI/CD access or publishing credentials, whether through account compromise or social engineering. Can publish artifacts under the Pkl team’s identity.

Threats

Language Runtime

I/O Sandbox Escape

A crafted Pkl program exploits a bug in the language runtime to perform I/O outside the allowlists established by the Evaluator Client. This could allow a Pkl module to read files outside --root-dir, access network endpoints not in the allowed list, or read environment variables the caller did not intend to expose.

Attacker: Remote Attacker; Malicious Module Author
Violates: Pkl cannot perform I/O outside what is explicitly allowed

Allowlist Bypass via Crafted URI

A crafted URI causes an allowlist regex to match more broadly than the operator intended. For example, a pattern meant to allow https://example.com/ also matches https://example.com.evil.com/ due to a missing anchor. The runtime is the last line of defense against such gaps.

Attacker: Remote Attacker; Malicious Module Author
Violates: Pkl cannot perform I/O outside what is explicitly allowed

Arbitrary Code Execution

A crafted Pkl program exploits a memory-safety bug or other vulnerability in the runtime to execute arbitrary native code within the host process.

Attacker: Remote Attacker; Malicious Module Author
Violates: Pkl cannot execute arbitrary system commands or load native code

Resource Exhaustion

A crafted Pkl program exhausts CPU and/or memory resources on the host machine.

Pkl currently does not have sandbox flags that limit CPU/memory usage. Users that wish to guard against resource exhaustion attacks must rely on external means, e.g. running within a hypervisor.

Attacker: Malicious Module Author
Violates: Sandboxing flags allows un-trusted Pkl code to be executed safely

Cache Poisoning

A bug in the runtime causes valid cached packages to be overwritten with malicious content.

Attacker: Malicious Module Author
Violates: Imported package dependencies are authentic

Package Resolution

Tampered Package

A malicious or compromised Package Server serves a modified version of a Pkl package. If the import URI does not include a SHA-256 checksum segment, the runtime performs no integrity verification and Pkl code within the tampered package is evaluated.

Package imports that include a checksum in the URI are verified against the declared hash. Direct imports without a checksum (e.g., package://example.com/pkg@1.0.0#/mod.pkl) are not verified.

Attacker: Network Attacker
Violates: Imported package dependencies are authentic

Supply Chain

Compromised Publish Pipeline

A compromised GitHub Actions pipeline or stolen registry credentials allows an attacker to publish a malicious artifact under the Pkl team’s identity to Maven Central, GitHub Releases, or NPM. Downstream users would receive and trust the malicious artifact.

Attacker: Compromised Insider
Violates: Published artifacts are authentic

pkg.pkl-lang.org Compromise

The pkg.pkl-lang.org redirect server is compromised, or its DNS is hijacked, causing package resolution requests to be redirected to attacker-controlled servers. Users whose package imports do not include checksums would fetch and execute malicious packages without any verification failure.

Attacker: Remote Attacker; Network Attacker
Violates: Imported package dependencies are authentic

IDE Tooling

Arbitrary Code Execution

A crafted Pkl program exploits a memory-safety bug or other vulnerability to execute arbitrary code within the IDE’s process.

Attacker: Malicious Module Author
Violates: Pkl cannot execute arbitrary system commands or load native code

Out of Scope

The following are real risks but are outside the Pkl team’s control and are not threats we can mitigate directly.

Third-party infrastructure

The Pkl supply chain relies on GitHub (source hosting and CI/CD), Maven Central, NPM, and GitHub Releases as trusted third-party services. The security of these platforms is governed by their respective operators.

Underlying runtime vulnerabilities

Pkl runs on top of GraalVM, the JVM, the Go runtime, or the Swift runtime depending on the embedding.

Any vulnerabilities in these runtimes must be fixed by their respective owners.

Operating system and hardware

Attacks that require local access to the machine running Pkl, OS kernel vulnerabilities, or hardware-level attacks.

Insecure Evaluator Client configurations

A User who configures overly broad evaluator permissions while executing un-trusted Pkl code. Some examples:

  • Allowing reads of env: resources.

  • Not configuring a root dir.

Malicious external readers

External readers are executables that run directly on a user’s machine. If a User downloads and configures the evaluator to run a malicious external reader, they are vulnerable to whatever the external reader is programmed to do.