registry  /  @juspay/shooter  /  1.29.0

@juspay/shooter@1.29.0

Bidirectional communication server for Claude Code and iOS — push notifications, remote terminal, session viewing

AI Security Review

scanned 2h ago · by lpm-firewall-ai

LPM treats this as warn-only first-party agent extension lifecycle risk. The package deliberately provides an AI-agent event notifier and authenticated remote-terminal server. Hook activation requires project/user configuration; persistence requires an explicit CLI subcommand. No unconsented install-time mutation or covert exfiltration was confirmed.

Static reason
High-risk behavior combination matched malicious policy.
Trigger
User runs `shooter`, explicitly enables autostart, or configures the bundled Claude/Codex hook.
Impact
Configured users may expose agent events or terminal control to their selected Shooter server.
Mechanism
Configured agent-event forwarding and authenticated terminal control.
Rationale
Source confirms a high-impact but package-aligned AI-agent/remote-terminal capability, not concrete malicious behavior. Because it ships enabled project-local Claude hooks that forward agent events, retain a warning rather than blocking.
Evidence
package.jsonbin/shooter.cjs.claude/settings.json.claude/hooks/codex-hooks.example.json.claude/hooks/notifier.cjsbuild/server/index.jssrc/lib/modules/server/auth.tssrc/routes/api/neurolink-proxy/+server.tssrc/lib/modules/client/neurolink/fetch-proxy.ts
Network endpoints6
localhost:54007${SHOOTER_API_URL}/api/notifyapi.openai.com/api.anthropic.com/api.mistral.ai/generativelanguage.googleapis.com/

Decision evidence

public snapshot
AI called this Suspicious at 90.0% confidence as Dangerous Capability with low false-positive risk.
Evidence for warning
  • `package.json` ships `.claude/settings.json`, which installs Claude hooks when this project is used.
  • `.claude/hooks/notifier.cjs` reads agent event JSON and forwards it to configured local/remote Shooter `/api/notify`.
  • `bin/shooter.cjs` can create user LaunchAgent/systemd persistence, but only through explicit `shooter autostart on`.
Evidence against
  • `package.json` postinstall only runs `node-gyp rebuild` for declared native dependencies.
  • No lifecycle script invokes the CLI, setup, autostart, hooks, or external network operations.
  • `.claude/hooks/codex-hooks.example.json` is an explicit copy-and-edit example, not an install-time writer.
  • Reported `build/server/index.js` global `Proxy` is bundled WebContainer/Svelte compatibility code, not network interception.
  • Notifier defaults to `http://localhost:54007`; remote host is supplied by the user via `SHOOTER_API_URL`.
  • Source auth protects terminal and API control paths with an API key.
Behavioral surface
Source
ChildProcessCryptoDynamicRequireEnvironmentVarsFilesystemNetworkShellWebSocket
Supply chain
HighEntropyStringsMinifiedObfuscatedTelemetryUrlStrings
ManifestNo manifest risk signals triggered.
scanned 341 file(s), 2.72 MB of source, external domains: aistudio.google.com, api.anthropic.com, api.mistral.ai, api.openai.com, console.anthropic.com, console.mistral.ai, datatracker.ietf.org, developers.cloudflare.com, fonts.googleapis.com, fonts.gstatic.com, generativelanguage.googleapis.com, github.com, my.site, platform.openai.com, shooter.breezehq.dev, svelte.dev, unpkg.com, www.apple.com, www.w3.org

Source & flagged code

13 flagged · loading source
package.jsonView file
scripts.postinstall = node-gyp rebuild --directory=node_modules/node-pty --loglevel=silent > /dev/null 2>&1 || echo '⚠ node-pty build failed — terminal features will not work. Install build tools: macOS...
High
Install Time Lifecycle Scripts

Package defines install-time lifecycle scripts.

package.jsonView on unpkg
scripts.postinstall = node-gyp rebuild --directory=node_modules/node-pty --loglevel=silent > /dev/null 2>&1 || echo '⚠ node-pty build failed — terminal features will not work. Install build tools: macOS...
Medium
Ambiguous Install Lifecycle Script

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

package.jsonView on unpkg
bin/shooter.cjsView file
9const path = require('path'); L10: const { spawn, execSync, execFileSync } = require('child_process'); L11: const fs = require('fs');
High
Child Process

Package source references child process execution.

bin/shooter.cjsView on unpkg · L9
9Manifest entrypoint (manifest.bin) carries capability families absent from dist/build output: environment+network, sensitive-file+network, execution+network L9: const path = require('path'); L10: const { spawn, execSync, execFileSync } = require('child_process'); L11: const fs = require('fs'); ... L25: // ── Resolve paths ─────────────────────────────────────────────────── L26: const PKG_ROOT = path.resolve(__dirname, '..'); L27: const SHOOTER_HOME = process.env.SHOOTER_HOME || path.join(os.homedir(), '.shooter'); L28: const PID_FILE = path.join(SHOOTER_HOME, 'shooter.pid'); ... L35: L36: const pkg = require(path.join(PKG_ROOT, 'package.json')); L37: ... L201: L202: const cf = spawn('cloudflared', ['tunnel', '--url', `http://localhost:${port}`], {
High
Entrypoint Build Divergence

