Spaces:
Sleeping
Sleeping
File size: 3,244 Bytes
ccb1bbd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | """
security.py
-----------
Implements the two security controls we demonstrate for the capstone:
1. NON-INTERACTIVE ACCESS
No agent ever fetches or renders raw third-party HTML. Every agent talks
only to structured, official APIs (Gemini, YouTube Data API, Semantic
Scholar, Google Books). This removes the indirect-prompt-injection
surface that comes from an agent reading arbitrary web pages.
2. OUTPUT CONTENT SAFETY GATE (Governance / Logic Review)
Before the final report is returned to the user, the Aggregator Agent's
output passes through a lightweight safety classifier. If the topic or
generated content falls into a disallowed category (e.g. weapons,
self-harm instructions, hate content), the report is blocked and a safe
message is returned instead — mirroring the "Vibe Diff" / mandatory
logic-review idea from the security framework: nothing ships without a
check, even though the check here is automated rather than a human.
"""
import re
# Minimal, fast keyword-based pre-filter. This runs before the LLM-based
# check so obviously disallowed topics are caught without spending tokens.
_BLOCKED_PATTERNS = [
r"\bmake\s+a\s+bomb\b",
r"\bsynthesiz(e|ing)\s+(nerve agent|sarin|ricin)\b",
r"\bhow to (kill|murder)\b",
r"\bchild\s+sexual\b",
]
def keyword_prefilter(topic: str) -> bool:
"""Returns True if topic is safe to proceed, False if blocked outright."""
lowered = topic.lower()
for pattern in _BLOCKED_PATTERNS:
if re.search(pattern, lowered):
return False
return True
def llm_safety_check(gemini_model, final_report_text: str) -> tuple[bool, str]:
"""
Uses Gemini itself as a lightweight judge over the FINAL aggregated
report (not the raw topic) to catch anything that slipped through.
Returns (is_safe, reason).
"""
prompt = (
"You are a content safety classifier. Read the study report below "
"and answer with exactly one word on the first line: SAFE or UNSAFE. "
"On the second line, give a one-sentence reason. "
"Mark UNSAFE only if the content provides actionable instructions "
"for violence, weapons, self-harm, or clearly illegal harmful acts. "
"Educational/academic framing of sensitive topics is SAFE.\n\n"
f"REPORT:\n{final_report_text[:4000]}"
)
try:
response = gemini_model["client"].models.generate_content(
model=gemini_model["model_name"], contents=prompt
)
text = response.text.strip()
first_line = text.splitlines()[0].strip().upper()
reason = text.splitlines()[1] if len(text.splitlines()) > 1 else ""
is_safe = first_line.startswith("SAFE")
return is_safe, reason
except Exception as e:
# If the check fails because of a 429 rate limit / quota exhaustion, bypass rather than block the whole application.
if "429" in str(e) or "RESOURCE_EXHAUSTED" in str(e) or "quota" in str(e).lower():
return True, f"Bypassed (API rate limit / quota exhausted)"
# Fail safe for other errors: if the classifier itself errors, don't silently ship unchecked content
return False, f"Safety check failed to run: {e}"
|