fix(security+agents): GAP-57 fail-loud PAPERCLIP_DB_URL + FU-13 analyst tool alignment

GAP-57 (אבטחה, CWE-798 / INV-ENV4): ה-default הקשיח
postgresql://paperclip:paperclip@... הוסר מ-3 קבצי web/. נוסף resolver משותף
require_paperclip_db_url() ב-paperclip_api.py שנכשל בקול אם PAPERCLIP_DB_URL לא
מוגדר — במקום ליפול בשקט ל-creds ידועים. Coolify מגדיר את המשתנה (אומת), אז
הייצור לא נפגע. (2 מופעים בסקריפטים מקומיים נותרו ל-FU-15 המלא.)

FU-13 (INV-AG3, GAP-46): יישור הרשאות-סוכן. התברר שהפער שמופה ב-31.5 היה רחב
מדי — יוחס לפי תיאור-תפקיד, לא ההוראות בפועל. הכרעת-יו"ר "היבריד":
- legal-analyst: נוסף aggregate_claims_to_arguments (frontmatter + שלב 7) — הכלי
  שמקבץ את הטענות שהוא חילץ לטיעונים משפטיים.
- extract_references/extract_internal_citations הם מטלת-researcher (שכבר מחזיק
  אותם), לא analyst — הוסרו מרשימת "החסרים".
- legal-researcher: כבר היה תקין; ה-spec היה מיושן.
עודכנו X4-agents.md (§2א, INV-AG3) ו-gap-audit.md (FU-13 , FU-15 חלקי).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 14:14:39 +00:00
parent 27b40dfec5
commit 482f302d54
6 changed files with 46 additions and 19 deletions

View File

@@ -45,7 +45,7 @@ from web.mcp_env_catalog import (
normalize_for_compare,
)
from web.progress_store import ProgressStore
from web.paperclip_api import emit_case_status_webhook, pc_request
from web.paperclip_api import emit_case_status_webhook, pc_request, require_paperclip_db_url
from web.paperclip_client import (
COMPANIES as PAPERCLIP_COMPANIES,
accept_interaction as pc_accept_interaction,
@@ -3907,9 +3907,7 @@ async def api_mcp_blocks():
@app.get("/api/settings/paperclip-companies")
async def api_paperclip_companies():
"""List all companies from Paperclip's DB."""
pc_url = os.environ.get(
"PAPERCLIP_DB_URL", "postgresql://paperclip:paperclip@127.0.0.1:54329/paperclip"
)
pc_url = require_paperclip_db_url() # INV-ENV4 / GAP-57: fail loud, no creds default
try:
conn = await asyncpg.connect(pc_url)
try:
@@ -4082,9 +4080,8 @@ async def api_reset_methodology(category: str, key: str):
# ── Skill Management API ───────────────────────────────────────────
PAPERCLIP_DB_URL = os.environ.get(
"PAPERCLIP_DB_URL", "postgresql://paperclip:paperclip@127.0.0.1:54329/paperclip"
)
# INV-ENV4 / GAP-57: no hard-coded credential default — fail loud if unset.
PAPERCLIP_DB_URL = require_paperclip_db_url()
# Paperclip skills directory. In the Coolify container this is bind-mounted from
# the host's ~/.paperclip/instances/default/skills (see PAPERCLIP_SKILLS_DIR env).
# Fallback to the host path for local/dev use.

View File

@@ -32,6 +32,25 @@ PAPERCLIP_BOARD_API_KEY = os.environ.get("PAPERCLIP_BOARD_API_KEY", "")
DEFAULT_TIMEOUT = 15.0
def require_paperclip_db_url() -> str:
"""Return ``PAPERCLIP_DB_URL`` or fail loud — never fall back to a default.
INV-ENV4 / GAP-57: a credential must not live in source as a default value.
The Coolify container and local tooling supply this explicitly; its absence
is a misconfiguration we surface immediately rather than silently connecting
with a well-known default credential (CWE-798). Mirrors the fail-loud guard
on ``PAPERCLIP_BOARD_API_KEY`` in ``_build_headers``.
"""
url = os.environ.get("PAPERCLIP_DB_URL", "")
if not url:
raise RuntimeError(
"PAPERCLIP_DB_URL not set — refusing hard-coded credential fallback "
"(INV-ENV4 / GAP-57). Set PAPERCLIP_DB_URL in the environment "
"(Coolify for the container, or your shell/.env for local tooling)."
)
return url
def _build_headers(run_id: str | None, has_body: bool) -> dict[str, str]:
if not PAPERCLIP_BOARD_API_KEY:
raise RuntimeError("PAPERCLIP_BOARD_API_KEY not set — cannot call Paperclip API")

View File

@@ -13,13 +13,12 @@ import uuid
import asyncpg
from web.paperclip_api import pc_request
from web.paperclip_api import pc_request, require_paperclip_db_url
logger = logging.getLogger(__name__)
PAPERCLIP_DB_URL = os.environ.get(
"PAPERCLIP_DB_URL", "postgresql://paperclip:paperclip@127.0.0.1:54329/paperclip"
)
# INV-ENV4 / GAP-57: no hard-coded credential default — fail loud if unset.
PAPERCLIP_DB_URL = require_paperclip_db_url()
PLUGIN_ID = "53461b5a-7f58-411a-9952-72f9c8d4a328" # marcusgroup.legal-ai
# PAPERCLIP_API_URL — moved to web.paperclip_api (used only by pc_request now).