...
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
"""Graphical sudo prompts: command rewriting and helper resolution.
|
||||
Pure logic — no display, no sudo, no password."""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
from bolt_pet import sudo_askpass
|
||||
|
||||
|
||||
# ── rewriting ────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"command,expected",
|
||||
[
|
||||
("sudo apt update", "sudo -A apt update"),
|
||||
("sudo apt update", "sudo -A apt update"), # spacing preserved
|
||||
("apt update && sudo apt upgrade", "apt update && sudo -A apt upgrade"),
|
||||
("ls; sudo reboot", "ls; sudo -A reboot"),
|
||||
("echo hi | sudo tee /etc/motd", "echo hi | sudo -A tee /etc/motd"),
|
||||
("sudo systemctl restart x\nsudo systemctl status x",
|
||||
"sudo -A systemctl restart x\nsudo -A systemctl status x"),
|
||||
],
|
||||
)
|
||||
def test_bare_sudo_gets_the_askpass_flag(command, expected):
|
||||
assert sudo_askpass.add_askpass_flag(command) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"command",
|
||||
[
|
||||
"sudo -n apt update", # explicitly non-interactive
|
||||
"sudo -A apt update", # already asking
|
||||
"sudo -u bob whoami", # the caller was explicit
|
||||
"ls -la", # no sudo at all
|
||||
"echo 'run sudo later'", # inside a quoted string
|
||||
"pseudo --version", # not the word sudo
|
||||
],
|
||||
)
|
||||
def test_commands_that_must_not_be_rewritten(command):
|
||||
assert sudo_askpass.add_askpass_flag(command) == command
|
||||
|
||||
|
||||
def test_blank_input():
|
||||
assert sudo_askpass.add_askpass_flag("") == ""
|
||||
assert sudo_askpass.add_askpass_flag(None) == ""
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"command,expected",
|
||||
[
|
||||
("sudo apt update", True),
|
||||
("ls && sudo reboot", True),
|
||||
("ls -la", False),
|
||||
("echo 'sudo'", False),
|
||||
("", False),
|
||||
],
|
||||
)
|
||||
def test_which_commands_get_the_longer_timeout(command, expected):
|
||||
assert sudo_askpass.needs_password_prompt(command) is expected
|
||||
|
||||
|
||||
# ── helper resolution ────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_a_configured_helper_wins():
|
||||
found = sudo_askpass.find_helper(
|
||||
configured="/opt/my-askpass", is_executable=lambda p: p == "/opt/my-askpass",
|
||||
)
|
||||
assert found == "/opt/my-askpass"
|
||||
|
||||
|
||||
def test_a_configured_helper_that_is_not_executable_is_not_silently_replaced():
|
||||
"""Better to have sudo fail than to quietly prompt with something the
|
||||
user didn't choose."""
|
||||
assert sudo_askpass.find_helper(
|
||||
configured="/opt/typo", is_executable=lambda p: False, which=lambda t: "/usr/bin/zenity",
|
||||
) is None
|
||||
|
||||
|
||||
def test_a_real_askpass_binary_beats_a_generated_wrapper():
|
||||
found = sudo_askpass.find_helper(
|
||||
configured="", is_executable=lambda p: p == "/usr/bin/ksshaskpass",
|
||||
which=lambda tool: "/usr/bin/zenity",
|
||||
)
|
||||
assert found == "/usr/bin/ksshaskpass"
|
||||
|
||||
|
||||
def test_falls_back_to_wrapping_a_dialog_tool(tmp_path):
|
||||
found = sudo_askpass.find_helper(
|
||||
configured="", is_executable=lambda p: False,
|
||||
which=lambda tool: "/usr/bin/zenity" if tool == "zenity" else None,
|
||||
cache_dir=tmp_path,
|
||||
)
|
||||
script = tmp_path / "askpass.sh"
|
||||
assert found == str(script)
|
||||
assert "zenity --password" in script.read_text()
|
||||
assert script.stat().st_mode & 0o777 == 0o700 # nobody else edits the password box
|
||||
|
||||
|
||||
def test_no_helper_available_at_all():
|
||||
assert sudo_askpass.find_helper(
|
||||
configured="", is_executable=lambda p: False, which=lambda tool: None,
|
||||
) is None
|
||||
|
||||
|
||||
def test_the_wrapper_passes_sudos_prompt_through():
|
||||
"""sudo hands the helper its prompt as $1 — it names the account the
|
||||
password is for, which is worth showing in the dialog."""
|
||||
script = sudo_askpass.helper_script("/usr/bin/zenity")
|
||||
assert script.startswith("#!/bin/sh")
|
||||
assert '"$1"' in script
|
||||
|
||||
|
||||
def test_environment_points_sudo_at_the_helper():
|
||||
env = sudo_askpass.environment("/tmp/askpass.sh", base={"PATH": "/usr/bin"})
|
||||
assert env["SUDO_ASKPASS"] == "/tmp/askpass.sh"
|
||||
assert env["PATH"] == "/usr/bin" # the rest of the environment survives
|
||||
Reference in New Issue
Block a user