registry  /  @lmctl-ai/lmctl  /  0.1.77

@lmctl-ai/lmctl@0.1.77

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

AI Security Review

scanned 7d ago · by lpm-firewall-ai

LPM treats this as warn-only first-party agent extension lifecycle risk. No install-time or import-time malware was found. The package is an AI-agent orchestration CLI with user-invoked remote workflow loading, provider CLI spawning, and MCP bridge setup, creating agent lifecycle risk rather than confirmed malicious delivery.

Static reason
One or more suspicious static signals were detected.
Trigger
Explicit lmctl CLI commands such as team seed/terminal/chat/workflow run/diagnose/serve
Impact
Could delegate prompts to local agent CLIs, write lmctl MCP bridge config, run loaded workflows, or collect diagnostics when an operator invokes those features.
Mechanism
user-invoked agent orchestration and MCP/workflow setup
Rationale
Static inspection found risky AI-agent control-plane capabilities, including MCP config writes and permissive provider CLI invocations, but they are exposed through explicit lmctl commands and there are no npm lifecycle hooks or import-time mutations. This fits warned agent extension/capability risk, not malicious package behavior.
Evidence
package.jsonbin/lmctldist/cli/index.jsREADME.mdCHANGELOG.md~/.lmctl/state.db~/.lmctl/.update-check.json~/.lmctl/recent-intents.log.mcp.json~/.copilot/mcp-config.json~/.claude/projects/*.jsonl
Network endpoints2
registry.npmjs.org/@lmctl-ai/lmctl/latestlmctl.com/templates/

Decision evidence

public snapshot
AI called this Suspicious at 84.0% confidence as Dangerous Capability with medium false-positive risk.
Evidence for warning
  • dist/cli/index.js contains user-invoked agent orchestration that can spawn provider CLIs with permissive flags such as codex/claude/gemini/qwen/cq equivalents
  • dist/cli/index.js can write MCP bridge config to workspace .mcp.json and ~/.copilot/mcp-config.json during team/session setup paths
  • dist/cli/index.js supports loading remote https/lmctl workflows and remote skills/templates into local lmctl cache
  • dist/cli/index.js diagnose can collect env/status/log data, but includes redaction and upload paths appear stubbed or explicit
Evidence against
  • package.json has no preinstall/install/postinstall lifecycle scripts
  • bin/lmctl only imports dist/cli/index.js and runs the CLI when invoked
  • MCP/config writes are tied to explicit CLI setup/seed/self-heal flows, not npm install or import-time execution
  • Remote workflow run shows a trust check with sha256/source and prompt unless --yes/--no-prompt is supplied
  • Update notifier is interactive-only, cached under ~/.lmctl, and skipped for mcp/help/json/non-TTY/env-disabled cases
  • Secret-looking diagnostic/environment output is redacted by key/name and pattern filters
Behavioral surface
Source
ChildProcessEnvironmentVarsFilesystemNetworkShell
Supply chain
HighEntropyStringsUrlStrings
Manifest
NoLicense
scanned 1 file(s), 807 KB of source, external domains: 127.0.0.1, antigravity.google, api.deepseek.com, api.github.com, api.githubcopilot.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, openrouter.ai, registry.npmjs.org, unpkg.com

Source & flagged code

4 flagged · loading source
dist/cli/index.jsView file
4`).map(e=>Dn(e)).join(` L5: `)}function zf(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=Pn(f.join(`
High
Child Process

Package source references child process execution.

dist/cli/index.jsView on unpkg · L4
4`).map(e=>Dn(e)).join(` L5: `)}function zf(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=Pn(f.join(` ... L16: `)}catch{}}function ov(){if(!eo){let e=sv().slice(0,13).replace("T","-");eo=to(cl(),`debug-${e}.log`)}ev(tv(eo),{recursive:!0})}function Je(...t){At({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 Ek=Object.defineProperty;var b=(t,e)=>()=>(t&&(e=t(t=0)),e);var le=(t,e)=>{for(var r in e)Ek(t,r,{get:e[r],enumerable:!0})};import{existsSync as qr,readdirSync as bk,readFileSy... L2: `)}function ol(){return xt(wr(),Nk)}function Gf(){return xt(wr(),$k)}function jk(){try{return qr(ol())}catch{return!1}}function Kf(t=process.stderr){jk()&&t.write(`note: legacy ~/.... L3: `)}var kk,Hf,vk,Ik,Jf,Nk,$k,Er,Ye=b(()=>{"use strict";kk=".lmctl",Hf="state.db",vk="workspaces",Ik="active-workspace",Jf="default",Nk="profiles",$k="active-profile";Er={default:Jf}... L4: `).map(e=>Dn(e)).join(` L5: `)}function zf(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=Pn(f.join(` ... L16: `)}catch{}}function ov(){if(!eo){let e=sv().slice(0,13).replace("T","-");eo=to(cl(),`debug-${e}.log`)}ev(tv(eo),{recursive:!0})}function Je(...t){At({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 Ek=Object.defineProperty;var b=(t,e)=>()=>(t&&(e=t(t=0)),e);var le=(t,e)=>{for(var r in e)Ek(t,r,{get:e[r],enumerable:!0})};import{existsSync as qr,readdirSync as bk,readFileSy... L2: `)}function ol(){return xt(wr(),Nk)}function Gf(){return xt(wr(),$k)}function jk(){try{return qr(ol())}catch{return!1}}function Kf(t=process.stderr){jk()&&t.write(`note: legacy ~/.... ... L4: `).map(e=>Dn(e)).join(` L5: `)}function zf(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=Pn(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:Pn(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 ov(){if(!eo){let e=sv().slice(0,13).replace("T","-");eo=to(cl(),`debug-${e}.log`)}ev(tv(eo),{recursive:!0})}function Je(...t){At({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