251e509ee0
Unit tests: - test_security_boundaries.py: vault block, vault lock sentinel - test_security_injection.py: all 4 injection categories, case-insensitive - test_vault_rw.py: roundtrip, file permissions (chmod 400), no key in config - test_config.py: schema roundtrip, no api_key field, chmod 600 on config.yaml - test_memory_reader.py: list, read, sandboxing, context loading - test_memory_writer.py: write, append, index update, traversal blocked, chmod 600 - test_providers.py: required fields, unique IDs, litellm prefix format - test_renderer.py: key redaction for sk-ant-, sk-, AIza patterns Security tests: - test_vault_ai_isolation.py: 7 traversal patterns blocked via memory read/write - test_path_traversal.py: 20+ traversal patterns — all rejected for read and write - test_prompt_injection.py: 21-item corpus + 5 clean texts (no false positives) Integration tests: - test_lmstudio.py: live call to localhost:1234, streaming, full stack session, injection scan on real output (skips if LM Studio not running) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
48 lines
1.6 KiB
Python
48 lines
1.6 KiB
Python
"""
|
|
Verify that vault paths cannot be reached via the memory reader API
|
|
regardless of what string the AI might supply.
|
|
"""
|
|
import pytest
|
|
from pyra.security.boundaries import VaultAccessError
|
|
|
|
|
|
TRAVERSAL_NAMES = [
|
|
"../../../../vault/secrets/api_keys.json",
|
|
"../vault/secrets/api_keys.json",
|
|
"context/../../vault/secrets/api_keys.json",
|
|
"user/../../../vault/secrets/api_keys.json",
|
|
"%2e%2e/vault/secrets/api_keys.json",
|
|
"/root/anywhere",
|
|
"~/secret",
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize("name", TRAVERSAL_NAMES)
|
|
def test_memory_read_cannot_reach_vault(tmp_pyra_home, name):
|
|
from pyra.memory.reader import read_memory
|
|
with pytest.raises((VaultAccessError, PermissionError, FileNotFoundError)):
|
|
read_memory(name)
|
|
|
|
|
|
def test_memory_write_cannot_reach_vault(tmp_pyra_home):
|
|
from pyra.memory.writer import write_memory
|
|
with pytest.raises((VaultAccessError, PermissionError, ValueError)):
|
|
write_memory("../../vault/secrets/api_keys.json", "evil content")
|
|
|
|
|
|
def test_direct_vault_read_blocked(tmp_pyra_home):
|
|
"""assert_safe_path must block the vault path directly."""
|
|
from pyra.security.boundaries import assert_safe_path, VAULT_PATH
|
|
with pytest.raises(VaultAccessError):
|
|
assert_safe_path(VAULT_PATH / "secrets" / "api_keys.json")
|
|
|
|
|
|
def test_vault_lock_sentinel_required(tmp_pyra_home):
|
|
"""Deleting .vault_lock causes bootstrap to raise PyraSecurityError."""
|
|
from pyra.security.boundaries import PyraSecurityError
|
|
lock = tmp_pyra_home / "vault" / ".vault_lock"
|
|
lock.unlink()
|
|
with pytest.raises(PyraSecurityError):
|
|
from pyra.security.boundaries import check_vault_lock
|
|
check_vault_lock()
|