Manifest entrypoint contains risky behavior absent from dist/build output.

bin/shooter.cjsView on unpkg · L9
973/** L974: * Perform the actual update: git pull → pnpm install → pnpm build. L975: * Returns true on success. On failure, rolls back and returns false. ... L984: try { L985: savedHead = execSync('git rev-parse HEAD', { L986: cwd: PKG_ROOT,
High
Runtime Package Install

Package source invokes a package manager install command at runtime.

bin/shooter.cjsView on unpkg · L973
7L8: const os = require('os'); L9: const path = require('path');
Medium
Dynamic Require

Package source references dynamic require/import behavior.

bin/shooter.cjsView on unpkg · L7
9const path = require('path'); L10: const { spawn, execSync, execFileSync } = require('child_process'); L11: const fs = require('fs'); ... L25: // ── Resolve paths ─────────────────────────────────────────────────── L26: const PKG_ROOT = path.resolve(__dirname, '..'); L27: const SHOOTER_HOME = process.env.SHOOTER_HOME || path.join(os.homedir(), '.shooter'); L28: const PID_FILE = path.join(SHOOTER_HOME, 'shooter.pid'); ... L35: L36: const pkg = require(path.join(PKG_ROOT, 'package.json')); L37: ... L201: L202: const cf = spawn('cloudflared', ['tunnel', '--url', `http://localhost:${port}`], {
Medium
Install Persistence

Source writes installer persistence such as shell profile or service configuration.

bin/shooter.cjsView on unpkg · L9
build/server/index.jsView file
1import { w as with_request_store, b as base64_decode, r as root, d as decode_pathname, n as normalize_path, a as disable_search, c as decode_params, v as validate_layout_server_exp... L2: import { U as UNDEFINED, N as NAN, P as POSITIVE_INFINITY, a as NEGATIVE_INFINITY, b as NEGATIVE_ZERO, S as SPARSE, i as is_valid_array_len, M as MAX_ARRAY_INDEX, c as is_valid_arr... ... L4: import { w as writable, r as readable } from './chunks/index-blpaaHOm.js'; L5: import { s as set_private_env, a as set_public_env, p as public_env } from './chunks/shared-server-DaWdgxVh.js'; L6: ... L191: } L192: case "URLSearchParams": { L193: const url = new URLSearchParams(value[1]); ... L945: // Svelte also escapes < because the escape function could be called inside a `noscript` there L946: // https://github.[redacted]-8266-84wp-wv5c L947: // However, that doesn't apply in SvelteKit ... L1151: },
Critical
Global Object Hijack Exfiltration

Source reassigns a global/builtin to a Proxy that forwards intercepted runtime data to an external endpoint.

build/server/index.jsView on unpkg · L1
scripts/install.shView file
path = scripts/install.sh kind = build_helper sizeBytes = 25144 magicHex = [redacted]
Medium
Ships Build Helper

Package ships non-JavaScript build or shell helper files.

scripts/install.shView on unpkg
build/client/sw.js.gzView file
path = build/client/sw.js.gz kind = compressed_blob sizeBytes = 773 magicHex = [redacted]
Medium
Ships Compressed Blob

Package ships compressed or archive-like blobs.

build/client/sw.js.gzView on unpkg
build/client/_app/immutable/nodes/11.CJoYnv7W.js.brView file
path = build/client/_app/immutable/nodes/11.CJoYnv7W.js.br kind = high_entropy_blob sizeBytes = 7465 magicHex = [redacted]
High
Ships High Entropy Blob

Package ships high-entropy non-source blobs.

build/client/_app/immutable/nodes/11.CJoYnv7W.js.brView on unpkg
README.mdView file
176patternName = private_key_rsa severity = critical line = 176 matchedText = APNS_KEY...---"
Critical
Secret Pattern

RSA private key in README.md

README.mdView on unpkg · L176
218patternName = private_key_rsa severity = critical line = 218 matchedText = FCM_PRIV...---"
Critical
Secret Pattern

RSA private key in README.md

README.mdView on unpkg · L218

Findings

3 Critical6 High8 Medium7 Low
CriticalGlobal Object Hijack Exfiltrationbuild/server/index.js
CriticalSecret PatternREADME.md
CriticalSecret PatternREADME.md
HighInstall Time Lifecycle Scriptspackage.json
HighChild Processbin/shooter.cjs
HighShell
HighEntrypoint Build Divergencebin/shooter.cjs
HighRuntime Package Installbin/shooter.cjs
HighShips High Entropy Blobbuild/client/_app/immutable/nodes/11.CJoYnv7W.js.br
MediumAmbiguous Install Lifecycle Scriptpackage.json
MediumDynamic Requirebin/shooter.cjs
MediumNetwork
MediumEnvironment Vars
MediumInstall Persistencebin/shooter.cjs
MediumShips Build Helperscripts/install.sh
MediumShips Compressed Blobbuild/client/sw.js.gz
MediumStructural Risk Force Deep Review
LowNon Install Lifecycle Scripts
LowScripts Present
LowFilesystem
LowObfuscated
LowHigh Entropy Strings
LowTelemetry
LowUrl Strings