Skip to content

Security: cortexkit/basal

SECURITY.md

Security

Reporting a vulnerability

Report a vulnerability privately through GitHub's private vulnerability reporting for this repository: on cortexkit/basal, open Security, then Report a vulnerability. Please don't open a public issue or pull request for it. Include the affected commit, the operating system, and the steps or flow script that reproduce the problem.

Threat model

basal runs on the operator's own machine as a module supervised by the subc daemon. Its security boundary is between the scripts it runs and everything else; the details below summarise docs/sandbox.md, docs/script.md and docs/builtins.md, which are authoritative.

What basal defends

  • Scripts are untrusted. Every flow and codemode script is untrusted input to a C JavaScript engine (QuickJS), including a flow script the operator has approved. basal assumes the engine can be subverted, and runs it in ck-basal-worker, a separate process that holds no durable state and is confined so that a subverted worker can do little more than talk to its parent over stdin and stdout (docs/sandbox.md).
  • Confinement on each operating system. On macOS the worker applies a deny-by-default Seatbelt profile to itself, both binaries are signed with the hardened runtime and no entitlements, and the worker is launched detached from the module's privacy (TCC) grants. On Linux the boundary is a seccomp system-call allowlist that kills the worker on any other call, with Landlock, no_new_privs and non-dumpable as further layers. On Windows the launcher creates the worker with a Less Privileged AppContainer (LPAC) restricted token, an owned job and process mitigations, and the worker lowers its token to Untrusted and attests its token, mitigations and handle table before reading any input. The parent accepts a worker only if its first message reports the confinement its operating system requires.
  • Every host call is checked in the parent. A script reaches the world only through host calls. The parent checks each call against the manifest of the exact version the operator approved, before journaling it for dispatch; for the file, git and network built-ins the host checks the journaled scope again before acting (docs/builtins.md). The code hash, and so the consent card, covers the script's exact text, so an edited script needs a new approval (docs/script.md, docs/manifest.md).
  • Caller identity comes from the daemon. Agent-facing operations and codemode take the caller's agent and scope from the daemon's route stamp, never from request arguments, and each tool a codemode program calls follows the same provider permission rules as the agent's direct call (docs/ops.md).

What basal does not defend

  • A local process running as the same user. basal's boundary is between the worker and the rest of the machine. It does not protect its store, binaries, configuration or approved roots from another process running as the operator's user. For example, the git built-ins check repository indirection when a call starts, but a local process that can change repository metadata can change it before git reads it (docs/builtins.md).
  • The operator's own approvals. basal enforces the grants in an approved manifest; it does not judge whether they are wise. A script can use everything its manifest grants, and a codemode program can call the agent's tools under the same permission rules as the agent's direct calls.
  • The residual risks the docs record. These include: on macOS, thread creation is not blocked, and there is no published claim about the Mach rights or executable memory of the worker; on macOS and Windows, CPU used by extra threads of a subverted engine is bounded only by the wall-clock deadline and idle retirement; Linux workers run without Landlock only when the operator configures that optional policy and the kernel lacks Landlock; the Windows non-claims about direct AFD access, KnownDlls-relative section opens, residual handles and AppContainer profile reuse (docs/sandbox.md); the file built-ins' remaining window between open and check (docs/builtins.md); and the script runtime's two deliberate limits, that the script body is not a syntactic boundary and that a script can make its own failure look like a stack overflow (docs/script.md).

There aren't any published security advisories