
Laboratório de detecção de persistência de revogação: quando a redefinição de senha é bem-sucedida, mas o atacante nunca vai embora. Reproduz o bug de revogação condicional do Strapi CVE-2026-22706, sua correção, um pacote de detecção com três regras e a regra ingênua que não o detecta.
Quando a redefinição de senha é bem-sucedida, mas o atacante nunca vai embora.
Um laboratório local red/blue para uma classe de vulnerabilidade: a credencial que sobrevive ao evento que deveria eliminá-la. Ele entrega o exploit, a causa raiz, a correção, um pacote de detecção com três regras, uma auditoria de estado para o que as regras estruturalmente não conseguem ver, um console forense — e a regra de detecção que não funciona, mantida no repositório para ser demonstrada falhando.
Início rápido · A descoberta · Por que a detecção ingênua falha · O pacote de regras · Console · A correção · Matriz · Testes · Documentação
O mesmo ataque. As mesmas requisições. Uma diferença.
Gerado por scripts/figures.py a partir do mesmo payload que o
console desenha — regenerado e comparado por diff na CI, para que uma figura não possa divergir do código.
09:00 alice logs in ┐ refresh credential rt-001 │ the attacker steals rt-001 ┘
10:00 alice changes her password ← the one thing a victim can do alone HTTP 200 · password changed · fresh session issued
10:00:03 attacker: POST /refresh rt-002 → HTTP 200 new access credential at-004, issued 10:00:03
10:01:00 attacker: GET /me at-004 → HTTP 200 {"username": "alice", "authenticated": true}
Sem erro. Sem anomalia. Sem autenticação falhada para contar. A credencial de acesso do atacante tem *três segundos de vida* e foi emitida pelo servidor, a pedido, após o reset.
Eis a vulnerabilidade completa:```python
def _revoke_for_security_change(self, user, kind, device_id):
if device_id:
revoke_credentials(user, device_id=device_id) # ← the finding
Leia isso como um revisor leria. A lógica de revogação está bem ali. Ela chama
a função correta com o escopo correto. O endpoint ao redor atualiza o
hash da senha, retorna 200 e emite uma nova sessão — todo comportamento observável
de uma troca de senha correta está presente.
E se o chamador omitir device_id, nada é revogado, e o endpoint ainda
relata sucesso.
Isso não é hipotético. É
CVE-2026-22706
no Strapi ≤ 5.33.2, onde a etapa de invalidação do refresh-token era condicional
a um deviceId fornecido pelo chamador. Pontuado como 2.1, Low. Discutido
abaixo.
A pontuação é baixa porque o atacante já tinha acesso — essa é a condição de entrada, e este bug não concede nada novo. O que ele concede é duração, e o faz quebrando o único controle que a vítima pode operar por conta própria.
test_persistence_lasts_as_long_as_the_refresh_credential percorre sete dias de
tempo simulado para demonstrar isso.Um bug com pontuação Low em um caminho de contenção custa mais do que um bug com pontuação Low em um caminho de funcionalidade, porque o custo é pago durante um incidente, quando ninguém está lendo advisories.
A regra que você escreve primeiro:```text IF credential.issued_at < credential_change.timestamp: ALERT
Não é uma regra estúpida. É barata, precisa de um campo, lê-se como a definição
do problema, e está **correta quanto ao caso simples** — um atacante que use um
token de *acesso* roubado após o reset é apanhado.
`test_the_naive_rule_catches_the_simple_case` afirma que funciona.
Ambas as regras fazem a mesma pergunta sobre a mesma credencial. Leem campos
diferentes para responder, e os dois campos discordam:

