registry  /  @volter-ai/portable.dev  /  3.5.3

@volter-ai/portable.dev@3.5.3

Portable — local-first launcher / tunnel-router (installable CLI)

AI Security Review

scanned 2h ago · by lpm-firewall-ai

Review flagged AI-agent configuration or capability changes. This remains warn-only unless evidence shows foreign-agent hijack through preinstall/install/postinstall, hidden persistence, exfiltration, remote code execution, or other concrete malicious behavior.

Static reason
High-risk behavior combination matched malicious policy.
Trigger
User runs `portable`, `portable connect`, or `portable start`.
Impact
Creates persistent Claude integration entries and exposes the local API via a registered public tunnel; source inspection did not confirm credential exfiltration or remote payload execution.
Mechanism
User-invoked local agent launcher with tunnel relay and Claude configuration mutation.
Rationale
Source does not establish malicious behavior, but normal user execution mutates Claude configuration and creates a relay-backed remote-access path. Per policy, explicit/user-command AI-agent configuration mutation and dangerous capability should be warned rather than blocked when no exfiltration or stealth chain is confirmed.
Evidence
package.jsoncli.jsserver.jsREADME.md~/.claude.json~/.claude/.credentials.json~/.config/gh/hosts.yml~/.portable/secret.key~/.portable/secrets.json
Network endpoints2
app.portable.devapp.portable-dev.com

Decision evidence

public snapshot
AI called this Suspicious at 86.0% confidence as Dangerous Capability with low false-positive risk.
Evidence for warning
  • `cli.js` default `portable` runtime installs Claude hooks and registers `portable-sidecar` in `~/.claude.json`.
  • `cli.js` reads existing Claude and GitHub credentials from environment, local credential files, macOS Keychain, `gh`, and git helpers.
  • `cli.js` launches a local API, provisions browser/tunnel tooling, and registers a public tunnel URL with its relay.
  • The Claude integration mutation occurs during normal user-invoked `portable`/`connect`, not as a separately confirmed opt-in command.
Evidence against
  • `package.json` has no `preinstall`, `install`, or `postinstall` lifecycle scripts.
  • `cli.js` encrypts its persisted local secret store under `~/.portable` with AES-GCM and restrictive modes.
  • Relay registration in `cli.js` sends PC ID, label, public URL, and TTL; no credential upload was confirmed.
  • Reviewed CLI network operations are consistent with the documented local launcher, tunnel, and relay workflow.
  • No remote code download/eval chain, destructive action, or hidden persistence was confirmed.
Behavioral surface
Source
ChildProcessCryptoEnvironmentVarsFilesystemNetworkShell
Supply chain
HighEntropyStringsUrlStrings
ManifestNo manifest risk signals triggered.
scanned 1 file(s), 428 KB of source, external domains: 127.0.0.1, accounts.google.com, api.apify.com, api.fly.io, api.github.com, api.slack.com, app.portable-dev.com, app.portable.dev, bun.sh, cdn.simpleicons.org, claude.com, console.apify.com, console.aws.amazon.com, dashboard.ngrok.com, developers.cloudflare.com, developers.google.com, docs.apify.com, docs.github.com, fly.io, github.com, modal.com, modal.portable-dev.com, modal.portable.dev, ngrok.com, oauth2.googleapis.com, playwright.dev, portable-dev.com, portable.dev, slack.com, www.googleapis.com
Oversized source lightweight scan
server.js29.3 MB file, sampled 256 KB
FilesystemNetworkChildProcessEnvironmentVarsCryptoShellHighEntropyStringsUrlStrings127.0.0.1accounts.google.comapi.apify.comapi.fly.ioapi.github.comapi.slack.comapp.portable-dev.comapp.portable.devcdn.simpleicons.orgconsole.apify.comconsole.aws.amazon.comdevelopers.google.comdocs.apify.comdocs.github.comfly.iogithub.commodal.commodal.portable-dev.commodal.portable.devoauth2.googleapis.complaywright.devportable-dev.comportable.devslack.com

