registry  /  @lmctl-ai/lmctl  /  0.1.59

@lmctl-ai/lmctl@0.1.59

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

AI Security Review

scanned 8d ago · by lpm-firewall-ai

No confirmed malicious attack surface was found. Suspicious primitives are aligned with a CLI control plane for AI-agent workflows and require explicit user commands.

Static reason
One or more suspicious static signals were detected.
Trigger
User runs lmctl commands such as workflow run, diagnose, seed, terminal, or workspace operations.
Impact
Can run configured workflows and modify lmctl/session configuration when invoked, but no unconsented install-time execution or covert exfiltration was identified.
Mechanism
User-invoked workflow orchestration, diagnostics, local DB/session management, and provider CLI spawning.
Rationale
Static inspection shows a feature-rich user-invoked CLI with network, env, and process execution capabilities, but no lifecycle hooks, import-time execution, credential harvesting, covert upload, or persistence beyond declared lmctl configuration/state behavior. The scanner findings are explained by legitimate workflow execution, diagnostics, update checking, and AI-provider session management.
Evidence
package.jsonbin/lmctldist/cli/index.jsREADME.mdworkflows/qa-suite.compound.jsonworkflows/pr-fix.compound.jsonworkflows/triage-v2.compound.json
Network endpoints4
registry.npmjs.org/@lmctl-ai/lmctl/latestlmctl.anthropic.com/diagnostics/initlmctl.com/workflows/research.compound.jsonlmctl.com/examples/opencode.json

Decision evidence

public snapshot
AI called this Clean at 86.0% confidence as Benign with low false-positive risk.
Evidence for block
  • dist/cli/index.js contains user-invoked child_process use for provider CLIs, tar/stat/du/uname, and terminal resume.
  • dist/cli/index.js can fetch user-supplied https workflow URLs and has a --yes/--no-prompt bypass for its trust prompt.
  • dist/cli/index.js has a TTY update notifier hitting npm registry and writing ~/.lmctl/.update-check.json.
Evidence against
  • package.json defines no lifecycle scripts; bin/lmctl only imports runCli from dist/cli/index.js.
  • Remote workflow URLs are restricted to https and shown with source, sha256, workflow, and step archetypes before execution.
  • diagnose sanitizes secrets by default and upload modes are stub/noop; --no-sanitize is rejected with upload flags.
  • Environment inspection redacts token/secret/key/password/auth values via Jf/Xt helpers.
  • MCP bridge and teamfile/session writes occur under explicit seed/workspace/project commands, not install/import time.
Behavioral surface
Source
ChildProcessEnvironmentVarsFilesystemNetworkShell
Supply chain
HighEntropyStringsUrlStrings
Manifest
NoLicense
scanned 1 file(s), 796 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=>Cn(e)).join(` L5: `)}function Jf(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=Mn(f.join(`
High
Child Process

Package source references child process execution.

dist/cli/index.jsView on unpkg · L4
4`).map(e=>Cn(e)).join(` L5: `)}function Jf(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=Mn(f.join(` ... L16: `)}catch{}}function Kk(){if(!Ys){let e=Gk().slice(0,13).replace("T","-");Ys=zs(nl(),`debug-${e}.log`)}qk(Hk(Ys),{recursive:!0})}function Je(...t){xt({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 ck=Object.defineProperty;var b=(t,e)=>()=>(t&&(e=t(t=0)),e);var le=(t,e)=>{for(var r in e)ck(t,r,{get:e[r],enumerable:!0})};import{existsSync as Br,readdirSync as uk,readFileSy... L2: `)}function Qa(){return Nt(gr(),_k)}function Wf(){return Nt(gr(),gk)}function yk(){try{return Br(Qa())}catch{return!1}}function Bf(t=process.stderr){yk()&&t.write(`note: legacy ~/.... L3: `)}var mk,Pf,pk,hk,Ff,_k,gk,yr,Ye=b(()=>{"use strict";mk=".lmctl",Pf="state.db",pk="workspaces",hk="active-workspace",Ff="default",_k="profiles",gk="active-profile";yr={default:Ff}... L4: `).map(e=>Cn(e)).join(` L5: `)}function Jf(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=Mn(f.join(` ... L16: `)}catch{}}function Kk(){if(!Ys){let e=Gk().slice(0,13).replace("T","-");Ys=zs(nl(),`debug-${e}.log`)}qk(Hk(Ys),{recursive:!0})}function Je(...t){xt({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 ck=Object.defineProperty;var b=(t,e)=>()=>(t&&(e=t(t=0)),e);var le=(t,e)=>{for(var r in e)ck(t,r,{get:e[r],enumerable:!0})};import{existsSync as Br,readdirSync as uk,readFileSy... L2: `)}function Qa(){return Nt(gr(),_k)}function Wf(){return Nt(gr(),gk)}function yk(){try{return Br(Qa())}catch{return!1}}function Bf(t=process.stderr){yk()&&t.write(`note: legacy ~/.... ... L4: `).map(e=>Cn(e)).join(` L5: `)}function Jf(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=Mn(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:Mn(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 Kk(){if(!Ys){let e=Gk().slice(0,13).replace("T","-");Ys=zs(nl(),`debug-${e}.log`)}qk(Hk(Ys),{recursive:!0})}function Je(...t){xt({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