registry  /  @lmctl-ai/lmctl  /  0.1.97

@lmctl-ai/lmctl@0.1.97

A provider-agnostic control plane for teams of AI coding agents — across providers, with independent review and durable memory.

AI Security Review

scanned 2h ago · by lpm-firewall-ai

LPM treats this as warn-only first-party agent extension lifecycle risk. Explicit lmctl team/session setup can modify AI-agent MCP configuration files, and explicit workflows can launch provider CLIs or shell commands. No install-time execution or unconsented lifecycle mutation was found.

Static reason
One or more suspicious static signals were detected.
Trigger
User runs lmctl team/session/workflow commands.
Impact
Can alter configured MCP integrations and execute workflow-defined commands in the user's selected project/session context.
Mechanism
CLI-managed MCP setup and user-invoked agent/workflow process execution.
Rationale
Flag as warn because explicit commands can mutate AI-agent MCP configuration and execute workflow-defined commands. The evidence does not establish malicious install-time behavior or covert exfiltration.
Evidence
package.jsonbin/lmctldist/cli/index.js.mcp.json.opencode/opencode.json~/.copilot/mcp-config.json~/.lmctl/.update-check.json
Network endpoints2
lmctl.com/templates/registry.npmjs.org/@lmctl-ai/lmctl/latest

Decision evidence

public snapshot
AI called this Suspicious at 88.0% confidence as Dangerous Capability with low false-positive risk.
Evidence for warning
  • `dist/cli/index.js` writes MCP configuration into `.mcp.json`, `.opencode/opencode.json`, and `~/.copilot/mcp-config.json` during team/session setup.
  • `dist/cli/index.js` executes provider CLIs and user-supplied workflow shell steps, including `sh -c`.
  • Remote workflow loading fetches user-supplied HTTPS URLs; execution is exposed by `workflow run`.
  • The package reads AI-agent session files under `~/.claude`, `~/.codex`, and related provider directories.
Evidence against
  • `package.json` has no `preinstall`, `install`, `postinstall`, or `prepare` lifecycle script.
  • `bin/lmctl` only invokes `runCli` after an explicit CLI launch.
  • Remote artifact retrieval enforces HTTPS, refuses redirects, validates shape, and supports SHA-256 pins.
  • Diagnostic upload paths are explicit CLI options and source labels both S3 and HQ upload implementations as stubs.
  • No confirmed credential harvesting or active outbound exfiltration chain was found in inspected source.
Behavioral surface
Source
ChildProcessEnvironmentVarsFilesystemNetworkShell
Supply chain
HighEntropyStringsUrlStrings
Manifest
NoLicense
scanned 1 file(s), 835 KB of source, external domains: 127.0.0.1, antigravity.google, api.github.com, cli.github.com, docs.anthropic.com, docs.claude.com, docs.github.com, github.com, lmctl.ai, lmctl.anthropic.com, lmctl.com, mailbox.local, nodejs.org, opencode.ai, registry.npmjs.org, unpkg.com

Source & flagged code