Source & flagged code

7 flagged · loading source
cli.jsView file
25function resolveDataDir(override) { L26: const fromEnv = override || process.env.PORTABLE_DATA_DIR || process.env.DATA_DIR || (process.env.XDG_DATA_HOME ? path.join(process.env.XDG_DATA_HOME, "portable") : undefined) || p... L27: if (fromEnv.startsWith("~")) { ... L41: ENVELOPE_PREFIX, L42: iv.toString("base64"), L43: tag.toString("base64"), ... L191: }; L192: this.write(next); L193: return next; ... L314: }); L315: import { spawnSync } from "child_process"; L316: function readProcessAncestors(startPid, depth = 3) {
Critical
Command Output Exfiltration

Source executes local commands and sends command output to an external endpoint.

cli.jsView on unpkg · L25
25Trigger-reachable chain: manifest.bin -> cli.js L25: function resolveDataDir(override) { L26: const fromEnv = override || process.env.PORTABLE_DATA_DIR || process.env.DATA_DIR || (process.env.XDG_DATA_HOME ? path.join(process.env.XDG_DATA_HOME, "portable") : undefined) || p... L27: if (fromEnv.startsWith("~")) { ... L41: ENVELOPE_PREFIX, L42: iv.toString("base64"), L43: tag.toString("base64"), ... L191: }; L192: this.write(next); L193: return next; ... L314: }); L315: import { spawnSync } from "child_process"; L316: function readProcessAncestors(startPid, depth = 3) {
Critical
Trigger Reachable Dangerous Capability

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

cli.jsView on unpkg · L25
314}); L315: import { spawnSync } from "child_process"; L316: function readProcessAncestors(startPid, depth = 3) {
High
Child Process

Package source references child process execution.

cli.jsView on unpkg · L314
1446const entry = options.entry ?? desiredSidecarEntry(process.execPath, process.argv[1] ?? "", options.bridgePath); L1447: const execAdd = options.execClaudeMcpAdd ?? defaultExecClaudeMcpAdd; L1448: const log = options.log ?? (() => {});
High
Shell

Package source references shell execution.

cli.jsView on unpkg · L1446
25function resolveDataDir(override) { L26: const fromEnv = override || process.env.PORTABLE_DATA_DIR || process.env.DATA_DIR || (process.env.XDG_DATA_HOME ? path.join(process.env.XDG_DATA_HOME, "portable") : undefined) || p... L27: if (fromEnv.startsWith("~")) { ... L41: ENVELOPE_PREFIX, L42: iv.toString("base64"), L43: tag.toString("base64"), ... L191: }; L192: this.write(next); L193: return next; ... L314: }); L315: import { spawnSync } from "child_process"; L316: function readProcessAncestors(startPid, depth = 3) {
High
Sandbox Evasion Gated Capability

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

cli.jsView on unpkg · L25
server.jsView file
path = server.js kind = oversized_source_file sizeBytes = 30696498 magicHex = [redacted]
High
Oversized Source File

Package contains source files above the static scanner size ceiling.

server.jsView on unpkg
package.jsonView file
scripts registry_only=start
Critical
Manifest Confusion

Tarball package.json differs from the npm registry version manifest for scripts or dependency sets.

package.jsonView on unpkg

Findings

3 Critical4 High3 Medium3 Low
CriticalCommand Output Exfiltrationcli.js
CriticalTrigger Reachable Dangerous Capabilitycli.js
CriticalManifest Confusionpackage.json
HighChild Processcli.js
HighShellcli.js
HighSandbox Evasion Gated Capabilitycli.js
HighOversized Source Fileserver.js
MediumNetwork
MediumEnvironment Vars
MediumStructural Risk Force Deep Review
LowFilesystem
LowHigh Entropy Strings
LowUrl Strings