Nayan Ghosh commited on
Commit
be5e8ad
Β·
1 Parent(s): 2bb63a3
Files changed (2) hide show
  1. app.py +10 -5
  2. templates/dashboard.html +11 -4
app.py CHANGED
@@ -633,12 +633,17 @@ def log_request(response):
633
  return response
634
 
635
  def _require_admin():
636
- if not _key_ok(ADMIN_API_KEY): return None
637
- provided = request.headers.get("X-Admin-Key","")
 
 
 
638
  if not provided and request.is_json:
639
- provided = (request.get_json() or {}).get("admin_key","")
640
- if provided != ADMIN_API_KEY:
641
- return jsonify({"error":"Unauthorized β€” X-Admin-Key required"}), 403
 
 
642
  return None
643
 
644
  SKIP = ("chrome://","about:","chrome-extension://","moz-extension://",
 
633
  return response
634
 
635
  def _require_admin():
636
+ # If ADMIN_API_KEY is not configured, block ALL admin operations
637
+ if not _key_ok(ADMIN_API_KEY):
638
+ return jsonify({"error": "Admin key not configured on server β€” admin endpoints disabled"}), 403
639
+ # Get key from header or JSON body
640
+ provided = request.headers.get("X-Admin-Key", "")
641
  if not provided and request.is_json:
642
+ provided = (request.get_json() or {}).get("admin_key", "")
643
+ # Constant-time comparison to prevent timing attacks
644
+ import hmac
645
+ if not provided or not hmac.compare_digest(provided, ADMIN_API_KEY):
646
+ return jsonify({"error": "Unauthorized β€” invalid or missing X-Admin-Key"}), 403
647
  return None
648
 
649
  SKIP = ("chrome://","about:","chrome-extension://","moz-extension://",
templates/dashboard.html CHANGED
@@ -491,16 +491,23 @@ function sendFeedback(correctLabel){
491
 
492
  document.getElementById('si').addEventListener('keydown',function(e){if(e.key==='Enter')doScan()});
493
 
494
- // ── Retrain Trigger ───────────────────────────────────────────────────────────
495
  function doRetrain(){
496
  var b=document.querySelector('.rtbtn');
 
 
497
  b.textContent='⏳ RETRAINING…';b.disabled=true;
498
- fetch('/retrain/trigger',{method:'POST'})
 
 
 
 
499
  .then(function(r){return r.json()})
500
  .then(function(d){
501
- b.textContent=d.status==='triggered'?'βœ“ RETRAIN STARTED':'⚠ '+d.message;
 
502
  setTimeout(function(){b.textContent='⚑ TRIGGER RETRAIN';b.disabled=false},3000);
503
- }).catch(function(){b.textContent='⚠ OFFLINE';setTimeout(function(){b.textContent='⚑ TRIGGER RETRAIN';b.disabled=false},3000)});
504
  }
505
 
506
  /* AUTO REFRESH β€” fetch stats every 60s instead of hard reload (FIX L3) */
 
491
 
492
  document.getElementById('si').addEventListener('keydown',function(e){if(e.key==='Enter')doScan()});
493
 
494
+ // ── Retrain Trigger (requires admin key) ──────────────────────────────────────
495
  function doRetrain(){
496
  var b=document.querySelector('.rtbtn');
497
+ var key=prompt('Enter Admin API Key to trigger retrain:');
498
+ if(!key||!key.trim()){b.textContent='⚠ Cancelled';setTimeout(function(){b.textContent='⚑ TRIGGER RETRAIN'},2000);return;}
499
  b.textContent='⏳ RETRAINING…';b.disabled=true;
500
+ fetch('/retrain/trigger',{
501
+ method:'POST',
502
+ headers:{'Content-Type':'application/json','X-Admin-Key':key.trim()},
503
+ body:JSON.stringify({})
504
+ })
505
  .then(function(r){return r.json()})
506
  .then(function(d){
507
+ if(d.error){b.textContent='πŸ”’ '+d.error;}
508
+ else{b.textContent=d.status==='triggered'?'βœ“ RETRAIN STARTED':'⚠ '+(d.message||d.status);}
509
  setTimeout(function(){b.textContent='⚑ TRIGGER RETRAIN';b.disabled=false},3000);
510
+ }).catch(function(){b.textContent='⚠ OFFLINE';setTimeout(function(){b.textContent='⚑ TRIGGER RETRAIN';b.disabled=false},3000);});
511
  }
512
 
513
  /* AUTO REFRESH β€” fetch stats every 60s instead of hard reload (FIX L3) */