clean up
This commit is contained in:
parent
af2e8ba51e
commit
c0ed9ba8e0
@ -1,257 +0,0 @@
|
|||||||
# PR-004: Runtime VFS Path Traversal Hardening
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
Hoje o `VirtualFS` normaliza barras, mas nao canonicaliza nem rejeita segmentos `..`. Em seguida, o backend de host concatena o caminho recebido com a raiz montada. Isso abre espaco para escapar da raiz virtual com caminhos como `/user/../../outside.txt`.
|
|
||||||
|
|
||||||
Este PR endurece a fronteira entre o filesystem virtual e o filesystem do host. O objetivo e garantir que toda operacao de `read/write/delete/list/exists` permaneça estritamente dentro da raiz montada.
|
|
||||||
|
|
||||||
## Problema
|
|
||||||
|
|
||||||
- O `VirtualFS` aceita caminhos relativos e absolutos sem validacao estrutural suficiente.
|
|
||||||
- O backend `HostDirBackend` faz `root.join(path)` sem bloquear traversal.
|
|
||||||
- O problema afeta confidencialidade, integridade e isolamento do runtime.
|
|
||||||
|
|
||||||
## Escopo
|
|
||||||
|
|
||||||
- Endurecer a normalizacao de caminhos no `VirtualFS`.
|
|
||||||
- Endurecer a resolucao no `HostDirBackend`.
|
|
||||||
- Garantir comportamento consistente para `read_file`, `write_file`, `delete`, `list_dir` e `exists`.
|
|
||||||
- Cobrir casos de traversal em testes unitarios.
|
|
||||||
|
|
||||||
## Fora de Escopo
|
|
||||||
|
|
||||||
- Suporte a links simbolicos com politicas avancadas.
|
|
||||||
- Politicas de permissao por namespace (`/system`, `/user`, `/apps`, etc).
|
|
||||||
- Refactor completo da API de filesystem.
|
|
||||||
|
|
||||||
## Abordagem
|
|
||||||
|
|
||||||
1. Introduzir uma regra unica de validacao de caminho virtual:
|
|
||||||
- converter `\` para `/`;
|
|
||||||
- exigir caminho absoluto virtual;
|
|
||||||
- colapsar `.` quando aparecer;
|
|
||||||
- rejeitar qualquer segmento vazio ambiguo ou `..`;
|
|
||||||
- retornar erro explicito em vez de tentar "corrigir" traversal.
|
|
||||||
2. Fazer o `VirtualFS` operar apenas sobre caminhos validados.
|
|
||||||
3. Endurecer o `HostDirBackend` para nunca confiar apenas na normalizacao acima:
|
|
||||||
- resolver o caminho relativo a partir da raiz;
|
|
||||||
- rejeitar novamente qualquer tentativa de escapar;
|
|
||||||
- manter defesa em profundidade mesmo se outro backend ou chamador evoluir errado.
|
|
||||||
4. Garantir que operacoes booleanas como `exists` nao silenciem traversal como se fosse "arquivo inexistente" sem distinguir erro estrutural quando isso for relevante para a API.
|
|
||||||
|
|
||||||
## Algoritmo
|
|
||||||
|
|
||||||
### Normalizacao de caminho virtual
|
|
||||||
|
|
||||||
Entrada: `path: &str`
|
|
||||||
|
|
||||||
Saida: caminho virtual sanitizado ou erro.
|
|
||||||
|
|
||||||
Passos:
|
|
||||||
|
|
||||||
1. Substituir `\` por `/`.
|
|
||||||
2. Se o caminho nao comecar com `/`, prefixar `/`.
|
|
||||||
3. Separar por `/`.
|
|
||||||
4. Ignorar segmentos vazios e `.`.
|
|
||||||
5. Se algum segmento for `..`, falhar com `FsError`.
|
|
||||||
6. Reconstruir o caminho como `/<seg1>/<seg2>/...`.
|
|
||||||
7. Preservar `/` como raiz quando nao houver segmentos.
|
|
||||||
|
|
||||||
### Resolucao no backend do host
|
|
||||||
|
|
||||||
Entrada: caminho virtual sanitizado.
|
|
||||||
|
|
||||||
Saida: `PathBuf` dentro de `root` ou erro.
|
|
||||||
|
|
||||||
Passos:
|
|
||||||
|
|
||||||
1. Remover o `/` inicial do caminho virtual.
|
|
||||||
2. Concatenar cada segmento validado manualmente em um `PathBuf` iniciado em `root`.
|
|
||||||
3. Nunca aceitar segmentos `..`, `.` ou componentes de prefixo/plataforma.
|
|
||||||
4. Antes de retornar, garantir que o caminho construido continua sob `root`.
|
|
||||||
|
|
||||||
## Plano de Execucao
|
|
||||||
|
|
||||||
### Gate arquitetural
|
|
||||||
|
|
||||||
Antes de iniciar a implementacao, esta PR deve congelar a seguinte decisao:
|
|
||||||
|
|
||||||
- `exists` permanece com retorno `bool` nesta PR.
|
|
||||||
- Consequencia: caminho invalido ou tentativa de traversal em `exists` deve resultar em `false`, sem acesso ao host.
|
|
||||||
- Justificativa: mudar `exists` para `Result<bool, FsError>` propaga alteracao de contrato para `FsBackend`, `VirtualFS`, runtime e syscall, o que expande o escopo desta PR.
|
|
||||||
|
|
||||||
Se essa decisao nao for aceita, a PR deve voltar para discussao arquitetural antes de qualquer alteracao de codigo.
|
|
||||||
|
|
||||||
### Fase 1 - Endurecer contrato de erro
|
|
||||||
|
|
||||||
Arquivos alvo:
|
|
||||||
|
|
||||||
- `crates/console/prometeu-system/src/services/fs/fs_error.rs`
|
|
||||||
|
|
||||||
Passos:
|
|
||||||
|
|
||||||
1. Introduzir um erro explicito para caminho invalido, por exemplo `FsError::InvalidPath(String)` ou equivalente.
|
|
||||||
2. Garantir mensagem clara para os casos:
|
|
||||||
- caminho com `..`;
|
|
||||||
- componente de plataforma/prefixo inesperado;
|
|
||||||
- caminho vazio estruturalmente invalido, se a implementacao optar por rejeita-lo.
|
|
||||||
|
|
||||||
Objetivo:
|
|
||||||
|
|
||||||
- impedir que traversal seja reportado genericamente como `IOError` ou `Other`.
|
|
||||||
|
|
||||||
### Fase 2 - Endurecer o `VirtualFS`
|
|
||||||
|
|
||||||
Arquivos alvo:
|
|
||||||
|
|
||||||
- `crates/console/prometeu-system/src/services/fs/virtual_fs.rs`
|
|
||||||
|
|
||||||
Passos:
|
|
||||||
|
|
||||||
1. Trocar `normalize_path(&self, path: &str) -> String` por uma funcao fallible:
|
|
||||||
- `normalize_path(path: &str) -> Result<String, FsError>`.
|
|
||||||
2. Implementar a normalizacao unica da camada virtual:
|
|
||||||
- converter `\` para `/`;
|
|
||||||
- garantir raiz virtual absoluta;
|
|
||||||
- colapsar `.` e segmentos vazios internos irrelevantes;
|
|
||||||
- rejeitar `..`.
|
|
||||||
3. Fazer `list_dir`, `read_file`, `write_file` e `delete` falharem antes de tocar no backend quando o caminho for invalido.
|
|
||||||
4. Manter `exists` como fronteira booleana:
|
|
||||||
- se a normalizacao falhar, retornar `false`;
|
|
||||||
- nao chamar o backend nesse caso.
|
|
||||||
|
|
||||||
Objetivo:
|
|
||||||
|
|
||||||
- estabelecer uma unica regra de caminho virtual para toda a API publica do VFS.
|
|
||||||
|
|
||||||
### Fase 3 - Endurecer o `HostDirBackend`
|
|
||||||
|
|
||||||
Arquivos alvo:
|
|
||||||
|
|
||||||
- `crates/host/prometeu-host-desktop-winit/src/fs_backend.rs`
|
|
||||||
|
|
||||||
Passos:
|
|
||||||
|
|
||||||
1. Trocar `resolve(&self, path: &str) -> PathBuf` por uma resolucao fallible.
|
|
||||||
2. Iterar manualmente pelos componentes do caminho e rejeitar:
|
|
||||||
- `ParentDir`;
|
|
||||||
- `CurDir`;
|
|
||||||
- `RootDir`;
|
|
||||||
- `Prefix(_)` em plataformas que exponham prefixos.
|
|
||||||
3. Construir o `PathBuf` a partir de `root` apenas com segmentos normais.
|
|
||||||
4. Validar ao final que o caminho produzido continua estritamente sob `root`.
|
|
||||||
5. Fazer `list_dir`, `read_file`, `write_file` e `delete` retornarem erro estrutural em vez de acessar o host.
|
|
||||||
6. Fazer `exists` retornar `false` para caminhos invalidos, sem side effect.
|
|
||||||
|
|
||||||
Objetivo:
|
|
||||||
|
|
||||||
- manter defesa em profundidade mesmo que outra camada no futuro normalize errado.
|
|
||||||
|
|
||||||
### Fase 4 - Cobertura de testes no `prometeu-system`
|
|
||||||
|
|
||||||
Arquivos alvo:
|
|
||||||
|
|
||||||
- `crates/console/prometeu-system/src/services/fs/virtual_fs.rs`
|
|
||||||
|
|
||||||
Passos:
|
|
||||||
|
|
||||||
1. Adicionar testes unitarios de normalizacao/rejeicao para:
|
|
||||||
- `../x`
|
|
||||||
- `/../x`
|
|
||||||
- `/user/../../x`
|
|
||||||
- `\\user\\..\\..\\x`
|
|
||||||
2. Adicionar um teste que prove que `exists("../x") == false`.
|
|
||||||
3. Adicionar um mock backend com contadores ou flags para comprovar que caminhos invalidos:
|
|
||||||
- nao chegam em `read_file`;
|
|
||||||
- nao chegam em `write_file`;
|
|
||||||
- nao chegam em `delete`;
|
|
||||||
- nao chegam em `list_dir`;
|
|
||||||
- nao chegam em `exists`.
|
|
||||||
|
|
||||||
Objetivo:
|
|
||||||
|
|
||||||
- provar que a barreira virtual bloqueia traversal antes de qualquer backend.
|
|
||||||
|
|
||||||
### Fase 5 - Cobertura de testes no host
|
|
||||||
|
|
||||||
Arquivos alvo:
|
|
||||||
|
|
||||||
- `crates/host/prometeu-host-desktop-winit/src/fs_backend.rs`
|
|
||||||
|
|
||||||
Passos:
|
|
||||||
|
|
||||||
1. Adicionar teste positivo de round-trip:
|
|
||||||
- criar `/user/test.txt`;
|
|
||||||
- ler;
|
|
||||||
- verificar `exists`;
|
|
||||||
- apagar.
|
|
||||||
2. Adicionar teste de traversal em leitura e escrita usando diretorio temporario:
|
|
||||||
- garantir erro para `/user/../../outside.txt`;
|
|
||||||
- garantir que nenhum arquivo e criado fora de `root`.
|
|
||||||
3. Adicionar teste para `exists` e `list_dir` com traversal:
|
|
||||||
- `exists` retorna `false`;
|
|
||||||
- `list_dir` retorna erro;
|
|
||||||
- nenhum acesso fora da raiz e observado.
|
|
||||||
|
|
||||||
Objetivo:
|
|
||||||
|
|
||||||
- provar que a defesa em profundidade do backend funciona mesmo isoladamente.
|
|
||||||
|
|
||||||
### Fase 6 - Validacao final
|
|
||||||
|
|
||||||
Comandos:
|
|
||||||
|
|
||||||
- `cargo test -p prometeu-system`
|
|
||||||
- `cargo test -p prometeu-host-desktop-winit`
|
|
||||||
|
|
||||||
Checklist de saida:
|
|
||||||
|
|
||||||
- nenhuma operacao acessa caminho fora da raiz montada;
|
|
||||||
- caminhos validos continuam funcionais;
|
|
||||||
- traversal falha de forma deterministica e explicita;
|
|
||||||
- `exists` preserva contrato booleano sem vazar acesso ao host.
|
|
||||||
|
|
||||||
## Sequencia recomendada
|
|
||||||
|
|
||||||
1. Implementar `FsError` primeiro.
|
|
||||||
2. Endurecer `VirtualFS` e fechar os testes unitarios da camada virtual.
|
|
||||||
3. Endurecer `HostDirBackend` com defesa em profundidade.
|
|
||||||
4. Adicionar os testes de host.
|
|
||||||
5. Rodar os testes dos dois crates.
|
|
||||||
|
|
||||||
## Riscos de execucao
|
|
||||||
|
|
||||||
- O comportamento atual aceita caminhos permissivos como `user/file.txt`; apos esta PR eles continuarao aceitos apenas se normalizarem para uma forma absoluta segura.
|
|
||||||
- A maior fonte de expansao de escopo e tentar mudar o contrato de `exists`; esta PR nao deve fazer isso.
|
|
||||||
- Se surgir necessidade de suportar links simbolicos ou canonicalizacao real de host, isso caracteriza outra PR.
|
|
||||||
|
|
||||||
## Criterios de Aceite
|
|
||||||
|
|
||||||
- Qualquer tentativa de traversal com `..` e rejeitada em `read_file`.
|
|
||||||
- Qualquer tentativa de traversal com `..` e rejeitada em `write_file`.
|
|
||||||
- Qualquer tentativa de traversal com `..` e rejeitada em `delete`.
|
|
||||||
- `exists` e `list_dir` nao acessam caminhos fora da raiz montada.
|
|
||||||
- Caminhos normais como `/user/save.dat` continuam funcionando.
|
|
||||||
- O backend de host continua criando subdiretorios validos dentro da raiz.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- Teste unitario no `VirtualFS` para rejeitar:
|
|
||||||
- `../x`
|
|
||||||
- `/../x`
|
|
||||||
- `/user/../../x`
|
|
||||||
- `\\user\\..\\..\\x`
|
|
||||||
- Teste unitario no backend do host validando que um caminho de traversal nao resulta em acesso fora da raiz temporaria.
|
|
||||||
- Teste positivo para operacoes validas:
|
|
||||||
- criar arquivo em `/user/test.txt`;
|
|
||||||
- ler o mesmo arquivo;
|
|
||||||
- confirmar `exists`;
|
|
||||||
- apagar o arquivo.
|
|
||||||
- Rodar:
|
|
||||||
- `cargo test -p prometeu-system`
|
|
||||||
- `cargo test -p prometeu-host-desktop-winit`
|
|
||||||
|
|
||||||
## Risco
|
|
||||||
|
|
||||||
Baixo para a arquitetura e medio para compatibilidade, porque caminhos hoje aceitos de forma permissiva podem passar a falhar explicitamente. Esse endurecimento e desejado.
|
|
||||||
@ -1,87 +0,0 @@
|
|||||||
# PR-005: Host Audio Fallible Init and Headless Tolerance
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
O host desktop atual assume que audio de saida esta sempre disponivel e funcional. Em ambientes headless, CI, VMs remotas ou sistemas sem device compativel, a inicializacao usa `expect(...)` e encerra o processo.
|
|
||||||
|
|
||||||
Este PR torna a pilha de audio tolerante a falhas de infraestrutura. O runtime deve continuar operando sem audio quando o host nao puder inicializar a saida sonora.
|
|
||||||
|
|
||||||
## Problema
|
|
||||||
|
|
||||||
- `default_output_device()` pode retornar `None`.
|
|
||||||
- `build_output_stream(...)` pode falhar por formato/configuracao.
|
|
||||||
- `stream.play()` pode falhar por indisponibilidade do backend.
|
|
||||||
- Todas essas falhas hoje derrubam o binario com `panic!`.
|
|
||||||
|
|
||||||
## Escopo
|
|
||||||
|
|
||||||
- Trocar inicializacao panica por API fallible.
|
|
||||||
- Permitir degradacao controlada para "sem audio".
|
|
||||||
- Manter telemetria/log suficiente para diagnostico.
|
|
||||||
- Cobrir o fluxo em testes de unidade onde viavel.
|
|
||||||
|
|
||||||
## Fora de Escopo
|
|
||||||
|
|
||||||
- Rework do mixer.
|
|
||||||
- Selecao automatica de formatos de audio mais sofisticada.
|
|
||||||
- Hot-reload de device de audio.
|
|
||||||
- Simulacao de audio em buffer offline.
|
|
||||||
|
|
||||||
## Abordagem
|
|
||||||
|
|
||||||
1. Mudar `HostAudio::init` para retornar `Result<(), HostAudioError>` ou tipo equivalente.
|
|
||||||
2. Representar explicitamente o estado "audio indisponivel":
|
|
||||||
- `producer = None`
|
|
||||||
- `stream = None`
|
|
||||||
- stats continuam funcionando sem crash.
|
|
||||||
3. No chamador, registrar aviso e seguir execucao sem audio.
|
|
||||||
4. Garantir que `send_commands` e `update_stats` sejam no-op seguros quando o audio nao estiver ativo.
|
|
||||||
|
|
||||||
## Algoritmo
|
|
||||||
|
|
||||||
### Inicializacao
|
|
||||||
|
|
||||||
1. Buscar `default_output_device`.
|
|
||||||
2. Se nao existir:
|
|
||||||
- retornar erro de inicializacao controlado.
|
|
||||||
3. Tentar construir `StreamConfig` e `build_output_stream`.
|
|
||||||
4. Se falhar:
|
|
||||||
- retornar erro controlado sem panicar.
|
|
||||||
5. Tentar `play()`.
|
|
||||||
6. Se falhar:
|
|
||||||
- retornar erro controlado sem panicar.
|
|
||||||
7. Apenas em caso de sucesso preencher `producer`, `perf_consumer` e `_stream`.
|
|
||||||
|
|
||||||
### Degradacao
|
|
||||||
|
|
||||||
1. O host chama `init`.
|
|
||||||
2. Se `Ok`, audio ativo.
|
|
||||||
3. Se `Err`, loga a falha e continua sem audio.
|
|
||||||
4. O loop principal segue responsivo e funcional.
|
|
||||||
|
|
||||||
## Criterios de Aceite
|
|
||||||
|
|
||||||
- O runtime nao entra em `panic!` quando nao existe device de audio.
|
|
||||||
- O runtime nao entra em `panic!` quando `build_output_stream` falha.
|
|
||||||
- O runtime nao entra em `panic!` quando `play()` falha.
|
|
||||||
- `send_commands` e `update_stats` continuam seguros sem stream ativo.
|
|
||||||
- O host continua executando cartridge em modo sem audio.
|
|
||||||
- Logs deixam claro por que o audio foi desativado.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- Introduzir testes de unidade para o estado sem audio:
|
|
||||||
- `send_commands` nao falha quando `producer` e `None`;
|
|
||||||
- `update_stats` nao falha quando `perf_consumer` e `None`.
|
|
||||||
- Se a estrutura atual permitir injecao de dependencia:
|
|
||||||
- testar caminho `no output device`;
|
|
||||||
- testar falha na criacao do stream;
|
|
||||||
- testar falha no `play`.
|
|
||||||
- Se injecao ainda nao existir, criar uma camada minima de abstracao para tornar esses cenarios testaveis sem depender do hardware real.
|
|
||||||
- Rodar:
|
|
||||||
- `cargo test -p prometeu-host-desktop-winit`
|
|
||||||
- `cargo test --workspace`
|
|
||||||
|
|
||||||
## Risco
|
|
||||||
|
|
||||||
Baixo. A mudanca principal e de robustez operacional. O maior cuidado e nao esconder a falha completamente; o modo sem audio precisa ser explicito em logs.
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
# PR-006: CLI Preserve Child Exit Status
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
O wrapper `prometeu` despacha para outros binarios do ecossistema. Hoje, se o processo filho terminar sem exit code convencional, o CLI usa `unwrap_or(0)` e pode reportar sucesso mesmo quando houve abort, sinal ou encerramento anormal.
|
|
||||||
|
|
||||||
Este PR corrige a propagacao de status do processo filho para tornar o CLI confiavel em automacao, CI e uso por scripts.
|
|
||||||
|
|
||||||
## Problema
|
|
||||||
|
|
||||||
- `status.code()` pode retornar `None`.
|
|
||||||
- `unwrap_or(0)` transforma falha anormal em sucesso.
|
|
||||||
- Isso mascara erros reais em `run`, `debug`, `build` e `verify`.
|
|
||||||
|
|
||||||
## Escopo
|
|
||||||
|
|
||||||
- Preservar corretamente o resultado do processo filho.
|
|
||||||
- Melhorar a mensagem de erro quando nao houver codigo numerico.
|
|
||||||
- Cobrir o comportamento com testes.
|
|
||||||
|
|
||||||
## Fora de Escopo
|
|
||||||
|
|
||||||
- Reestruturar todo o dispatcher.
|
|
||||||
- Implementar subcomandos ainda nao existentes.
|
|
||||||
- Alterar o protocolo entre CLI e bins filhos.
|
|
||||||
|
|
||||||
## Abordagem
|
|
||||||
|
|
||||||
1. Remover `unwrap_or(0)` no ponto de saida.
|
|
||||||
2. Tratar dois casos:
|
|
||||||
- filho retorna exit code numerico: repassar exatamente;
|
|
||||||
- filho termina sem codigo numerico: encerrar com codigo nao-zero e mensagem clara.
|
|
||||||
3. Se necessario, extrair a traducao de `ExitStatus` para uma funcao pequena e testavel.
|
|
||||||
|
|
||||||
## Algoritmo
|
|
||||||
|
|
||||||
Entrada: `ExitStatus` do processo filho.
|
|
||||||
|
|
||||||
Passos:
|
|
||||||
|
|
||||||
1. Chamar `status.code()`.
|
|
||||||
2. Se retornar `Some(code)`:
|
|
||||||
- encerrar o wrapper com `code`.
|
|
||||||
3. Se retornar `None`:
|
|
||||||
- escrever mensagem de erro indicando encerramento anormal;
|
|
||||||
- encerrar o wrapper com `1`.
|
|
||||||
|
|
||||||
## Criterios de Aceite
|
|
||||||
|
|
||||||
- Se o processo filho sair com `0`, o wrapper sai com `0`.
|
|
||||||
- Se o processo filho sair com codigo nao-zero, o wrapper replica o mesmo codigo.
|
|
||||||
- Se o processo filho terminar sem codigo numerico, o wrapper sai com codigo nao-zero.
|
|
||||||
- O comportamento fica coberto por teste unitario.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- Extrair funcao pura ou quase pura para mapear `ExitStatus -> i32` e testá-la.
|
|
||||||
- Cobrir:
|
|
||||||
- `0`;
|
|
||||||
- `1`;
|
|
||||||
- outro codigo nao-zero;
|
|
||||||
- status sem code numerico quando a plataforma permitir criar esse caso em teste.
|
|
||||||
- Se o caso `None` depender de plataforma:
|
|
||||||
- encapsular sob `cfg(unix)` ou `cfg(windows)` conforme apropriado;
|
|
||||||
- manter ao menos o contrato da funcao testado por unidade.
|
|
||||||
- Rodar:
|
|
||||||
- `cargo test -p prometeu-cli`
|
|
||||||
- `cargo test --workspace`
|
|
||||||
|
|
||||||
## Risco
|
|
||||||
|
|
||||||
Muito baixo. A mudanca corrige semantica operacional do CLI e tende a melhorar observabilidade sem alterar a logica de dispatch.
|
|
||||||
Loading…
x
Reference in New Issue
Block a user