registry  /  terminalhire  /  0.16.0

terminalhire@0.16.0

Local-first job matching for developers — ambient job matches in the Claude Code spinner. Matching runs on your machine; your profile never leaves it.

AI Security Review

scanned 3d ago · by lpm-firewall-ai

No confirmed malicious attack surface. The package has consent-gated Claude Code spinner/statusLine integration and user-invoked network/GitHub features consistent with its job-matching CLI purpose.

Static reason
One or more suspicious static signals were detected.; previous stored version diff introduced dangerous source
Trigger
npm install prints a notice; terminalhire init/spinner/statusline/claim commands activate features interactively
Impact
No unconsented execution, persistence, credential exfiltration, or AI-agent control-surface mutation confirmed.
Mechanism
consent-gated local CLI configuration and package-aligned API/GitHub operations
Rationale
Static source inspection found risky primitives, but they are either print-only at install time or gated behind explicit user commands and consent prompts. The observed network, file, and child_process behavior is package-aligned and does not establish malicious intent.
Evidence
package.jsonpostinstall.jsinstall.jsstatusline-install.jsdist/bin/jpi-init.jsdist/bin/jpi-dispatch.jsdist/bin/jpi-refresh.jsdist/bin/jpi-claim.jsdist/bin/jpi-statusline-launch.js
Network endpoints12
terminalhire.comwww.terminalhire.comapi.github.comgithub.com/login/device/codegithub.com/login/oauth/access_tokenboards-api.greenhouse.ioapi.ashbyhq.comapi.lever.cohimalayas.appweworkremotely.comhn.algolia.comapi.opire.dev

Decision evidence

public snapshot
AI called this Clean at 86.0% confidence as Benign with medium false-positive risk.
Evidence for block
  • package.json defines postinstall and prepublishOnly lifecycle scripts.
  • install.js/statusline-install.js can modify ~/.claude/settings.json after typed consent.
  • dist/bin/jpi-claim.js can run git/gh commands for user-invoked bounty submission.
Evidence against
  • postinstall.js is print-only: no fs writes, network, imports, or child process execution.
  • install.js and statusline-install.js require explicit typed yes before writing Claude settings and create backups.
  • dist/bin/jpi-init.js invokes setup steps interactively; statusLine setup is a separate consent step.
  • Network calls are aligned with job matching, GitHub OAuth/API, chat/inbox, and public job feeds.
  • Git/gh child_process use is limited to user-invoked claim/submit and repository fingerprinting, not install/import time.
  • No credential harvesting or unconsented exfiltration found; GitHub/web tokens are stored locally encrypted or as local session files.
Behavioral surface
Source
ChildProcessCryptoDynamicRequireEnvironmentVarsFilesystemNetworkShell
Supply chain
HighEntropyStringsObfuscatedUrlStrings
ManifestNo manifest risk signals triggered.
scanned 43 file(s), 3.44 MB of source, external domains: 127.0.0.1, api.ashbyhq.com, api.github.com, api.lever.co, api.opire.dev, apply.workable.com, boards-api.greenhouse.io, github.com, himalayas.app, hn.algolia.com, jobs.ashbyhq.com, jobs.lever.co, news.ycombinator.com, terminalhire.com, weworkremotely.com, www.terminalhire.com

Source & flagged code

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

Package defines install-time lifecycle scripts.

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

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

