{"id":"MAL-2026-17630","kind":"package","aliases":["MAL-2026-17630"],"related":[],"title":"Malicious code in virgil-cli (npm)","description":"In local mode virgil-cli prints that it runs on the user's machine and is free and private. After each turn, reportLearning() in dist/api.js posts the whole conversation history, including shell and file tool output, to https://api.nm-streetwear.space/vx/learn with a hardcoded token and a device id. I found no mention of this in the README and no setting to turn it off. I did not see it read credentials itself, but tool output can contain secrets. I read 0.1.4 only and did not run it.\n\n---\n_-= Per source details. Do not edit below this line.=-_\n\n## Source: amazon-inspector (a603c1f5f6674c45322b6017e44aa35a824a3c4d57ecf9571b214276b2538624)\nvirgil-cli is an AI agent command-line tool whose README advertises that it 'runs commands' on the user's machine. On explicit CLI invocation (not on npm install or require), api.js POSTs the conversation to a hardcoded control plane at https://api.nm-streetwear.space/vx/agent using a shared static token 'virgil-ext-8528'. The server's response includes tool_calls that loop.js dispatches locally, including a run_shell tool implemented in runShell.js as child_process.spawn(command, { cwd, shell: true,... }). A per-command confirmation prompt exists but is bypassed by the documented --auto / -y flag and by an 'always allow' persistence option. The baseUrl is overridable via ~/.virgil/config.json. Separately, when local-Ollama inference is configured, api.js reportLearning() POSTs the full message history (including read_file contents, run_shell stdout/stderr, and list_dir output harvested from the user's machine during the session) to https://api.nm-streetwear.space/vx/learn after every turn, despite setup.js framing local mode as a privacy option. The destination is the vendor's own first-party endpoint and user-reconfigurable; the shared static bearer token offers no per-installer authentication, so any party able to reach the control plane or MITM the TLS channel can direct shell execution on hosts running the CLI, and the local-mode transcript upload contradicts the local-only privacy framing.","summary":null,"severity":"critical","cvss":null,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"virgil-cli","fixed":[],"affected":"= 0.1.4","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["malicious","package"],"not_affected_if":[],"how_to_check":null,"fix":null,"status":"open","published_at":"2026-10-03T22:47:45+00:00","first_seen_at":"2026-10-29T14:24:38+00:00","triaged_at":"2026-10-29T14:25:47+00:00","updated_at":"2026-10-29T14:25:47+00:00","category":"package","confidence":null,"triage":null,"analyst_status":null,"analyst_note":null,"search":"'-17630':10A,13A '-2026':9A,12A '/.virgil/config.json':229C '/vx/agent':166C '/vx/learn':59C,268C '0.1.4':100C '8528':175C 'a603c1f5f6674c45322b6017e44aa35a824a3c4d57ecf9571b214276b2538624':120C 'abl':310C 'advertis':134C 'agent':127C 'ai':126C 'allow':221C 'alway':220C 'amazon':118C 'amazon-inspector':117C 'api.js':154C,238C 'api.nm-streetwear.space':58C,165C,267C 'api.nm-streetwear.space/vx/agent':164C 'api.nm-streetwear.space/vx/learn':57C,266C 'authent':306C 'auto':214C 'baseurl':225C 'bearer':299C 'bypass':210C 'call':182C 'channel':320C 'child_process.spawn':196C 'cli':6A,16A,24C,123C,146C,329C 'code':2A 'command':129C,138C,197C,204C 'command-lin':128C 'configur':237C 'confirm':205C 'contain':96C 'content':248C 'contradict':337C 'control':161C,314C 'convers':48C,157C 'credenti':90C 'cwd':198C 'despit':272C 'destin':282C 'detail':109C 'devic':66C 'dir':254C 'direct':322C 'dispatch':185C 'dist/api.js':44C 'document':213C 'edit':112C 'endpoint':291C 'everi':270C 'execut':324C 'exist':207C 'explicit':145C 'ext':174C 'file':53C,247C 'first':289C 'first-parti':288C 'flag':216C 'found':69C 'frame':274C,343C 'free':36C 'full':242C 'hardcod':62C,160C 'harvest':256C 'histori':49C,244C 'host':326C 'id':67C 'implement':192C 'includ':50C,180C,187C,245C 'infer':235C 'inspector':119C 'instal':151C,305C 'invoc':147C 'line':115C,130C 'list':253C 'local':20C,186C,233C,275C,333C,340C 'local-mod':332C 'local-ollama':232C 'local-on':339C 'loop.js':184C 'machin':33C,143C,261C 'mal':8A,11A 'malici':1A,17B 'mention':71C 'messag':243C 'mitm':317C 'mode':21C,276C,334C 'npm':7A,150C 'offer':301C 'ollama':234C 'option':223C,280C 'output':55C,94C,255C 'overrid':227C 'packag':18B 'parti':290C,309C 'per':107C,203C,304C 'per-command':202C 'per-instal':303C 'persist':222C 'plane':162C,315C 'post':45C,155C,240C 'print':25C 'privaci':279C,342C 'privat':38C 'prompt':206C 'reach':312C 'read':89C,99C,246C 'readm':76C,133C 'reconfigur':295C 'reportlearn':42C,239C 'requir':153C 'respons':179C 'run':28C,105C,137C,189C,249C,327C 'runshell.js':194C 'secret':97C 'see':87C 'separ':230C 'server':177C 'session':264C 'set':79C 'setup.js':273C 'share':169C,297C 'shell':51C,190C,199C,250C,323C 'sourc':108C,116C 'static':170C,298C 'stdout/stderr':251C 'tls':319C 'token':63C,171C,300C 'tool':54C,93C,131C,181C,191C 'transcript':335C 'true':200C 'turn':41C,81C,271C 'upload':336C 'use':167C 'user':31C,141C,259C,294C 'user-reconfigur':293C 'vendor':285C 'via':228C 'virgil':5A,15A,23C,122C,173C 'virgil-c':4A,22C,121C 'virgil-cli':14A 'virgil-ext':172C 'whole':47C 'whose':132C 'y':215C","nicknames":[],"mitigations":[],"who_affected":null,"tier":null,"verified":{},"analyst_revisit_at":null,"fix_status":null,"audience":null,"plain_action":null,"first_public_at":"2026-10-03T22:47:45+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"osv-malicious","external_id":"MAL-2026-17630","url":"https://osv.dev/vulnerability/MAL-2026-17630","severity":"critical","published_at":"2026-10-03T22:47:45+00:00","modified_at":"2026-10-29T13:48:11+00:00","first_seen_at":"2026-10-29T14:24:38+00:00","fetched_at":"2026-10-29T14:24:38+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}