registry  /  @lmctl-ai/lmctl  /  0.1.81

@lmctl-ai/lmctl@0.1.81

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

AI Security Review

scanned 5d ago · by lpm-firewall-ai

No confirmed malicious attack surface was found. The package is a CLI that can fetch HTTPS workflows, update-check npm metadata, manage a local ~/.lmctl SQLite state, and launch agent/provider CLIs when the user runs lmctl commands.

Static reason
One or more suspicious static signals were detected.
Trigger
User runs the lmctl CLI; no npm lifecycle hook or import-time payload found.
Impact
Expected package behavior includes spawning agent tools and writing lmctl state/log files; no credential exfiltration, stealth persistence, or unconsented install-time agent-control mutation was identified.
Mechanism
User-invoked AI-agent orchestration CLI with local state and optional HTTPS workflow loading.
Rationale
Static findings are package-aligned: child_process, env handling, and HTTPS fetches are part of an explicit lmctl AI-agent orchestration CLI and are not activated by npm lifecycle scripts. Sensitive env/status output is redacted and no concrete exfiltration or unauthorized install-time control-surface mutation was found.
Evidence
package.jsonbin/lmctldist/cli/index.jsREADME.mdworkflows/pr-fix.compound.jsonworkflows/qa-suite.compound.json~/.lmctl/state.db~/.lmctl/.update-check.json~/.lmctl/recent-intents.log~/.lmctl/debug-*.log
Network endpoints2
registry.npmjs.org/@lmctl-ai/lmctl/latestlmctl.com

Decision evidence

public snapshot
AI called this Clean at 86.0% confidence as Benign with low false-positive risk.
Evidence for block
    Evidence against
    • package.json has no preinstall/install/postinstall scripts; only bin lmctl -> bin/lmctl.
    • bin/lmctl only imports dist/cli/index.js and calls runCli with argv.
    • dist/cli/index.js network fetches are for explicit remote workflow URLs and a TTY update check to registry.npmjs.org.
    • dist/cli/index.js env/status collection redacts token/secret/key/password values before display.
    • dist/cli/index.js child_process use launches configured provider CLIs or local utilities from user CLI commands, not at install/import time.
    • README frames the package as an AI-agent control-plane CLI; workflows/examples align with that purpose.
    Behavioral surface
    Source
    ChildProcessEnvironmentVarsFilesystemNetworkShell
    Supply chain
    HighEntropyStringsUrlStrings
    Manifest
    NoLicense
    scanned 1 file(s), 817 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=>Un(e)).join(` L5: `)}function cm(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=Wn(f.join(`
    High
    Child Process

    Package source references child process execution.

    dist/cli/index.jsView on unpkg · L4
    4`).map(e=>Un(e)).join(` L5: `)}function cm(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=Wn(f.join(` ... L16: `)}catch{}}function Dv(){if(!oo){let e=Mv().slice(0,13).replace("T","-");oo=io(gl(),`debug-${e}.log`)}Rv(Ov(oo),{recursive:!0})}function Xe(...t){Ot({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 Zk=Object.defineProperty;var b=(t,e)=>()=>(t&&(e=t(t=0)),e);var se=(t,e)=>{for(var r in e)Zk(t,r,{get:e[r],enumerable:!0})};import{existsSync as Xr,readdirSync as Qk,readFileSy... L2: `)}function ml(){return At(Tr(),ov)}function om(){return At(Tr(),iv)}function lv(){try{return Xr(ml())}catch{return!1}}function im(t=process.stderr){lv()&&t.write(`note: legacy ~/.... L3: `)}var rv,rm,nv,sv,nm,ov,iv,kr,Ze=b(()=>{"use strict";rv=".lmctl",rm="state.db",nv="workspaces",sv="active-workspace",nm="default",ov="profiles",iv="active-profile";kr={default:nm}... L4: `).map(e=>Un(e)).join(` L5: `)}function cm(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=Wn(f.join(` ... L16: `)}catch{}}function Dv(){if(!oo){let e=Mv().slice(0,13).replace("T","-");oo=io(gl(),`debug-${e}.log`)}Rv(Ov(oo),{recursive:!0})}function Xe(...t){Ot({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 Zk=Object.defineProperty;var b=(t,e)=>()=>(t&&(e=t(t=0)),e);var se=(t,e)=>{for(var r in e)Zk(t,r,{get:e[r],enumerable:!0})};import{existsSync as Xr,readdirSync as Qk,readFileSy... L2: `)}function ml(){return At(Tr(),ov)}function om(){return At(Tr(),iv)}function lv(){try{return Xr(ml())}catch{return!1}}function im(t=process.stderr){lv()&&t.write(`note: legacy ~/.... ... L4: `).map(e=>Un(e)).join(` L5: `)}function cm(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=Wn(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:Wn(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 Dv(){if(!oo){let e=Mv().slice(0,13).replace("T","-");oo=io(gl(),`debug-${e}.log`)}Rv(Ov(oo),{recursive:!0})}function Xe(...t){Ot({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