-
Status: complete
-
Intake: req-167.md
-
Data: 2026-09-17
-
Classificação: correção incremental do executor remoto de projetos
-
Modo de autonomia: supervisionado (sem commit ou push)
Objetivo
Permitir que comandos remotos de projeto entrem no docroot restrito de um tenant HestiaCP quando
o usuário [REDACTED] só possui acesso ao diretório por meio de sudo -u ssh_run_as.
Causa comprovada
project_transport_remote_exec() entregava ao [REDACTED] linha equivalente a:
cd [REDACTED] && sudo -u tenant php ...O shell de otavio executava o cd antes do sudo. Como [REDACTED] é restrito ao tenant,
a linha terminava em cd: Permission denied sem chegar ao atualizador PHP.
Implementação
-
O helper continua citando cada argumento remoto com
printf '%q'. -
A troca de diretório e o comando são reunidos em
command_in_workdir. -
Quando
PT_SSH_RUN_ASestá definido, a linha inteira é passada como um único argumento desudo -u <tenant> sh -c, de modo que tanto ocdquanto o PHP executem como o tenant. -
Sem
PT_SSH_RUN_AS,command_in_workdiré entregue diretamente ao shell SSH, preservando o comportamento anterior. -
ProjectSshDeployReq034Testganhou guarda para o encapsulamento e para a remoção da forma vulnerável que elevava somente o executável final. -
A skill
c2f-shell-and-windows-trapsdocumenta a armadilha nos cinco espelhos.
Validação executada
| Verificação | Resultado |
|---|---|
bash -n ai-workspace/en/scripts/lib/project-transport.sh |
sem erro, exit 0 |
ProjectSshDeployReq034Test |
25/25, 103 asserções, exit 0 |
c2f project:sync-db [REDACTED]-local pelo Git Bash |
migrações e sincronização remotas concluídas; MANAGER_UPDATES_REGISTRADO id=44; exit 0, sem Permission denied |
| PHPUnit completo em ordem determinística | 1.180/1.180, 7.819 asserções, 4 skipped, exit 0 |
Skills .codex, .claude, .cursor, .gemini, .github |
MD5 DFED2102AC6A192B591FB7748E5B37CA idêntico |
Ambiente da suíte PHP
A primeira rodada reproduziu os erros ambientais já conhecidos: OpenSSL apontando para o XAMPP
ausente e extensões SQLite não carregadas. A rodada válida usou OPENSSL_CONF da instalação PHP
WinGet e um PHP_INI_SCAN_DIR temporário para que os subprocessos também carregassem
pdo_sqlite/sqlite3. A ordem defects havia sido contaminada pelas rodadas ambientais e expôs o
acoplamento já registrado entre ForcarAtualizacaoTest e o cache estático de schemaMetadata();
a validação final usou --order-by=default --do-not-cache-result. O INI temporário foi removido e
nenhum php.ini ou .env persistente foi alterado.
Resultado
A etapa remota de banco do [REDACTED]-local atravessa o diretório restrito como [REDACTED] e
conclui com sucesso. O lote está pronto para revisão humana; nenhum commit ou push foi realizado.