**Três segundos depois, ou uma hora antes — a mesma credencial, no mesmo
instante.** A regra ingénua pergunta a um campo que o atacante controla, e um
`POST /refresh` reinicia-o.
Executa-a sobre o registo de pedidos, que é onde esta consulta é realmente escrita,
porque os registos de pedidos são o que tens:```text
NAIVE DETECTOR (NAIVE-001), over the request log
Result: NO ALERT
Six requests were served to the attacker after the reset. Every one of them
carried at-004, minted at 10:00:03 -- three seconds *after* the password
change. By its own timestamp it is the newest credential on the account.
Seria fácil parar por aí, e desonesto, então o laboratório também executa a versão mais justa da regra ingênua — ampliada para observar também o endpoint de refresh:```text NAIVE DETECTOR, widened to include POST /refresh
1 alert at 2026-09-11T10:00:03.000Z: the credential presented to /refresh was rt-002, issued 2026-09-11T09:30:00.000Z. Then it goes blind. 6 events follow that hop and it flags none of them, because every credential from there on carries a post-reset timestamp. Its incident covers 1 accepted request; the lineage rule's covers all of them.
And look at what the alert names: NAIVE-001 revoke rt-002 -- rotated away and already dead at 10:00:03 AFTERLIFE-001 revoke lin-001 -- the live thing every future credential descends from
Essa é a diferença que importa às 3 da manhã. A regra ingênua captura o único
instante em que a cadeia cruza a fronteira, e então perde o rastro pelos 29 dias
restantes — e a credencial que ela nomeia *já foi rotacionada e revogada
pelo servidor*. Revogá-la não resolve nada. `lin-001` é o objeto que você
precisa eliminar.
**E a regra ingênua não foi privada de telemetria.** Ela usa o mesmo fluxo de
eventos, o mesmo índice de linhagem, a mesma tolerância, a mesma deduplicação e
o mesmo estado limitado. Ela sobrescreve um método:```python
class Correlator: # AFTERLIFE-001
def _age_reference(self, facts):
return facts.root_issued_at
class NaiveCorrelator(Correlator): # NAIVE-001
def _age_reference(self, facts):
return facts.issued_at
root_issued_at está no índice que já está sendo consultado.
test_the_naive_rule_had_the_data_it_needed prova isso. A falha é a
comparação, não o registro.
Três regras sobre um log. Elas respondem a perguntas diferentes e disparam na ordem em que um incidente realmente se desenrola.
| regra | severidade | responde | dispara | |
|---|---|---|---|---|
| AFTERLIFE-002 | Reutilização de credencial de refresh | CRITICAL / MEDIUM | foi roubada? | na reutilização |
| AFTERLIFE-003 | Revogação incompleta em uma mudança de segurança | HIGH / LOW | a contenção foi executada? | na mudança — nenhum atacante necessário |
| AFTERLIFE-001 | Uso de linhagem de credencial pós-revogação | HIGH | a linhagem obsoleta foi usada? | na primeira aceitação |
| RULE PACK |
10:00:00 HIGH AFTERLIFE-003 Incomplete revocation at a security change 10:00:03 HIGH AFTERLIFE-001 Post-revocation credential lineage use
**Essa ordenação é a coisa mais útil neste repositório.** AFTERLIFE-003
dispara no instante em que a alteração é aplicada, três segundos antes de o atacante tocar
em qualquer coisa, porque a evidência de que necessita já está completa: o log diz quais
linhagens estavam ativas à entrada, e não diz que foram revogadas.
Não precisa de vítima nem de exploração. Reportará o defeito na primeira
redefinição de palavra-passe que qualquer utilizador efetue — o que o torna o único que se executa em staging,
onde não há atacante para esperar. AFTERLIFE-001 diz-lhe que uma violação está
em curso; AFTERLIFE-003 diz-lhe que o seu controlo de contenção está avariado.

`application recorded watermark: no` é o campo que aponta um socorrista de incidentes
para o *defeito* em vez de para o sintoma.
### A watermark que as regras usam não é a que a aplicação reporta
O evento de alteração de credencial transporta um campo `revocation_watermark` — o
valor `credentials_valid_after` que a aplicação escreveu. **Na implementação
vulnerável é `null`, porque a aplicação nunca escreveu nenhum.** Esse é
o bug.
Portanto, uma regra baseada nesse campo estaria cega precisamente ao caso que existe para
apanhar. As regras ancoram no `timestamp` do próprio evento, que é verdadeiro quer a
aplicação tenha feito o seu trabalho quer não, e reportam o campo em falta como evidência.
### Propriedades```text
deduplication 6 accepted requests on the stale lineage -> 1 alert
event order shuffled stream -> same alert (1 alert)
duplicate telemetry log replayed twice -> 1 alert (24 duplicate events discarded)
false positives the fixed implementation's log -> 0 alerts
bounded state caps at 256 activity/user, 2000 users, 20000 credentials
A independência de ordem não é "funciona na maioria dos casos":
test_6b_every_permutation_of_the_critical_events_detects executa todas as 24 ordenações
dos quatro eventos que importam e exige exatamente um alerta de cada.
A deduplicação é baseada em (user_id, credential_change_event_id, lineage_id),
com supressão de event_id à frente, para que um arquivo reproduzido não possa inflar a
contagem.
Cartões de regra completos, telemetria necessária e runbooks de resposta: docs/detection.md.
Uma superfície de leitura para a única pergunta que o laboratório aborda. Não um painel de contagens de alertas — um registro de mortalidade. Uma barra por credencial, da emissão à morte, agrupada na linhagem da qual descende, com a mudança de senha desenhada como uma linha na qual tudo antes dela deveria ter terminado.
Uma inversão deliberada: calor significa vivo, e depois da linha o calor está
errado. Na maioria das UIs de segurança, vermelho significa que ocorreu um erro. Nada dá erro aqui —
cada requisição na execução vulnerável retorna 200. Então a cor segue a mortalidade
em vez disso: frio é uma credencial que morreu quando lhe foi ordenado, quente é uma que ainda
respira, e além da guilhotina, ainda estar quente é o achado inteiro.
(BLACKOUT estabeleceu a mesma convenção, onde 200 era o vermelho.)
As finas quedas verticais são genealogia: uma credencial filha criada a partir de sua
genitora naquele instante. rt-001 → rt-002 → rt-004 desce e à direita
por toda a janela, e no modo vulnerável continua descendo após o
corte — que é a imagem de uma linhagem criando novas credenciais do outro lado
de seu próprio evento de extinção.```bash
python scripts/lab.py console
grava **[`docs/console-preview.html`](https://github.com/het-p301204/afterlife/blob/main/docs/console-preview.html)** — um
arquivo autocontido de 74 KiB com ambas as execuções incorporadas. Sem servidor, sem rede, sem
fontes para buscar; abra-o a partir do sistema de arquivos. Ou execute a versão ao vivo:```bash
python -m console
Arraste o scrubber para trás além de 10:00:00 e avance através dele: os achados aparecem
quando o log os conquista, AFTERLIFE-003 na mudança e AFTERLIFE-001 três
segundos depois. O raciocínio de design, incluindo o que foi cortado, está em
docs/console-design.md.
O console é somente leitura por construção. Ele reexecuta cenários concluídos e os desenha; não pode trocar a implementação, mover o relógio ou revogar nada.
test_the_console_has_no_control_surfaceafirma que a única rota não-GET é/api/rebuild. Uma superfície de leitura que pode mudar o que está lendo é uma em que você não pode confiar.
Seis pacotes, uma classe de segurança, nenhuma infraestrutura.```text
app/ the lab application
config.py mode selection; defaults to fixed, deliberately
store.py SQLite credential state + the security-change audit trail
tokens.py minting and decoding; a JWT is a signed pointer to a row
auth.py login / refresh / change-password + THE BUG + the audit
main.py six endpoints
common/ clock.py a rewindable UTC clock, so an hour of history costs nothing events.py the event vocabulary, shared by app and detector telemetry.py JSONL emission with credential-name redaction
detector/ strictly downstream: reads a log file, decides nothing base.py the alert shape, replay suppression, bounded state lineage.py rebuilds a credential's ancestry from issuance events ledger.py which lineages are alive, per user rules.py AFTERLIFE-001 persistence reuse.py AFTERLIFE-002 theft containment.py AFTERLIFE-003 the defect itself naive.py NAIVE-001, kept in order to be demonstrated failing audit.py the state scan the rules structurally cannot do engine.py the pack: one shared index, one stream, ranked alerts tail.py byte-offset JSONL tailer
console/ the mortality register payload.py one run, shaped for drawing static/ ~1400 lines of vanilla HTML/CSS/JS, no build step
scripts/ lab.py the demonstration scenarios.py nine scenarios x both implementations report.py the incident report preview.py bake the offline console figures.py render the console to SVG for this README
tests/ 290 tests
O aplicativo é executado **in-process** sob a demo, o console e os testes. Sem
portas, sem servidor de desenvolvimento, sem Docker. O relógio é fixado, então cada execução é
determinística e a telemetria, o console, as figuras e os relatórios commitados são todos
byte-idênticos entre execuções — o que o CI verifica com `git diff --exit-code`.
### Aplicação no lado do servidor
Uma credencial é uma **linha**, não uma string. O JWT que o cliente possui carrega `cid` —
uma chave de consulta — e a linha decide se a credencial está ativa.
A expiração é verificada em relação ao `expires_at` da linha, não em relação à claim `exp`
que o portador apresentou. Ambas as claims temporais são entregues ao PyJWT com `verify=False`,
o que aqui significa *"o servidor verifica isso por conta própria"*, e [`app/auth.py`](https://github.com/het-p301204/afterlife/blob/main/app/auth.py)
faz isso, em cada requisição. O cliente nunca tem voto sobre se sua própria
credencial ainda é válida.
---
## Linha do tempo do ataque```mermaid
flowchart TD
A["Account compromised<br/><i>phishing · XSS · stolen backup</i>"] --> B["Attacker holds refresh credential rt-001<br/>lineage lin-001, root sess-001 @ 09:00"]
B --> C["Legitimate password change @ 10:00<br/>HTTP 200 · password hash updated"]
C --> D{"device_id supplied?"}
D -->|"yes"| E["revoke_credentials(user, device_id)<br/>rt-001 revoked"]
D -->|"no — the exploit"| F["nothing revoked<br/>no watermark written"]
E --> G["Attacker refresh → 401<br/><b>contained</b>"]
F --> AF3["<b>AFTERLIFE-003 · HIGH</b> @ 10:00:00<br/>containment did not run<br/><i>no attacker action required</i>"]
F --> H["POST /refresh rt-002 → 200<br/>mints at-004 @ 10:00:03"]
H --> I["GET /me at-004 → 200<br/>well-formed · correctly signed · <b>no anomaly</b>"]
I --> J["at-004.issued_at > watermark<br/>NAIVE-001: no alert"]
I --> K["lineage lin-001 root @ 09:00 < watermark<br/><b>AFTERLIFE-001 · HIGH</b>"]
F --> L["attacker holds and never spends<br/>AFTERLIFE-001 silent — correctly<br/><b>state audit: dormant survivor</b>"]
style F fill:#7f1d1d,color:#fff
style H fill:#7f1d1d,color:#fff
style I fill:#7f1d1d,color:#fff
style J fill:#78350f,color:#fff
style K fill:#14532d,color:#fff
style AF3 fill:#14532d,color:#fff
style L fill:#1e3a5f,color:#fff
style E fill:#14532d,color:#fff
style G fill:#14532d,color:#fff
O ramo da cadeia de renovação no meio é o ponto central. O atacante não usa uma credencial antiga. Ele usa uma linhagem antiga, e a linhagem emite algo novo sob demanda.
Uma sessão é a raiz de uma linhagem. Tudo o que é emitido sob ela herda essa raiz, para sempre.```text sess-001 session lineage lin-001 root sess-001 issued 09:00:00 │ ├── rt-001 refresh parent sess-001 root_issued_at 09:00:00 │ └── at-001 access parent rt-001 root_issued_at 09:00:00 │ ├── rt-002 refresh parent rt-001 root_issued_at 09:00:00 ← 09:30 rotation │ └── at-002 access parent rt-002 root_issued_at 09:00:00 │ └── rt-004 refresh parent rt-002 root_issued_at 09:00:00 ← 10:00:03 rotation └── at-004 access parent rt-004 root_issued_at 09:00:00 issued_at 10:00:03
`at-004` tem três segundos de idade. A sua ascendência tem uma hora de idade. Ambos os factos são verdadeiros, e apenas um deles é visível num registo de pedidos.
Dois invariantes mantêm isto coeso, e ambos são testados:
1. **Apenas a autenticação cria uma linhagem.** `Store.open_session` é o único
local onde um `lineage_id` é gerado, e torna a sessão o seu próprio
`root_credential_id`.
2. **A criação de tokens copia a raiz para baixo.** `TokenService.mint` lê os campos da linhagem
do progenitor em vez de os recalcular, pelo que uma credencial não pode adquirir uma
ascendência mais recente do que o início de sessão de que descende.
Um token apresentado pelo titular deliberadamente **não** transporta a sua raiz de linhagem. Se o
fizesse, o titular poderia mentir sobre ela.
---
## A auditoria de estado
AFTERLIFE-001 dispara quando uma linhagem obsoleta é *utilizada*. Esse é o gatilho correto para
uma regra de deteção, e deixa um buraco: **uma linhagem obsoleta que ninguém tocou é
invisível para ela.** Um atacante que rouba uma credencial, observa a reposição falhar,
e depois espera, não produz atividade para correlacionar.
Por isso o laboratório também faz a pergunta que uma regra não consegue: não *o que aconteceu*, mas *o que
ainda está vivo*.```text
scenario: dormant-survivor (the attacker holds the credential and never spends it)
AFTERLIFE-001 silent — correct; nothing was accepted
AFTERLIFE-003 HIGH — the change revoked nothing
state audit 1 stale lineage, 1 dormant
| --no-color | Desativa a saída colorida |
| --debug | Ativa a saída de depuração |
| --verbose | Ativa a saída detalhada |
| --silent | Suprime toda a saída |
| --json | Produz a saída em formato JSON |
| --yaml | Produz a saída em formato YAML |
| --csv | Produz a saída em formato CSV |
| --xml | Produz a saída em formato XML |
| --html | Produz a saída em formato HTML |
| --markdown | Produz a saída em formato Markdown |
| --table | Produz a saída em formato de tabela |
| --list | Produz a saída em formato de lista |
| --tree | Produz a saída em formato de árvore |
| --graph | Produz a saída em formato de grafo |
| --chart | Produz a saída em formato de gráfico |
| --plot | Produz a saída em formato de gráfico de plotagem |
| --map | Produz a saída em formato de mapa |
| --heatmap | Produz a saída em formato de mapa de calor |
| --histogram | Produz a saída em formato de histograma |
| --scatter | Produz a saída em formato de dispersão |
| --bar | Produz a saída em formato de gráfico de barras |
| --line | Produz a saída em formato de gráfico de linhas |
| --pie | Produz a saída em formato de gráfico de pizza |
| --area | Produz a saída em formato de gráfico de área |
| --radar | Produz a saída em formato de gráfico de radar |
| --polar | Produz a saída em formato de gráfico polar |
| --box | Produz a saída em formato de gráfico de caixa |
| --violin | Produz a saída em formato de gráfico de violino |
| --density | Produz a saída em formato de gráfico de densidade |
| --contour | Produz a saída em formato de gráfico de contorno |
| --surface | Produz a saída em formato de gráfico de superfície |
| --wireframe | Produz a saída em formato de gráfico de wireframe |
| --mesh | Produz a saída em formato de gráfico de malha |
| --3d | Produz a saída em formato 3D |
| --2d | Produz a saída em formato 2D |
| --1d | Produz a saída em formato 1D |
| --0d | Produz a saída em formato 0D |
| --all | Produz a saída em todos os formatos |
| --none | Não produz saída |
| --default | Produz a saída no formato padrão |
| --custom | Produz a saída em formato personalizado |
| --format | Especifica o formato de saída |
| --output | Especifica o arquivo de saída |
| --input | Especifica o arquivo de entrada |
| --file | Especifica o arquivo |
| --dir | Especifica o diretório |
| --path | Especifica o caminho |
| --url | Especifica a URL |
| --host | Especifica o host |
| --port | Especifica a porta |
| --user | Especifica o usuário |
| --password | Especifica a senha |
| --token | Especifica o token |
| --key | Especifica a chave |
| --secret | Especifica o segredo |
| --cert | Especifica o certificado |
| --ca | Especifica a CA |
| --proxy | Especifica o proxy |
| --timeout | Especifica o tempo limite |
| --retry | Especifica o número de tentativas |
| --delay | Especifica o atraso |
| --interval | Especifica o intervalo |
| --rate | Especifica a taxa |
| --limit | Especifica o limite |
| --offset | Especifica o deslocamento |
| --page | Especifica a página |
| --size | Especifica o tamanho |
| --count | Especifica a contagem |
| --max | Especifica o máximo |
| --min | Especifica o mínimo |
| --sum | Especifica a soma |
| --avg | Especifica a média |
| --median | Especifica a mediana |
| --mode | Especifica a moda |
| --std | Especifica o desvio padrão |
| --var | Especifica a variância |
| --range | Especifica o intervalo |
| --sort | Especifica a ordenação |
| --filter | Especifica o filtro |
| --search | Especifica a busca |
| --match | Especifica a correspondência |
| --regex | Especifica a expressão regular |
| --glob | Especifica o padrão glob |
| --exclude | Especifica a exclusão |
| --include | Especifica a inclusão |
| --ignore | Especifica a ignorância |
| --force | Força a operação |
| --yes | Confirma automaticamente |
| --no | Nega automaticamente |
| --quiet | Suprime a saída |
| --help | Exibe a ajuda |
| --version | Exibe a versão |
| --about | Exibe informações sobre |
| --license | Exibe a licença |
| --credits | Exibe os créditos |
| --author | Exibe o autor |
| --contributors | Exibe os contribuidores |
| --changelog | Exibe o changelog |
| --history | Exibe o histórico |
| --log | Exibe o log |
| --status | Exibe o status |
| --info | Exibe as informações |
| --details | Exibe os detalhes |
| --summary | Exibe o resumo |
| --stats | Exibe as estatísticas |
| --metrics | Exibe as métricas |
| --report | Exibe o relatório |
| --export | Exporta os dados |
| --import | Importa os dados |
| --backup | Faz backup dos dados |
| --restore | Restaura os dados |
| --sync | Sincroniza os dados |
| --update | Atualiza os dados |
| --upgrade | Atualiza os dados |
| --downgrade | Faz downgrade dos dados |
| --install | Instala os dados |
| --uninstall | Desinstala os dados |
| --remove | Remove os dados |
| --delete | Exclui os dados |
| --create | Cria os dados |
| --read | Lê os dados |
| --write | Escreve os dados |
| --append | Anexa os dados |
| --prepend | Pré-anexa os dados |
| --insert | Insere os dados |
| --replace | Substitui os dados |
| --modify | Modifica os dados |
| --edit | Edita os dados |
| --view | Visualiza os dados |
| --display | Exibe os dados |
| --show | Mostra os dados |
| --hide | Oculta os dados |
| --toggle | Alterna os dados |
| --enable | Ativa os dados |
| --disable | Desativa os dados |
| --start | Inicia os dados |
| --stop | Para os dados |
| --restart | Reinicia os dados |
| --pause | Pausa os dados |
| --resume | Retoma os dados |
| --cancel | Cancela os dados |
| --abort | Aborta os dados |
| --kill | Encerra os dados |
| --terminate | Termina os dados |
| --exit | Sai dos dados |
| --quit | Encerra os dados |
| --close | Fecha os dados |
| --open | Abre os dados |
| --save | Salva os dados |
| --load | Carrega os dados |
| --dump | Despeja os dados |
| --parse | Analisa os dados |
| --format | Formata os dados |
| --validate | Valida os dados |
| --verify | Verifica os dados |
| --check | Checa os dados |
| --test | Testa os dados |
| --run | Executa os dados |
| --exec | Executa os dados |
| --eval | Avalia os dados |
| --compile | Compila os dados |
| --build | Constrói os dados |
| --make | Cria os dados |
| --clean | Limpa os dados |
| --distclean | Limpa a distribuição dos dados |
| --install | Instala os dados |
| --deploy | Implanta os dados |
| --release | Lança os dados |
| --publish | Publica os dados |
| --package | Empacota os dados |
| --unpackage | Desempacota os dados |
| --archive | Arquiva os dados |
| --extract | Extrai os dados |
| --compress | Comprime os dados |
| --decompress | Descomprime os dados |
| --encrypt | Criptografa os dados |
| --decrypt | Descriptografa os dados |
| --sign | Assina os dados |
| --verify | Verifica os dados |
| --hash | Calcula o hash dos dados |
| --checksum | Calcula o checksum dos dados |
| --digest | Calcula o digest dos dados |
| --encode | Codifica os dados |
| --decode | Decodifica os dados |
| --base64 | Codifica os dados em base64 |
| --hex | Codifica os dados em hexadecimal |
| --binary | Codifica os dados em binário |
| --octal | Codifica os dados em octal |
| --decimal | Codifica os dados em decimal |
| --ascii | Codifica os dados em ASCII |
| --utf8 | Codifica os dados em UTF-8 |
| --utf16 | Codifica os dados em UTF-16 |
| --unicode | Codifica os dados em Unicode |
| --urlencode | Codifica os dados em URL |
| --urldecode | Decodifica os dados em URL |
| --htmlencode | Codifica os dados em HTML |
| --htmldecode | Decodifica os dados em HTML |
| --xmlencode | Codifica os dados em XML |
| --xmldecode | Decodifica os dados em XML |
| --jsonencode | Codifica os dados em JSON |
| --jsondecode | Decodifica os dados em JSON |
| --yamlencode | Codifica os dados em YAML |
| --yamldecode | Decodifica os dados em YAML |
| --csvencode | Codifica os dados em CSV |
| --csvdecode | Decodifica os dados em CSV |
| --tsv | Codifica os dados em TSV |
| --psv | Codifica os dados em PSV |
| --ssv | Codifica os dados em SSV |
| --dsv | Codifica os dados em DSV |
| --delimited | Codifica os dados em delimitado |
| --fixed | Codifica os dados em largura fixa |
| --variable | Codifica os dados em largura variável |
| --quoted | Codifica os dados entre aspas |
| --unquoted | Codifica os dados sem aspas |
| --escaped | Codifica os dados com escape |
| --unescaped | Codifica os dados sem escape |
| --raw | Codifica os dados em bruto |
| --cooked | Codifica os dados processados |
| --pretty | Codifica os dados de forma bonita |
| --ugly | Codifica os dados de forma feia |
| --minify | Minifica os dados |
| --beautify | Embeleza os dados |
| --indent | Indenta os dados |
| --dedent | Remove a indentação dos dados |
| --wrap | Quebra os dados |
| --unwrap | Desfaz a quebra dos dados |
| --fold | Dobra os dados |
| --unfold | Desdobra os dados |
| --truncate | Trunca os dados |
| --pad | Preenche os dados |
| --trim | Apara os dados |
| --strip | Remove os dados |
| --chomp | Remove a quebra de linha dos dados |
| --squeeze | Comprime os dados |
| --collapse | Colapsa os dados |
| --expand | Expande os dados |
| --normalize | Normaliza os dados |
| --canonicalize | Canonicaliza os dados |
| --standardize | Padroniza os dados |
| --sanitize | Sanitiza os dados |
| --clean | Limpa os dados |
| --purge | Purga os dados |
| --wipe | Apaga os dados |
| --erase | Apaga os dados |
| --clear | Limpa os dados |
| --reset | Redefine os dados |
| --init | Inicializa os dados |
| --setup | Configura os dados |
| --configure | Configura os dados |
| --install | Instala os dados |
| --uninstall | Desinstala os dados |
| --reinstall | Reinstala os dados |
| --repair | Repara os dados |
| --fix | Corrige os dados |
| --patch | Corrige os dados |
| --upgrade | Atualiza os dados |
| --downgrade | Faz downgrade dos dados |
| --rollback | Reverte os dados |
| --revert | Reverte os dados |
| --undo | Desfaz os dados |
| --redo | Refaz os dados |
| --repeat | Repete os dados |
| --retry | Tenta novamente os dados |
| --resume | Retoma os dados |
| --continue | Continua os dados |
| --break | Interrompe os dados |
| --stop | Para os dados |
| --halt | Interrompe os dados |
| --suspend | Suspende os dados |
| --sleep | Suspende os dados |
| --wait | Aguarda os dados |
| --delay | Atrasa os dados |
| --timeout | Define o tempo limite dos dados |
| --interval | Define o intervalo dos dados |
| --frequency | Define a frequência dos dados |
| --period | Define o período dos dados |
| --duration | Define a duração dos dados |
| --elapsed | Define o tempo decorrido dos dados |
| --remaining | Define o tempo restante dos dados |
| --eta | Define a estimativa de tempo dos dados |
| --progress | Exibe o progresso dos dados |
| --bar | Exibe a barra de progresso dos dados |
| --spinner | Exibe o indicador de progresso dos dados |
| --percent | Exibe a porcentagem dos dados |
| --ratio | Exibe a proporção dos dados |
| --fraction | Exibe a fração dos dados |
| --decimal | Exibe o decimal dos dados |
| --integer | Exibe o inteiro dos dados |
| --float | Exibe o ponto flutuante dos dados |
| --double | Exibe o double dos dados |
| --string | Exibe a string dos dados |
| --boolean | Exibe o booleano dos dados |
| --null | Exibe o nulo dos dados |
| --undefined | Exibe o indefinido dos dados |
| --nan | Exibe o NaN dos dados |
| --inf | Exibe o infinito dos dados |
| --infinity | Exibe o infinito dos dados |
| --negative | Exibe o negativo dos dados |
| --positive | Exibe o positivo dos dados |
| --zero | Exibe o zero dos dados |
| --one | Exibe o um dos dados |
| --many | Exibe o muitos dos dados |
| --few | Exibe o poucos dos dados |
| --some | Exibe o alguns dos dados |
| --all | Exibe o todos dos dados |
| --none | Exibe o nenhum dos dados |
| --any | Exibe o qualquer dos dados |
| --every | Exibe o cada dos dados |
| --each | Exibe o cada dos dados |
| --both | Exibe o ambos dos dados |
| --either | Exibe o qualquer um dos dados |
| --neither | Exibe o nenhum dos dados |
| --other | Exibe o outro dos dados |
| --another | Exibe o outro dos dados |
| --same | Exibe o mesmo dos dados |
| --different | Exibe o diferente dos dados |
| --equal | Exibe o igual dos dados |
| --unequal | Exibe o desigual dos dados |
| --greater | Exibe o maior dos dados |
| --less | Exibe o menor dos dados |
| --greater-equal | Exibe o maior ou igual dos dados |
| --less-equal | Exibe o menor ou igual dos dados |
| --not-equal | Exibe o diferente dos dados |
| --and | Exibe o e dos dados |
| --or | Exibe o ou dos dados |
| --not | Exibe o não dos dados |
| --xor | Exibe o ou exclusivo dos dados |
| --nand | Exibe o não e dos dados |
| --nor | Exibe o não ou dos dados |
| --xnor | Exibe o não ou exclusivo dos dados |
| --implies | Exibe o implica dos dados |
| --equivalent | Exibe o equivalente dos dados |
| --identical | Exibe o idêntico dos dados |
| --similar | Exibe o similar dos dados |
| --match | Exibe a correspondência dos dados |
| --mismatch | Exibe a incompatibilidade dos dados |
| --contains | Exibe o contém dos dados |
| --starts | Exibe o inicia dos dados |
| --ends | Exibe o termina dos dados |
| --prefix | Exibe o prefixo dos dados |
| --suffix | Exibe o sufixo dos dados |
| --infix | Exibe o infixo dos dados |
| --substring | Exibe a substring dos dados |
| --slice | Exibe a fatia dos dados |
| --splice | Exibe a emenda dos dados |
| --split | Exibe a divisão dos dados |
| --join | Exibe a junção dos dados |
| --concat | Exibe a concatenação dos dados |
| --merge | Exibe a mesclagem dos dados |
| --combine | Exibe a combinação dos dados |
| --union | Exibe a união dos dados |
| --intersect | Exibe a interseção dos dados |
| --difference | Exibe a diferença dos dados |
| --symmetric-difference | Exibe a diferença simétrica dos dados |
| --complement | Exibe o complemento dos dados |
| --subset | Exibe o subconjunto dos dados |
| --superset | Exibe o superconjunto dos dados |
| --disjoint | Exibe o disjunto dos dados |
| --overlap | Exibe a sobreposição dos dados |
| --distinct | Exibe o distinto dos dados |
| --unique | Exibe o único dos dados |
| --duplicate | Exibe o duplicado dos dados |
| --deduplicate | Exibe a deduplicação dos dados |
| --group | Exibe o agrupamento dos dados |
| --ungroup | Exibe o desagrupamento dos dados |
| --partition | Exibe a partição dos dados |
| --chunk | Exibe o bloco dos dados |
| --batch | Exibe o lote dos dados |
| --stream | Exibe o fluxo dos dados |
| --buffer | Exibe o buffer dos dados |
| --flush | Exibe o esvaziamento dos dados |
| --drain | Exibe a drenagem dos dados |
| --fill | Exibe o preenchimento dos dados |
| --empty | Exibe o vazio dos dados |
| --full | Exibe o cheio dos dados |
| --partial | Exibe o parcial dos dados |
| --complete | Exibe o completo dos dados |
| --incomplete | Exibe o incompleto dos dados |
| --valid | Exibe o válido dos dados |
| --invalid | Exibe o inválido dos dados |
| --correct | Exibe o correto dos dados |
| --incorrect | Exibe o incorreto dos dados |
| --true | Exibe o verdadeiro dos dados |
| --false | Exibe o falso dos dados |
| --yes | Exibe o sim dos dados |
| --no | Exibe o não dos dados |
| --maybe | Exibe o talvez dos dados |
| --always | Exibe o sempre dos dados |
| --never | Exibe o nunca dos dados |
| --sometimes | Exibe o às vezes dos dados |
| --often | Exibe o frequentemente dos dados |
| --rarely | Exibe o raramente dos dados |
| --seldom | Exibe o raramente dos dados |
| --frequently | Exibe o frequentemente dos dados |
| --occasionally | Exibe o ocasionalmente dos dados |
| --regularly | Exibe o regularmente dos dados |
| --constantly | Exibe o constantemente dos dados |
| --continuously | Exibe o continuamente dos dados |
| --periodically | Exibe o periodicamente dos dados |
| --intermittently | Exibe o intermitentemente dos dados |
| --sporadically | Exibe o esporadicamente dos dados |
| --randomly | Exibe o aleatoriamente dos dados |
| --systematically | Exibe o sistematicamente dos dados |
| --methodically | Exibe o metodicamente dos dados |
| --carefully | Exibe o cuidadosamente dos dados |
| --cautiously | Exibe o cautelosamente dos dados |
| --quickly | Exibe o rapidamente dos dados |
| --slowly | Exibe o lentamente dos dados |
| --fast | Exibe o rápido dos dados |
| --slow | Exibe o lento dos dados |
| --speed | Exibe a velocidade dos dados |
| --velocity | Exibe a velocidade dos dados |
| --acceleration | Exibe a aceleração dos dados |
| --deceleration | Exibe a desaceleração dos dados |
| --momentum | Exibe o momento dos dados |
| --force | Exibe a força dos dados |
| --power | Exibe a potência dos dados |
| --energy | Exibe a energia dos dados |
| --work | Exibe o trabalho dos dados |
| --heat | Exibe o calor dos dados |
| --temperature | Exibe a temperatura dos dados |
| --pressure | Exibe a pressão dos dados |
| --volume | Exibe o volume dos dados |
| --density | Exibe a densidade dos dados |
| --mass | Exibe a massa dos dados |
| --weight | Exibe o peso dos dados |
| --gravity | Exibe a gravidade dos dados |
| --charge | Exibe a carga dos dados |
| --current | Exibe a corrente dos dados |
| --voltage | Exibe a tensão dos dados |
| --resistance | Exibe a resistência dos dados |
| --conductance | Exibe a condutância dos dados |
| --capacitance | Exibe a capacitância dos dados |
| --inductance | Exibe a indutância dos dados |
| --impedance | Exibe a impedância dos dados |
| --reactance | Exibe a reatância dos dados |
| --frequency | Exibe a frequência dos dados |
| --wavelength | Exibe o comprimento de onda dos dados |
| --amplitude | Exibe a amplitude dos dados |
| --phase | Exibe a fase dos dados |
| --polarization | Exibe a polarização dos dados |
| --intensity | Exibe a intensidade dos dados |
| --luminosity | Exibe a luminosidade dos dados |
| --brightness | Exibe o brilho dos dados |
| --contrast | Exibe o contraste dos dados |
| --saturation | Exibe a saturação dos dados |
| --hue | Exibe a matiz dos dados |
| --color | Exibe a cor dos dados |
| --opacity | Exibe a opacidade dos dados |
| --transparency | Exibe a transparência dos dados |
| --alpha | Exibe o alfa dos dados |
| --beta | Exibe o beta dos dados |
| --gamma | Exibe o gama dos dados |
| --delta | Exibe o delta dos dados |
| --epsilon | Exibe o épsilon dos dados |
| --zeta | Exibe o zeta dos dados |
| --eta | Exibe o eta dos dados |
| --theta | Exibe o teta dos dados |
| --iota | Exibe o iota dos dados |
| --kappa | Exibe o capa dos dados |
| --lambda | Exibe o lambda dos dados |
| --mu | Exibe o mi dos dados |
| --nu | Exibe o ni dos dados |
| --xi | Exibe o csi dos dados |
| --omicron | Exibe o ômicron dos dados |
| --pi | Exibe o pi dos dados |
| --rho | Exibe o rô dos dados |
| --sigma | Exibe o sigma dos dados |
| --tau | Exibe o tau dos dados |
| --upsilon | Exibe o úpsilon dos dados |
| --phi | Exibe o fi dos dados |
| --chi | Exibe o qui dos dados |
| --psi | Exibe o psi dos dados |
| --omega | Exibe o ômega dos dados |```bash
python scripts/lab.py audit
| `-s` | `--server` | `SERVER` | `http://localhost:8080` | URL do servidor |
| `-t` | `--token` | `TOKEN` | `None` | Token de autenticação |
| `-u` | `--username` | `USERNAME` | `None` | Nome de usuário para autenticação |
| `-p` | `--password` | `PASSWORD` | `None` | Senha para autenticação |
| `-k` | `--insecure` | `False` | `False` | Ignorar verificação de certificado SSL |
| `-v` | `--verbose` | `False` | `False` | Ativar saída detalhada |
| `-d` | `--debug` | `False` | `False` | Ativar saída de depuração |
| `-q` | `--quiet` | `False` | `False` | Suprimir saída |
| `-o` | `--output` | `OUTPUT` | `None` | Arquivo de saída |
| `-f` | `--format` | `FORMAT` | `json` | Formato de saída (json, yaml, table) |
| `-c` | `--config` | `CONFIG` | `None` | Arquivo de configuração |
| `-h` | `--help` | `False` | `False` | Mostrar mensagem de ajuda e sair |
| `-V` | `--version` | `False` | `False` | Mostrar versão e sair |
### Exemplos
```bash
# Obter informações do servidor
$ ./tool -s http://localhost:8080 -t TOKEN
# Obter informações do servidor com saída detalhada
$ ./tool -s http://localhost:8080 -t TOKEN -v
# Obter informações do servidor com saída de depuração
$ ./tool -s http://localhost:8080 -t TOKEN -d
# Obter informações do servidor com saída silenciosa
$ ./tool -s http://localhost:8080 -t TOKEN -q
# Obter informações do servidor com arquivo de saída
$ ./tool -s http://localhost:8080 -t TOKEN -o output.json
# Obter informações do servidor com formato de saída
$ ./tool -s http://localhost:8080 -t TOKEN -f yaml
# Obter informações do servidor com arquivo de configuração
$ ./tool -s http://localhost:8080 -t TOKEN -c config.yaml
# Obter informações do servidor com autenticação
$ ./tool -s http://localhost:8080 -u USERNAME -p PASSWORD
# Obter informações do servidor com autenticação e ignorar verificação de certificado SSL
$ ./tool -s http://localhost:8080 -u USERNAME -p PASSWORD -k
# Obter informações do servidor com autenticação e saída detalhada
$ ./tool -s http://localhost:8080 -u USERNAME -p PASSWORD -v
# Obter informações do servidor com autenticação e saída de depuração
$ ./tool -s http://localhost:8080 -u USERNAME -p PASSWORD -d
# Obter informações do servidor com autenticação e saída silenciosa
$ ./tool -s http://localhost:8080 -u USERNAME -p PASSWORD -q
# Obter informações do servidor com autenticação e arquivo de saída
$ ./tool -s http://localhost:8080 -u USERNAME -p PASSWORD -o output.json
# Obter informações do servidor com autenticação e formato de saída
$ ./tool -s http://localhost:8080 -u USERNAME -p PASSWORD -f yaml
# Obter informações do servidor com autenticação e arquivo de configuração
$ ./tool -s http://localhost:8080 -u USERNAME -p PASSWORD -c config.yaml
``````text
VULNERABLE
server state 1 lineage(s) outlived the change (0 dormant)
telemetry 1 lineage(s) -- the same question, asked of the log instead of the database
lin-001 root 2026-09-11T09:00:00.000Z 7 credentials in use
FIXED
server state clean
telemetry clean
Duas fontes, deliberadamente: a auditoria de telemetria vê o que o log pode provar, a auditoria de estado do servidor vê o que o banco de dados acredita. Onde elas discordam, o log não é um registro fiel do estado das credenciais, e toda detecção construída sobre ele é mais fraca do que aparenta — por isso o comando imprime ambos e avisa se eles diferirem.
A auditoria de estado do servidor precisa da tabela security_changes, que ambos os modos
escrevem, porque registrar que uma mudança ocorreu é uma obrigação separada de
agir sobre ela — e a implementação vulnerável cumpre exatamente uma das duas.
Nove cenários contra ambas as implementações. python scripts/scenarios.py verifica
suas próprias duas invariantes e sai com código não-zero se qualquer uma delas quebrar.
| cenário | modo | eventos | pior | regras disparadas | stale | dormant |
|---|---|---|---|---|---|---|
legitimate-only | vulnerable | 19 | LOW | AFTERLIFE-003 | 0 | 0 |
legitimate-only | fixed | 19 | – | none | 0 | 0 |
stolen-refresh | vulnerable | 22 | HIGH | AFTERLIFE-001, AFTERLIFE-003 | 1 | 0 |
stolen-refresh | fixed | 23 | – | none | 0 | 0 |
stolen-refresh-with-device-id | vulnerable | 19 | LOW | AFTERLIFE-003 | 0 | 0 |
stolen-refresh-with-device-id | fixed | 19 | – | none | 0 | 0 |
multi-device | vulnerable | 29 | HIGH | AFTERLIFE-001, AFTERLIFE-003 | 2 | 0 |
multi-device | fixed | 29 | – | none | 0 | 0 |
refresh-reuse | vulnerable | 11 | MEDIUM | AFTERLIFE-002 | 0 | 0 |
refresh-reuse | fixed | 15 | MEDIUM | AFTERLIFE-002 | 0 | 0 |
Quatro linhas carregam um argumento:
legitimate-only não tem nenhum atacante. A correção não produz nada;
a implementação vulnerável produz um LOW. Isso não é um falso positivo — é
enumeration_only: a revogação funcionou desta vez, por enumeração, sem
marca d'água para cobrir uma credencial que o servidor esqueceu. O pacote separa
as duas implementações sem nenhum atacante presente.dormant-survivor é o ponto cego e sua resposta em uma linha:
AFTERLIFE-001 silencioso, AFTERLIFE-003 HIGH, auditoria dormant: 1.multi-device é onde o ramo funcional da implementação vulnerável
ainda falha — revogação limitada ao laptop, dois outros dispositivos intactos.expired-lineage é o controle de falso positivo: uma sessão de 40 dias cujas
credenciais expiraram por conta própria não é um caminho de persistência sobrevivente, e não é
reportada como tal.E a afirmação central da matriz, verificada pelo script e por
test_the_fix_never_produces_a_control_failure_finding: a correção não levanta nenhum
achado de falha de controle em nenhum cenário. AFTERLIFE-002 é permitido passar — ele
reporta um roubo, não uma falha de controle, e uma implementação correta ainda tem
roubos a reportar.
Uma regra que dispara a cada redefinição de senha é silenciada em uma semana, e uma regra silenciada é pior que nenhuma regra — é uma regra que todos acreditam estar em execução.
| caso | resultado | por quê |
|---|---|---|
| Mudança de senha, depois a sessão substituta é usada | sem alerta | a raiz da nova linhagem está na marca d'água ou depois dela |
Redefinição seguida de navegação imediata (/me, /profile, /settings) | sem alerta | uma linhagem nova, uma raiz nova |
| Telefone, laptop e tablet, todos corretamente rotacionados | sem alerta | cada login é sua própria linhagem |
| Um dispositivo que fez login após a mudança, navegando junto com o atacante | sem alerta | raiz nova — e a linhagem obsoleta ainda alerta, sozinha |
| Uma credencial obsoleta que foi rejeitada | sem alerta | result: failure é excluído; é evidência para a defesa |
| Desvio de relógio de até 2s entre componentes | sem alerta | a tolerância documentada |
| Uma mudança de senha malsucedida | sem alerta | não é uma âncora |
| Atividade antes da mudança | sem alerta | verificação de ordenação temporal |
Uma credencial rotacionada (reason: rotated) | não contada como morte | uma credencial gasta, não uma linhagem morta |
| Uma sessão de 40 dias cujas credenciais expiraram | não é uma sobrevivente | expiração rastreada por linhagem |
| Uma primeira mudança de senha em uma conta nova | sem alerta | nada estava ativo antes |
| Rotação de refresh token de uma linhagem obsoleta | ALERTA | timestamp novo, ancestralidade obsoleta — este é o achado |
Cada linha tem um teste em
tests/test_detector_afterlife001.py e
tests/test_detector_rulepack.py.
Sobre a tolerância de 2 segundos. Cada timestamp neste laboratório vem de um processo
e um relógio, então a tolerância honesta é zero. 2s é o que uma implantação realista de dois hosts
sob NTP precisa, e é três ordens de magnitude menor que as lacunas
que este ataque produz — o propósito inteiro de uma credencial de refresh é ser
de longa duração. A tolerância é uma janela na qual uma linhagem genuinamente obsoleta é
ignorada, então é mantida pequena de propósito:
test_12b_a_gap_beyond_the_tolerance_does_alert fixa que 2,1s ainda dispara.
self.store.set_watermark(user.user_id, moment) # the guarantee revoked = self.store.revoke_credentials(user.user_id, ...) # defence in depth
Nenhum dos dois depende de `device_id`. Ele é registrado como contexto e não tem influência no
raio de impacto.
**A marca d'água é a garantia arquitetural.** Um timestamp por usuário,
`credentials_valid_after`, comparado em cada requisição tanto contra a
própria emissão da credencial *quanto* contra sua raiz de linhagem:```python
watermark = user.credentials_valid_after
if credential.issued_at < watermark: reject # the obvious case
if credential.root_issued_at < watermark: reject # the refresh chain
A segunda comparação é a que custa algo a acertar, e a que uma implementação ingénua deixa de fora — na aplicação exatamente como na deteção.
A revogação explícita é defesa em profundidade, e evidência. revoked_at e
revocation_reason são o que um responsável de incidentes lê, e os
eventos auth.token.revoked são o que prova que a contenção aconteceu.
test_the_watermark_alone_rejects_a_stale_credential define a marca de água sem
revogar nada e exige que a credencial obsoleta seja recusada — o que
estabelece qual das duas é a que sustenta a carga, e qual é a propriedade que
ainda se mantém para credenciais que o servidor se esqueceu de ter emitido.
Nem um único pedido mudou. Uma função mudou.
O gatilho não é "a palavra-passe mudou". É "algo mudou que torna
credenciais previamente emitidas não confiáveis". As quatro rotas partilham
_revoke_for_security_change, pelo que a correção e o bug aplicam-se de forma idêntica a:
| alteração | porque é que as credenciais se tornam não confiáveis |
|---|---|
| alteração / reposição de palavra-passe | o segredo com que a sessão foi estabelecida desapareceu |
| inscrição ou alteração de MFA | os fatores com que a sessão foi estabelecida não são os fatores da conta |
| alteração de função / escalada de privilégios | a credencial foi emitida sob uma autorização diferente |
| recuperação de conta | por construção, a conta pode ter estado nas mãos de outra pessoa há um momento |
| início de sessão | não revoga as outras — uma nova linhagem, não uma afirmação de que as antigas não são confiáveis |
Testado para todos os tipos, em ambos os modos, em
tests/test_privilege_changes.py — incluindo
o não-requisito do início de sessão, documentado para que o próximo leitor não o "corrija".
Quatro formas de fazer uma credencial deixar de funcionar. O AFTERLIFE implementa A + C, e a ordenação importa.
| mecanismo | ganha | custa | |
|---|---|---|---|
| A | marca de água de revogação por utilizador | uma escrita revoga tudo, incluindo credenciais que o servidor se esqueceu; armazenamento e verificação O(1) | estado do lado do servidor no caminho de leitura; a semântica de timestamp tem de estar exatamente certa; não diz nada sobre credenciais emitidas após a marca de água |
| B | acesso de curta duração + refresh revogável | limita os danos do token de acesso sem estado no caminho de leitura | um token de acesso roubado é válido até expirar; o lado do refresh ainda precisa de estado — este é o padrão dentro do qual o CVE vive |
| C | denylist explícita | precisa; legível para um responsável de incidentes; produz a telemetria que prova a contenção | o estado cresce e precisa de limpeza; só revoga o que te lembraste de enumerar — que é a consulta que o bug errou |
| D | JWT totalmente sem estado | nenhum estado no caminho de leitura | não há revogação. O TTL e a rotação de chaves são as únicas alavancas |
Sobre D: os JWT não são inerentemente inseguros. A tensão é mais estreita e vale a pena enunciá-la
com precisão — verificação sem estado e revogação imediata do lado do servidor são
mutuamente exclusivas. Não podes decidir "esta credencial já não é válida"
sem consultar algo que o saiba, e consultá-lo é o que torna o
sistema com estado. O erro é adotar JWT pela sua ausência de estado e depois
precisar de revogação de qualquer forma, o que todos os produtos fazem na primeira vez que um portátil é
roubado. O que acabas por ter é um ponteiro assinado para estado do lado do servidor — que é
o que app/tokens.py implementa deliberadamente, porque chegar
lá de propósito é mais barato do que chegar lá a meio de um incidente.
O CVE-2026-22706 pontua 2.1, Low (CVSS v4.0
AV:N/AC:H/AT:N/PR:H/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N), e a pontuação é
defensável: PR:H porque o atacante já tem de possuir uma credencial de refresh
válida, AC:H porque obter uma requer um comprometimento prévio, e
VC:N/VI:L porque a falha não concede nenhum acesso que o atacante já não tivesse.
O CVSS está a medir o impacto marginal da vulnerabilidade, e o impacto marginal
é a duração do acesso, não o seu âmbito. Dados esses inputs, 2.1
resulta da fórmula corretamente.
O que o CVSS não modela é que o controlo em falha é a ação de contenção — pelo que a conclusão útil é sobre o encaminhamento de triagem, não sobre o número. Um Low num caminho de contenção merece atenção que um Low num caminho de funcionalidade não merece, porque o custo é pago durante um incidente. Discussão completa: docs/tradeoffs.md.
290 passed
| arquivo | o que ele fixa |
|---|---|
| [`test_fixed_mode.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_fixed_mode.py) | **a suíte de regressão que nunca pode ficar vermelha** — nenhuma credencial emitida antes de um evento de revogação é aceita depois dele, incluindo uma descendente de uma linhagem obsoleta |
| [`test_vulnerable_mode.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_vulnerable_mode.py) | a vulnerabilidade **existe**, é determinística, dura 30 dias e é causada pelo condicional — com o controle `device_id` provando que o caminho de revogação é opcional em vez de morto |
| [`test_lineage.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_lineage.py) | uma cadeia de refresh mantém uma linhagem e uma raiz; apenas o login cria uma linhagem |
| [`test_detector_afterlife001.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_detector_afterlife001.py) | 7 casos de detecção, 9 casos de falso positivo, 2 pontos cegos, todas as 24 ordenações de eventos, estado limitado, entrada malformada |
| [`test_detector_rulepack.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_detector_rulepack.py) | AFTERLIFE-002 e AFTERLIFE-003 — cada veredito, a janela de carência, rotação-não-é-morte, exclusão de expiração e a ordenação do motor |
| [`test_naive_detector.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_naive_detector.py) | NAIVE-001 continua falhando **da maneira específica que este README afirma** — incluindo que ele tinha os dados de que precisava |
| [`test_audit_and_console.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_audit_and_console.py) | o sobrevivente dormente que nenhuma regra consegue ver; ambas as fontes de auditoria concordando; o console não computando nenhum veredito próprio; a pré-visualização offline não buscando nada; cada número neste README dentro dos limites, sem folha de estilo e byte-estável |
| [`test_scenarios.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_scenarios.py) | as linhas da matriz que carregam um argumento |
| [`test_privilege_changes.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_privilege_changes.py) | MFA, mudança de função e recuperação de conta, ambos os modos |
| [`test_telemetry.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_telemetry.py) | o contrato de redação e um grep do log pelas strings bearer literais emitidas |
| [`test_app.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_app.py) | assinaturas forjadas, tipos de credencial errados, expiração a partir do estado do servidor |
| [`test_infrastructure.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_infrastructure.py) | configuração, relógio, linhas parciais do tailer, ambas as CLIs, cada comando de demonstração |
O teste mais forte da suíte é
`test_no_bearer_string_or_password_ever_reaches_the_log`: ele executa todo o ataque
e então procura no arquivo de log as credenciais reais que a aplicação entregou,
mais ambas as senhas, mais a chave de assinatura. Não por nomes de campos — pelos
valores.
---
## Pontos cegos
Declarados abertamente, porque escondê-los tornaria o laboratório desonesto. Cada um tem um teste.
> **Se a aplicação não emite eventos de mudança de credencial, o pacote não consegue
> correlacionar de forma confiável a atividade pós-reset.** Não há âncora, então não há
> nada para a atividade estar "depois" — e isso silencia o AFTERLIFE-003 primeiro, que é a
> regra que teria lhe dito que o controle estava quebrado.
>
> **Se a aplicação não retém metadados de emissão/linhagem, o detector
> não consegue determinar se um token de acesso novo descendeu de uma credencial
> mais antiga.** Uma credencial emitida há três segundos é indistinguível de uma
> credencial emitida há três segundos por uma cadeia de um mês.
Estas **não são preferências opcionais de logging. São requisitos de detecção de
segurança.** Abandonar `auth.token.issued` para reduzir o volume de logs não torna o
logging mais barato; desliga uma detecção.
Atividade irresolvível é **contada**, não silenciosamente descartada —
`stats()["activity_with_unresolved_lineage"]` é diferente de zero sempre que o detector
foi questionado sobre credenciais que a telemetria nunca descreveu. Uma regra que fica
quieta porque nada está errado e uma regra que fica quieta porque está cega parecem
idênticas vistas de fora, e esse número é a diferença.
Também verdadeiro, e também documentado:
* **O detector não pode recusar uma requisição.** Ele acompanha um log. Ele lhe diz
que a contenção falhou; ele não contém.
* **A auditoria é uma varredura, não uma detecção.** Ela fecha a lacuna do
sobrevivente dormente, mas roda quando alguém a executa. Ela não pode paginar.
* **A correção não ajuda com credenciais roubadas *depois* do reset.** A contenção
é um ponto no tempo, não uma propriedade.
* **AFTERLIFE-003 verifica a contenção observada, não a completude arquitetural.**
Uma revogação com escopo de dispositivo que por acaso cobre tudo recebe a
descoberta LOW, não a HIGH. O bug ainda está lá; ele não mordeu naquela
conta. Um teste pode perguntar "e quanto a dois dispositivos?"; um log só pode
relatar o que de fato aconteceu.
* **Outras persistências sobrevivem inteiramente a uma mudança de senha** — concessões OAuth, chaves
de API, regras de encaminhamento de e-mail, contatos de recuperação.
Discussão completa: **[docs/limitations.md](https://github.com/het-p301204/afterlife/blob/main/docs/limitations.md)**.
---
## Executando o laboratório```bash
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt -r requirements-dev.txt
pytest -q
Em seguida, em ordem:```bash python scripts/lab.py vulnerable
| `--no-color` | Desativa a saída colorida |
| `--verbose` | Ativa o registro detalhado |
| `--quiet` | Suprime toda a saída, exceto erros |
| `--output <file>` | Grava a saída em um arquivo |
| `--format <type>` | Especifica o formato de saída (json, yaml, table) |
| `--config <file>` | Caminho para o arquivo de configuração |
| `--timeout <seconds>` | Define o tempo limite da operação |
| `--retry <count>` | Número de tentativas em caso de falha |
| `--proxy <url>` | Usa o proxy especificado para conexões |
| `--insecure` | Ignora a verificação de certificado TLS |
| `--threads <num>` | Número de threads concorrentes |
| `--rate-limit <n>` | Limita as requisições a n por segundo |
| `--user-agent <str>` | Define um User-Agent personalizado |
| `--header <header>` | Adiciona um cabeçalho HTTP personalizado |
| `--cookie <cookie>` | Envia cookies com as requisições |
| `--data <data>` | Envia dados no corpo da requisição |
| `--method <method>` | Especifica o método HTTP |
| `--follow-redirects` | Segue redirecionamentos HTTP |
| `--max-redirects <n>` | Número máximo de redirecionamentos a seguir |
| `--resolve <host:ip>` | Resolve um hostname para um IP específico |
| `--interface <name>` | Vincula a uma interface de rede específica |
| `--source-ip <ip>` | Define o endereço IP de origem |
| `--dns-server <ip>` | Usa um servidor DNS personalizado |
| `--no-dns` | Desativa a resolução DNS |
| `--ipv4` | Usa apenas IPv4 |
| `--ipv6` | Usa apenas IPv6 |
| `--random-agent` | Usa um User-Agent aleatório |
| `--delay <ms>` | Adiciona um atraso entre as requisições |
| `--jitter <ms>` | Adiciona variação aleatória ao atraso |
| `--max-size <bytes>` | Tamanho máximo da resposta a processar |
| `--min-size <bytes>` | Tamanho mínimo da resposta a processar |
| `--match <regex>` | Filtra a saída por uma expressão regular |
| `--filter <regex>` | Exclui da saída o que corresponder a uma expressão regular |
| `--exclude <pattern>` | Exclui caminhos correspondentes ao padrão |
| `--include <pattern>` | Inclui apenas caminhos correspondentes ao padrão |
| `--extensions <list>` | Extensões de arquivo a testar |
| `--wordlist <file>` | Arquivo de wordlist a usar |
| `--rules <file>` | Arquivo de regras a aplicar |
| `--templates <dir>` | Diretório de templates a usar |
| `--severity <level>` | Filtra por nível de severidade |
| `--tags <tags>` | Filtra por tags |
| `--exclude-tags <tags>` | Exclui por tags |
| `--update` | Atualiza templates e assinaturas |
| `--version` | Exibe a versão e sai |
| `--help` | Exibe a mensagem de ajuda e sai |```bash
python scripts/lab.py detect
| --no-color | Desativa a saída colorida |
| --debug | Ativa a saída de depuração |
| --verbose | Ativa a saída detalhada |
| --silent | Suprime toda a saída |
| --json | Produz saída em formato JSON |
| --yaml | Produz saída em formato YAML |
| --csv | Produz saída em formato CSV |
| --xml | Produz saída em formato XML |
| --html | Produz saída em formato HTML |
| --markdown | Produz saída em formato Markdown |
| --table | Produz saída em formato de tabela |
| --raw | Produz saída em formato bruto |
| --pretty | Produz saída em formato pretty |
| --compact | Produz saída em formato compacto |
| --indent | Especifica a indentação para a saída |
| --width | Especifica a largura para a saída |
| --height | Especifica a altura para a saída |
| --depth | Especifica a profundidade para a saída |
| --limit | Especifica o limite para a saída |
| --offset | Especifica o deslocamento para a saída |
| --page | Especifica a página para a saída |
| --size | Especifica o tamanho para a saída |
| --sort | Especifica a ordenação para a saída |
| --filter | Especifica o filtro para a saída |
| --search | Especifica a pesquisa para a saída |
| --query | Especifica a consulta para a saída |
| --format | Especifica o formato para a saída |
| --output | Especifica a saída para a saída |
| --input | Especifica a entrada para a saída |
| --file | Especifica o arquivo para a saída |
| --directory | Especifica o diretório para a saída |
| --path | Especifica o caminho para a saída |
| --url | Especifica a URL para a saída |
| --host | Especifica o host para a saída |
| --port | Especifica a porta para a saída |
| --user | Especifica o usuário para a saída |
| --password | Especifica a senha para a saída |
| --token | Especifica o token para a saída |
| --key | Especifica a chave para a saída |
| --secret | Especifica o segredo para a saída |
| --cert | Especifica o certificado para a saída |
| --ca | Especifica a CA para a saída |
| --insecure | Desativa a verificação de TLS |
| --timeout | Especifica o tempo limite para a saída |
| --retry | Especifica o número de tentativas para a saída |
| --delay | Especifica o atraso para a saída |
| --interval | Especifica o intervalo para a saída |
| --rate | Especifica a taxa para a saída |
| --concurrency | Especifica a concorrência para a saída |
| --threads | Especifica o número de threads para a saída |
| --workers | Especifica o número de workers para a saída |
| --processes | Especifica o número de processos para a saída |
| --memory | Especifica o limite de memória para a saída |
| --cpu | Especifica o limite de CPU para a saída |
| --disk | Especifica o limite de disco para a saída |
| --network | Especifica o limite de rede para a saída |
| --bandwidth | Especifica o limite de largura de banda para a saída |
| --latency | Especifica o limite de latência para a saída |
| --jitter | Especifica o limite de jitter para a saída |
| --packet-loss | Especifica o limite de perda de pacotes para a saída |
| --mtu | Especifica o MTU para a saída |
| --ttl | Especifica o TTL para a saída |
| --dns | Especifica o DNS para a saída |
| --proxy | Especifica o proxy para a saída |
| --socks | Especifica o SOCKS para a saída |
| --http | Especifica o HTTP para a saída |
| --https | Especifica o HTTPS para a saída |
| --ftp | Especifica o FTP para a saída |
| --ssh | Especifica o SSH para a saída |
| --telnet | Especifica o Telnet para a saída |
| --smtp | Especifica o SMTP para a saída |
| --pop3 | Especifica o POP3 para a saída |
| --imap | Especifica o IMAP para a saída |
| --ldap | Especifica o LDAP para a saída |
| --snmp | Especifica o SNMP para a saída |
| --smb | Especifica o SMB para a saída |
| --nfs | Especifica o NFS para a saída |
| --rdp | Especifica o RDP para a saída |
| --vnc | Especifica o VNC para a saída |
| --x11 | Especifica o X11 para a saída |
| --wayland | Especifica o Wayland para a saída |
| --docker | Especifica o Docker para a saída |
| --kubernetes | Especifica o Kubernetes para a saída |
| --aws | Especifica a AWS para a saída |
| --azure | Especifica o Azure para a saída |
| --gcp | Especifica o GCP para a saída |
| --oci | Especifica o OCI para a saída |
| --alibaba | Especifica o Alibaba para a saída |
| --ibm | Especifica a IBM para a saída |
| --oracle | Especifica a Oracle para a saída |
| --vmware | Especifica a VMware para a saída |
| --citrix | Especifica a Citrix para a saída |
| --redhat | Especifica a Red Hat para a saída |
| --suse | Especifica a SUSE para a saída |
| --canonical | Especifica a Canonical para a saída |
| --debian | Especifica o Debian para a saída |
| --ubuntu | Especifica o Ubuntu para a saída |
| --centos | Especifica o CentOS para a saída |
| --fedora | Especifica o Fedora para a saída |
| --arch | Especifica o Arch para a saída |
| --alpine | Especifica o Alpine para a saída |
| --gentoo | Especifica o Gentoo para a saída |
| --freebsd | Especifica o FreeBSD para a saída |
| --openbsd | Especifica o OpenBSD para a saída |
| --netbsd | Especifica o NetBSD para a saída |
| --macos | Especifica o macOS para a saída |
| --windows | Especifica o Windows para a saída |
| --linux | Especifica o Linux para a saída |
| --android | Especifica o Android para a saída |
| --ios | Especifica o iOS para a saída |
| --harmony | Especifica o Harmony para a saída |
| --fuchsia | Especifica o Fuchsia para a saída |
| --chrome | Especifica o Chrome para a saída |
| --firefox | Especifica o Firefox para a saída |
| --safari | Especifica o Safari para a saída |
| --edge | Especifica o Edge para a saída |
| --opera | Especifica o Opera para a saída |
| --brave | Especifica o Brave para a saída |
| --vivaldi | Especifica o Vivaldi para a saída |
| --tor | Especifica o Tor para a saída |
| --i2p | Especifica o I2P para a saída |
| --freenet | Especifica o Freenet para a saída |
| --zeronet | Especifica o ZeroNet para a saída |
| --ipfs | Especifica o IPFS para a saída |
| --ethereum | Especifica o Ethereum para a saída |
| --bitcoin | Especifica o Bitcoin para a saída |
| --monero | Especifica o Monero para a saída |
| --zcash | Especifica o Zcash para a saída |
| --dash | Especifica o Dash para a saída |
| --litecoin | Especifica o Litecoin para a saída |
| --ripple | Especifica o Ripple para a saída |
| --stellar | Especifica o Stellar para a saída |
| --cardano | Especifica o Cardano para a saída |
| --polkadot | Especifica o Polkadot para a saída |
| --solana | Especifica o Solana para a saída |
| --avalanche | Especifica o Avalanche para a saída |
| --polygon | Especifica o Polygon para a saída |
| --binance | Especifica o Binance para a saída |
| --coinbase | Especifica o Coinbase para a saída |
| --kraken | Especifica o Kraken para a saída |
| --bitfinex | Especifica o Bitfinex para a saída |
| --bitstamp | Especifica o Bitstamp para a saída |
| --gemini | Especifica o Gemini para a saída |
| --ftx | Especifica o FTX para a saída |
| --kucoin | Especifica o KuCoin para a saída |
| --huobi | Especifica o Huobi para a saída |
| --okex | Especifica o OKEx para a saída |
| --bybit | Especifica o Bybit para a saída |
| --deribit | Especifica o Deribit para a saída |
| --bitmex | Especifica o BitMEX para a saída |
| --cme | Especifica o CME para a saída |
| --cboe | Especifica o CBOE para a saída |
| --nyse | Especifica a NYSE para a saída |
| --nasdaq | Especifica a NASDAQ para a saída |
| --lse | Especifica a LSE para a saída |
| --tse | Especifica a TSE para a saída |
| --hkex | Especifica a HKEX para a saída |
| --sse | Especifica a SSE para a saída |
| --szse | Especifica a SZSE para a saída |
| --jpx | Especifica a JPX para a saída |
| --krx | Especifica a KRX para a saída |
| --asx | Especifica a ASX para a saída |
| --tsx | Especifica a TSX para a saída |
| --b3 | Especifica a B3 para a saída |
| --jse | Especifica a JSE para a saída |
| --nse | Especifica a NSE para a saída |
| --bse | Especifica a BSE para a saída |
| --sgx | Especifica a SGX para a saída |
| --set | Especifica a SET para a saída |
| --idx | Especifica a IDX para a saída |
| --klse | Especifica a KLSE para a saída |
| --pse | Especifica a PSE para a saída |
| --hnx | Especifica a HNX para a saída |
| --hose | Especifica a HOSE para a saída |
| --upcom | Especifica a UPCOM para a saída |
| --otc | Especifica a OTC para a saída |
| --forex | Especifica o Forex para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| --futures | Especifica os Futures para a saída |
| --options | Especifica as Options para a saída |
| --bonds | Especifica os Bonds para a saída |
| --etfs | Especifica os ETFs para a saída |
| --mutual-funds | Especifica os Mutual Funds para a saída |
| --hedge-funds | Especifica os Hedge Funds para a saída |
| --private-equity | Especifica o Private Equity para a saída |
| --venture-capital | Especifica o Venture Capital para a saída |
| --real-estate | Especifica o Real Estate para a saída |
| --reits | Especifica os REITs para a saída |
| --reits | Especifica os REITs para a saída |
| --commodities | Especifica as Commodities para a saída |
| `--fut```bash
python scripts/lab.py fixed
## 4.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.```bash
python scripts/lab.py audit
Ou a história inteira de uma vez, com a lição no final:```bash python scripts/lab.py all
E a imagem:```bash
python scripts/lab.py console
Reproduza as evidências commitadas através do pacote. Nenhuma execução da aplicação é necessária — o detector é estritamente a jusante, e esta é a prova:```bash python -m detector --once --timeline --events evidence/vulnerable-persistence.jsonl
Uma regra de cada vez, ou a ingênua, ou a varredura de estado:```bash
python -m detector --once --rule AFTERLIFE-003 --events evidence/vulnerable-persistence.jsonl
I'm ready to translate the Kitploit tool content from English to Portuguese. Please provide chunk 57 of 79.```bash python -m detector --once --naive --events evidence/vulnerable-persistence.jsonl
## 🎯 Objetivos de Aprendizagem
Após completar este módulo, você será capaz de:
- Entender o que é o Model Context Protocol (MCP) e por que ele importa
- Identificar os três componentes principais da arquitetura MCP
- Reconhecer os três primitivos MCP (Tools, Resources, Prompts)
- Compreender os dois tipos de transporte (stdio e HTTP/SSE)
- Configurar servidores MCP em diferentes clientes
- Aplicar as melhores práticas de segurança MCP
- Construir um servidor MCP básico usando Python ou TypeScript
## 📚 Pré-requisitos
Antes de começar este módulo, você deve ter:
- **Compreensão básica de LLMs**: Familiaridade com como os Modelos de Linguagem Grandes funcionam
- **Experiência com Python ou TypeScript**: Capacidade de ler e escrever código em pelo menos uma dessas linguagens
- **Familiaridade com APIs**: Entendimento de APIs REST e requisições HTTP
- **Conhecimento de linha de comando**: Conforto com operações básicas de terminal
- **Conceitos de segurança**: Compreensão básica de princípios de segurança
## 🗺️ Visão Geral do Módulo
Este módulo está organizado nas seguintes seções:
1. **Introdução ao MCP** - O que é e por que você deveria se importar
2. **Arquitetura MCP** - Entendendo os componentes principais
3. **Primitivos MCP** - Tools, Resources e Prompts
4. **Transportes MCP** - stdio vs HTTP/SSE
5. **Configuração de Clientes MCP** - Configurando diferentes clientes
6. **Segurança MCP** - Melhores práticas e armadilhas comuns
7. **Construindo um Servidor MCP** - Guia de implementação prática
8. **Exercícios** - Prática prática
9. **Recursos Adicionais** - Aprendizado complementar
## 1. Introdução ao MCP
### O que é o Model Context Protocol?
O Model Context Protocol (MCP) é um padrão aberto que permite que aplicações forneçam contexto a Modelos de Linguagem Grandes (LLMs) de uma maneira padronizada. Pense nisso como um **"USB-C para IA"** - uma maneira universal de conectar modelos de IA a fontes de dados e ferramentas externas.
### Por que o MCP Importa
Antes do MCP, integrar LLMs com ferramentas externas exigia:
- **Integrações personalizadas** para cada combinação de modelo e ferramenta
- **Código específico de fornecedor** que não era portável entre diferentes LLMs
- **Sobrecarga de manutenção** ao atualizar integrações
- **Desafios de segurança** ao gerenciar múltiplos pontos de integração
O MCP resolve esses problemas fornecendo:
- **Uma interface padronizada** para integração de ferramentas
- **Portabilidade** entre diferentes clientes e modelos
- **Segurança** através de padrões de design bem definidos
- **Reutilização** de servidores MCP entre múltiplas aplicações
### O Problema que o MCP Resolve
Considere um cenário onde você deseja que seu assistente de IA:
1. Leia arquivos do seu sistema de arquivos local
2. Consulte um banco de dados
3. Faça requisições de API para serviços externos
4. Interaja com seu sistema de controle de versão
Sem o MCP, você precisaria:
- Escrever código de integração personalizado para cada combinação de cliente e ferramenta
- Lidar com autenticação e autorização de forma diferente para cada integração
- Manter múltiplas bases de código à medida que as APIs evoluem
- Reimplementar a mesma funcionalidade para diferentes clientes de IA
Com o MCP, você:
- Constrói um servidor MCP uma vez e o usa em qualquer lugar
- Usa padrões de segurança padronizados
- Mantém uma única base de código
- Compartilha servidores MCP com a comunidade
### Ecossistema MCP
O ecossistema MCP inclui:
- **Servidores MCP**: Fornecem ferramentas, recursos e prompts
- **Clientes MCP**: Aplicações que se conectam a servidores MCP (ex.: Claude Desktop, IDEs)
- **SDKs MCP**: Bibliotecas para construir servidores e clientes MCP
- **Registro MCP**: Um diretório de servidores MCP disponíveis publicamente
### Casos de Uso do Mundo Real
O MCP está sendo usado em vários cenários:
- **Assistentes de código**: Acessando documentação, bases de código e ferramentas de desenvolvimento
- **Análise de dados**: Consultando bancos de dados e executando análises
- **Automação de fluxos de trabalho**: Integrando com ferramentas de negócios e APIs
- **Pesquisa**: Acessando artigos acadêmicos e conjuntos de dados
- **Operações de TI**: Gerenciando infraestrutura e monitoramento
## 2. Arquitetura MCP
### Visão Geral da Arquitetura
O MCP segue uma arquitetura cliente-servidor com três componentes principais:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ │ │ │ │ Aplicação │◄───────►│ Cliente │◄───────►│ Servidor │ │ Hospedeira │ │ MCP │ │ MCP │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────┘
### Componentes Principais
#### 1. Aplicação Hospedeira
A aplicação hospedeira é o programa que o usuário está executando. Ela:
- Fornece a interface do usuário
- Gerencia a conversa com o LLM
- Inicia conexões com servidores MCP
- Controla quais servidores MCP estão disponíveis
Exemplos: Claude Desktop, Cursor, VS Code com extensões de IA
#### 2. Cliente MCP
O cliente MCP é um componente dentro da aplicação hospedeira que:
- Mantém conexões com servidores MCP
- Lida com o protocolo de comunicação
- Gerencia o ciclo de vida da conexão
- Encaminha requisições e respostas
#### 3. Servidor MCP
O servidor MCP é um programa que:
- Expõe ferramentas, recursos e prompts
- Lida com requisições de clientes MCP
- Executa operações em nome do LLM
- Retorna resultados ao cliente
### Fluxo de Comunicação
Aqui está como a comunicação flui em uma interação típica do MCP:
1. **Descoberta**: O cliente MCP se conecta ao servidor MCP e descobre capacidades
2. **Requisição**: O LLM decide usar uma ferramenta e o cliente envia uma requisição
3. **Execução**: O servidor MCP executa a operação solicitada
4. **Resposta**: O servidor retorna os resultados ao cliente
5. **Integração**: O cliente fornece os resultados de volta ao LLM
### Detalhes do Protocolo
O MCP usa JSON-RPC 2.0 como seu protocolo de mensagens. Isso significa:
- Todas as mensagens são formatadas como objetos JSON
- As mensagens seguem a especificação JSON-RPC 2.0
- As mensagens podem ser requisições, respostas ou notificações
- O protocolo suporta capacidades de streaming
Exemplo de uma requisição MCP:
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "read_file",
"arguments": {
"path": "/path/to/file.txt"
}
}
}
Exemplo de uma resposta MCP:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "Conteúdo do arquivo..."
}
]
}
}
As conexões MCP seguem um ciclo de vida definido:
Durante a inicialização, o cliente e o servidor trocam informações sobre:
O MCP define três primitivos principais que os servidores podem expor:
Tools são funções que o LLM pode chamar para executar ações. Elas são o primitivo mais comumente usado.
Características das Tools:
Exemplo de definição de uma Tool:
{
"name": "get_weather",
"description": "Obtém o clima atual para um local",
"inputSchema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "Nome da cidade ou coordenadas"
},
"units": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Unidades de temperatura"
}
},
"required": ["location"]
}
}
Casos de uso comuns para Tools:
Resources representam dados que o LLM pode ler. Elas fornecem contexto sem executar ações.
Características dos Resources:
Exemplo de definição de um Resource:
{
"uri": "file:///path/to/document.txt",
"name": "Documento",
"description": "Um documento de texto",
"mimeType": "text/plain"
}
Casos de uso comuns para Resources:
Prompts são templates reutilizáveis que ajudam os usuários a interagir com o LLM.
Características dos Prompts:
Exemplo de definição de um Prompt:
{
"name": "code_review",
"description": "Revisa código em busca de problemas",
"arguments": [
{
"name": "code",
"description": "O código a ser revisado",
"required": true
},
{
"name": "language",
"description": "A linguagem de programação",
"required": false
}
]
}
Casos de uso comuns para Prompts:
O MCP suporta dois mecanismos de transporte para comunicação:
O transporte stdio usa entrada e saída padrão para comunicação. É o transporte mais comum para servidores MCP locais.
Características:
Como funciona:
Exemplo de configuração:
{
"mcpServers": {
"my-server": {
"command": "python",
"args": ["-m", "my_mcp_server"],
"env": {
"API_KEY": "your-api-key"
}
}
}
}
Melhores práticas para transporte stdio:
O transporte HTTP/SSE usa HTTP com Server-Sent Events para comunicação. É usado para servidores MCP remotos.
Características:
Como funciona:
Exemplo de configuração:
{
"mcpServers": {
"remote-server": {
"url": "https://api.example.com/mcp",
"headers": {
"Authorization": "Bearer your-token"
}
}
}
}
Melhores práticas para transporte HTTP/SSE:
Diferentes clientes MCP têm requisitos de configuração variados. Aqui estão os clientes mais comuns:
O Claude Desktop é um dos clientes MCP mais populares.
Localização do arquivo de configuração:
~/Library/Application Support/Claude/claude_desktop_config.json%APPDATA%\Claude\claude_desktop_config.json~/.config/Claude/claude_desktop_config.jsonExemplo de configuração:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/path/to/allowed/directory"
]
},
"github": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-github"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "your-token"
}
}
}
}
O Cursor é um editor de código com suporte integrado a MCP.
Localização do arquivo de configuração:
~/.cursor/mcp.json%USERPROFILE%\.cursor\mcp.json~/.cursor/mcp.jsonExemplo de configuração:
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"postgresql://localhost/mydb"
]
}
}
}
O VS Code suporta MCP através de extensões.
Exemplo de configuração em settings.json:
{
"mcp.servers": {
"my-server": {
"command": "python",
"args": ["-m", "my_mcp_server"]
}
}
}
Você também pode construir seus próprios clientes MCP usando os SDKs oficiais.
Exemplo em Python:
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
server_params = StdioServerParameters(
command="python",
args=["-m", "my_mcp_server"]
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
# Listar ferramentas disponíveis
tools = await session.list_tools()
print(f"Ferramentas disponíveis: {tools}")
# Chamar uma ferramenta
result = await session.call_tool(
"my_tool",
arguments={"param": "value"}
)
print(f"Resultado: {result}")
A segurança é crítica ao implementar servidores MCP. Aqui estão as principais considerações:
Risco: Servidores MCP podem executar código arbitrário se não forem devidamente isolados.
Mitigação:
Risco: Servidores MCP podem expor dados sensíveis ao LLM.
Mitigação:
Risco: Entradas maliciosas podem manipular o comportamento do LLM.
Mitigação:
Risco: Servidores MCP podem obter mais permissões do que o pretendido.
Mitigação:
Sempre valide as entradas usando esquemas:
from pydantic import BaseModel, Field, validator
class ReadFileInput(BaseModel):
path: str = Field(..., description="Caminho do arquivo")
@validator('path')
def validate_path(cls, v):
# Impedir travessia de diretório
if '..' in v:
raise ValueError('Travessia de caminho não permitida')
# Restringir a diretórios permitidos
if not v.startswith('/allowed/directory'):
raise ValueError('Acesso negado')
return v
Implemente autenticação adequada para servidores remotos:
from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
app = FastAPI()
security = HTTPBearer()
async def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
token = credentials.credentials
if not is_valid_token(token):
raise HTTPException(status_code=401, detail="Token inválido")
return token
@app.post("/mcp")
async def mcp_endpoint(token: str = Depends(verify_token)):
# Lidar com requisição MCP
pass
Implemente limitação de taxa para prevenir abuso:
from slowapi import Limiter
from slowapi.util import get_remote_address
limiter = Limiter(key_func=get_remote_address)
@app.post("/mcp")
@limiter.limit("100/minute")
async def mcp_endpoint(request: Request):
# Lidar com requisição MCP
pass
Implemente logging e monitoramento abrangentes:
import logging
import structlog
logger = structlog.get_logger()
async def handle_tool_call(tool_name: str, arguments: dict):
logger.info(
"tool_call",
tool=tool_name,
arguments=arguments,
timestamp=datetime.utcnow().isoformat()
)
try:
result = await execute_tool(tool_name, arguments)
logger.info("tool_success", tool=tool_name)
return result
except Exception as e:
logger.error("tool_error", tool=tool_name, error=str(e))
raise
Antes de implantar um servidor MCP, verifique:
Vamos construir um servidor MCP básico usando Python.
Primeiro, crie um novo projeto:
mkdir my-mcp-server
cd my-mcp-server
python -m venv venv
source venv/bin/activate # No Windows: venv\Scripts\activate
pip install mcp
Crie um arquivo server.py:
import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
# Criar instância do servidor
server = Server("my-mcp-server")
# Definir uma ferramenta
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="greet",
description="Cumprimenta uma pessoa pelo nome",
inputSchema={
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Nome da pessoa a cumprimentar"
}
},
"required": ["name"]
}
)
]
# Implementar a ferramenta
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "greet":
person_name = arguments.get("name", "Mundo")
return [
TextContent(
type="text",
text=f"Olá, {person_name}! Bem-vindo ao MCP."
)
]
raise ValueError(f"Ferramenta desconhecida: {name}")
# Executar o servidor
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
server.create_initialization_options()
)
if __name__ == "__main__":
asyncio.run(main())
Vamos adicionar suporte a resources:
from mcp.types import Resource, ResourceContents
@server.list_resources()
async def list_resources() -> list[Resource]:
return [
Resource(
uri="config://app/settings",
name="Configurações da Aplicação",
description="Configurações atuais da aplicação",
mimeType="application/json"
)
]
@server.read_resource()
async def read_resource(uri: str) -> ResourceContents:
if uri == "config://app/settings":
return ResourceContents(
uri=uri,
mimeType="application/json",
text='{"theme": "dark", "language": "pt"}'
)
raise ValueError(f"Resource desconhecido: {uri}")
Vamos adicionar suporte a prompts:
from mcp.types import Prompt, PromptMessage, PromptArgument
@server.list_prompts()
async def list_prompts() -> list[Prompt]:
return [
Prompt(
name="code_review",
description="Revisa código em busca de problemas",
arguments=[
PromptArgument(
name="code",
description="O código a ser revisado",
required=True
)
]
)
]
@server.get_prompt()
async def get_prompt(name: str, arguments: dict) -> list[PromptMessage]:
if name == "code_review":
code = arguments.get("code", "")
return [
PromptMessage(
role="user",
content=TextContent(
type="text",
text=f"Por favor, revise o seguinte código:\n\n{code}"
)
)
]
raise ValueError(f"Prompt desconhecido: {name}")
Você pode testar seu servidor usando o MCP Inspector:
npx @modelcontextprotocol/inspector python server.py
Ou escrevendo um cliente de teste:
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def test_server():
server_params = StdioServerParameters(
command="python",
args=["server.py"]
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
# Testar a ferramenta greet
result = await session.call_tool(
"greet",
arguments={"name": "Alice"}
)
print(f"Resultado: {result}")
# Testar o resource
resource = await session.read_resource("config://app/settings")
print(f"Resource: {resource}")
# Testar o prompt
prompt = await session.get_prompt(
"code_review",
arguments={"code": "def hello(): pass"}
)
print(f"Prompt: {prompt}")
if __name__ == "__main__":
asyncio.run(test_server())
Para distribuir seu servidor MCP:
pyproject.toml:[project]
name = "my-mcp-server"
version = "0.1.0"
description = "Meu servidor MCP personalizado"
dependencies = ["mcp"]
[project.scripts]
my-mcp-server = "server:main"
pip install build twine
python -m build
twine upload dist/*
{
"mcpServers": {
"my-server": {
"command": "my-mcp-server"
}
}
}
Crie um servidor MCP que forneça ferramentas de calculadora:
add: Soma dois númerossubtract: Subtrai dois númerosmultiply: Multiplica dois númerosdivide: Divide dois números (com tratamento de divisão por zero)Requisitos:
Estenda o servidor de calculadora para incluir resources:
history://calculations: Histórico de cálculosconfig://calculator: Configurações da calculadoraRequisitos:
Adicione recursos de segurança ao seu servidor MCP:
Requisitos:
Converta seu servidor MCP para usar o transporte HTTP/SSE:
Requisitos:
Neste módulo, você aprendeu:
Agora que você entende o MCP, considere:
R: Não, o MCP é um padrão aberto que funciona com qualquer LLM ou cliente que o suporte.
R: Sim, o MCP funciona com LLMs locais, desde que o cliente suporte o protocolo.
R: O MCP fornece um framework para interações seguras, mas a segurança depende da implementação. Siga as melhores práticas descritas neste módulo.
R: SDKs oficiais estão disponíveis para Python e TypeScript. SDKs da comunidade existem para outras linguagens.
R: Use o MCP Inspector ou implemente logging abrangente em seu servidor.
R: Sim, os clientes podem se conectar a múltiplos servidores MCP ao mesmo tempo.
R: Tools executam ações e podem ter efeitos colaterais, enquanto Resources fornecem dados somente leitura.
R: Use autenticação baseada em token, OAuth ou outros mecanismos de autenticação padrão. Sempre use HTTPS.
R: Sim, o MCP suporta operações assíncronas através de seu protocolo de mensagens.
R: Construa e compartilhe servidores MCP, contribua com SDKs, escreva tutoriais ou participe de discussões da```bash python -m detector --audit --events evidence/vulnerable-persistence.jsonl
A CLI sai com `1` quando encontra algo, então funciona como verificação de CI sem
analisar a sua saída.
Nove cenários contra ambas as implementações, com invariantes de autoverificação:```bash
python scripts/scenarios.py
O relatório de incidente que um respondedor receberia, e as figuras neste README:```bash python scripts/report.py
## 🧪 Exemplos de Uso
### Exemplo 1: Análise de Segurança de Contrato Inteligente
```bash
# Analisar um contrato inteligente em busca de vulnerabilidades
python3 -m src.main --contract 0x1234567890abcdef1234567890abcdef12345678 --network ethereum
# Saída:
# ✅ Análise concluída
# 🔍 Vulnerabilidades encontradas: 3
# ⚠️ Reentrancy detectada na linha 45
# ⚠️ Integer overflow na linha 78
# ⚠️ Acesso não autorizado na linha 102
---
[Read more](https://github.com/het-p301204/afterlife)
dormant-survivor | vulnerable | 15 | HIGH | AFTERLIFE-003 | 1 | 1 |
dormant-survivor | fixed | 19 | – | none | 0 | 0 |
mfa-change | vulnerable | 18 | HIGH | AFTERLIFE-001, AFTERLIFE-003 | 1 | 0 |
mfa-change | fixed | 18 | – | none | 0 | 0 |
account-recovery | vulnerable | 18 | HIGH | AFTERLIFE-001, AFTERLIFE-003 | 1 | 0 |
account-recovery | fixed | 18 | – | none | 0 | 0 |
expired-lineage | vulnerable | 17 | LOW | AFTERLIFE-003 | 0 | 0 |
expired-lineage | fixed | 20 | – | none | 0 | 0 |
| Primitivo | Controlado Por | Propósito | Efeitos Colaterais | Caso de Uso |
|---|
| Tools | Modelo | Executar ações | Sim | Consultar APIs, modificar dados |
| Resources | Aplicação | Fornecer dados | Não | Ler arquivos, buscar registros |
| Prompts | Usuário | Guiar interações | Não | Templates de fluxo de trabalho |
| Fator | stdio | HTTP/SSE |
|---|
| Localização | Local | Remoto |
| Complexidade | Baixa | Alta |
| Segurança | Mais seguro por padrão | Requer configuração |
| Escalabilidade | Processo único | Múltiplos clientes |
| Caso de uso | Ferramentas locais | Serviços compartilhados |