registry  /  haltija  /  1.3.0-beta.10

haltija@1.3.0-beta.10

Browser control for AI agents - query DOM, click, type, run JS, watch mutations

AI Security Review

scanned 2d ago · by lpm-firewall-ai

The package is a local AI/browser control server with high-risk capabilities exposed over HTTP. By default it has no token requirement and permits broad CORS/private-network access, so this is a dangerous capability surface rather than confirmed malware.

Static reason
High-risk behavior combination matched malicious policy.; previous stored version diff introduced dangerous source
Trigger
User runs haltija server/desktop or hj auto-starts the local server
Impact
Local command execution, file read/write, browser automation, and Claude agent execution if reachable by a local or browser-origin attacker
Mechanism
Unauthenticated local REST/WebSocket control server with shell, file, browser, and AI-agent actions
Attack narrative
If a user starts Haltija without HALTIJA_TOKEN, its local server exposes REST and WebSocket endpoints that can drive browser actions, execute shell commands, read/write files, and launch a Claude agent with dontAsk permissions. This is severe if reachable, but the capabilities are documented and activated by user runtime/setup actions, with no install-time execution or external exfiltration found.
Rationale
Static inspection confirms a real high-risk local control surface, but it is package-aligned and user-invoked rather than concrete malicious behavior. Warn rather than block unless policy treats unauthenticated local RCE surfaces as publish-blocking.
Evidence
package.jsonbin/tosijs-dev.mjsbin/hj.mjsbin/mcp-setup.mjsbin/cli-subcommand.mjsdist/server.jsdist/index.jsdist/hj.js~/.haltija/servers/<name>.json~/.local/bin/hj.mcp.json~/.claude.jsonClaude Desktop claude_desktop_config.json/tmp/haltija-screenshots/*
Network endpoints4
localhost:8700localhost:8701ws://localhost:8700/ws/browserwss://localhost:8700/ws/browser

Decision evidence

public snapshot
AI called this Suspicious at 86.0% confidence as Dangerous Capability with medium false-positive risk.
Evidence for warning
  • dist/server.js exposes unauthenticated REST by default unless HALTIJA_TOKEN is set
  • dist/server.js sets Access-Control-Allow-Origin:* and Access-Control-Allow-Private-Network:true
  • dist/server.js /terminal command path executes shell commands via spawn("sh", ["-c", fullCommand])
  • dist/server.js /files/read and /files/write read/write arbitrary absolute paths requested over REST
  • dist/server.js can spawn claude -p with --permission-mode dontAsk and broad allowed tools
  • bin/mcp-setup.mjs writes .mcp.json/Claude config only when setup command is invoked
Evidence against
  • package.json has no install/preinstall/postinstall lifecycle hooks
  • Dangerous shell/browser/agent controls match the declared browser-control-for-AI-agents purpose
  • Network endpoints are local server URLs and documented examples, not external exfiltration hosts
  • dist/hj.js invisible Unicode occurs in EVIL_UNICODE test data, not executable control flow
  • MCP/Claude config mutation is explicit CLI setup, not import-time or install-time
  • No credential harvesting or hardcoded exfiltration endpoint found
Behavioral surface
Source
ChildProcessCryptoEnvironmentVarsEvalFilesystemNetworkShellWebSocket
Supply chain
HighEntropyStringsMinifiedObfuscatedUrlStrings
ManifestNo manifest risk signals triggered.
scanned 36 file(s), 3.45 MB of source, external domains: bun.sh, claude.ai, example.com, github.com, haltija-test.example, www.google.com, www.w3.org

Source & flagged code

10 flagged · loading source
bin/tosijs-dev.mjsView file
14L15: import { spawn, execSync as execSyncImported } from 'child_process' L16: import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs' ... L20: L21: const __dirname = dirname(fileURLToPath(import.meta.url)) L22: const serverPath = join(__dirname, '../dist/server.js') ... L55: --https-port <n> Set HTTPS port (default: 8701) L56: --token <value> Require X-Haltija-Token header on REST and ?token= on WebSocket L57: (default: off; sets HALTIJA_TOKEN) ... L146: try { L147: const config = JSON.parse(readFileSync(configPath, 'utf8')) L148: if (config.mcpServers?.haltija) {
Critical
Download Execute

Source downloads or fetches remote code and executes it.

bin/tosijs-dev.mjsView on unpkg · L14
14L15: import { spawn, execSync as execSyncImported } from 'child_process' L16: import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'
High
Child Process

Package source references child process execution.

bin/tosijs-dev.mjsView on unpkg · L14
14Cross-file remote execution chain: bin/tosijs-dev.mjs spawns dist/server.js; helper contains network access plus dynamic code execution. L14: L15: import { spawn, execSync as execSyncImported } from 'child_process' L16: import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs' ... L20: L21: const __dirname = dirname(fileURLToPath(import.meta.url)) L22: const serverPath = join(__dirname, '../dist/server.js') ... L55: --https-port <n> Set HTTPS port (default: 8701) L56: --token <value> Require X-Haltija-Token header on REST and ?token= on WebSocket L57: (default: off; sets HALTIJA_TOKEN) ... L146: try { L147: const config = JSON.parse(readFileSync(configPath, 'utf8')) L148: if (config.mcpServers?.haltija) {
High
Cross File Remote Execution Context

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

bin/tosijs-dev.mjsView on unpkg · L14
5* Usage: L6: * npx haltija # Launch desktop app (or server if electron unavailable) L7: * npx haltija --server # Server only (for CI, headless, bookmarklet usage) ... L14: L15: import { spawn, execSync as execSyncImported } from 'child_process' L16: import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'
High
Runtime Package Install

Package source invokes a package manager install command at runtime.

bin/tosijs-dev.mjsView on unpkg · L5
dist/client.jsView file
70} L71: async eval(code) { L72: const res = await fetch(`${this.baseUrl}/eval`, {
High
Eval

Package source references dynamic code evaluation.

dist/client.jsView on unpkg · L70
apps/desktop/main.jsView file
23const os = require('os') L24: const { spawn } = require('child_process') L25: const http = require('http') L26: const { attachNetwork, detachNetwork, getNetworkLog, getNetworkStats, clearNetwork, isMonitoring } = require('./cdp-network.js') ... L32: // Haltija server config L33: const HALTIJA_PORT = parseInt(process.env.HALTIJA_PORT || '8700') L34: const HALTIJA_SERVER = `http://localhost:${HALTIJA_PORT}`
High
Same File Env Network Execution

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

apps/desktop/main.jsView on unpkg · L23
dist/index.jsView file
49Trigger-reachable chain: manifest.main -> dist/index.js L49: import { join as join2 } from "path"; L50: import { spawn } from "child_process"; L51: function createTerminalState(maxPushBuffer = 100) { ... L72: ws, L73: cwd: defaultCwd || process.env.HOME || process.cwd() L74: }; ... L171: const raw = readFileSync2(configPath, "utf-8"); L172: const parsed = JSON.parse(raw); L173: if (parsed && typeof parsed === "object" && parsed.tools) { ... L265: }); L266: let stdout = ""; L267: let stderr = "";
Critical
Trigger Reachable Dangerous Capability

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

dist/index.jsView on unpkg · L49
49import { join as join2 } from "path"; L50: import { spawn } from "child_process"; L51: function createTerminalState(maxPushBuffer = 100) { ... L72: ws, L73: cwd: defaultCwd || process.env.HOME || process.cwd() L74: }; ... L171: const raw = readFileSync2(configPath, "utf-8"); L172: const parsed = JSON.parse(raw); L173: if (parsed && typeof parsed === "object" && parsed.tools) { ... L265: }); L266: let stdout = ""; L267: let stderr = "";
High
Obfuscated Payload Loader

Source contains an obfuscator-style string-array loader that reconstructs and executes hidden code.

dist/index.jsView on unpkg · L49
dist/hj.jsView file
558contains invisible/control Unicode U+202E (right-to-left override) "<U+202E>Reverse",
Critical
Trojan Source Unicode

Source contains bidi control or invisible Unicode characters associated with Trojan Source attacks.

dist/hj.jsView on unpkg · L558
bin/cli-subcommand.mjsView file
matchType = previous_version_dangerous_delta matchedPackage = haltija@1.3.0 matchedIdentity = npm:aGFsdGlqYQ:1.3.0 similarity = 0.889 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; route for source-aware review.

bin/cli-subcommand.mjsView on unpkg

Findings

4 Critical7 High3 Medium5 Low
CriticalDownload Executebin/tosijs-dev.mjs
CriticalTrojan Source Unicodedist/hj.js
CriticalTrigger Reachable Dangerous Capabilitydist/index.js
CriticalPrevious Version Dangerous Deltabin/cli-subcommand.mjs
HighChild Processbin/tosijs-dev.mjs
HighShell
HighEvaldist/client.js
HighSame File Env Network Executionapps/desktop/main.js
HighObfuscated Payload Loaderdist/index.js
HighCross File Remote Execution Contextbin/tosijs-dev.mjs
HighRuntime Package Installbin/tosijs-dev.mjs
MediumNetwork
MediumEnvironment Vars
MediumStructural Risk Force Deep Review
LowScripts Present
LowFilesystem
LowObfuscated
LowHigh Entropy Strings
LowUrl Strings