[seguranca] feat(sync-docs): filtro de segurança PII/confidencial no reusável, global-first [TH-68789] #57

Merged
tarcisio merged 1 commit from fix/TH-68789-sync-docs-filtro-pii into develop 2026-10-08 12:20:46 -03:00
Owner

TH-68789 — Filtro de segurança (PII/confidencial) no sync docs → Hub

Tarefa de CI de segurança, global-first: o filtro passa a morar no reusável/canônico do ci-shared, não em cópia por produto.

Decisão de engenharia (como o reusável obtém script + anonimizador)

O reusável sync-docs-to-hub.yml clona o próprio ci-shared na MESMA ref do uses: do caller (padrão já usado em generate-docs.yml/release.yml) e roda os scripts canônicos contra o docs/ do produto via --repo-root ..

  • O script canônico scripts/docs/sync_docs_to_hub.py passa a viver aqui.
  • O anonimizador foi vendorizado em scripts/docs/anonimizar-docs.py (em vez de clonar o TMF/ORG em runtime). Motivo: ci-shared é público (clone sem token), enquanto o TMF/ORG é privado e exige o baile de token (ORG_LEITURA_TOKEN/Infisical). Vendorizar deixa o gate self-contained e robusto — um único clone público traz script + gate. (O verificar-docs-produto.yml ainda clona o ORG porque o verificador tem outras dependências do acervo; o anonimizador é autocontido e cabe no mirror canônico do runtime.)
  • Novo parâmetro --repo-root no script canônico reaponta ROOT/DOCS_DIR para o checkout do produto.
  • Todo step novo roda em shell: bash (pipefail), padrão do repo.

O que o filtro bloqueia (fail-closed)

  1. Anonimização (gate de processo): anonimizar-docs.py --check roda ANTES do sync; achado de PII (ou binário não escaneado) → exit != 0 → o job aborta e NADA vai ao Hub.
  2. Denylist por documento (dentro do script canônico):
    • caminho sob legal/ ou seguranca/ (qualquer nível de docs/);
    • front-matter audiencia: confidencial;
    • ausência de audiencia no front-matter (default = não enviar).

Teste de controle (negativo real)

tests/sync-docs-filtro/test_filtro_pii.py (12 testes). Prova que doc confidencial, doc sem audiencia, legal/ e seguranca/ são BARRADOS, e que o --check reprova com PII. O controle negativo roda uma cópia do canônico com o filtro neutralizado e prova que, sem o filtro, esses docs SERIAM enviados. Validado: ao remover o filtro de verdade do script, 3 testes positivos falham (controle real, não decorativo).

Follow-up (NÃO neste PR)

Substituir as cópias do script nos 7 produtos pela do ci-shared (a canônica passa a viver aqui e o reusável a executa): dani, fina, tmf9, priceshow, tmf-platform, infra, tmf-management. HelpCor fica FORA do Hub por decisão (HCOR-64764).

🤖 Generated with Claude Code

https://claude.ai/code/session_01HEu6RgsuRhe5fsePE3Dojv

## TH-68789 — Filtro de segurança (PII/confidencial) no sync docs → Hub Tarefa de CI de segurança, **global-first**: o filtro passa a morar no reusável/canônico do `ci-shared`, não em cópia por produto. ### Decisão de engenharia (como o reusável obtém script + anonimizador) O reusável `sync-docs-to-hub.yml` **clona o próprio `ci-shared`** na MESMA ref do `uses:` do caller (padrão já usado em `generate-docs.yml`/`release.yml`) e roda os scripts canônicos contra o `docs/` do produto via `--repo-root .`. - O **script canônico** `scripts/docs/sync_docs_to_hub.py` passa a viver aqui. - O **anonimizador foi vendorizado** em `scripts/docs/anonimizar-docs.py` (em vez de clonar o `TMF/ORG` em runtime). Motivo: `ci-shared` é **público** (clone sem token), enquanto o `TMF/ORG` é privado e exige o baile de token (`ORG_LEITURA_TOKEN`/Infisical). Vendorizar deixa o gate **self-contained e robusto** — um único clone público traz script + gate. (O `verificar-docs-produto.yml` ainda clona o ORG porque o verificador tem outras dependências do acervo; o anonimizador é autocontido e cabe no mirror canônico do runtime.) - Novo parâmetro `--repo-root` no script canônico reaponta `ROOT`/`DOCS_DIR` para o checkout do produto. - Todo step novo roda em `shell: bash` (pipefail), padrão do repo. ### O que o filtro bloqueia (fail-closed) 1. **Anonimização (gate de processo)**: `anonimizar-docs.py --check` roda ANTES do sync; achado de PII (ou binário não escaneado) → exit != 0 → **o job aborta e NADA vai ao Hub**. 2. **Denylist por documento** (dentro do script canônico): - caminho sob `legal/` ou `seguranca/` (qualquer nível de `docs/`); - front-matter `audiencia: confidencial`; - **ausência** de `audiencia` no front-matter (default = não enviar). ### Teste de controle (negativo real) `tests/sync-docs-filtro/test_filtro_pii.py` (12 testes). Prova que doc confidencial, doc sem `audiencia`, `legal/` e `seguranca/` são BARRADOS, e que o `--check` reprova com PII. O **controle negativo** roda uma cópia do canônico com o filtro neutralizado e prova que, sem o filtro, esses docs SERIAM enviados. Validado: ao remover o filtro de verdade do script, **3 testes positivos falham** (controle real, não decorativo). ### Follow-up (NÃO neste PR) Substituir as cópias do script nos **7 produtos** pela do `ci-shared` (a canônica passa a viver aqui e o reusável a executa): **dani, fina, tmf9, priceshow, tmf-platform, infra, tmf-management**. **HelpCor fica FORA do Hub** por decisão (HCOR-64764). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01HEu6RgsuRhe5fsePE3Dojv
feat(sync-docs): filtro de segurança PII/confidencial no reusável, global-first [TH-68789]
All checks were successful
PR checks (ci-shared) / testes (pull_request) Successful in 3m30s
a21a1759ef
O filtro de segurança do sync docs→Hub passa a morar no reusável/canônico do
ci-shared (não em cópia por produto). Script canônico
`scripts/docs/sync_docs_to_hub.py` + anonimizador vendorizado
`scripts/docs/anonimizar-docs.py` vivem aqui; o reusável clona o próprio
ci-shared (público, sem token) na mesma ref do caller e os roda contra o
`docs/` do produto via `--repo-root`.

Filtro fail-closed:
- denylist de caminho: `legal/` e `seguranca/` barrados;
- front-matter `audiencia: confidencial` barrado;
- ausência de `audiencia` barra (default = não enviar);
- anonimização `--check` antes do sync aborta tudo se houver PII.

Teste de controle negativo real em tests/sync-docs-filtro/ (falha se o filtro
for removido). Follow-up: substituir as cópias nos 7 produtos.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HEu6RgsuRhe5fsePE3Dojv
tmf-ia changed title from feat(sync-docs): filtro de segurança PII/confidencial no reusável, global-first [TH-68789] to [seguranca] feat(sync-docs): filtro de segurança PII/confidencial no reusável, global-first [TH-68789] 2026-10-08 11:57:16 -03:00
tarcisio merged commit 6abae397f2 into develop 2026-10-08 12:20:46 -03:00
tarcisio deleted branch fix/TH-68789-sync-docs-filtro-pii 2026-10-08 12:20:46 -03:00
Sign in to join this conversation.
No reviewers
No labels
seguranca
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
TMF/ci-shared!57
No description provided.