Files
OBLITERATUS/tests/test_secrets.py
T

230 lines
8.1 KiB
Python

"""Provider-neutral runtime secret resolution contracts."""
from __future__ import annotations
import os
import subprocess
import pytest
from obliteratus.credential_sources import (
SecretResolutionError,
resolve_first,
resolve_secret,
secret_available,
)
pytestmark = pytest.mark.cpu
_TEST_ENVIRONMENT = {
"HF_TOKEN",
"HF_TOKEN_FILE",
"HF_PUSH_TOKEN",
"HF_PUSH_TOKEN_FILE",
"OPENROUTER_API_KEY",
"OPENROUTER_API_KEY_FILE",
"OBLITERATUS_HUB_TOKEN",
"OBLITERATUS_HUB_TOKEN_FILE",
"OBLITERATUS_SECRET_DIR",
"OBLITERATUS_SECRET_COMMAND",
"OBLITERATUS_SECRET_COMMAND_TIMEOUT",
"CREDENTIALS_DIRECTORY",
}
@pytest.fixture(autouse=True)
def _clean_secret_environment(monkeypatch):
for name in _TEST_ENVIRONMENT:
monkeypatch.delenv(name, raising=False)
def test_environment_is_the_default_and_precedes_advanced_sources(monkeypatch, tmp_path):
missing_file = tmp_path / "not-used"
monkeypatch.setenv("HF_TOKEN", "environment-value")
monkeypatch.setenv("HF_TOKEN_FILE", str(missing_file))
monkeypatch.setenv("OBLITERATUS_SECRET_COMMAND", "relative-command")
assert resolve_secret("HF_TOKEN") == "environment-value"
def test_explicit_value_precedes_environment(monkeypatch):
monkeypatch.setenv("HF_TOKEN", "environment-value")
assert resolve_secret("HF_TOKEN", explicit="explicit-value\n") == "explicit-value"
def test_per_secret_file_supports_vault_agent_and_docker_mounts(monkeypatch, tmp_path):
secret_file = tmp_path / "openrouter"
secret_file.write_text("mounted-value\n", encoding="utf-8")
monkeypatch.setenv("OPENROUTER_API_KEY_FILE", str(secret_file))
assert resolve_secret("OPENROUTER_API_KEY") == "mounted-value"
def test_configured_file_fails_closed_instead_of_falling_through(monkeypatch, tmp_path):
monkeypatch.setenv("HF_TOKEN_FILE", str(tmp_path / "missing"))
monkeypatch.setenv("OBLITERATUS_SECRET_COMMAND", "relative-command")
with pytest.raises(SecretResolutionError, match="unreadable"):
resolve_secret("HF_TOKEN")
def test_configured_file_must_be_regular(monkeypatch, tmp_path):
monkeypatch.setenv("HF_TOKEN_FILE", str(tmp_path))
with pytest.raises(SecretResolutionError, match="not a file"):
resolve_secret("HF_TOKEN")
@pytest.mark.parametrize("directory_variable", ["OBLITERATUS_SECRET_DIR", "CREDENTIALS_DIRECTORY"])
def test_normalized_mounted_secret_directories(monkeypatch, tmp_path, directory_variable):
secret_file = tmp_path / "openrouter-api-key"
secret_file.write_text("directory-value", encoding="utf-8")
monkeypatch.setenv(directory_variable, str(tmp_path))
assert resolve_secret("OPENROUTER_API_KEY") == "directory-value"
def test_uppercase_filename_is_supported_for_existing_secret_mounts(monkeypatch, tmp_path):
secret_file = tmp_path / "HF_TOKEN"
secret_file.write_text("uppercase-file", encoding="utf-8")
monkeypatch.setenv("OBLITERATUS_SECRET_DIR", str(tmp_path))
assert resolve_secret("HF_TOKEN") == "uppercase-file"
@pytest.mark.skipif(os.name == "nt", reason="executable broker fixture is POSIX-specific")
def test_executable_broker_receives_only_normalized_name(monkeypatch, tmp_path):
broker = tmp_path / "secret-broker"
broker.write_text(
"#!/bin/sh\n"
"[ \"$#\" -eq 1 ] || exit 9\n"
"[ \"$1\" = OPENROUTER_API_KEY ] || exit 2\n"
"printf 'broker-value\\n'\n",
encoding="utf-8",
)
broker.chmod(0o700)
monkeypatch.setenv("OBLITERATUS_SECRET_COMMAND", str(broker))
assert resolve_secret("OPENROUTER_API_KEY") == "broker-value"
assert resolve_secret("HF_TOKEN") is None
def test_broker_requires_absolute_executable(monkeypatch):
monkeypatch.setenv("OBLITERATUS_SECRET_COMMAND", "vault read secret")
with pytest.raises(SecretResolutionError, match="absolute path"):
resolve_secret("HF_TOKEN")
def test_broker_timeout_is_bounded(monkeypatch, tmp_path):
broker = tmp_path / "broker"
broker.write_text("placeholder", encoding="utf-8")
broker.chmod(0o700)
monkeypatch.setenv("OBLITERATUS_SECRET_COMMAND", str(broker))
monkeypatch.setenv("OBLITERATUS_SECRET_COMMAND_TIMEOUT", "31")
with pytest.raises(SecretResolutionError, match="between 0.1 and 30"):
resolve_secret("HF_TOKEN")
def test_broker_timeout_must_be_numeric(monkeypatch, tmp_path):
broker = tmp_path / "broker"
broker.write_text("placeholder", encoding="utf-8")
broker.chmod(0o700)
monkeypatch.setenv("OBLITERATUS_SECRET_COMMAND", str(broker))
monkeypatch.setenv("OBLITERATUS_SECRET_COMMAND_TIMEOUT", "fast")
with pytest.raises(SecretResolutionError, match="must be a number"):
resolve_secret("HF_TOKEN")
def test_missing_and_non_executable_brokers_fail_closed(monkeypatch, tmp_path):
missing = tmp_path / "missing"
monkeypatch.setenv("OBLITERATUS_SECRET_COMMAND", str(missing))
with pytest.raises(SecretResolutionError, match="unavailable"):
resolve_secret("HF_TOKEN")
broker = tmp_path / "broker"
broker.write_text("placeholder", encoding="utf-8")
broker.chmod(0o600)
monkeypatch.setenv("OBLITERATUS_SECRET_COMMAND", str(broker))
with pytest.raises(SecretResolutionError, match="executable regular file"):
resolve_secret("HF_TOKEN")
broker.chmod(0o722)
with pytest.raises(SecretResolutionError, match="group/world writable"):
resolve_secret("HF_TOKEN")
@pytest.mark.parametrize(
("completed", "message"),
[
(subprocess.CompletedProcess([], 9, stdout=b"", stderr=b"private"), "exit status 9"),
(subprocess.CompletedProcess([], 0, stdout=b"x" * (64 * 1024 + 1)), "64 KiB"),
(subprocess.CompletedProcess([], 0, stdout=b"\xff"), "not UTF-8"),
],
)
def test_broker_errors_never_surface_stderr(monkeypatch, tmp_path, completed, message):
broker = tmp_path / "broker"
broker.write_text("placeholder", encoding="utf-8")
broker.chmod(0o700)
monkeypatch.setenv("OBLITERATUS_SECRET_COMMAND", str(broker))
monkeypatch.setattr(
"obliteratus.credential_sources.subprocess.run",
lambda *_a, **_k: completed,
)
with pytest.raises(SecretResolutionError, match=message) as failure:
resolve_secret("HF_TOKEN")
assert "private" not in str(failure.value)
def test_broker_execution_failure_is_normalized(monkeypatch, tmp_path):
broker = tmp_path / "broker"
broker.write_text("placeholder", encoding="utf-8")
broker.chmod(0o700)
monkeypatch.setenv("OBLITERATUS_SECRET_COMMAND", str(broker))
monkeypatch.setattr(
"obliteratus.credential_sources.subprocess.run",
lambda *_a, **_k: (_ for _ in ()).throw(subprocess.TimeoutExpired("broker", 1)),
)
with pytest.raises(SecretResolutionError, match="execution failed"):
resolve_secret("HF_TOKEN")
@pytest.mark.parametrize("payload", ["", "value\x00suffix"])
def test_invalid_file_payloads_are_rejected(monkeypatch, tmp_path, payload):
secret_file = tmp_path / "invalid"
secret_file.write_bytes(payload.encode("utf-8"))
monkeypatch.setenv("HF_TOKEN_FILE", str(secret_file))
with pytest.raises(SecretResolutionError, match="empty|NUL"):
resolve_secret("HF_TOKEN")
def test_oversized_secret_file_is_rejected(monkeypatch, tmp_path):
secret_file = tmp_path / "oversized"
secret_file.write_bytes(b"x" * (64 * 1024 + 1))
monkeypatch.setenv("HF_TOKEN_FILE", str(secret_file))
with pytest.raises(SecretResolutionError, match="64 KiB"):
resolve_secret("HF_TOKEN")
def test_resolve_first_preserves_caller_priority_and_availability(monkeypatch):
monkeypatch.setenv("HF_TOKEN", "read-token")
monkeypatch.setenv("HF_PUSH_TOKEN", "push-token")
assert resolve_first("HF_PUSH_TOKEN", "HF_TOKEN") == "push-token"
assert secret_available("OPENROUTER_API_KEY", "HF_TOKEN") is True
assert secret_available("OPENROUTER_API_KEY") is False
@pytest.mark.parametrize("name", ["hf_token", "HF-TOKEN", "", "1TOKEN"])
def test_secret_names_are_normalized_and_not_shell_fragments(name):
with pytest.raises(ValueError, match="uppercase environment-variable"):
resolve_secret(name)