Browse Source

Detecta extensões ativas sem depender de /etc/mime.types

A imagem python:3.12-slim não tem /etc/mime.types, então mimetypes não
reconhecia .xht, .xslt, .kml etc. e o upload/entrega deixavam passar.
EXTENSOES_ATIVAS passa a cobrir as mesmas extensões do nginx (mais .kml e
.xspf), com teste que mantém as duas listas iguais. media-types entra na
imagem para o tipo_ativo reconhecer os demais +xml.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pull/3865/head
Edward Ribeiro 2 weeks ago
parent
commit
0c25dd836d
  1. 2
      docker/Dockerfile
  2. 2
      docker/config/nginx/sapl.conf
  3. 21
      sapl/base/tests/test_media.py
  4. 1
      sapl/test_utils.py
  5. 9
      sapl/utils.py

2
docker/Dockerfile

@ -51,7 +51,7 @@ ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 \
RUN set -eux; \ RUN set -eux; \
apt-get update; \ apt-get update; \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
curl jq bash tzdata fontconfig tini libmagic1 \ curl jq bash tzdata fontconfig tini libmagic1 media-types \
libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf-2.0-0 \ libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf-2.0-0 \
libharfbuzz0b libfreetype6 libjpeg62-turbo zlib1g fonts-dejavu-core; \ libharfbuzz0b libfreetype6 libjpeg62-turbo zlib1g fonts-dejavu-core; \
if [ "$WITH_GRAPHVIZ" = "1" ]; then apt-get install -y --no-install-recommends graphviz; fi; \ if [ "$WITH_GRAPHVIZ" = "1" ]; then apt-get install -y --no-install-recommends graphviz; fi; \

2
docker/config/nginx/sapl.conf

@ -66,7 +66,7 @@ server {
# Demais arquivos são públicos e servidos direto do disco; conteúdo que o # Demais arquivos são públicos e servidos direto do disco; conteúdo que o
# navegador executaria como página é forçado a download. # navegador executaria como página é forçado a download.
location ~* ^/media/.+\.(html?|xhtml|xht|shtml|svgz?|xml|xsl|xslt|rdf|js|mjs|mht|mhtml|rss|atom|mml)$ { location ~* ^/media/.+\.(html?|xhtml|xht|shtml|svgz?|xml|xsl|xslt|rdf|js|mjs|mht|mhtml|rss|atom|mml|kml|xspf)$ {
root /var/interlegis/sapl; root /var/interlegis/sapl;
types { } types { }
default_type application/octet-stream; default_type application/octet-stream;

21
sapl/base/tests/test_media.py

@ -17,6 +17,7 @@ from sapl.base.models import AppConfig, Autor
from sapl.materia.models import Proposicao from sapl.materia.models import Proposicao
from sapl.protocoloadm.models import (DocumentoAcessorioAdministrativo, from sapl.protocoloadm.models import (DocumentoAcessorioAdministrativo,
DocumentoAdministrativo) DocumentoAdministrativo)
from sapl.utils import EXTENSOES_ATIVAS
DOC_PATH = 'sapl/private/documentoadministrativo/2026/1/texto.pdf' DOC_PATH = 'sapl/private/documentoadministrativo/2026/1/texto.pdf'
ACESSORIO_PATH = 'sapl/public/documentoacessorioadministrativo/2026/1/a.pdf' ACESSORIO_PATH = 'sapl/public/documentoacessorioadministrativo/2026/1/a.pdf'
@ -237,7 +238,7 @@ def test_arquivo_publico_passa_por_x_accel(client):
@pytest.mark.django_db(transaction=False) @pytest.mark.django_db(transaction=False)
@pytest.mark.parametrize('nome', ['x.svg', 'x.html', 'x.XML', 'x.js', @pytest.mark.parametrize('nome', ['x.svg', 'x.html', 'x.XML', 'x.js',
'x.xht', 'x.rdf', 'x.xslt']) 'x.xht', 'x.rdf', 'x.xslt', 'x.kml'])
def test_arquivo_publico_ativo_e_baixado(client, nome): def test_arquivo_publico_ativo_e_baixado(client, nome):
response = _get(client, 'sapl/public/normajuridica/2026/1/' + nome) response = _get(client, 'sapl/public/normajuridica/2026/1/' + nome)
@ -363,3 +364,21 @@ def test_nginx_envia_ao_django_todos_os_caminhos_protegidos():
no_nginx = set(regex.group(1).split('|')) no_nginx = set(regex.group(1).split('|'))
assert set(RESOLVEDORES) | set(MODELOS_DE_SESSAO) <= no_nginx assert set(RESOLVEDORES) | set(MODELOS_DE_SESSAO) <= no_nginx
assert conf.index(regex.group(0)) < conf.index('location /media/ {') assert conf.index(regex.group(0)) < conf.index('location /media/ {')
def test_nginx_forca_download_das_extensoes_ativas():
caminho = os.path.join(settings.PROJECT_DIR, 'docker', 'config',
'nginx', 'sapl.conf')
with open(caminho) as f:
conf = f.read()
regex = re.search(r'location ~\*? \^/media/\.\+\\\.\(([a-z?|]+)\)\$',
conf)
assert regex, 'location de extensões ativas não encontrada'
no_nginx = set()
for ext in regex.group(1).split('|'):
if ext.endswith('?'):
no_nginx |= {ext[:-2], ext[:-1]}
else:
no_nginx.add(ext)
assert {'.' + e for e in no_nginx} == EXTENSOES_ATIVAS

1
sapl/test_utils.py

@ -43,6 +43,7 @@ ATIVOS = [
('x.XHTML', b'%PDF-1.4'), ('x.XHTML', b'%PDF-1.4'),
('x.xht', b'%PDF-1.4\n<html><script>alert(1)</script></html>'), ('x.xht', b'%PDF-1.4\n<html><script>alert(1)</script></html>'),
('x.rdf', b'%PDF-1.4'), ('x.rdf', b'%PDF-1.4'),
('x.kml', b'<kml><script>alert(1)</script></kml>'),
('x.pdf', b'<!DOCTYPE html><html><script>alert(1)</script></html>'), ('x.pdf', b'<!DOCTYPE html><html><script>alert(1)</script></html>'),
] ]

9
sapl/utils.py

@ -603,10 +603,13 @@ class FilterOverridesMetaMixin:
} }
# Extensões que o navegador pode executar como página na origem do SAPL # Extensões que o navegador pode executar como página na origem do SAPL.
# Não depender só de mimetypes: sem /etc/mime.types (imagem slim) várias
# destas não são reconhecidas. Manter em sincronia com sapl.conf.
EXTENSOES_ATIVAS = { EXTENSOES_ATIVAS = {
'.html', '.htm', '.xhtml', '.shtml', '.svg', '.svgz', '.xml', '.xsl', '.html', '.htm', '.xhtml', '.xht', '.shtml', '.svg', '.svgz', '.xml',
'.js', '.mht', '.mhtml', '.xsl', '.xslt', '.rdf', '.js', '.mjs', '.mht', '.mhtml', '.rss', '.atom',
'.mml', '.kml', '.xspf',
} }
MIMES_ATIVOS = { MIMES_ATIVOS = {

Loading…
Cancel
Save