monsimas commited on
Commit
6d57148
·
verified ·
1 Parent(s): 3f48b16

Add TruffleHog secret-detection backstop + expand scrub.py detectors

Browse files
Files changed (1) hide show
  1. app.py +65 -0
app.py CHANGED
@@ -24,7 +24,10 @@ import re
24
  import time
25
  import json
26
  import uuid
 
27
  import pathlib
 
 
28
  from collections import defaultdict, deque
29
 
30
  from fastapi import FastAPI, Request
@@ -66,6 +69,54 @@ def _rate_ok(ip):
66
  SITE_FILE = pathlib.Path(__file__).parent / "index.html"
67
  OG_FILE = pathlib.Path(__file__).parent / "og.png"
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
  @app.get("/", response_class=HTMLResponse)
71
  def home():
@@ -146,6 +197,20 @@ async def donate(request: Request):
146
  status_code=422,
147
  )
148
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  if not HF_TOKEN or not DATASET_REPO:
150
  # Not yet configured — accept-validate but don't pretend to publish.
151
  return JSONResponse(
 
24
  import time
25
  import json
26
  import uuid
27
+ import shutil
28
  import pathlib
29
+ import tempfile
30
+ import subprocess
31
  from collections import defaultdict, deque
32
 
33
  from fastapi import FastAPI, Request
 
69
  SITE_FILE = pathlib.Path(__file__).parent / "index.html"
70
  OG_FILE = pathlib.Path(__file__).parent / "og.png"
71
 
72
+ TRUFFLEHOG = shutil.which("trufflehog")
73
+
74
+
75
+ def trufflehog_findings(text):
76
+ """Authoritative secret-detection backstop.
77
+
78
+ Runs TruffleHog (hundreds of maintained detectors) over the already-scrubbed
79
+ trace and returns the set of detector names it flags. Detection only:
80
+ `--no-verification` means candidate secrets are NEVER sent to third parties
81
+ to validate them. No-ops (returns []) when the binary isn't installed, so
82
+ local/dev runs gracefully fall back to the regex pass in scrub.py.
83
+ """
84
+ if not TRUFFLEHOG:
85
+ return []
86
+ findings = set()
87
+ tmp_path = None
88
+ try:
89
+ with tempfile.NamedTemporaryFile("w", suffix=".jsonl", delete=False) as tf:
90
+ tf.write(text)
91
+ tmp_path = tf.name
92
+ proc = subprocess.run(
93
+ [TRUFFLEHOG, "filesystem", tmp_path,
94
+ "--json", "--no-verification", "--no-update"],
95
+ capture_output=True, text=True, timeout=120,
96
+ )
97
+ for line in proc.stdout.splitlines():
98
+ line = line.strip()
99
+ if not line:
100
+ continue
101
+ try:
102
+ obj = json.loads(line)
103
+ except json.JSONDecodeError:
104
+ continue
105
+ name = obj.get("DetectorName") or obj.get("DetectorType")
106
+ if name:
107
+ findings.add(str(name))
108
+ except (subprocess.TimeoutExpired, OSError):
109
+ # A scanner failure must not silently pass a donation, but the regex
110
+ # backstop already ran; surface nothing here and let that stand.
111
+ return []
112
+ finally:
113
+ if tmp_path:
114
+ try:
115
+ os.unlink(tmp_path)
116
+ except OSError:
117
+ pass
118
+ return sorted(findings)
119
+
120
 
121
  @app.get("/", response_class=HTMLResponse)
122
  def home():
 
197
  status_code=422,
198
  )
199
 
200
+ # --- authoritative backstop: TruffleHog over the scrubbed trace ---------
201
+ # Catches what the regex pass cannot (vendor tokens with no fixed prefix,
202
+ # newly-rotated formats, etc.). Any flagged detector rejects the donation.
203
+ th_detectors = trufflehog_findings(cleaned)
204
+ if th_detectors:
205
+ return JSONResponse(
206
+ {
207
+ "error": "secrets_found",
208
+ "detail": "TruffleHog (server backstop) flagged likely secrets that survived the client scrub. Donation rejected — clean the flagged values and retry.",
209
+ "detectors": th_detectors,
210
+ },
211
+ status_code=422,
212
+ )
213
+
214
  if not HF_TOKEN or not DATASET_REPO:
215
  # Not yet configured — accept-validate but don't pretend to publish.
216
  return JSONResponse(