mirror of
https://github.com/Strategic-Automation/violin.git
synced 2026-08-14 12:33:37 +02:00
@@ -2,6 +2,7 @@
|
||||
|
||||
## 3.0.0
|
||||
|
||||
- Fixed target extraction for dotted identifiers and direct `/dev/tcp`/`/dev/udp` redirections, and prevented network-capable local-looking commands from bypassing execution accounting.
|
||||
- Made interrupted skill preparation recoverable with expiring reservations and stale-owner protection; batch review now remains tied to the delivered execution receipt.
|
||||
- Stabilized the core engagement workflow: domain/URL-only scopes now validate, runtime execution cannot substitute another scope file, PTT/review CLI contracts carry skill metadata, and review reuses the active delivered binding.
|
||||
- Added receipt-backed skill routing, delivery, task binding, browser enforcement, Kali auto-backend selection, proof-based finding review, and semantic anti-stuck enforcement.
|
||||
|
||||
@@ -14,6 +14,7 @@ class CommandSegment:
|
||||
raw_text: str
|
||||
words: list[str] = field(default_factory=list)
|
||||
executable: str = ""
|
||||
redirects: list[str] = field(default_factory=list)
|
||||
|
||||
|
||||
class _CommandVisitor:
|
||||
@@ -31,8 +32,19 @@ class _CommandVisitor:
|
||||
segment_text = self.command[start:end]
|
||||
words = self._collect_words(node)
|
||||
executable = self._extract_executable(words)
|
||||
redirects = [
|
||||
str(getattr(getattr(part, "output", None), "word", ""))
|
||||
for part in getattr(node, "parts", [])
|
||||
if getattr(part, "kind", None) == "redirect"
|
||||
and getattr(getattr(part, "output", None), "word", None)
|
||||
]
|
||||
self.segments.append(
|
||||
CommandSegment(raw_text=segment_text, words=words, executable=executable)
|
||||
CommandSegment(
|
||||
raw_text=segment_text,
|
||||
words=words,
|
||||
executable=executable,
|
||||
redirects=redirects,
|
||||
)
|
||||
)
|
||||
|
||||
if hasattr(node, "parts"):
|
||||
@@ -52,6 +64,11 @@ class _CommandVisitor:
|
||||
if kind == "word" and hasattr(n, "word"):
|
||||
words.append(n.word)
|
||||
self.words.append(n.word)
|
||||
elif kind == "redirect":
|
||||
output = getattr(n, "output", None)
|
||||
if output is not None and hasattr(output, "word"):
|
||||
words.append(output.word)
|
||||
self.words.append(output.word)
|
||||
if hasattr(n, "parts"):
|
||||
for child in n.parts:
|
||||
collect(child)
|
||||
|
||||
@@ -170,8 +170,16 @@ def mutate_json(path: Path, mutation) -> Any:
|
||||
|
||||
def is_local_bookkeeping_command(command: str) -> bool:
|
||||
"""Whether a command is a harmless local bookkeeping action."""
|
||||
leading = command.strip().split(maxsplit=1)
|
||||
return bool(leading) and leading[0] in LOCAL_TOOLS
|
||||
from .bash_ast import parse_bash_segments
|
||||
from .targets import extract_target_candidates
|
||||
|
||||
segments = parse_bash_segments(command)
|
||||
if len(segments) != 1:
|
||||
return False
|
||||
segment = segments[0]
|
||||
if segment.executable not in LOCAL_TOOLS or segment.redirects:
|
||||
return False
|
||||
return not extract_target_candidates(command)
|
||||
|
||||
|
||||
# Sync credit / pending sync
|
||||
|
||||
@@ -140,3 +140,15 @@ def test_callback_hosts_are_secondary_only_and_exclusions_still_win(tmp_path: Pa
|
||||
scope, "nc -l -v -s 10.10.10.99 4444", primary_target="10.10.10.10"
|
||||
)
|
||||
assert any("excluded target 10.10.10.99" in error for error in excluded.errors)
|
||||
|
||||
|
||||
def test_direct_dev_tcp_redirection_is_checked_and_not_bookkeeping(tmp_path: Path) -> None:
|
||||
scope = tmp_path / "scope.yaml"
|
||||
_write_scope(scope, callback_hosts="10.10.10.10")
|
||||
|
||||
result = check_scope_targets(
|
||||
scope,
|
||||
"echo ready > /dev/tcp/10.10.10.99/4444",
|
||||
primary_target="10.10.10.10",
|
||||
)
|
||||
assert any("10.10.10.99" in error for error in result.errors)
|
||||
|
||||
@@ -33,6 +33,8 @@ def test_network_clients_are_not_local_bookkeeping() -> None:
|
||||
for command in ("curl https://10.10.10.10", "dig 10.10.10.10", "host 10.10.10.10"):
|
||||
assert not state.is_local_bookkeeping_command(command)
|
||||
assert state.is_local_bookkeeping_command("echo local-note")
|
||||
assert not state.is_local_bookkeeping_command("echo ping > /dev/tcp/10.10.10.10/80")
|
||||
assert not state.is_local_bookkeeping_command("cat < /dev/tcp/10.10.10.10/80")
|
||||
|
||||
|
||||
def test_phase_window_runs_without_yolo_then_next_command_blocks(
|
||||
|
||||
Reference in New Issue
Block a user