Analisador de complexidade de algoritmos com recomendação técnica contextualizada por tipo de dado e padrão de distribuição.
algorithm_analyzer/
│
├── .github/
│ └── workflows/
│ └── ci.yml ← CI: lint → SAST → supply chain → testes
│
├── core/ ← Lógica pura — sem I/O, sem efeitos colaterais
│ ├── parser/
│ │ └── expression_parser.py ← Compilação segura de expressões via AST
│ ├── analyzer/
│ │ └── complexity_analyzer.py ← Faixas de dominância + bisseção numérica
│ └── classifier/
│ └── asymptotic_classifier.py ← Classificação assintótica real (O(n), O(n²)…)
│
├── domain/ ← Regras de negócio
│ ├── types/
│ │ └── models.py ← Dataclasses, enums, tipos funcionais
│ ├── cost_model/
│ │ └── cost_adjuster.py ← Fatores de custo por tipo/padrão de dado
│ └── justifier/
│ └── recommendation.py ← Gerador de recomendação com justificativa técnica
│
├── interface/
│ ├── api/
│ │ ├── fastapi_app.py ← Application factory (CORS, security headers)
│ │ ├── routes.py ← Endpoints REST + view HTML
│ │ ├── schemas.py ← Schemas Pydantic (validação entrada/saída)
│ │ └── charts.py ← Geração dos gráficos Plotly (servidor)
│ ├── cli/
│ │ └── cli_runner.py ← Interface de linha de comando
│ └── web/
│ ├── templates/
│ │ └── index.html ← Template HTML
│ └── static/
│ ├── css/
│ │ └── style.css ← Estilos (terminal industrial, âmbar)
│ ├── js/
│ │ └── app.js ← JS puro — zero dependências de runtime
│ └── vendor/
│ └── plotly-basic.min.js ← Baixar manualmente (ver abaixo)
│
├── tests/
│ ├── unit/
│ │ └── test_core.py ← 12 testes unitários (core + domain)
│ └── integration/
│ └── test_pipeline.py ← 11 testes de integração (FastAPI)
│
├── main.py ← Entry point (uvicorn)
├── requirements.txt ← Dependências de produção (versões fixadas)
├── requirements-dev.txt ← Ferramentas de dev/CI
├── .bandit ← Configuração SAST
├── .flake8 ← Configuração linter
└── .gitignore
| Princípio | Onde |
|---|---|
| SRP (Single Responsibility) | Cada arquivo tem exatamente uma responsabilidade |
| Dependency Rule (Clean Architecture) | core não importa domain; domain não importa interface |
| Imutabilidade | dataclass(frozen=True) — sem estado mutável oculto |
| Fail fast | Parser rejeita expressões inválidas imediatamente |
| Separation of concerns | Lógica pura em core/, regras em domain/, I/O em interface/ |
| Application Factory | criar_app() em fastapi_app.py — testável e configurável |
O arquivo plotly-basic.min.js não está incluso no repositório.
Deve ser baixado uma vez e colocado em interface/web/static/vendor/.
# PowerShell
Invoke-WebRequest `
-Uri "https://github.com/plotly/plotly.js v3.5.0 CDN" `
-OutFile "interface/web/static/vendor/plotly-basic.min.js"# Linux / macOS
curl -L -o interface/web/static/vendor/plotly-basic.min.js \
"https://github.com/plotly/plotly.js v3.5.0 CDN"# PowerShell
Get-FileHash interface/web/static/vendor/plotly-basic.min.js -Algorithm SHA256# Linux / macOS
sha256sum interface/web/static/vendor/plotly-basic.min.jsHash esperado para v3.5.0:
5BD80F6D8FDD7BC055F8E48099403355FC7223A47F95B1D9193DA24C0BAB6B55
Se o hash calculado não bater com este valor, não use o arquivo. Pode indicar arquivo corrompido ou comprometido no CDN. Nesse caso, siga o Passo 2.1 abaixo para obter o arquivo da fonte oficial.
O CDN pode servir arquivos diferentes do publicado oficialmente — isso já foi observado na prática com este projeto. Se o hash não bater, siga este processo para obter o arquivo diretamente do tarball oficial do npm, que é imutável e possui assinatura criptográfica verificável.
Por que o npm é mais confiável que o CDN? O npm registra o hash SHA-512 de cada pacote no momento do publish e nunca o altera. O CDN é uma camada de distribuição que pode divergir por cache, compressão ou, no pior caso, comprometimento.
# 1. Baixa o tarball oficial diretamente do registro npm
Invoke-WebRequest `
-Uri "https://registry.npmjs.org/plotly.js-basic-dist-min/-/plotly.js-basic-dist-min-3.5.0.tgz" `
-OutFile "$env:TEMP\plotly-basic-3.5.0.tgz"
# 2. Extrai o tarball
tar -xzf "$env:TEMP\plotly-basic-3.5.0.tgz" -C "$env:TEMP"
# 3. Verifica o hash do arquivo extraído
Get-FileHash "$env:TEMP\package\plotly-basic.min.js" -Algorithm SHA256
# Deve retornar: 5BD80F6D8FDD7BC055F8E48099403355FC7223A47F95B1D9193DA24C0BAB6B55# Linux / macOS
curl -L -o /tmp/plotly-basic-3.5.0.tgz \
"https://registry.npmjs.org/plotly.js-basic-dist-min/-/plotly.js-basic-dist-min-3.5.0.tgz"
tar -xzf /tmp/plotly-basic-3.5.0.tgz -C /tmp
sha256sum /tmp/package/plotly-basic.min.js
# Deve retornar: 5bd80f6d8fdd7bc055f8e48099403355fc7223a47f95b1d9193da24c0bab6b55Se o hash bater: copie o arquivo extraído para o vendor:
# PowerShell
Copy-Item "$env:TEMP\package\plotly-basic.min.js" `
-Destination "interface\web\static\vendor\plotly-basic.min.js" -Force# Linux / macOS
cp /tmp/package/plotly-basic.min.js interface/web/static/vendor/plotly-basic.min.jsSe o hash não bater: não use o arquivo. Abra uma issue no repositório informando o hash que você obteve — pode indicar um problema de integridade no registro npm para a versão 3.5.0.
Limpeza após verificação — os arquivos temporários não são mais necessários:
# PowerShell — limpa arquivos temporários do processo de verificação
Remove-Item "$env:TEMP\plotly-basic-3.5.0.tgz" -Force
Remove-Item "$env:TEMP\package" -Recurse -Force# Linux / macOS
rm /tmp/plotly-basic-3.5.0.tgz
rm -rf /tmp/packageℹ️ Esses arquivos ficam em
%TEMP%(Windows) ou/tmp(Linux/macOS). Não representam risco de segurança — são arquivos públicos baixados de fonte oficial. A limpeza é boa prática de higiene do sistema.
⚠ Se o hash não bater, não use o arquivo. Baixe novamente de outra rede ou verifique a release oficial em https://github.com/plotly/plotly.js/releases
CDNs introduzem supply chain risk: um atacante comprometendo o CDN pode injetar código malicioso em todos os usuários. Com vendor local e hash verificado, a superfície de ataque é eliminada.
- Python 3.9 ou superior
- pip atualizado
# 1. Criar e ativar virtualenv
python -m venv .venv
.venv\Scripts\Activate.ps1
# 2. Instalar dependências
pip install -r requirements.txt
# 3. Baixar Plotly (ver seção Setup do Vendor acima)
# 4. Iniciar servidor
python main.pypython -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python main.pyhttp://127.0.0.1:8000 ← Interface web
http://127.0.0.1:8000/docs ← Documentação automática FastAPI (Swagger UI)
http://127.0.0.1:8000/redoc ← Documentação alternativa (ReDoc)
pip install -r requirements-dev.txtpytest tests/unit/ -v --cov=core --cov=domain --cov-report=term-missingpytest tests/integration/ -vpytest tests/ -vpython tests/unit/test_core.py
python tests/integration/test_pipeline.pyO pipeline .github/workflows/ci.yml executa 5 jobs em ordem:
lint ──────────────────────────────── flake8 + flake8-bugbear
sast ──────────────────────────────── bandit (SAST — falha se severity HIGH)
supply-chain ──────────────────────── pip-audit (falha se CVE em prod deps)
└── tests ──────────────────── pytest unit + integration (cov ≥ 80%)
compatibility ─────────────────────── Python 3.9, 3.11, 3.12 em paralelo
Boas práticas de segurança no CI:
permissions: contents: read— princípio do menor privilégio- Todas as
actions/fixadas por SHA de commit — sem supply chain attack via tag mutável pip-auditbloqueia build se CVE encontrada emrequirements.txtbanditbloqueia build se issue de severidade HIGH encontrada
Request:
{
"algoritmos": [
{"nome": "Quicksort", "expressao": "n*log2(n)"},
{"nome": "Bubblesort", "expressao": "n**2"}
],
"n": 50000,
"tipo": "string",
"padrao": "quase_ordenado"
}Valores aceitos para tipo: int float string objeto
Valores aceitos para padrao: aleatorio quase_ordenado ordenado invertido muitas_duplicatas
Response 200:
{
"recomendacao": {
"algoritmo_ideal": "timsort",
"classe_assintotica": "O(n log n)",
"custo_estimado": 234145.0,
"justificativa": "Timsort é especialmente eficiente...",
"aviso": null,
"proximo_cruzamento": "em n ≈ 120.000, mergesort passa a vencer",
"ranking": [
{"nome": "timsort", "custo": 234145.0},
{"nome": "quicksort", "custo": 830000.0}
]
},
"faixas": [
{"algoritmo": "timsort", "n_inicio": 1, "n_fim": 9999, "cruzamento": 47.2}
],
"ordem_assintotica": [
{"algoritmo": "timsort", "classe": "O(n log n)", "indice": 4}
],
"grafico_curvas": "{ ...JSON Plotly... }",
"grafico_faixas": "{ ...JSON Plotly... }"
}Erros:
| Status | Situação |
|---|---|
400 |
tipo ou padrao inválido |
422 |
Expressão matemática inválida, termo proibido, ou N fora do range |
- Usa
ast.parse+ whitelist de nós AST — semevallivre __builtins__bloqueados na execução- Nomes permitidos: apenas
n,log,log2,exp,sqrt - Termos proibidos validados no cliente (JS) e no servidor (Pydantic)
- Headers de segurança em toda resposta:
X-Content-Type-Options,X-Frame-Options,X-XSS-Protection,Content-Security-Policy MAX_CONTENT_LENGTHlimitado a 64KB- CORS restrito a
localhost:8000
- Todas as dependências Python com versão
==fixada - Sem dependências JS externas em runtime
- Plotly vendorizado localmente com verificação de hash SHA-256
pip-auditno CI bloqueia CVEs em produção
| Fase | Descrição | O que muda na arquitetura |
|---|---|---|
| 2 | Benchmark real na máquina | Novo módulo core/benchmark/ com timeit; endpoint async /api/benchmark |
| 3 | Complexidade de espaço | Campo espaco no modelo Algoritmo; novo bloco no frontend |
| 4 | Perfil de acesso ao vetor | Enum PadraoAcesso em domain/types/; ajuste em cost_adjuster.py |
| 5 | Inferência via análise estática | Novo módulo core/static_analyzer/ com ast walker de loops |
Cada fase adiciona módulos sem modificar os existentes — respeitando o Open/Closed Principle (OCP) da Clean Architecture.
secret-scanning ──── Gitleaks (histórico git completo) ┐
lint ──── flake8 + flake8-bugbear ├─ paralelo
sast ──── Bandit + Semgrep │
sca ──── pip-audit + SBOM CycloneDX ┘
│
todos passam?
│
tests ──── pytest unit + integração (cov ≥ 80%) ┐ sequencial
compatibility ──── Python 3.9, 3.11, 3.12 ┘
| Ferramenta | O que detecta |
|---|---|
| Bandit | eval/exec livre, subprocess com shell=True, hashes fracos (MD5, SHA1), assert em produção |
Semgrep p/python |
Bugs comuns, uso incorreto de APIs, type confusion |
Semgrep p/security-audit |
Path traversal, f-string injection, open redirect, deserialização insegura |
Configuração Bandit: .bandit
Bloqueia build se: qualquer finding de severidade HIGH (Bandit) ou ERROR (Semgrep)
Rodar localmente:
bandit -r . -c .bandit
semgrep scan --config p/python --config p/security-audit --severity ERROR .| Ferramenta | O que faz |
|---|---|
| pip-audit | Verifica CVEs no NVD e PyPI Advisory Database para cada dependência |
| cyclonedx-bom | Gera SBOM (Software Bill of Materials) em formato CycloneDX v1.4 JSON |
O SBOM é exigido por:
- NIST SP 800-161 (supply chain risk management)
- Executive Order 14028 (EUA, 2021)
- ISO 27001 e SOC 2 (auditorias de compliance)
Bloqueia build se: qualquer CVE encontrada em requirements.txt
Não bloqueia: CVEs em requirements-dev.txt (reporta apenas)
SBOM retido: 90 dias como artefato auditável
Rodar localmente:
pip-audit -r requirements.txt --strict
cyclonedx-py environment --output-format json --output-file sbom.cyclonedx.json| Ferramenta | O que detecta |
|---|---|
| Gitleaks | Chaves AWS/GCP/Azure, tokens GitHub/Slack/Stripe, senhas hardcoded, certificados privados, JWTs |
Varre todo o histórico git (fetch-depth: 0), não só o commit atual.
Bloqueia build imediatamente se qualquer segredo for detectado.
Rodar localmente (requer Docker):
docker run -v ${PWD}:/path zricethezav/gitleaks detect --source /pathNão implementado no MVP — requer app rodando em container isolado no CI. Será adicionado na Fase 2 com:
- Docker + docker-compose no CI
- OWASP ZAP (Zed Attack Proxy) ou Nuclei contra endpoints reais
- Testa: XSS, CSRF, headers ausentes, endpoints expostos, injeção
| Camada | Ferramenta | Status |
|---|---|---|
| Código estático | Bandit + Semgrep | ✅ |
| Dependências | pip-audit | ✅ |
| Inventário (SBOM) | CycloneDX | ✅ |
| Segredos no código | Gitleaks | ✅ |