akm-cli
Version:
akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cur
68 lines (67 loc) • 3.56 kB
JavaScript
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
/**
* `EDITOR`/`VISUAL`/`PAGER` are flagged by `isDangerousEnvKey` for a
* documented RCE vector (many tools invoke them to launch an editor/pager),
* but env-key-rules.ts's own module doc calls out their "high FP rate" in
* the same breath — an installed bundle's env file can supply a value, but
* nothing in akm's own env-injection path ever *invokes* EDITOR/VISUAL/PAGER
* with that value, so the RCE vector these three describe cannot fire from
* an injected env the way LD_PRELOAD or GIT_SSH_COMMAND can. Blocking a
* third-party install over them protects nothing while making "the operator
* legitimately wants to set their editor" the common case that eats the
* refusal. Kept as a name-level literal set (not an import of
* `commands/lint/env-key-rules.ts`) so this module stays the pure leaf its
* own doc comment promises — no new import edges into the rest of the tree.
*/
const INTERACTIVE_TOOL_ENV_KEYS = new Set(["EDITOR", "VISUAL", "PAGER"]);
/**
* Decide whether injecting an env with the given dangerous keys is allowed,
* warned, or blocked. Third-party (registry-installed) stashes hard-block a
* genuine RCE-class key; first-party stashes warn. The interactive-tool
* group (see {@link INTERACTIVE_TOOL_ENV_KEYS}) only ever warns, since akm's
* own env-injection path never invokes those keys as a command. An explicit
* `--allow-dangerous-env-keys` (threaded through by the caller, same override
* `decideDangerousKeyInstall`'s `"warn-allow"` already honors for rule 2)
* downgrades a remaining block to a warning too — the operator is not racing
* themselves. See rule 1 above.
*
* @param dangerousKeys The subset of injected keys flagged as process-hijacking
* (already filtered by the caller via `isDangerousEnvKey`).
* @param thirdParty `true` when the env's source is a third-party stash — i.e.
* its origin carries a `registryId`.
* @param allowDangerousEnvKeys `true` when the operator passed `--allow-dangerous-env-keys` (or
* its equivalent) for this injection. Defaults to `false`.
*/
export function decideDangerousEnvInjection(input) {
if (input.dangerousKeys.length === 0)
return "allow";
if (!input.thirdParty)
return "warn";
if (input.allowDangerousEnvKeys)
return "warn";
const onlyInteractiveTool = input.dangerousKeys.every((key) => INTERACTIVE_TOOL_ENV_KEYS.has(key));
return onlyInteractiveTool ? "warn" : "block";
}
/**
* Decide the baseline install stance for a freshly-installed stash's
* dangerous-key scan. The interactive confirm / rollback UX is applied by the
* caller on top of the `"gate"` stance — this predicate only fixes the policy.
* See rule 2 above.
*/
export function decideDangerousKeyInstall(input) {
if (!input.findingsPresent)
return "allow";
return input.allowDangerousEnvKeys ? "warn-allow" : "gate";
}
// ── Rule 3: write activation (search-source.ts, installations.ts) ────────────
/**
* Whether a resolved source is write-activated. Only the primary stash and
* sources explicitly marked `writable: true` are writable; registry-cached
* (installed, read-only) sources are never written in place because
* `akm update` overwrites them. See rule 3 above.
*/
export function isSourceWriteActivated(source) {
return source.writable === true;
}