registry  /  @swirls/cli  /  0.1.106

@swirls/cli@0.1.106

Swirls command line application

AI Security Review

scanned 6h ago · by lpm-firewall-ai

Global installation executes an unpinned remote shell script from swirls.ai at npm postinstall time. The fetched script is outside the package tarball and can replace the packaged CLI binary via ~/.local/bin/swirls.

Static reason
High-risk behavior combination matched malicious policy.; source matched previously finalized malicious package; routed for review; source fingerprint signature matched known malicious package; routed for review
Trigger
npm install -g @swirls/cli or global Bun install with lifecycle scripts enabled
Impact
Remote server controls code executed on installer host and the CLI binary copied into the package.
Mechanism
install-time remote shell execution
Attack narrative
On global install, npm runs package.json postinstall, which invokes scripts/install.js. That script shells out to curl the live https://swirls.ai/install script and pipes it directly to bash, then copies the resulting ~/.local/bin/swirls binary into the package bin path. The executable payload is remote, mutable, and not included in the reviewed package.
Rationale
The package contains lifecycle-triggered remote code execution by piping a live network script into bash and installing its output as the CLI binary. The CI/non-global guards reduce reach but do not remove the unpinned install-time execution risk for the intended global install path.
Evidence
package.jsonscripts/install.jsbin/swirlsREADME.md~/.local/bin/swirls
Network endpoints1
swirls.ai/install

Decision evidence

public snapshot
AI called this Malicious at 95.0% confidence as Malware with low false-positive risk.
Evidence for block
  • package.json defines postinstall: node scripts/install.js
  • scripts/install.js runs execSync with shell:true
  • scripts/install.js executes curl -fsSL https://swirls.ai/install | bash during global install
  • scripts/install.js copies ~/.local/bin/swirls into package bin/swirls after remote installer runs
  • bin/swirls tells users to run curl -fsSL https://swirls.ai/install | bash if postinstall was skipped
Evidence against
  • postinstall exits in CI, Windows, and non-global installs
  • Network endpoint is package-aligned swirls.ai
  • No source evidence of credential harvesting, AI-agent config writes, persistence files, or destructive commands in packaged files
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

9 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
matchType = normalized_sha256 matchedPackage = @swirls/cli@0.1.104 matchedPath = scripts/install.js matchedIdentity = npm:QHN3aXJscy9jbGk:0.1.104 similarity = 1.000 summary = normalized source hash matched finalized malicious source
High
Known Malware Source Similarity

Source file is highly similar to a previously finalized malicious package; route for source-aware review.

scripts/install.jsView on unpkg
matchType = malicious_source_fingerprint_signature signature = 87fc30553c66ec35 signatureType = suspicious_hashes sourceLabel = final_verdict:malicious matchedPackage = @swirls/cli@0.1.104 matchedPath = scripts/install.js matchedIdentity = npm:QHN3aXJscy9jbGk:0.1.104 similarity = 1.000 shingleOverlap = 1 summary = package final verdict is malicious
High
Known Malware Source Fingerprint Signature

Source fingerprint signature matches a known malicious package signature; route for source-aware review.

scripts/install.jsView on unpkg

Findings

2 Critical6 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
HighKnown Malware Source Similarityscripts/install.js
HighKnown Malware Source Fingerprint Signaturescripts/install.js
MediumAmbiguous Install Lifecycle Scriptpackage.json
MediumEnvironment Vars
MediumStructural Risk Force Deep Review
LowScripts Present
LowFilesystem
LowUrl Strings
LowNo License