OMK is pre-1.0 and ships from master only. The latest tagged GitHub release
is the supported version; older tags are not back-ported.
| Version | Supported |
|---|---|
| Latest tagged release | ✅ |
| Older tags | ❌ |
If you discover a security vulnerability in OMK, please report it responsibly:
- Do not open a public issue.
- Email ekhodzitsky@gmail.com with:
- A description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if any)
We acknowledge receipt within 48 hours and provide a timeline for a fix.
omk is a local orchestrator: it spawns external CLIs (Kimi, advisors,
gates) on behalf of a human operator and persists evidence on the local
filesystem. The trust model is intentionally narrow.
| Boundary | Trust level | Why |
|---|---|---|
| Source code in this repository | Trusted | Reviewed by maintainers via PR. |
~/.config/omk/, ~/.local/state/omk/, ~/.local/share/omk/, ~/.cache/omk/ (or the $XDG_* overrides) |
Trusted | Owned by the human running omk; created with 0700/0600. |
.omk/gates.toml and other per-project config inside the working tree |
Trusted | Lives in the repo you already trust; treat changes to it as you would treat any code change. |
| User-provided prompts, goals, task descriptions | Untrusted | Never spliced into shell strings, never expanded as paths. See "Untrusted Input Handling" below. |
| Agent (Kimi CLI) output and proposed tasks | Untrusted | Validated through goal task policy before any scheduling. |
External advisor CLIs (claude, codex, gemini) |
Untrusted | Spawned via argv (no shell interpreter); output is captured but not executed. |
MOCK_KIMI env var |
Trusted in dev/test, must not be set in production | Replaces the kimi binary path; documented as a test seam only. |
XDG_* env vars |
Trusted | Belong to the same user who runs omk. |
OMK_GOAL_INTERRUPT_POLL_MS, OMK_GOAL_AGENT_LEASE_SECS |
Trusted, numeric only | Parsed as bounded integers; invalid values fall back to defaults. |
- Local secrets in env vars, shell history, dotfiles, and
~/.ssh. - Wire / event log evidence persisted under
~/.local/state/omk/— must not leak credentials reviewers see by accident. - The repository working tree — must not be mutated outside the
project root or in
.git/.githubmetadata. - CI tokens in GitHub Actions — must not be readable by build steps that don't need them.
| Threat | Mitigation |
|---|---|
| Prompt-injected shell command via advisor or goal description | Advisors spawn via Command::new(provider).arg("-p").arg(prompt) (no bash -c). Gates and git use direct argv (Command::new("git").args(...)). The only remaining shell use is shlex::try_quote for human-displayed commands. |
| Path traversal in goal artifacts | runtime::sanitize::sanitize_name/resolve_safe_path reject .., /, \, :, leading dots, oversize names. Goal worktree branch/path components are normalized in runtime/goal/worktree.rs. |
| Agent proposes write to CI metadata or git internals | is_safe_goal_agent_path rejects any path component beginning with .git (covers .git/, .gitignore, .gitmodules, .gitattributes, .github/workflows/..., .gitlab-ci.yml) plus absolute paths, ~, control characters, and parent-dir traversal. |
| Secret-scanner exfiltration via symlinked file | runtime/goal/verifier.rs::scan_goal_security_findings canonicalizes each candidate and skips any path that resolves outside the canonicalized project root. |
| Credentials leaking into wire / event logs | wire::protocol::redact::redact_wire_secrets redacts on both well-known keys (api_key, token, authorization, *_secret, …) and known value shapes (GitHub PATs, AWS access keys, Slack tokens, Stripe keys, Bearer headers, PEM private-key blocks). Redaction is idempotent. |
| Untrusted file permissions on state | runtime/config::ensure_private_dir chmods 0o700; runtime/atomic::atomic_write chmods 0o600 (Unix). State paths default to $XDG_STATE_HOME/omk/. |
| Untrusted dependency or registry pulled in by a transitive crate | deny.toml denies unknown registries and unknown git sources, and runs in CI through cargo-deny on every PR. |
GitHub Actions step exfiltrates GITHUB_TOKEN |
All workflows declare an explicit permissions: block (least-privilege), and actions/checkout is invoked with persist-credentials: false. The release workflow scopes contents: write to the release job only; build jobs run with contents: read. |
- Trojaned dependency at the registry source. Once a dependency is
published to crates.io, we have no way to detect malicious code in its
source. We mitigate the blast radius through
cargo-denyadvisories, a pinnedCargo.lock, and code review of dependency bumps, but a compromised upstream is still a viable attack. - Compromised host running
omk. Local state is hardened to0700/0600but the user's shell/env is fully trusted. A compromised shell sees everythingomksees. - Mocked Kimi binaries. Setting
MOCK_KIMI=/path/to/binarylets a caller substitute an arbitrary executable forkimi. This is documented as a test-only seam; production deployments must not set it. - Out-of-band agent side effects. Once Kimi receives a prompt, omk
controls only the stated intent (
read_set/write_set/is_safe_goal_agent_path), not the actual filesystem operations the agent performs. Defense lies in prompt boundaries (do-not-commit/do-not-publish reminders) plus the per-task mutation diff captured for review.
| Input source | Sink | Defense |
|---|---|---|
| Goal text from CLI | Prompts to Kimi, persisted artifacts | Whitespace-normalized; never spliced into shell strings. |
| Agent task proposals | Scheduler tasks, prompts, write-set enforcement | validate_goal_agent_task_proposals rejects empty/dup ids, publishing intents, and any read/write path that fails is_safe_goal_agent_path. |
Changed-file list from git diff |
Security review scanner | safe_project_file_path rejects absolute / parent / prefix components; the scanner canonicalizes and refuses files that escape the project root. |
| Advisor prompts | Command::new(provider).arg("-p").arg(prompt) |
Argv-mode invocation; no shell interpreter; provider name is gated against ALL_PROVIDERS. |
Gates config (.omk/gates.toml) |
Command::new(gate.command).args(gate.args) |
Gate config is trusted (lives in the repo). Direct argv prevents shell injection through gate output. |
Cargo.lockis committed and reviewed alongside any dependency bump.deny.tomlenforces:advisories.yanked = "warn"— yanked crates surface as warnings;advisories.ignoreis small and annotated with the upstream reason.sources.unknown-registry = "deny"andsources.unknown-git = "deny"— only crates.io is sanctioned.licenses.allowis an allow-list (nodeny/unknownlicense slips in by accident).
- CI runs
cargo deny --all-features check advisories licenses sourceson every PR via.github/workflows/ci.yml.
- Every workflow declares an explicit top-level
permissions:block. Workflows that do not need write access pin tocontents: read. The release workflow declarescontents: readat the workflow level and scopescontents: writeto the publish job only. - Every
actions/checkoutinvocation setspersist-credentials: falseso theGITHUB_TOKENis not left on disk for downstream steps to read. cargo install cargo-tarpaulinis pinned with--locked --version 0.35.4so a tarpaulin compromise cannot trivially target our coverage step.- Non-GitHub third-party actions are pinned to a commit SHA
(
dtolnay/rust-toolchain,EmbarkStudios/cargo-deny-action,codecov/codecov-action). GitHub-owned actions (actions/checkout,actions/cache) andtaiki-e/install-actionare pinned to a major-version tag or reference.
taiki-e/install-actionand GitHub-owned actions (actions/checkout,actions/cache) are pinned by version tag or reference, not commit SHA. A malicious force-push or compromised maintainer at the action repository would still be picked up. Track full SHA pinning as a future tightening pass.- The wire value-pattern redactor is best-effort — a high-entropy random
string that does not match the curated patterns will still be persisted.
Operators who handle exotic token formats should add a redaction pattern
in
src/wire/protocol/redact.rsbefore persisting logs. MOCK_KIMIis honoured at runtime. There is no separate "release build" that disables the env-var test seam; production deployments must rely on operational hygiene instead.
omkruns local commands and Kimi processes. Always validate input withvalidate_safe()before passing data to shell-command helpers.omkusesshlex::try_quoteonly for human-displayed command rendering. Process spawn uses argv (Command::new(...).args(...)) so a future regression inshlexcannot become an RCE.- State files may contain sensitive task descriptions. Ensure
~/.local/state/omk/has appropriate permissions (0700);omkenforces this on every write.