portal-mcp-server@4.0.3
CAUTION· not startedPublisher claims: "Agent-feels-local SSH orchestration MCP server — persistent bash, hash-protected editing, SFTP, tunnels, multi-host orchestration over AsyncSSH" Observed in sandbox: no network, touches nothing outside the project, writes no memory.
Coverage
Not started: dependency install failed; the server itself was not observed.- Tested
- the dependency install step · every file read, file written and host contacted by what ran
- Not tested
- server startup · the tool list · tool behavior
What this means for the label: Caution here covers what ran. The server's tools were not exercised with a real credential, so nothing about their behavior is asserted; the credential summary below says what to do with that.
Before you give it your token
- It will ask for
GITHUB_TOKENPORTAL_AUTH_TOKENPORTAL_CREDENTIAL_AGENT_SOCKETConfiguration, not credentials: PORTAL_ALLOW_LOCAL_EXEC, PORTAL_ALLOW_TUNNEL_EXPOSURE, PORTAL_BASH_HEARTBEAT_INTERVAL, PORTAL_JOB_MAX_LIVE, PORTAL_JOB_PERSIST, PORTAL_JOB_STATE_FILE, PORTAL_JOB_TTL, PORTAL_LOGIN_SHELL, PORTAL_MAX_TIMEOUT, PORTAL_SSH_CONFIG, PROGRAMDATA, XDG_RUNTIME_DIR- Where it talks to
- no vendor host named in the code
- What the honeypot saw
- no credential files read · no network
- What we could not test
- server startup · the tool list · tool behavior
Connect it with a scoped credential, under the guard. It asks for GITHUB_TOKEN, PORTAL_AUTH_TOKEN, PORTAL_CREDENTIAL_AGENT_SOCKET and talks to its vendor. Give the credential to the guard, not the server: the value never enters the server's environment and only travels to the host you bind. What the tools do with a real credential was not observed.
Never paste a credential into a chat, a ticket or a report, including ours. How the guard holds it.
Is [email protected] safe to install?
KillerSkills ran it in the KillerSkills honeypot sandbox (2 scenarios; not started: dependency install failed; the server itself was not observed) on 2026-11-14 and scored it 62/100 (Caution). It is not known to be malicious, but it has behaviour or code a security policy should decide on before install. Notable findings: Privileged sudo command; Access to sensitive file paths; Privileged sudo command. Summary: no network, touches nothing outside the project, writes no memory.
Behavior trace
Honeypot run · 0 s · 2 scenarios · 0 deviationsStatic findings
f"No sudo password available for host '{host}'; the command was NOT "Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# (c) Only a name was given. If ~/.ssh/config has a matching HostFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose."""Friendly error when no sudo password is available for the LOCAL machine.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell."in a separate terminal, type the password at the hidden prompt, then "Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.<summary>Expand</summary> A stdio MCP server's stderr is invisible to the user, so operationally important warnings (misconfigured yaml, missing credentials, igFix: Remove the text that addresses the agent rather than the reader; describe behavior, do not instruct.Hosts already defined in ~/.ssh/config are auto-resolved on first use; explicitFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.or just `name` if ~/.ssh/config has a matching Host alias (registers aFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose."No sudo password available for THIS machine (local_exec); the command "Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.<summary>为什么这是一条不为便利让步的边界("活过 agent 关闭"为何不靠子进程)</summary> agent 常想要"持久传输 / 活过 agent 关闭",最直觉的实现是 `nohup` 一个 `scp` / `rsync` 子进程。portal **故意不做**——子进程那条路绕开了上面这条统一凭Fix: Remove the text that addresses the agent rather than the reader; describe behavior, do not instruct.<summary>Expand</summary> Every credential kind — SSH key, login password, key passphrase, sudo password, named secret — is resolved on one in-process asyncssh Fix: Remove the text that addresses the agent rather than the reader; describe behavior, do not instruct.f" • Temporary (no-echo, cached with a TTL): run `portal sudo set "Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.client config (`~/.ssh/config` + system-wide fallback; or onlyFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.<summary>Maintainer boundaries & footguns</summary> - **exec-output-strip vs. file-read-must-not-strip**: one-shot exec strips trailing newlines (shell conventiFix: Remove the text that addresses the agent rather than the reader; describe behavior, do not instruct.22), key_path (else asyncssh falls back to ~/.ssh/id_* or ssh-agent),Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.expanded to ``cd /tmp; rm -rf / && <cmd>``.Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.``cd`` argument. Before this fix, ``cwd="/tmp; rm -rf /"`` would haveFix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.worst (e.g. ``"FOO; rm -rf /"``) an injection sink. Reject up-front.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.f"{host}` in a separate terminal, type the password at the hidden "Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.f"user to paste the secret value into this conversation."Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.<summary>Expand</summary> [asyncssh](https://github.com/ronf/asyncssh) (EPL-2.0 / GPL-2.0 dual-licensed) is an independent pure-Python SSHv2 implementation, proFix: Remove the text that addresses the agent rather than the reader; describe behavior, do not instruct.`portal secret set <name>` in their own terminal; you passFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose."terminal first — never have them paste the password into the chat."Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose."""Friendly error when no sudo password is available for ``host``.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.22), key_path (else asyncssh falls back to ~/.ssh/id_* or ssh-agent),Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose."terminal first (never have them paste the password into the conversation)."Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.<summary>Expand the dev setup</summary> ```bash git clone [email protected]:TMYTiMidlY/portal-mcp-server.git cd portal-mcp-server uv sync --all-extras source .venvFix: Remove the text that addresses the agent rather than the reader; describe behavior, do not instruct.Hosts already defined in ~/.ssh/config are auto-resolved on first use; explicitFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.- command="rm -rf /" : check whether this command would be allowed on this host.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.- command="rm -rf /" : check whether this command would be allowed on this host.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.you pass secrets=["<name>"] and reference it as the uppercased env varFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.f"chmod +x {quoted_path} && {interpreter} {quoted_path}",Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.sudo password (see :mod:`sudo_creds`): the value must reach the executed commandFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# asyncssh's *discovery* is a single inline line — ``Path('~/.ssh/config')``Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.# Unix signal names accepted by `kill -<NAME>`. We don't accept numeric signalsFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.input framing in one place. Values are fed in list order, AFTER the sudoFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell."the literal 'none' is accepted. Falling back to ~/.ssh/config.", raw,Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.[](https://pypi.org/project/portal-mcp-server/)Fix: Namespace tool names and keep tool descriptions literal; no instructions to the agent inside them."feeds the stored sudo password to `sudo -S -k` on stdin. If no password is "Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.conventional ``~/.ssh/config`` default. :func:`ssh_config_files` remains theFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.log). ``-k`` forces a fresh auth so a cached sudo ticket can't mask a wrongFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# The zh_CN sudo prompt "的密码" is its UTF-8 bytes. Kept deliberately small:Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell."""Best-effort kill of a stuck subprocess (timeout path)."""Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.model entirely, then feeds it to ``sudo -S`` on stdin (see remote_bash):Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.TEST_KEY = os.environ.get("PORTAL_TEST_KEY_PATH", os.path.expanduser("~/.ssh/id_ed25519"))Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose."(most often sudo asking for a password, but also ssh's first-connect "Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.expanded to ``cd /tmp; rm -rf / && <cmd>``.Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.the loop keep scheduling other coroutines while the npx subprocessFix: Pin versions and sources; vendor anything fetched at install time.keeps scheduling other coroutines while the npx subprocess runs — a syncFix: Pin versions and sources; vendor anything fetched at install time.# sudo command is the one-shot sudo path, not a retry (which would wedge again).Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.``cd`` argument. Before this fix, ``cwd="/tmp; rm -rf /"`` would haveFix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.wrapped = f"sudo -S -k -p '' -- bash -c {quote_shell(body)}"Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# * unset -> user ``~/.ssh/config`` + system ``/etc/ssh/ssh_config``Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.f"if kill -0 {rec.remote_pid} 2>/dev/null; then A=yes; else A=no; fi; "Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# Clear credential caches (sudo / ssh / key passphrase / named-secret).Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.* ``use_sudo`` / ``secrets`` are NOT supported in the background (sudo -S wantsFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.key passphrase and not the sudo password (the one fed to ``sudo -S`` on stdinFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# (e.g. './.github/x' -> 'github/x', './.env' -> 'env').Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.f'[ -n "$PGID" ] && kill -{sig} -"$PGID" 2>/dev/null; 'Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.f'kill -{sig} {pid} 2>/dev/null; 'Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# still kill its jobs (consistent with remote_tunnel close).Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.derivative (it happens to reuse the secret / sudo credential machinery), so theFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.rb"\[sudo\][^\n]*(?:password for |\xe7\x9a\x84\xe5\xaf\x86\xe7\xa0\x81)"Fix: Put hosts, commands and strings in plain source. Decoding at runtime looks identical to malware and costs points on its own.1. **Live input (1b)** — ``portal sudo set <host>`` prompts withFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.the shell / sudo don't survive a timeout. Falls back to killing just theFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.command="rm -rf /tmp/x", timeout=5)Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.worst (e.g. ``"FOO; rm -rf /"``) an injection sink. Reject up-front.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.return "~/.ssh/config"Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.📖 [Repository & full docs](https://github.com/TMYTiMidlY/portal-mcp-server) | 🇬🇧 [English README](https://github.com/TMYTiMidlY/portal-mcp-server/blob/main/README.en.md)Fix: Namespace tool names and keep tool descriptions literal; no instructions to the agent inside them."""sudo_creds — out-of-band sudo password provisioning.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.``secret-tool lookup sudo web01``). Symmetric to the existingFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.prevents mismatches like a sudo password cached under ``'web01 '`` neverFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.``sudo -S -k -p '' -- bash -c <body>`` and writes the password to theFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.The agent (LLM) must never see a sudo password: if a password were passed asFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# backgrounded npx that keeps holding pipes open.Fix: Pin versions and sources; vendor anything fetched at install time."and runnable (`npx -y cc-safety-net@latest doctor`), or set "Fix: Pin versions and sources; vendor anything fetched at install time.name, AFTER the sudo password) and decoded back inside the already-elevatedFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.f'if kill -0 {pid} 2>/dev/null; then echo ALIVE; else echo DEAD; fi'Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.user ~/.ssh/config and the system-wide config are suppressed."""Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.TEST_KEY = os.environ.get("PORTAL_TEST_KEY_PATH", os.path.expanduser("~/.ssh/id_ed25519"))Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.``localhost``. The sudo password reaches ``sudo -S`` only through the in-memoryFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell."""hosts.yaml <-> ~/.ssh/config interaction: conflict detection + overlay.Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.out = await cli.remote_shell("live-smoke", "rm -rf /tmp/whatever", timeout=2)Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# ── explicit ssh-login-password → sudo cache convenience ────────────────────Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.err = await pol.check_command("rm -rf /")Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.err = await cli._gate_exec(["safe-01"], ["echo a", "rm -rf /"])Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.assert "~/.ssh/config" in outFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.to the sudo exec as env (delivered on stdin inside the elevated shell), andFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.(the reported ``sudo password for host 'LisaHost '`` bug).Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.TEST_KEY = os.environ.get("PORTAL_TEST_KEY_PATH", os.path.expanduser("~/.ssh/id_ed25519"))Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.pol.command_blocklist = ["rm -rf /"]Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell."""Local sudo for local_exec — the LOCAL counterpart of the remoteFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.(PORTAL_SSH_CONFIG), not a hardcoded ~/.ssh/config."""Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.# sudo + secrets moved here (one-shot paths).Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.assert cmd.startswith("cd '/tmp; rm -rf /'")Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.asyncssh so GBK / Latin-1 bytes don't kill the channel."""Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.with pytest.raises(ToolError, match="No sudo password"):Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.(read nothing) instead of letting it fall back to ~/.ssh/config."""Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.# which would have run `rm -rf /` as a separate command.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.".github/workflows/ci.yml", ".env", "src/app.py"}Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.assert cmd.startswith("cd '/tmp; rm -rf /'")Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.["portal-mcp-server", "--transport", "streamable_http", "--host", "0.0.0.0"])Fix: Namespace tool names and keep tool descriptions literal; no instructions to the agent inside them.# BEFORE the fix this produced: cd /tmp; rm -rf / && idFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell._assert_no_unquoted_metas(cmd, ["; rm -rf /", "&& rm"])Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.assert "portal sudo set web01" in msg # temporary (no-echo)Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.leading dots: './.github/x' -> 'github/x', './.env' -> 'env'). Regression."""Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.err = await cli._gate_exec(["safe-01"], ["echo a", "rm -rf /"])Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell._assert_no_unquoted_metas(cmd, ["; rm -rf /", "&& rm"])Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.monkeypatch.setenv("PORTAL_SSH_CONFIG", "none") # don't read real ~/.ssh/configFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.TEST_KEY = os.environ.get("PORTAL_TEST_KEY_PATH", os.path.expanduser("~/.ssh/id_ed25519"))Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.await ssh_exec("h", "id", cwd="/tmp; rm -rf /")Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# ── sudo across the fan-out ─────────────────────────────────────────────────Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.* sudo / secret exec results are flagged high_risk so the agent reports it;Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.err = await pol.check_command("rm -rf /")Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.cache / per-user agent (``portal sudo set-local``) or a TOP-LEVEL ``<local>:``Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.pol.command_blocklist = ["rm -rf /"]Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell."./.github/workflows/ci.yml\n./.env\n./src/app.py\n",Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.monkeypatch.setenv("PORTAL_SSH_CONFIG", "none") # don't read real ~/.ssh/configFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.await ssh_exec("h", "id", cwd="/tmp; rm -rf /")Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.proc = subprocess.run(["bash", "-c", body], input=payload.encode(),Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.assert all("no sudo password" in r.get("error", "") for r in out)Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# BEFORE the fix this produced: cd /tmp; rm -rf / && idFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.assert "sudo password also cached" in capsys.readouterr().outFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.# Empty tuple = "use default ~/.ssh/known_hosts and be strict",Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.Runs the generated script body (minus the sudo wrapper) through real bashFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# The shortcut path that defers to ~/.ssh/config used to beFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.command="rm -rf /", timeout=5)Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.command="rm -rf /tmp/x", serialize=True, timeout=5,Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.await sm.execute_in_session(sid, "sudo whoami", timeout=10)Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.result = subprocess.run(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.wired["stat"] = "ro;rm -rf / root 644\n" # injected metacharsFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.with pytest.raises(rte.RemoteEditError, match="sudo password"):Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.await rte._sudo_cat("h", "/etc/shadow", "utf-8")Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.await sm.execute_in_session(sid, "sudo whoami", timeout=10)Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.assert "; rm -rf /" not in out.split(" && ", 1)[0].replace("'", "")[:0]Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell."h", ["echo a", "sudo whoami", "echo b"])Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.res = await rte.remote_read("h", "/etc/shadow", use_sudo=True)Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.("a;rm -rf /", "'a;rm -rf /'"),Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.assert out.startswith("cd '/tmp; rm -rf /'")Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.`portal sudo set`, `portal secret set`) must keep independent caches so clearing oneFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.assert out.startswith("cd '/tmp; rm -rf /'")Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.assert "; rm -rf /" not in out.split(" && ", 1)[0].replace("'", "")[:0]Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.commands=["uptime", "rm -rf /var", "echo done"], timeout=5)Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.assert cmd.startswith("sudo -S -k -p '' cat -- ") and stdin == "sekret\n"Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.assert "chown root:root" in place and "chmod 644" in placeFix: Request the narrowest permission that works; replace wildcards with the exact paths or hosts.out = build_cwd_prefix("/tmp; rm -rf /", "ls")Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.(home / ".ssh").mkdir(parents=True) # no ~/.ssh/config writtenFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.assert res["stopped_at"] == "sudo whoami"Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.assert not any("kill -" in c and "PGID=" in c for c in rec) # no signal sentFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.await cli.remote_job(action="submit", host="h", command="rm -rf /")Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.marker, cancel uses `kill -`).Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.out = build_cwd_prefix("/tmp; rm -rf /", "ls")Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.out = await ssh_read_file("h", "/etc/passwd\x00trick")Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.``sudo -S`` only through the in-memory cache (populated by the ``portal sudo set``Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.assert any("kill -KILL 100" in c for c in rec)Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.command="rm -rf /", timeout=5)Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.validate_remote_path("/tmp/legit\x00/etc/passwd")Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose."""Install a policy that blocks ``rm -rf /`` and only allows hostsFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.assert paths.ssh_config_source_label() == "~/.ssh/config"Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.Mirrors the philosophy of test_password_auth.py: a sudo password must reachFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.await cli.remote_job(action="submit", host="h", command="rm -rf /")Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.event loop must not be blocked while the npx subprocess runs).Fix: Pin versions and sources; vendor anything fetched at install time.out = await rte._sudo_cat("h", "/etc/shadow", "utf-8")Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.assert paths.ssh_config_source_label() == "~/.ssh/config"Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose."""sudo password provisioning — out-of-band sources, never via the LLM.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.await remote_read("h", "/etc/passwd\x00fake")Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.("a;rm -rf /", "'a;rm -rf /'"),Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.* ssh_exec_script's cleanup ``rm -f /tmp/<x>`` was outside ``finally``, soFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.sudo path that preserves the patch hash contract, owner/mode, and atomicity.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.wired["stat"] = "ro;rm -rf / root 644\n" # injected metacharsFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.assert validate_remote_path("../etc/passwd") == "../etc/passwd"Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.commands=["uptime", "rm -rf /opt"],Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# ── remote_read(use_sudo=True) uses sudo cat with a valid hash ───────────────Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# connection_manager — patch both so every sudo hop hits the fake pool.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# Live agent round trip (1b): `portal sudo set` client → per-user agent cacheFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.assert "/remote/dir/a.txt" in sftp.put_callsFix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why.Cost to the agent
Tokens estimated at four characters each. The tool list is re-read by the agent on every turn, so it is the number to compare between servers that do the same job: ranked across the index.
Known vulnerabilities
Not checked against OSV yet; the weekly check has not reached this version.
What would make this Verified
18 points to 80 · weights: behavior 45%, static 25%, prompt 15%, publisher 15%- Prompting user to enter sensitive credentials≈ +9 on the totalStop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.
- Reads credential paths from the home directory≈ +9 on the totalStop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.
- Destructive command against a filesystem root or device≈ +9 on the totalBuild commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.
- Unclaimed publisher≈ +8 on the totalClaim tmytimidly: a verified publisher gets +50 on publisher trust. It is free and usually instant with GitHub sign-in.
- Hidden instructions≈ +6 on the totalRemove HTML comments, zero-width characters and white-on-white text that carry instructions. Everything the agent is meant to read should be visible to the person reading the file.
- Privileged sudo command≈ +4 on the totalBuild commands from fixed arguments; do not interpolate untrusted strings into a shell.