Pin the Hermes remote installer and harden consent (#7179)

Pin the fetched Hermes install.sh/install.ps1 and the checkout they perform to an immutable upstream commit, and distinguish pinned from unpinned sources in the consent warning.
This commit is contained in:
Lee Jackson 2026-07-20 05:04:28 +01:00 committed by GitHub
commit e0132b6d6c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 75 additions and 17 deletions

View file

@ -49,13 +49,22 @@ _HERMES_PROVIDER = "unsloth"
# the wizard's global API-key/model prompts would block the launch and point the
# user at a different (global) provider than the one Unsloth just configured.
# Both installers expose a skip flag: `-SkipSetup` (PowerShell) and
# `--skip-setup` (POSIX; passed to the piped script via `bash -s --`).
# `--skip-setup` (POSIX; passed to the piped script via `bash -s --`). Pin both
# the fetched script and the repository checkout it performs to the same full
# commit so a later change to either upstream branch cannot silently replace
# code that Unsloth executes with the user's privileges.
_HERMES_INSTALL_COMMIT = "f1af945f6c576eccb126fa955edc9be258b33020"
_HERMES_INSTALL_BASE = (
"https://raw.githubusercontent.com/NousResearch/hermes-agent/"
f"{_HERMES_INSTALL_COMMIT}/scripts"
)
_HERMES_WINDOWS_INSTALL_HINT = (
"& ([scriptblock]::Create((irm https://hermes-agent.nousresearch.com/install.ps1))) -SkipSetup"
f"& ([scriptblock]::Create((irm {_HERMES_INSTALL_BASE}/install.ps1)))"
f" -SkipSetup -Commit {_HERMES_INSTALL_COMMIT}"
)
_HERMES_POSIX_INSTALL_HINT = (
"curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent"
"/main/scripts/install.sh | bash -s -- --skip-setup"
f"curl -fsSL {_HERMES_INSTALL_BASE}/install.sh | bash -s --"
f" --skip-setup --commit {_HERMES_INSTALL_COMMIT}"
)
# Hermes refuses to initialize when the model window is under 64,000 tokens; its
# error message points at the model.context_length / auxiliary.compression
@ -1199,6 +1208,16 @@ def _install_source(install_hint: str) -> Optional[str]:
return match.group(0) if match else None
def _pinned_raw_github_commit(source: str) -> Optional[str]:
"""Return the immutable full commit in a raw GitHub URL, if present."""
match = re.match(
r"^https://raw\.githubusercontent\.com/[^/]+/[^/]+/([0-9a-f]{40})/",
source,
flags = re.IGNORECASE,
)
return match.group(1).lower() if match else None
def _install_agent(name: str, install_hint: str) -> Optional[str]:
# Missing agent under --launch: offer to run its documented install command, then
# re-resolve it on PATH. Consent-based (we never auto-run a remote install script
@ -1212,12 +1231,27 @@ def _install_agent(name: str, install_hint: str) -> Optional[str]:
# and nothing checks a signature or hash on the fetched content. Naming the source
# turns a blind "yes" into informed consent.
source = _install_source(install_hint)
warning = (
f"This will download and RUN a script from {source} with your privileges"
if source
else f"This will RUN `{install_hint}` with your privileges"
)
typer.secho(f"{warning}; there is no signature or hash check.", fg = "yellow", err = True)
if source:
pinned_commit = _pinned_raw_github_commit(source)
if pinned_commit:
warning = (
"Security warning: This will download and execute a third-party script "
f"from {source} with your privileges. Unsloth pins this content to "
f"immutable upstream commit {pinned_commit}, but does not independently "
"verify or sandbox it. Continue only if you trust this source and commit."
)
else:
warning = (
"Security warning: This will download and execute an unverified third-party "
f"script from {source} with your privileges. Unsloth does not pin or verify "
"the downloaded content. Continue only if you trust this source."
)
else:
warning = (
f"This will RUN `{install_hint}` with your privileges; "
"there is no signature or hash check."
)
typer.secho(warning, fg = "yellow", err = True)
if not typer.confirm(f"Install `{name}` now with `{install_hint}`?", default = False):
return None
# Run each hint through the shell it is written for: PowerShell (irm | iex, or npm)

View file

@ -7,6 +7,7 @@ from __future__ import annotations
import json
import os
import re
import shlex
import sys
import urllib.error
@ -128,7 +129,7 @@ def test_install_agent_uses_powershell_on_windows(monkeypatch):
assert ran == [["powershell", "-NoProfile", "-Command", install_hint]]
def test_install_agent_warns_and_names_remote_source(monkeypatch, capsys):
def test_install_agent_warns_remote_installer_is_unverified_third_party(monkeypatch, capsys):
# Before the confirm, a remote installer must name the URL it fetches so the
# user consents to a specific source rather than blindly accepting.
monkeypatch.setattr(start.os, "name", "nt")
@ -137,9 +138,23 @@ def test_install_agent_warns_and_names_remote_source(monkeypatch, capsys):
hint = "& ([scriptblock]::Create((irm https://hermes-agent.nousresearch.com/install.ps1))) -SkipSetup"
assert start._install_agent("hermes", hint) is None
err = capsys.readouterr().err
assert "Security warning" in err
assert "unverified third-party script" in err
assert "https://hermes-agent.nousresearch.com/install.ps1" in err
assert "download and RUN" in err
assert "signature or hash" in err
assert "Unsloth does not pin or verify the downloaded content" in err
assert "Continue only if you trust this source" in err
def test_install_agent_reports_immutable_remote_installer_pin(monkeypatch, capsys):
monkeypatch.setattr(start.os, "name", "posix")
monkeypatch.setattr(start.sys, "stdin", SimpleNamespace(isatty = lambda: True))
monkeypatch.setattr(start.typer, "confirm", lambda *a, **k: False)
assert start._install_agent("hermes", start._HERMES_POSIX_INSTALL_HINT) is None
err = capsys.readouterr().err
assert start._HERMES_INSTALL_COMMIT in err
assert "immutable upstream commit" in err
assert "does not independently verify or sandbox it" in err
assert "does not pin or verify" not in err
def test_install_agent_warns_for_package_installer(monkeypatch, capsys):
@ -160,8 +175,8 @@ def test_hermes_install_hint_is_windows_native_on_windows(monkeypatch):
# Scriptblock form so `-SkipSetup` reaches the installer and the interactive
# setup wizard is skipped during the unattended `unsloth start hermes` run.
assert start._hermes_install_hint() == (
"& ([scriptblock]::Create((irm https://hermes-agent.nousresearch.com/install.ps1)))"
" -SkipSetup"
f"& ([scriptblock]::Create((irm {start._HERMES_INSTALL_BASE}/install.ps1)))"
f" -SkipSetup -Commit {start._HERMES_INSTALL_COMMIT}"
)
@ -170,11 +185,20 @@ def test_hermes_install_hint_is_bash_on_posix(monkeypatch):
# `bash -s -- --skip-setup` forwards the skip flag to the piped installer.
assert start._hermes_install_hint() == (
"curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent"
"/main/scripts/install.sh | bash -s -- --skip-setup"
f"curl -fsSL {start._HERMES_INSTALL_BASE}/install.sh | bash -s --"
f" --skip-setup --commit {start._HERMES_INSTALL_COMMIT}"
)
def test_hermes_install_hints_pin_script_and_checkout_to_full_commit():
commit = start._HERMES_INSTALL_COMMIT
assert re.fullmatch(r"[0-9a-f]{40}", commit)
for hint in (start._HERMES_WINDOWS_INSTALL_HINT, start._HERMES_POSIX_INSTALL_HINT):
assert hint.count(commit) == 2
assert "/main/" not in hint
assert "hermes-agent.nousresearch.com" not in hint
def test_refresh_windows_path_noop_off_windows(monkeypatch):
monkeypatch.setattr(start.os, "name", "posix")
before = os.environ.get("PATH", "")