registry  /  terminalhire  /  0.13.0

terminalhire@0.13.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 is a CLI that can opt-in modify Claude Code spinner/statusLine settings and contact terminalhire/GitHub services for job matching, auth, chat, and profile sync.

Static reason
One or more suspicious static signals were detected.; previous stored version diff introduced dangerous source
Trigger
npm install prints notice; user-run terminalhire commands activate network/settings behavior
Impact
User-approved local job matching, Claude settings updates, GitHub OAuth token storage, and optional profile/chat sync.
Mechanism
consent-gated CLI integration with local config and package-aligned APIs
Rationale
Static inspection shows suspicious primitives are aligned with a consent-gated developer job-matching CLI, not hidden install/import-time compromise. The lifecycle hook is print-only and AI-agent control-surface writes occur only after explicit user commands and typed consent.
Evidence
package.jsonpostinstall.jsinstall.jsstatusline-install.jsdist/bin/jpi-dispatch.jsdist/bin/jpi-init.jsdist/bin/jpi-sync.jsdist/bin/jpi-statusline-launch.jsdist/bin/spinner.js~/.claude/settings.json~/.terminalhire/config.json~/.terminalhire/github-token.enc~/.terminalhire/profile.enc~/.terminalhire/web-session~/.terminalhire/statusline-launch.js~/.terminalhire/statusline-foreign.json
Network endpoints9
terminalhire.com/api/indexterminalhire.com/api/leadterminalhire.com/api/directorywww.terminalhire.com/api/profile-syncwww.terminalhire.com/api/auth/linkwww.terminalhire.com/api/chat/keysgithub.com/login/device/codegithub.com/login/oauth/access_tokenapi.github.com

Decision evidence

public snapshot
AI called this Clean at 88.0% confidence as Benign with medium false-positive risk.
Evidence for block
    Evidence against
    • package.json postinstall runs only node ./postinstall.js; postinstall.js prints an interactive notice and exits without writes or network.
    • install.js and statusline-install.js require typed yes before modifying ~/.claude/settings.json and create backups.
    • dist/bin/jpi-init.js prompts before GitHub login, spinner install, and optional statusLine install.
    • Network calls are package-aligned: job index, GitHub OAuth/API, terminalhire auth/chat/profile-sync endpoints.
    • Profile sync in dist/bin/jpi-sync.js shows fields and requires browser verification before POST /api/profile-sync.
    • No install-time credential harvesting, hidden payload download, persistence, destructive action, or prompt/reviewer manipulation found.
    Behavioral surface
    Source
    ChildProcessCryptoDynamicRequireEnvironmentVarsFilesystemNetworkShell
    Supply chain
    HighEntropyStringsObfuscatedUrlStrings
    ManifestNo manifest risk signals triggered.
    scanned 42 file(s), 3.14 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-init.jsView file
    7import { createInterface } from "readline"; L8: import { spawnSync, spawn } from "child_process"; L9: import { homedir } from "os";
    High
    Child Process

    Package source references child process execution.

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

    Package source references dynamic require/import behavior.

    dist/bin/jpi-init.jsView on unpkg · L92
    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-sync.jsView file
    1115try { L1116: const child = spawn(cmd, args, { stdio: "ignore", detached: true }); L1117: child.on("error", () => { ... L1124: // bin/jpi-sync.js L1125: var TH_DIR = process.env["TERMINALHIRE_DIR"] || join3(homedir2(), ".terminalhire"); L1126: var TIER1_MARKER = join3(TH_DIR, "tier1.json"); L1127: var API_URL = process.env["TERMINALHIRE_API_URL"] || process.env["JPI_API_URL"] || "https://terminalhire.com"; L1128: var SYNC_BASE = "https://www.terminalhire.com";
    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-sync.jsView on unpkg · L1115
    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
    matchType = previous_version_dangerous_delta matchedPackage = terminalhire@0.12.0 matchedIdentity = npm:dGVybWluYWxoaXJl:0.12.0 similarity = 0.585 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-dispatch.jsView on unpkg
    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

    Findings

    1 Critical6 High5 Medium6 Low
    CriticalPrevious Version Dangerous Deltadist/bin/jpi-dispatch.js
    HighInstall Time Lifecycle Scriptspackage.json
    HighChild Processdist/bin/jpi-init.js
    HighShelldist/bin/jpi-statusline-launch.js
    HighSame File Env Network Executiondist/bin/jpi-sync.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