package.jsonView on unpkg
dist/bin/jpi-chat.jsView file
4938}); L4939: import { spawn } from "child_process"; L4940: function openInBrowser(url) {
High
Child Process

Package source references child process execution.

dist/bin/jpi-chat.jsView on unpkg · L4938
dist/bin/jpi-statusline-launch.jsView file
49if (!cmd) return ""; L50: const r = spawnSync(cmd, { shell: true, input, timeout: 4e3 }); L51: return firstLine(r.stdout);
High
Shell

Package source references shell execution.

dist/bin/jpi-statusline-launch.jsView on unpkg · L49
dist/bin/jpi-init.jsView file
91try { L92: const { maybePromptPeerConnect } = await import(pathToFileURL(resolveScript("peer-connect-prompt")).href); L93: let login;
Medium
Dynamic Require

Package source references dynamic require/import behavior.

dist/bin/jpi-init.jsView on unpkg · L91
dist/bin/jpi.jsView file
5import { isatty } from "tty"; L6: import net from "net"; L7: import { join } from "path"; ... L9: import { fileURLToPath } from "url"; L10: import { spawn } from "child_process"; L11: var TERMINALHIRE_DIR = process.env.TERMINALHIRE_DIR || join(homedir(), ".terminalhire"); L12: var INDEX_CACHE_FILE = join(TERMINALHIRE_DIR, "index-cache.json");
High
Same File Env Network Execution

A single source file combines environment access, network access, and code or shell execution; review context before blocking.

dist/bin/jpi.jsView on unpkg · L5
dist/bin/jpi-sync.jsView file
99{ id: "azure", synonyms: ["microsoft-azure"], related: [{ to: "aws", w: 0.4 }] }, L100: { id: "ci-cd", synonyms: ["cicd", "jenkins", "circleci", "circle-ci", "travis"], related: [{ to: "github-actions", w: 0.6 }, { to: "gitlab-ci", w: 0.6 }] }, L101: { id: "github-actions", parents: ["ci-cd"], synonyms: ["github-action"] }, ... L191: { id: "microservices" }, L192: { id: "websockets", synonyms: ["ws", "socket.io"], related: [{ to: "realtime", w: 0.6 }] }, L193: { id: "realtime", synonyms: ["real-time"] }, ... L774: "use strict"; L775: KDF_INFO = Buffer.from("terminalhire-chat-v1"); L776: } ... L1063: init_src(); L1064: TERMINALHIRE_DIR = join2(homedir(), ".terminalhire"); L1065: PROFILE_FILE = join2(TERMINALHIRE_DIR, "profile.enc");
High
Sandbox Evasion Gated Capability

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

dist/bin/jpi-sync.jsView on unpkg · L99
dist/bin/jpi-dispatch.jsView file
31Cross-file remote execution chain: dist/bin/jpi-dispatch.js spawns dist/bin/jpi-link.js; helper contains network access plus dynamic code execution. L31: function terminalhireDir() { L32: return join(homedir(), ".terminalhire"); L33: } ... L49: if (fromFile) return fromFile; L50: const env = process.env["TERMINALHIRE_WEB_SESSION"]; L51: return typeof env === "string" && env.length > 0 ? env : null; ... L91: const raw = readFileSync2(CONFIG_FILE, "utf8"); L92: const parsed = JSON.parse(raw); L93: return { ...DEFAULT_CONFIG, ...parsed }; ... L188: const candidates = [ L189: join3(__dirname, "..", "..", "package.json"), L190: join3(__dirname, "..", "package.json")
High
Cross File Remote Execution Context

Source spawns a local helper that also contains network and dynamic execution context; review data flow before blocking.

dist/bin/jpi-dispatch.jsView on unpkg · L31
dist/bin/jpi-claim.jsView file
matchType = previous_version_dangerous_delta matchedPackage = terminalhire@0.13.0 matchedIdentity = npm:dGVybWluYWxoaXJl:0.13.0 similarity = 0.762 summary = stored previous version shares package body but lacks this dangerous source file
Critical
Previous Version Dangerous Delta

This package version adds a dangerous source file absent from the previous stored version.

dist/bin/jpi-claim.jsView on unpkg

Findings

1 Critical6 High5 Medium6 Low
CriticalPrevious Version Dangerous Deltadist/bin/jpi-claim.js
HighInstall Time Lifecycle Scriptspackage.json
HighChild Processdist/bin/jpi-chat.js
HighShelldist/bin/jpi-statusline-launch.js
HighSame File Env Network Executiondist/bin/jpi.js
HighSandbox Evasion Gated Capabilitydist/bin/jpi-sync.js
HighCross File Remote Execution Contextdist/bin/jpi-dispatch.js
MediumAmbiguous Install Lifecycle Scriptpackage.json
MediumDynamic Requiredist/bin/jpi-init.js
MediumNetwork
MediumEnvironment Vars
MediumStructural Risk Force Deep Review
LowNon Install Lifecycle Scripts
LowScripts Present
LowFilesystem
LowObfuscated
LowHigh Entropy Strings
LowUrl Strings