mirror of https://github.com/interlegis/sapl.git
Browse Source
- nginx sapl.conf: return 444 for scanner extension probes (.php, .asp, .jsp, .env, etc.) before requests reach Gunicorn — zero Python cost - ratelimit.py: remove check 2b (scanner_probe) — dead code now that nginx handles it; remove unused `import os` - ratelimit.py: authenticated users skip the rl:ip:blocked check (check 2) to prevent anonymous NAT traffic from blocking legislative house staff - ratelimit.py: add _handle_not_found — post-response 404 counter per anonymous IP; blocks after RATE_LIMIT_404_THRESHOLD (default 10) hits in the anon window, catching path probes without known extensions - settings.py: replace RATE_LIMIT_SCANNER_EXTENSIONS with RATE_LIMIT_404_THRESHOLD; add RL_IP_404S Redis key constant - painel: remove dead mensagem/parlamentares/votacao templates, views, and URL entries — unreachable from any menu or template - RATE-LIMITER-PLAN.md: update decision flow, mermaid diagram, enforcement graduation table, and key schema to reflect all changes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>rate-limiter-2026
9 changed files with 83 additions and 428 deletions
@ -1,120 +0,0 @@ |
|||
{% load i18n %} |
|||
{% load common_tags %} |
|||
{% load render_bundle from webpack_loader %} |
|||
{% load webpack_static from webpack_loader %} |
|||
|
|||
<!DOCTYPE HTML> |
|||
<!--[if IE 8]> <html class="no-js lt-ie9" lang="pt-br"> <![endif]--> |
|||
<!--[if gt IE 8]><!--> |
|||
<html lang="pt-br"> |
|||
<!--<![endif]--> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<!-- TODO: does it need this head_title here? --> |
|||
<title>{% block head_title %}{% trans 'SAPL - Sistema de Apoio ao Processo Legislativo' %}{% endblock %}</title> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
|
|||
{% render_chunk_vendors 'css' %} |
|||
{% render_bundle 'global' 'css' %} |
|||
{% render_bundle 'painel' 'css' %} |
|||
|
|||
|
|||
<STYLE type="text/css"> |
|||
@media screen { |
|||
body {font-size: medium; color: white; line-height: 1em; background: black;} |
|||
} |
|||
</STYLE> |
|||
|
|||
</head> |
|||
<body> |
|||
<h1>{{ context.title }}</h1> |
|||
<input id="json_url" type="hidden" value="{% url 'sapl.painel:dados_painel' %}"> |
|||
<h2>Ajax refresh counter: <span id="counter"></span></h2> |
|||
<h3> |
|||
<span id="sessao_plenaria"></span><br/><br/> |
|||
<span id="sessao_plenaria_data"></span><br/><br/> |
|||
<span id="sessao_plenaria_hora_inicio"></span></br><br/> |
|||
<h2><span id="relogio"></span></h2></br><br/><br/> |
|||
<span id="materia_legislativa_texto"></span><br/> |
|||
<span id="observacao_materia"></span> |
|||
</h3> |
|||
</body> |
|||
|
|||
{% render_chunk_vendors 'js' %} |
|||
{% render_bundle 'global' 'js' %} |
|||
{% render_bundle 'painel' 'js' %} |
|||
|
|||
<script type="text/javascript"> |
|||
$(document).ready(function() { |
|||
|
|||
//TODO: replace by a fancy jQuery clock |
|||
function checkTime(i) { |
|||
if (i<10) {i = "0" + i}; // add zero in front of numbers < 10 |
|||
return i; |
|||
} |
|||
function startTime() { |
|||
var today=new Date(); |
|||
var h=today.getHours(); |
|||
var m=today.getMinutes(); |
|||
var s=today.getSeconds(); |
|||
m = checkTime(m); |
|||
s = checkTime(s); |
|||
$("#relogio").text(h+":"+m+":"+s) |
|||
var t = setTimeout(function(){ |
|||
startTime() |
|||
},500); |
|||
} |
|||
|
|||
startTime(); |
|||
|
|||
var counter = 1; |
|||
(function poll() { |
|||
$.ajax({ |
|||
url: $("#json_url").val(), |
|||
type: "GET", |
|||
success: function(data) { |
|||
|
|||
//TODO: json spitted out is very complex, have to simplify/flat it |
|||
//TODO: probably building it by hand on REST side |
|||
|
|||
console.debug(data) |
|||
|
|||
var presentes = $("#parlamentares"); |
|||
presentes.children().remove(); |
|||
|
|||
presentes_ordem_dia = data.presentes_ordem_dia |
|||
$.each(presentes_ordem_dia, function(index, parlamentar) { |
|||
$('<li />', {text: parlamentar.nome + '/' + parlamentar.partido + ' ' + parlamentar.voto }).appendTo(presentes); |
|||
}); |
|||
|
|||
var votacao = $("#votacao") |
|||
votacao.children().remove() |
|||
votacao.append("<li>Sim: " + data["numero_votos_sim"] + "</li>") |
|||
votacao.append("<li>Não: " + data["numero_votos_nao"] + "</li>") |
|||
votacao.append("<li>Abstenções: " + data["numero_abstencoes"] + "</li>") |
|||
votacao.append("<li>Presentes: " + data["presentes"] + "</li>") |
|||
votacao.append("<li>Total votos: " + data["total_votos"] + "</li>") |
|||
|
|||
$("#sessao_plenaria").text(data["sessao_plenaria"]) |
|||
$("#sessao_plenaria_data").text("Data Início: " + data["sessao_plenaria_data"]) |
|||
$("#sessao_plenaria_hora_inicio").text("Hora Início: " + data["sessao_plenaria_hora_inicio"]) |
|||
|
|||
$("#materia_legislativa_texto").text(data["materia_legislativa_texto"]) |
|||
$("#observacao_materia").text(data["observacao_materia"]) |
|||
$("#resultado_votacao").text(data["tipo_resultado"]) |
|||
|
|||
$("#counter").text(counter); |
|||
counter++; |
|||
}, |
|||
error: function(err) { |
|||
console.error(err); |
|||
}, |
|||
dataType: "json", |
|||
//complete: setTimeout(function() {poll()}, 5000), |
|||
timeout: 20000 // TODO: decrease |
|||
}) |
|||
})(); |
|||
}); |
|||
</script> |
|||
</html> |
|||
@ -1,128 +0,0 @@ |
|||
{% load i18n %} |
|||
{% load common_tags %} |
|||
|
|||
{% load render_bundle from webpack_loader %} |
|||
{% load webpack_static from webpack_loader %} |
|||
|
|||
<!DOCTYPE HTML> |
|||
<!--[if IE 8]> <html class="no-js lt-ie9" lang="pt-br"> <![endif]--> |
|||
<!--[if gt IE 8]><!--> |
|||
<html lang="pt-br"> |
|||
<!--<![endif]--> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<!-- TODO: does it need this head_title here? --> |
|||
<title>{% block head_title %}{% trans 'SAPL - Sistema de Apoio ao Processo Legislativo' %}{% endblock %}</title> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
|
|||
{% render_chunk_vendors 'css' %} |
|||
{% render_bundle 'global' 'css' %} |
|||
{% render_bundle 'painel' 'css' %} |
|||
|
|||
|
|||
|
|||
<STYLE type="text/css"> |
|||
@media screen { |
|||
body {font-size: medium; color: white; line-height: 1em; background: black;} |
|||
} |
|||
</STYLE> |
|||
|
|||
</head> |
|||
<body> |
|||
<h1>{{ context.title }}</h1> |
|||
<input id="json_url" type="hidden" value="{% url 'sapl.painel:dados_painel' %}"> |
|||
<h3> |
|||
<span id="sessao_plenaria"></span><br/><br/> |
|||
<span id="sessao_plenaria_data"></span><br/><br/> |
|||
<span id="sessao_plenaria_hora_inicio"></span></br><br/> |
|||
<h2><span id="relogio"></span></h2> |
|||
<table> |
|||
<tr> |
|||
<td> |
|||
<ul id="parlamentares"> |
|||
</ul> |
|||
</td> |
|||
</tr> |
|||
</table> |
|||
</h3> |
|||
</body> |
|||
|
|||
{% render_chunk_vendors 'js' %} |
|||
{% render_bundle 'global' 'js' %} |
|||
{% render_bundle 'painel' 'js' %} |
|||
|
|||
<script type="text/javascript"> |
|||
$(document).ready(function() { |
|||
|
|||
//TODO: replace by a fancy jQuery clock |
|||
function checkTime(i) { |
|||
if (i<10) {i = "0" + i}; // add zero in front of numbers < 10 |
|||
return i; |
|||
} |
|||
function startTime() { |
|||
var today=new Date(); |
|||
var h=today.getHours(); |
|||
var m=today.getMinutes(); |
|||
var s=today.getSeconds(); |
|||
m = checkTime(m); |
|||
s = checkTime(s); |
|||
$("#relogio").text(h+":"+m+":"+s) |
|||
var t = setTimeout(function(){ |
|||
startTime() |
|||
},500); |
|||
} |
|||
|
|||
startTime(); |
|||
|
|||
var counter = 1; |
|||
(function poll() { |
|||
$.ajax({ |
|||
url: $("#json_url").val(), |
|||
type: "GET", |
|||
success: function(data) { |
|||
|
|||
//TODO: json spitted out is very complex, have to simplify/flat it |
|||
//TODO: probably building it by hand on REST side |
|||
|
|||
console.debug(data) |
|||
|
|||
var presentes = $("#parlamentares"); |
|||
presentes.children().remove(); |
|||
|
|||
presentes_ordem_dia = data.presentes_ordem_dia |
|||
$.each(presentes_ordem_dia, function(index, parlamentar) { |
|||
$('<li />', {text: parlamentar.nome + '/' + parlamentar.partido }).appendTo(presentes); |
|||
/*$('<li />', {text: parlamentar.nome + '/' + parlamentar.partido + ' ' + parlamentar.voto }).appendTo(presentes);*/ |
|||
}); |
|||
|
|||
var votacao = $("#votacao") |
|||
votacao.children().remove() |
|||
votacao.append("<li>Sim: " + data["numero_votos_sim"] + "</li>") |
|||
votacao.append("<li>Não: " + data["numero_votos_nao"] + "</li>") |
|||
votacao.append("<li>Abstenções: " + data["numero_abstencoes"] + "</li>") |
|||
votacao.append("<li>Presentes: " + data["presentes"] + "</li>") |
|||
votacao.append("<li>Total votos: " + data["total_votos"] + "</li>") |
|||
|
|||
$("#sessao_plenaria").text(data["sessao_plenaria"]) |
|||
$("#sessao_plenaria_data").text("Data Início: " + data["sessao_plenaria_data"]) |
|||
$("#sessao_plenaria_hora_inicio").text("Hora Início: " + data["sessao_plenaria_hora_inicio"]) |
|||
|
|||
$("#materia_legislativa_texto").text(data["materia_legislativa_texto"]) |
|||
$("#observacao_materia").text(data["observacao_materia"]) |
|||
$("#resultado_votacao").text(data["tipo_resultado"]) |
|||
|
|||
$("#counter").text(counter); |
|||
counter++; |
|||
}, |
|||
error: function(err) { |
|||
console.error(err); |
|||
}, |
|||
dataType: "json", |
|||
//complete: setTimeout(function() {poll()}, 5000), |
|||
timeout: 20000 // TODO: decrease |
|||
}) |
|||
})(); |
|||
}); |
|||
</script> |
|||
</html> |
|||
@ -1,123 +0,0 @@ |
|||
{% load i18n %} |
|||
{% load render_bundle from webpack_loader %} |
|||
{% load webpack_static from webpack_loader %} |
|||
|
|||
<!DOCTYPE HTML> |
|||
<!--[if IE 8]> <html class="no-js lt-ie9" lang="pt-br"> <![endif]--> |
|||
<!--[if gt IE 8]><!--> |
|||
<html lang="pt-br"> |
|||
<!--<![endif]--> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<!-- TODO: does it need this head_title here? --> |
|||
<title>{% block head_title %}{% trans 'SAPL - Sistema de Apoio ao Processo Legislativo' %}{% endblock %}</title> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
|
|||
{% render_chunk_vendors 'css' %} |
|||
{% render_bundle 'global' 'css' %} |
|||
{% render_bundle 'painel' 'css' %} |
|||
|
|||
<STYLE type="text/css"> |
|||
@media screen { |
|||
body {font-size: medium; color: white; line-height: 1em; background: black;} |
|||
} |
|||
</STYLE> |
|||
</head> |
|||
<body> |
|||
<h1>{{ context.title }}</h1> |
|||
<input id="json_url" type="hidden" value="{% url 'sapl.painel:dados_painel' %}"> |
|||
<h3> |
|||
<span id="sessao_plenaria"></span><br/><br/> |
|||
<span id="sessao_plenaria_data"></span><br/><br/> |
|||
<span id="sessao_plenaria_hora_inicio"></span></br><br/> |
|||
<h2><span id="relogio"></span></h2> |
|||
<table> |
|||
<tr> |
|||
<td> |
|||
<ul id="votacao"> |
|||
</ul> |
|||
</td> |
|||
</tr> |
|||
</table> |
|||
<span id="resultado_votacao"></span><br/> |
|||
</h3> |
|||
</body> |
|||
|
|||
{% render_chunk_vendors 'js' %} |
|||
{% render_bundle 'global' 'js' %} |
|||
{% render_bundle 'painel' 'js' %} |
|||
|
|||
<script type="text/javascript"> |
|||
$(document).ready(function() { |
|||
|
|||
//TODO: replace by a fancy jQuery clock |
|||
function checkTime(i) { |
|||
if (i<10) {i = "0" + i}; // add zero in front of numbers < 10 |
|||
return i; |
|||
} |
|||
function startTime() { |
|||
var today=new Date(); |
|||
var h=today.getHours(); |
|||
var m=today.getMinutes(); |
|||
var s=today.getSeconds(); |
|||
m = checkTime(m); |
|||
s = checkTime(s); |
|||
$("#relogio").text(h+":"+m+":"+s) |
|||
var t = setTimeout(function(){ |
|||
startTime() |
|||
},500); |
|||
} |
|||
|
|||
startTime(); |
|||
|
|||
var counter = 1; |
|||
(function poll() { |
|||
$.ajax({ |
|||
url: $("#json_url").val(), |
|||
type: "GET", |
|||
success: function(data) { |
|||
|
|||
//TODO: json spitted out is very complex, have to simplify/flat it |
|||
//TODO: probably building it by hand on REST side |
|||
|
|||
console.debug(data) |
|||
|
|||
var presentes = $("#parlamentares"); |
|||
presentes.children().remove(); |
|||
|
|||
presentes_ordem_dia = data.presentes_ordem_dia |
|||
$.each(presentes_ordem_dia, function(index, parlamentar) { |
|||
$('<li />', {text: parlamentar.nome + '/' + parlamentar.partido + ' ' + parlamentar.voto }).appendTo(presentes); |
|||
}); |
|||
|
|||
var votacao = $("#votacao") |
|||
votacao.children().remove() |
|||
votacao.append("<li>Sim: " + data["numero_votos_sim"] + "</li>") |
|||
votacao.append("<li>Não: " + data["numero_votos_nao"] + "</li>") |
|||
votacao.append("<li>Abstenções: " + data["numero_abstencoes"] + "</li>") |
|||
votacao.append("<li>Presentes: " + data["presentes"] + "</li>") |
|||
votacao.append("<li>Total votos: " + data["total_votos"] + "</li>") |
|||
|
|||
$("#sessao_plenaria").text(data["sessao_plenaria"]) |
|||
$("#sessao_plenaria_data").text("Data Início: " + data["sessao_plenaria_data"]) |
|||
$("#sessao_plenaria_hora_inicio").text("Hora Início: " + data["sessao_plenaria_hora_inicio"]) |
|||
|
|||
$("#materia_legislativa_texto").text(data["materia_legislativa_texto"]) |
|||
$("#observacao_materia").text(data["observacao_materia"]) |
|||
$("#resultado_votacao").text(data["tipo_resultado"]) |
|||
|
|||
$("#counter").text(counter); |
|||
counter++; |
|||
}, |
|||
error: function(err) { |
|||
console.error(err); |
|||
}, |
|||
dataType: "json", |
|||
//complete: setTimeout(function() {poll()}, 5000), |
|||
timeout: 20000 // TODO: decrease |
|||
}) |
|||
})(); |
|||
}); |
|||
</script> |
|||
</html> |
|||
Loading…
Reference in new issue