4 flagged · loading source
dist/cli/index.jsView file
4`).map(e=>Yn(e)).join(` L5: `)}function Sm(t,e){let r=t.toUpperCase();return r.includes("TOKEN")||r.includes("SECRET")||r.includes("KEY")||r.includes("PASSWORD")||r.includes("PASSWD")||r.includes("CREDENTIAL"... L6: `),f=d.slice(Math.max(0,d.length-n));c=f.length,a=zn(f.join(`
High
Child Process

Package source references child process execution.

dist/cli/index.jsView on unpkg · L4
4`).map(e=>Yn(e)).join(` L5: `)}function Sm(t,e){let r=t.toUpperCase();return r.includes("TOKEN")||r.includes("SECRET")||r.includes("KEY")||r.includes("PASSWORD")||r.includes("PASSWD")||r.includes("CREDENTIAL"... L6: `),f=d.slice(Math.max(0,d.length-n));c=f.length,a=zn(f.join(` ... L16: `)}catch{}}function hI(){if(!go){let e=pI().slice(0,13).replace("T","-");go=yo(Nl(),`debug-${e}.log`)}uI(dI(go),{recursive:!0})}function Ue(...t){Le({event:"legacy",msg:t.join(" ")... L17: `)){let r=e.trim();if(!r||r.startsWith("#"))continue;if(r.startsWith("["))break;let n=/^debug\s*=\s*(.+)$/.exec(r);if(n){let s=n[1].trim().toLowerCase().replace(/^["']|["']$/g,"");... L18: CREATE TABLE IF NOT EXISTS session_size (
High
Same File Env Network Execution

A single source file combines environment access, network access, and code or shell execution; review context before blocking.

dist/cli/index.jsView on unpkg · L4
1var $v=Object.defineProperty;var E=(t,e)=>()=>(t&&(e=t(t=0)),e);var ne=(t,e)=>{for(var r in e)$v(t,r,{get:e[r],enumerable:!0})};import{existsSync as Vr,readdirSync as Rv,readFileSy... L2: `)}function Sl(){return Ct(Ir(),Dv)}function wm(){return Ct(Ir(),Pv)}function Uv(){try{return Vr(Sl())}catch{return!1}}function Em(t=process.stderr){Uv()&&t.write(`note: legacy ~/.... L3: `)}var Lv,_m,Cv,Mv,gm,Dv,Pv,xr,Ge=E(()=>{"use strict";Lv=".lmctl",_m="state.db",Cv="workspaces",Mv="active-workspace",gm="default",Dv="profiles",Pv="active-profile";xr={default:gm}... L4: `).map(e=>Yn(e)).join(` L5: `)}function Sm(t,e){let r=t.toUpperCase();return r.includes("TOKEN")||r.includes("SECRET")||r.includes("KEY")||r.includes("PASSWORD")||r.includes("PASSWD")||r.includes("CREDENTIAL"... L6: `),f=d.slice(Math.max(0,d.length-n));c=f.length,a=zn(f.join(` ... L16: `)}catch{}}function hI(){if(!go){let e=pI().slice(0,13).replace("T","-");go=yo(Nl(),`debug-${e}.log`)}uI(dI(go),{recursive:!0})}function Ue(...t){Le({event:"legacy",msg:t.join(" ")... L17: `)){let r=e.trim();if(!r||r.startsWith("#"))continue;if(r.startsWith("["))break;let n=/^debug\s*=\s*(.+)$/.exec(r);if(n){let s=n[1].trim().toLowerCase().rep
High
Command Output Exfiltration

Source combines command execution, command-output handling, and outbound requests; review data flow before blocking.

dist/cli/index.jsView on unpkg · L1
1var $v=Object.defineProperty;var E=(t,e)=>()=>(t&&(e=t(t=0)),e);var ne=(t,e)=>{for(var r in e)$v(t,r,{get:e[r],enumerable:!0})};import{existsSync as Vr,readdirSync as Rv,readFileSy... L2: `)}function Sl(){return Ct(Ir(),Dv)}function wm(){return Ct(Ir(),Pv)}function Uv(){try{return Vr(Sl())}catch{return!1}}function Em(t=process.stderr){Uv()&&t.write(`note: legacy ~/.... ... L4: `).map(e=>Yn(e)).join(` L5: `)}function Sm(t,e){let r=t.toUpperCase();return r.includes("TOKEN")||r.includes("SECRET")||r.includes("KEY")||r.includes("PASSWORD")||r.includes("PASSWD")||r.includes("CREDENTIAL"... L6: `),f=d.slice(Math.max(0,d.length-n));c=f.length,a=zn(f.join(` ... L14: `).filter(i=>i.length>0),o=s.slice(Math.max(0,s.length-r));return{source_path:e,tail_lines:o.length,tail:zn(o.join(` L15: `)),note:null}}catch(n){return n.code==="ENOENT"?{source_path:e,tail_lines:0,tail:"",note:"no intent log present (status never run on this host)"}:{source_path:e,tail_lines:0,tail:... L16: `)}catch{}}function hI(){if(!go){let e=pI().slice(0,13).replace("T","-");go=yo(Nl(),`debug-${e}.log`)}uI(dI(go),{recursive:!0})}function Ue(...t){Le({event:"legacy",msg:t.join(" ")... L17: `)){let r=e.trim();if(!r||r.s
Low
Weak Crypto

Package source references weak cryptographic algorithms.

dist/cli/index.jsView on unpkg · L1

Findings

4 High2 Medium5 Low
HighChild Processdist/cli/index.js
HighShell
HighSame File Env Network Executiondist/cli/index.js
HighCommand Output Exfiltrationdist/cli/index.js
MediumNetwork
MediumEnvironment Vars
LowWeak Cryptodist/cli/index.js
LowFilesystem
LowHigh Entropy Strings
LowUrl Strings
LowNo License