This commit is contained in:
@@ -2,9 +2,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Gemini fallback kill-switch contract."""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from services.ai_provider import AIProviderService, AIResponse
|
||||
from services.gemini_service import GeminiService
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def _production_python_files():
|
||||
for folder in ("services", "routes"):
|
||||
yield from sorted((ROOT / folder).rglob("*.py"))
|
||||
for filename in ("scheduler.py", "config.py"):
|
||||
path = ROOT / filename
|
||||
if path.exists():
|
||||
yield path
|
||||
|
||||
|
||||
def _rel(path: Path) -> str:
|
||||
return path.relative_to(ROOT).as_posix()
|
||||
|
||||
|
||||
def test_gemini_guard_defaults_disabled(monkeypatch):
|
||||
from services.gemini_guard import get_gemini_api_key, is_gemini_fallback_enabled
|
||||
@@ -110,3 +128,50 @@ def test_openclaw_direct_gemini_call_is_blocked_by_default(monkeypatch):
|
||||
monkeypatch.setenv("GEMINI_API_KEY", "test-key")
|
||||
|
||||
assert svc._call_gemini("system", "user", caller="openclaw_qa_gemini_fallback") is None
|
||||
|
||||
|
||||
def test_no_direct_gemini_api_key_env_read_outside_guard_or_config():
|
||||
allowed = {"config.py", "services/gemini_guard.py"}
|
||||
offenders = []
|
||||
pattern = re.compile(r"os\.getenv\(\s*['\"]GEMINI_API_KEY['\"]")
|
||||
|
||||
for path in _production_python_files():
|
||||
if _rel(path) in allowed:
|
||||
continue
|
||||
if pattern.search(path.read_text(encoding="utf-8")):
|
||||
offenders.append(_rel(path))
|
||||
|
||||
assert offenders == []
|
||||
|
||||
|
||||
def test_gemini_outbound_files_are_guarded():
|
||||
allowed = {
|
||||
"routes/openclaw_bot_routes.py",
|
||||
"services/code_review_pipeline_service.py",
|
||||
"services/gemini_service.py",
|
||||
"services/mcp_collector_service.py",
|
||||
"services/openclaw_strategist_service.py",
|
||||
}
|
||||
outbound_markers = (
|
||||
"google.generativeai",
|
||||
"genai.configure",
|
||||
"GenerativeModel",
|
||||
"generate_content(",
|
||||
"generateContent?key=",
|
||||
)
|
||||
offenders = []
|
||||
unguarded = []
|
||||
|
||||
for path in _production_python_files():
|
||||
text = path.read_text(encoding="utf-8")
|
||||
has_outbound = any(marker in text for marker in outbound_markers)
|
||||
if not has_outbound:
|
||||
continue
|
||||
rel = _rel(path)
|
||||
if rel not in allowed:
|
||||
offenders.append(rel)
|
||||
if "get_gemini_api_key" not in text:
|
||||
unguarded.append(rel)
|
||||
|
||||
assert offenders == []
|
||||
assert unguarded == []
|
||||
|
||||
Reference in New Issue
Block a user