registry  /  @swirls/cli  /  0.1.101

@swirls/cli@0.1.101

Swirls command line application

AI Security Review

scanned 1d ago · by lpm-firewall-ai

Global install executes an unpinned remote shell installer from swirls.ai, then copies the installed CLI binary into the package bin path. The package source itself does not show credential theft, AI-agent hijack, persistence, or destructive behavior.

Static reason
High-risk behavior combination matched malicious policy.
Trigger
npm/Bun global install postinstall on non-Windows outside CI
Impact
Remote host can run arbitrary install-time shell code and determine the final swirls binary contents.
Mechanism
install-time curl-to-bash remote installer
Attack narrative
On global installation, package.json invokes scripts/install.js. That script skips CI, Windows, and non-global installs, then executes curl -fsSL https://swirls.ai/install | bash via execSync with shell:true. If ~/.local/bin/swirls exists after the remote installer, it is copied over bin/swirls and made executable. This is a real install-time remote code execution surface, but inspection found it is package-aligned CLI bootstrap behavior rather than confirmed malware or foreign AI-agent control hijack.
Rationale
The package contains an install-time remote shell execution path, which is too risky to mark clean, but static inspection did not confirm malicious payload behavior in the packaged source. Treat as a warn-level remote-code-execution install hook rather than publish-block malware.
Evidence
package.jsonscripts/install.jsbin/swirlsbin/README.txtREADME.md~/.local/bin/swirls
Network endpoints2
swirls.ai/installswirls.ai

Decision evidence

public snapshot
AI called this Suspicious at 86.0% confidence as Dangerous Capability with medium false-positive risk.
Evidence for warning
  • package.json defines postinstall: node scripts/install.js
  • scripts/install.js runs curl -fsSL https://swirls.ai/install | bash with shell:true
  • Remote installer runs during global npm/Bun install outside CI and non-Windows
  • scripts/install.js copies ~/.local/bin/swirls into package bin/swirls after remote install
Evidence against
  • No package source writes Claude/Codex/Cursor/MCP control surfaces
  • No credential harvesting, destructive file operations, persistence hooks, or exfiltration found in package files
  • bin/swirls is a placeholder shell script that only prints reinstall guidance
  • README documents global CLI install and supported platforms
Behavioral surface
Source
ChildProcessEnvironmentVarsFilesystemShell
Supply chain
UrlStrings
Manifest
NoLicense
scanned 1 file(s), 1.35 KB of source, external domains: swirls.ai

Source & flagged code

7 flagged · loading source
package.jsonView file
scripts.postinstall = node scripts/install.js
High
Install Time Lifecycle Scripts

Package defines install-time lifecycle scripts.

package.jsonView on unpkg
scripts.postinstall = node scripts/install.js
Medium
Ambiguous Install Lifecycle Script

Install-time lifecycle script is not statically allowlisted and needs review.

package.jsonView on unpkg
scripts/install.jsView file
13L14: import { execSync } from 'node:child_process' L15: import { chmodSync, copyFileSync, existsSync } from 'node:fs' ... L18: L19: const INSTALL_URL = 'https://swirls.ai/install' L20: ... L28: const INSTALLED_BINARY = join( L29: process.env.HOME || process.env.USERPROFILE || '', L30: '.local', ... L36: L37: if (process.env.CI === 'true' || process.env.CI === '1') { L38: process.exit(0)
Critical
Download Execute

Source downloads or fetches remote code and executes it.

scripts/install.jsView on unpkg · L13
13Trigger-reachable chain: scripts.postinstall -> scripts/install.js L13: L14: import { execSync } from 'node:child_process' L15: import { chmodSync, copyFileSync, existsSync } from 'node:fs' ... L18: L19: const INSTALL_URL = 'https://swirls.ai/install' L20: ... L28: const INSTALLED_BINARY = join( L29: process.env.HOME || process.env.USERPROFILE || '', L30: '.local', ... L36: L37: if (process.env.CI === 'true' || process.env.CI === '1') { L38: process.exit(0)
Critical
Trigger Reachable Dangerous Capability

A package entrypoint or install-time lifecycle script reaches a source file with blocking dangerous behavior.

scripts/install.jsView on unpkg · L13
13L14: import { execSync } from 'node:child_process' L15: import { chmodSync, copyFileSync, existsSync } from 'node:fs'
High
Child Process

Package source references child process execution.

scripts/install.jsView on unpkg · L13
49stdio: 'inherit', L50: shell: true, L51: })
High
Shell

Package source references shell execution.

scripts/install.jsView on unpkg · L49
13L14: import { execSync } from 'node:child_process' L15: import { chmodSync, copyFileSync, existsSync } from 'node:fs' ... L18: L19: const INSTALL_URL = 'https://swirls.ai/install' L20: ... L28: const INSTALLED_BINARY = join( L29: process.env.HOME || process.env.USERPROFILE || '', L30: '.local', ... L36: L37: if (process.env.CI === 'true' || process.env.CI === '1') { L38: process.exit(0)
High
Sandbox Evasion Gated Capability

Source gates dangerous network, credential, or execution behavior behind CI, host, platform, time, or geo fingerprint checks.

scripts/install.jsView on unpkg · L13

Findings

2 Critical4 High3 Medium4 Low
CriticalDownload Executescripts/install.js
CriticalTrigger Reachable Dangerous Capabilityscripts/install.js
HighInstall Time Lifecycle Scriptspackage.json
HighChild Processscripts/install.js
HighShellscripts/install.js
HighSandbox Evasion Gated Capabilityscripts/install.js
MediumAmbiguous Install Lifecycle Scriptpackage.json
MediumEnvironment Vars
MediumStructural Risk Force Deep Review
LowScripts Present
LowFilesystem
LowUrl Strings
LowNo License