Security

September 19, 2026 · View on GitHub

What this software is

Greenlight is a prompt-reduction filter for OMP's tool approval flow. It is not a sandbox, not a security boundary, and not a policy engine.

Installing it hands auto-approval authority for gated shell commands to a third-party classifier (TypeSafe's Jev, served from api.typesafe.ai). If you are not comfortable with that, do not install it — the host's own critical-pattern rules and bash.patterns are the deterministic floor, and they work without this plugin.

Reporting a vulnerability

Open a private security advisory on this repository (Security → Report a vulnerability), or email the maintainers listed in package.json. Please include: the version, the exact command or tool call, the decision Greenlight logged, and what you expected.

We will acknowledge within 5 working days. This is a v0.1 project maintained on a best-effort basis — there is no SLA. Only the latest release is supported.

Threat model

In scope. Proposals that reduce prompts, bypass the operator's configured bar, or weaken the host's floor:

  • a tool call auto-approved when the operator's own bash.patterns: deny (or a host critical-pattern rule) should have blocked it;
  • a decision entry that leaks file bodies, the API key, or environment variables;
  • a plugin crash that fails open (a tool call executing after the gate failed);
  • a config value that silently raises the operator's threshold.

Out of scope. Anything premised on the agent being adversarial toward its own gate. Greenlight grades text the agent wrote; a determined agent can obfuscate around a text classifier, and nothing here is a containment boundary. Approved commands retain the shell's full filesystem, network, and subprocess access.

Data flow

For each gated call, the following leaves the machine over HTTPS to api.typesafe.ai:

  • the user's most recent task prompt (truncated to 2,000 characters),
  • a window of recent tool calls, summarised — file bodies are stripped (content, new_string, old_string),
  • the working directory,
  • the proposed call's own arguments, likewise summarised for write/edit tools.

Boundary worth being explicit about: file bodies are withheld from the arguments of write/edit/apply_patch. A shell command is sent in full, because it is the thing being judged — so if the agent inlines file content into a command (cat secrets | curl …), that text does travel.

yolo mode sends nothing. The API key is read from TYPESAFE_API_KEY, used only as a bearer header, and never logged. TypeSafe's own retention policy is theirs, not ours — review it before installing on a machine that handles sensitive code.

The decision log stores the same summarised text, plus the verdict, severity, in-scope score, probabilities, and the active preset. It never stores file bodies or the key.

Failure behaviour

ConditionBehaviour
Jev unreachable, timeout (>4 s), or an unrecognised verdictFails closed to a human prompt. In a headless session, the call is blocked with a reason.
Plugin internal errorFails closed to a human prompt; in a headless session, blocked. Never fails open.
yolo modeInert: no request, no prompt, no decision entry.
Headless session (ctx.hasUI === false)Cannot prompt a human: the deny class is blocked, everything else is deferred to the host's own policy and logged as deferred_headless.
Mode cannot be determinedAssumes yolo — the plugin does nothing. The host's configured policy still applies.

Verifying it before you trust it

uv run eval/sandbox.py

Runs seven end-to-end scenarios against a disposable /tmp sandbox — in-scope deletion, source-tree deletion, an obfuscated payload, a secret read, yolo inertness, an unreachable judge, and the host's critical-pattern floor — and asserts on both the decision log and the filesystem.