test: comprehensive test suite
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>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import json
|
||||
import pytest
|
||||
|
||||
|
||||
def test_set_and_get_key(tmp_pyra_home):
|
||||
from pyra.vault.writer import set_key
|
||||
from pyra.vault.reader import get_key
|
||||
|
||||
set_key("anthropic", "sk-ant-test-secret")
|
||||
result = get_key("anthropic")
|
||||
assert result == "sk-ant-test-secret"
|
||||
|
||||
|
||||
def test_key_not_in_config(tmp_pyra_home):
|
||||
from pyra.vault.writer import set_key
|
||||
from pyra.config.schema import ProviderConfig, PyraConfig
|
||||
from pyra.config.manager import save_config, load_config
|
||||
|
||||
set_key("openai", "sk-supersecret")
|
||||
|
||||
cfg = PyraConfig(ai=ProviderConfig(provider_id="openai", model="gpt-4o"))
|
||||
save_config(cfg)
|
||||
|
||||
config_text = (tmp_pyra_home / "config.yaml").read_text()
|
||||
assert "sk-supersecret" not in config_text
|
||||
assert "supersecret" not in config_text
|
||||
|
||||
|
||||
def test_keys_file_permissions(tmp_pyra_home):
|
||||
import os
|
||||
from pyra.vault.writer import set_key
|
||||
|
||||
set_key("deepseek", "test-key-value")
|
||||
keys_file = tmp_pyra_home / "vault" / "secrets" / "api_keys.json"
|
||||
|
||||
if os.name != "nt":
|
||||
mode = oct(keys_file.stat().st_mode)[-3:]
|
||||
assert mode == "400", f"Expected 400, got {mode}"
|
||||
|
||||
|
||||
def test_multiple_providers(tmp_pyra_home):
|
||||
from pyra.vault.writer import set_key
|
||||
from pyra.vault.reader import get_key
|
||||
|
||||
set_key("anthropic", "key-1")
|
||||
set_key("openai", "key-2")
|
||||
set_key("gemini", "key-3")
|
||||
|
||||
assert get_key("anthropic") == "key-1"
|
||||
assert get_key("openai") == "key-2"
|
||||
assert get_key("gemini") == "key-3"
|
||||
|
||||
|
||||
def test_delete_key(tmp_pyra_home):
|
||||
from pyra.vault.writer import set_key, delete_key
|
||||
from pyra.vault.reader import get_key
|
||||
|
||||
set_key("anthropic", "to-be-deleted")
|
||||
assert get_key("anthropic") == "to-be-deleted"
|
||||
|
||||
existed = delete_key("anthropic")
|
||||
assert existed is True
|
||||
assert get_key("anthropic") is None
|
||||
|
||||
|
||||
def test_get_missing_key_returns_none(tmp_pyra_home):
|
||||
from pyra.vault.reader import get_key
|
||||
result = get_key("nonexistent_provider")
|
||||
assert result is None
|
||||
Reference in New Issue
Block a user