Spec reads user-provided spec files (YAML, TOML, JSON) and validates them against a JSON Schema. It also executes shell commands indirectly through the Kujo runtime for validation, rendering, and export operations. The following protections are in place:
Filenames are passed to Kujo and Python helpers as arguments (not interpolated into executable source), eliminating the command injection vector that existed in earlier inline Python code.
The is_safe_path() function blocks:
- Paths containing
..directory traversal - Absolute paths outside the current working directory
Symlink trust boundary policy:
- Symlinks are allowed only when their resolved target remains inside the current project directory.
- Symlinks that resolve outside the project root are denied.
The --name parameter in spec init and spec init-interactive is validated against a safe character class ([a-zA-Z0-9 _-]+), rejecting quotes, backticks, and other special characters. Interactive goal and tag values are serialized as data instead of being interpolated into YAML.
The Kujo validate module enforces a 1MB maximum file size before parsing spec content.
Spec input conversion uses Kujo's parse_yaml, parse_toml, and parse_json functions. Spec content is never evaluated as executable code. Interactive YAML creation uses yaml.safe_dump() when PyYAML is available and a JSON-compatible YAML subset otherwise.
When safe-write mode is enabled (SPEC_SAFE_WRITE=on or auto in spec ci context), output-producing commands restrict writes to the current project directory.
- Blocked by default: output paths outside project root (including resolved symlink escapes)
- Allowed by explicit intent: pass
--unsafe-writeto override for a single command
Mermaid and DOT graph labels are escaped before untrusted spec names are embedded. Render and export commands fail closed when the Kujo runtime fails or returns no output.
Conversion caches use a per-user directory with mode 0700. A symlinked or otherwise unsafe cache directory disables caching for that operation.
Docker builds that download a Kujo runtime with KUJO_BIN_URL must also supply KUJO_BIN_SHA256; the build verifies the binary before making it executable.
- YAML parsing: Complex YAML features (anchors, tags, multi-document) are outside the supported subset and may produce parse errors.
- TOML parsing: Nested tables and arrays of tables remain outside the supported subset.
- Kujo runtime: The validate/render/export modules run via the Kujo language runtime, which may have its own security characteristics.
- No sandboxing: Spec processing runs with the user's full permissions. Filesystem access is required for reading spec files.
- Temp files: Temporary JSON files are created during processing (via
mktemp). These are cleaned up on script exit. - Path boundary scope: Path checks are anchored to the caller's current working directory; run commands from the intended project root for strictest boundaries.
To report a security issue, please open an issue on the GitHub repository at: https://github.com/kujolang/spec
Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
Please allow reasonable time for fixes before public disclosure. Security issues will be addressed as priority.