Merge pull request #18 from Strategic-Automation/codex/issue-1-terminal-compound-policy

Fix compound terminal command policy (v2.0.3)
This commit is contained in:
Dan
2026-07-18 18:12:33 -04:00
committed by GitHub
7 changed files with 96 additions and 40 deletions
+4
View File
@@ -1,5 +1,9 @@
# Changelog
## 2.0.3
- Fixed raw-terminal compound-command classification so every pipeline, logical, semicolon, and newline segment is checked independently, and package/source exemptions require every URL in the segment to use an approved source host.
## 2.0.2
- Restricted all GitHub Actions workflow tokens to read-only repository contents, resolving the three least-privilege code-scanning alerts without changing workflow behavior.
+1 -1
View File
@@ -1,6 +1,6 @@
# violin - supervised agentic Hermes pentest profile
name: violin
version: 2.0.2
version: 2.0.3
description: "A supervised agentic Hermes penetration testing profile for authorised Kali/Parrot-based security assessment, reconnaissance, exploit validation, and reporting workflows."
hermes_requires: ">=0.18.0"
author: "Violin contributors"
+1 -1
View File
@@ -1,5 +1,5 @@
name: violin-guard
version: "2.0.2"
version: "2.0.3"
description: Typed scope guards and an execute-and-record boundary with bounded synchronization windows.
kind: standalone
provides_tools:
+58 -36
View File
@@ -55,11 +55,18 @@ _DOMAIN_RE = re.compile(
re.IGNORECASE,
)
_URL_RE = re.compile(r"\b(?:https?|ftp|wss?|file)://[^\s'\"<>]+", re.IGNORECASE)
_KNOWN_SOURCE_HOST_RE = re.compile(
r"https?://(?:[^/]*\.)?(?:github\.com|gitlab\.com|bitbucket\.org|"
r"pypi\.org|files\.pythonhosted\.org|registry\.npmjs\.org|"
r"crates\.io|proxy\.golang\.org|go\.dev)(?::\d+)?(?:/|$)",
re.IGNORECASE,
_KNOWN_SOURCE_HOSTS = frozenset(
{
"bitbucket.org",
"crates.io",
"files.pythonhosted.org",
"github.com",
"gitlab.com",
"go.dev",
"proxy.golang.org",
"pypi.org",
"registry.npmjs.org",
}
)
_NETWORK_PATH_RE = re.compile(r"/(?:dev/)?(?:tcp|udp)/", re.IGNORECASE)
_NETWORK_MODULE_RE = re.compile(
@@ -123,6 +130,13 @@ def _url_hosts(command: str) -> list[str]:
return hosts
def _is_known_source_host(host: str) -> bool:
normalized = host.lower().rstrip(".")
return any(
normalized == known or normalized.endswith(f".{known}") for known in _KNOWN_SOURCE_HOSTS
)
def _has_target_literal(command: str) -> bool:
"""Inspect shell arguments, not arbitrary source code or file paths."""
for segment in _COMMAND_SPLIT_RE.split(command):
@@ -163,6 +177,42 @@ def _has_target_literal(command: str) -> bool:
return False
def _block_terminal_segment(segment: str) -> str | None:
if _NETWORK_PATH_RE.search(segment):
return _message("network socket path detected in the raw terminal command")
executable = _first_executable(segment)
if executable in _SCRIPT_INTERPRETERS and _NETWORK_MODULE_RE.search(segment):
return _message("network-capable script primitive detected in the raw terminal command")
# Package/source retrieval is allowed for local setup (for example git
# clone or pip install). URLs and host literals in all other commands are
# treated as target interaction and must use the typed guard.
is_source_command = _is_package_or_source_command(segment)
url_hosts = _url_hosts(segment)
if not is_source_command and url_hosts:
return _message("URL detected in a non-package raw terminal command")
# Public package/source URLs are host-local setup, not assessment traffic.
# Keep numeric authorities conservative: a clone/install from an IP may be
# an engagement target and must go through the typed guard.
if (
is_source_command
and url_hosts
and all(_is_known_source_host(host) for host in url_hosts)
and not _IPV4_RE.search(segment)
):
return None
if executable not in _LOCAL_COMMANDS and _has_target_literal(segment):
return _message("target host literal detected in the raw terminal command")
if executable in _SCRIPT_INTERPRETERS and _SUSPICIOUS_SCRIPT_RE.search(segment):
return _message("assessment script detected in the raw terminal command")
return None
def block_terminal_command(command: str) -> str | None:
"""Return a block message for clearly target-touching raw terminal calls.
@@ -174,37 +224,9 @@ def block_terminal_command(command: str) -> str | None:
if not isinstance(command, str) or not command.strip():
return None
if _NETWORK_PATH_RE.search(command):
return _message("network socket path detected in the raw terminal command")
executable = _first_executable(command)
if executable in _SCRIPT_INTERPRETERS and _NETWORK_MODULE_RE.search(command):
return _message("network-capable script primitive detected in the raw terminal command")
# Package/source retrieval is allowed for local setup (for example git
# clone or pip install). URLs and host literals in all other commands are
# treated as target interaction and must use the typed guard.
url_hosts = _url_hosts(command)
if not _is_package_or_source_command(command) and url_hosts:
return _message("URL detected in a non-package raw terminal command")
# Public package/source URLs are host-local setup, not assessment traffic.
# Keep numeric authorities conservative: a clone/install from an IP may be
# an engagement target and must go through the typed guard.
if (
_is_package_or_source_command(command)
and url_hosts
and _KNOWN_SOURCE_HOST_RE.search(command)
and not _IPV4_RE.search(command)
):
return None
if executable not in _LOCAL_COMMANDS and _has_target_literal(command):
return _message("target host literal detected in the raw terminal command")
if executable in _SCRIPT_INTERPRETERS and _SUSPICIOUS_SCRIPT_RE.search(command):
return _message("assessment script detected in the raw terminal command")
for segment in _COMMAND_SPLIT_RE.split(command):
if message := _block_terminal_segment(segment):
return message
return None
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "violin"
version = "2.0.2"
version = "2.0.3"
description = "Supervised agentic Hermes penetration-testing profile"
requires-python = ">=3.11"
dependencies = ["filelock>=3.13,<4"]
+30
View File
@@ -113,6 +113,36 @@ def test_local_source_retrieval_remains_available() -> None:
assert result is None
@pytest.mark.parametrize(
"raw_command",
[
"echo x | nc victim.example 80",
"git clone https://github.com/org/repo; curl https://victim.example/admin",
"git clone https://github.com/org/repo && nmap victim.example",
(
"pip install https://files.pythonhosted.org/package.whl "
"https://victim.example/package.whl"
),
],
)
def test_compound_terminal_commands_cannot_hide_target_segments(raw_command: str) -> None:
result = _pre_tool_call_hook(tool_name="terminal", args={"command": raw_command})
assert result["action"] == "block"
assert "violin_exec" in result["message"]
@pytest.mark.parametrize(
"raw_command",
[
"git clone https://github.com/example/project.git && echo cloned",
"echo local | cat",
],
)
def test_safe_compound_terminal_commands_remain_available(raw_command: str) -> None:
assert _pre_tool_call_hook(tool_name="terminal", args={"command": raw_command}) is None
def test_safe_local_terminal_command_remains_available() -> None:
result = _pre_tool_call_hook(
tool_name="terminal",
Generated
+1 -1
View File
@@ -154,7 +154,7 @@ wheels = [
[[package]]
name = "violin"
version = "2.0.2"
version = "2.0.3"
source = { virtual = "." }
dependencies = [
{ name = "filelock" },