aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/csam.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/csam.py')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/csam.py158
1 files changed, 0 insertions, 158 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/csam.py b/packages/meshbay-hub/src/meshbay_hub/csam.py
deleted file mode 100644
index b8e8d04..0000000
--- a/packages/meshbay-hub/src/meshbay_hub/csam.py
+++ /dev/null
@@ -1,158 +0,0 @@
-"""
-MeshBay Hub — CSAM hash matching.
-
-Checks public content hashes against known CSAM (Child Sexual Abuse Material)
-hash databases before allowing content to be registered or served publicly.
-
-Production integration:
- - NCMEC (National Center for Missing & Exploited Children): PhotoDNA hash database
- Access requires formal application: https://www.missingkids.org/gethelpnow/cybertipline
- - IWF (Internet Watch Foundation): URL and hash list (UK-based)
- Access via IWF membership: https://www.iwf.org.uk/our-technology/our-products/hash-list/
-
-This module provides:
- 1. A local CSAM hash database (SQLite file, populated from official sources)
- 2. A check function used before content registration
- 3. An admin endpoint to update the hash list
-
-IMPORTANT: Never log matched hashes or file contents. CSAM detection
-must be reported to NCMEC (US law) or relevant authority immediately.
-"""
-
-import logging
-from pathlib import Path
-
-from fastapi import APIRouter, Depends, HTTPException
-
-from meshbay_hub.api.deps import require_admin
-from meshbay_hub.db.models import User
-
-log = logging.getLogger(__name__)
-
-# Default path for the CSAM hash database (blake3 hex hashes, one per line)
-DEFAULT_CSAM_DB_PATH = Path("/var/lib/meshbay/hub/csam_hashes.txt")
-
-
-class CSAMChecker:
- """
- Checks content hashes against a known CSAM hash database.
-
- Usage:
- checker = CSAMChecker()
- checker.load()
- if checker.is_known_csam(blake3_hex):
- # refuse to serve, report to authority
- pass
- """
-
- def __init__(self, db_path: Path = DEFAULT_CSAM_DB_PATH):
- self._db_path = db_path
- self._hashes: set[str] = set()
- self._loaded = False
-
- def load(self, db_path: Path | None = None) -> int:
- """
- Load CSAM hashes from the hash database file.
- Returns the number of hashes loaded.
-
- File format: one blake3 hex hash per line (64 chars), comments with #.
- """
- path = db_path or self._db_path
- if not path.exists():
- log.warning("CSAM hash database not found: %s. "
- "Contact NCMEC (US) or IWF (EU) for access.", path)
- self._loaded = True
- return 0
-
- count = 0
- with open(path) as f:
- for line in f:
- line = line.strip()
- if line and not line.startswith("#") and len(line) == 64:
- self._hashes.add(line.lower())
- count += 1
-
- self._loaded = True
- log.info("CSAM hash database loaded: %d hashes from %s", count, path)
- return count
-
- def is_known_csam(self, content_hash_hex: str) -> bool:
- """
- Return True if the hash matches a known CSAM hash.
- NEVER logs the hash or any file information.
- """
- if not self._loaded:
- self.load()
- return content_hash_hex.lower() in self._hashes
-
- @property
- def hash_count(self) -> int:
- return len(self._hashes)
-
- def add_hash(self, hash_hex: str) -> None:
- """Add a hash to the in-memory set (and optionally persist)."""
- self._hashes.add(hash_hex.lower())
-
- def update_from_file(self, new_db_path: Path) -> int:
- """Hot-reload from a new hash database file."""
- old_count = len(self._hashes)
- self._hashes.clear()
- count = self.load(new_db_path)
- log.info("CSAM database updated: %d → %d hashes", old_count, count)
- return count
-
-
-# Module-level singleton (initialised in hub lifespan)
-_checker = CSAMChecker()
-
-
-def get_csam_checker() -> CSAMChecker:
- return _checker
-
-
-def check_content_hash(blake3_hex: str) -> bool:
- """
- Check a content hash against the CSAM database.
- Returns True if the content is KNOWN CSAM — block immediately.
-
- Callers MUST:
- 1. Refuse to serve the content
- 2. Log the event (without the hash) for legal audit purposes
- 3. Report to NCMEC CyberTipline if operating in the US:
- https://www.missingkids.org/gethelpnow/cybertipline
- """
- return _checker.is_known_csam(blake3_hex)
-
-
-# ── Hub API integration ───────────────────────────────────────────────────────
-
-csam_router = APIRouter(prefix="/v1/admin/csam", tags=["csam"])
-
-
-@csam_router.get("/status")
-async def csam_status(current_user: User = Depends(require_admin)):
- """Return CSAM checker status (hash count, database path)."""
- return {
- "hash_count": _checker.hash_count,
- "db_path": str(_checker._db_path),
- "loaded": _checker._loaded,
- "note": "Contact NCMEC or IWF for hash database access.",
- }
-
-
-@csam_router.post("/check")
-async def check_hash(
- body: dict,
- current_user: User = Depends(require_admin),
-):
- """
- Check a single hash. Admin use only.
- Returns True/False WITHOUT logging the hash (legal requirement).
- """
- hash_hex = body.get("hash", "")
- if len(hash_hex) != 64:
- raise HTTPException(status_code=422, detail="hash must be 64 hex chars")
- matched = check_content_hash(hash_hex)
- # Do NOT log whether a match was found — only log the check attempt
- log.info("CSAM check performed by admin %s", current_user.username)
- return {"matched": matched}