fix: Security hardening from CT116 deep-dive review
- API keys moved from hardcoded dict to env var (API_KEYS JSON) with fallback - Rate limiting added: token bucket per API key (Redis-backed), 429 responses with Retry-After - Rate limit tiers: enterprise 120/min, professional 60/min, starter 20/min - X-RateLimit-* headers on all responses - Dashboard polling reduced from 3s to 5s backend, 10s JS fallback - SSE detection disables redundant polling when stream is connected - Deleted ts_patch.py (dead one-shot migration, already applied) - Added ssl/README.md documenting upstream SSL termination Ref: Relay #444 (Mumuni CT116 harness deep-dive) Reviewed-by: Abiba <abiba@sysloggh.com>
This commit is contained in:
@@ -16,7 +16,7 @@ def fetch_state():
|
||||
|
||||
def broadcast_loop():
|
||||
while True:
|
||||
time.sleep(3)
|
||||
time.sleep(5)
|
||||
data = fetch_state(); payload = json.dumps(data)
|
||||
with sse_lock:
|
||||
dead = [q for q in sse_subscribers if not q.put(payload)]
|
||||
@@ -256,7 +256,13 @@ var aHTML=agents.map(function(a){return'<div class="mb-2" style="font-size:11px"
|
||||
$('perf-agents').innerHTML=aHTML;
|
||||
}else{$('perf-agents').innerHTML='<div class="text-secondary small text-center py-3">-</div>';}
|
||||
}
|
||||
function poll(){fetch('/api/state').then(function(r){return r.json()}).then(function(data){render(data);$('connection-status').textContent='live';}).catch(function(){$('connection-status').textContent='reconnecting';});}
|
||||
var sseConnected = false;
|
||||
function poll(){if(sseConnected)return;fetch('/api/state').then(function(r){return r.json()}).then(function(data){render(data);$('connection-status').textContent='live';}).catch(function(){$('connection-status').textContent='reconnecting';});}
|
||||
// SSE connection tracking
|
||||
var es = new EventSource('/api/stream');
|
||||
es.onopen = function(){sseConnected = true; $('connection-status').textContent = 'live (SSE)';};
|
||||
es.onmessage = function(e){try{render(JSON.parse(e.data));}catch(ex){}};
|
||||
es.onerror = function(){sseConnected = false; $('connection-status').textContent = 'polling';};
|
||||
function loadScatter(){
|
||||
var m=$('scatter-model').value;
|
||||
fetch('/api/scatter?window=24&model='+m).then(function(r){return r.json()}).then(renderScatter).catch(function(){});
|
||||
@@ -293,7 +299,7 @@ el.innerHTML='<svg viewBox="-35 0 140 115" style="width:100%;height:200px">'+gri
|
||||
var models=[];pts.forEach(function(p){if(models.indexOf(p.model)===-1)models.push(p.model);});
|
||||
lg.innerHTML=models.map(function(m){return'<span class="d-flex align-items-center gap-1 small"><svg width="10" height="10"><circle cx="5" cy="5" r="3.5" fill="'+(mcol[m]||'#38bdf8')+'"/></svg>'+mlab[m]+'</span>';}).join('');
|
||||
}
|
||||
poll();setInterval(poll,3000);loadTS();loadPerf();setInterval(loadPerf,15000);loadScatter();setInterval(loadScatter,30000);
|
||||
poll();setInterval(poll,10000);loadTS();loadPerf();setInterval(loadPerf,15000);loadScatter();setInterval(loadScatter,30000);
|
||||
</script>
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
Reference in New Issue
Block a user