123 lines
4.3 KiB
Python
123 lines
4.3 KiB
Python
"""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
|