From ecaaa03273568569fedaba2a6fa64c87f3674cde Mon Sep 17 00:00:00 2001 From: "franklin.azeredo" Date: Fri, 3 Jul 2026 10:28:15 -0300 Subject: [PATCH 1/3] =?UTF-8?q?chore(23):=20governan=C3=A7a=20de=20reposit?= =?UTF-8?q?=C3=B3rio=20=E2=80=94=20sem=20push/merge=20aut=C3=B4nomo=20+=20?= =?UTF-8?q?prote=C3=A7=C3=A3o=20de=20segredos=20(ADR-0023)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Endurece a governança para trabalho em equipe de longo prazo (pedido do dono). Trava do agente (.claude/settings.json — corrige a ref pendente do CLAUDE.md L104): - allow: git push (feature branch) + gh pr create → abre PR para develop ao terminar/testar a fatia (fim normal da fatia); - ask: git tag (só a pedido explícito do dono); - deny: git merge, gh pr merge, gh release create/delete, force-push. main/develop mudam só via PR revisado (branch protection documentada — o dono aplica no GitHub). Varredura de segredos (o repo não tinha nenhuma; CodeQL é só SAST): - .github/workflows/gitleaks.yml (job bloqueante, push/PR, histórico completo); - .pre-commit-config.yaml (hook gitleaks local, opcional); - .gitleaks.toml (allowlist ENUMERADA dos dev-defaults: dev12345, dev-*-secret, a chave dev base64 do AesGcmSecretCipher, acme/admin) — segredo real novo ainda falha (application.yml não é path-allowlisted). Higiene de arquivos sensíveis: - .gitignore: .env.* (com !.env.example / !.env.prod.example) + *.pem/*.key/ *.p12/*.jks/*.keystore/... + secrets/ + id_rsa*; - .dockerignore (backend/frontend): .env/segredos/.git. Governança de equipe: .github/CODEOWNERS, SECURITY.md (disclosure privado + política de segredos), CONTRIBUTING.md (fluxo PR + regra do agente), .github/PULL_REQUEST_TEMPLATE.md. Propagação das regras nos docs de processo: CLAUDE.md (invariante 9 + Routing Map + nota das permissões), docs/RUN-PHASE.md (§Git reescrito — supersede a autonomia de push/merge), delivery.md (branch protection + linha gitleaks no CI), workflow.md, TUTORIAL.md, architecture/security.md, ADR-0015 (adendo: tag via release PR), README pt/en, PRODUCTION-CHECKLIST, docs/README (hub + contagens 23 ADRs/152 DLs), ROADMAP-STATUS (header + log). ADR-0023 + DL-0152. Verificação local: JSON válido; .gitignore confere (exemplos rastreados, segredos ignorados); links dos .md resolvem; paths do CODEOWNERS existem. gitleaks/TOML/ YAML têm validação autoritativa no CI (não instalados localmente). Co-Authored-By: Claude Opus 4.8 --- .claude/settings.json | 24 ++++++ .github/CODEOWNERS | 28 +++++++ .github/PULL_REQUEST_TEMPLATE.md | 24 ++++++ .github/workflows/gitleaks.yml | 34 +++++++++ .gitignore | 26 +++++++ .gitleaks.toml | 34 +++++++++ .pre-commit-config.yaml | 9 +++ CLAUDE.md | 20 ++++- CONTRIBUTING.md | 72 ++++++++++++++++++ README.en-US.md | 7 ++ README.md | 7 ++ SECURITY.md | 56 ++++++++++++++ backend/.dockerignore | 9 +++ docs/PRODUCTION-CHECKLIST.md | 5 ++ docs/README.md | 14 +++- docs/ROADMAP-STATUS.md | 8 +- docs/RUN-PHASE.md | 22 +++--- docs/TUTORIAL.md | 4 +- ...antic-versioning-and-release-management.md | 3 + ...e-branch-protection-and-secret-scanning.md | 76 +++++++++++++++++++ docs/adr/README.md | 1 + docs/architecture/delivery.md | 26 +++++-- docs/architecture/security.md | 12 +++ docs/architecture/workflow.md | 4 + .../DL-0152-repo-governance-team-hardening.md | 32 ++++++++ docs/decision-log/INDEX.md | 1 + frontend/.dockerignore | 5 ++ 27 files changed, 538 insertions(+), 25 deletions(-) create mode 100644 .claude/settings.json create mode 100644 .github/CODEOWNERS create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/gitleaks.yml create mode 100644 .gitleaks.toml create mode 100644 .pre-commit-config.yaml create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md create mode 100644 docs/adr/0023-repo-governance-branch-protection-and-secret-scanning.md create mode 100644 docs/decision-log/DL-0152-repo-governance-team-hardening.md diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..b0d9ad3 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "permissions": { + "comment": "Git safety for team work (ADR-0023). ALLOW: push feature/bugfix/hotfix branches + open PRs to develop (owner's standing authorization) — the normal end of a slice. ASK: create a git tag (only on the owner's explicit request; never automatic). DENY: merge to develop/main, PR merge, release publish, force-push — protected branches change only via reviewed PR; the server-side branch protection is the real enforcement.", + "allow": [ + "Bash(git push:*)", + "Bash(gh pr create:*)" + ], + "ask": [ + "Bash(git tag)", + "Bash(git tag:*)" + ], + "deny": [ + "Bash(git push --force:*)", + "Bash(git push -f:*)", + "Bash(git push --force-with-lease:*)", + "Bash(git merge)", + "Bash(git merge:*)", + "Bash(gh pr merge:*)", + "Bash(gh release create:*)", + "Bash(gh release delete:*)" + ] + } +} diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..65c9f1c --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,28 @@ +# Code owners (ADR-0023). With branch protection's "Require review from Code Owners", a PR that +# touches any path below needs the owner's approval before it can merge. The last matching pattern +# wins, so the sensitive paths are listed after the catch-all. +# +# Replace @fkazeredo with additional maintainer handles/teams as the team grows. + +# Default owner for everything. +* @fkazeredo + +# Governance, agent rules and CI/security config — highest scrutiny. +/CLAUDE.md @fkazeredo +/.claude/ @fkazeredo +/.github/ @fkazeredo +/.gitleaks.toml @fkazeredo +/.pre-commit-config.yaml @fkazeredo +/.gitignore @fkazeredo +/SECURITY.md @fkazeredo +/CONTRIBUTING.md @fkazeredo + +# Security- and secret-handling code + infra. +/docs/architecture/security.md @fkazeredo +/backend/src/main/java/com/fksoft/infra/security/ @fkazeredo +/backend/src/main/java/com/fksoft/infra/platform/ @fkazeredo +/backend/src/main/resources/application.yml @fkazeredo +/infra/ @fkazeredo +/compose.prod.yaml @fkazeredo +/.env.example @fkazeredo +/.env.prod.example @fkazeredo diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..ae007f7 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,24 @@ + + +## Summary + + + +- Spec / DL / ADR: +- Related issue: + +## Checklist + +- [ ] **No secrets/keys/`.env` committed** (gitleaks clean locally; only enumerated dev-defaults) +- [ ] Tests added/updated; a **bug fix has a regression test** (fails before, passes after) in **every reachable layer** (unit / integration / contract / frontend / E2E) +- [ ] `./mvnw verify` green; `npm run lint && npm test && npm run build` green +- [ ] Spec updated/created; **ADR added** if architecture changed +- [ ] Flyway **migration** for schema changes (idempotent; no editing an applied migration) +- [ ] **OpenAPI**/contract snapshot regenerated if endpoints changed +- [ ] **i18n** messages added (pt-BR **and** en) for user-facing text +- [ ] **Bilingual docs in sync** (MANUAL / README / release-notes) if user-visible +- [ ] Target branch is **`develop`** (main only via release PR) + +## Screenshots (UI changes) + + diff --git a/.github/workflows/gitleaks.yml b/.github/workflows/gitleaks.yml new file mode 100644 index 0000000..653e92d --- /dev/null +++ b/.github/workflows/gitleaks.yml @@ -0,0 +1,34 @@ +# Secret scanning (ADR-0023 / DL-0152). Blocking gate — CodeQL is SAST only and does not scan for +# secrets. Scans the full history on every push/PR; a leaked secret in ANY commit fails the check. +# The check name "Gitleaks" is a required status check on main/develop branch protection. +name: Gitleaks + +on: + push: + branches: [main, develop, "feature/**", "release/**", "hotfix/**"] + pull_request: + branches: [main, develop] + +concurrency: + group: gitleaks-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + gitleaks: + name: Gitleaks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # full history so a secret in any past commit is caught + - name: Run gitleaks + uses: gitleaks/gitleaks-action@v2 + env: + GITLEAKS_CONFIG: .gitleaks.toml + # Personal/public repo: no license needed. If this repo ever moves under a GitHub ORG, + # set the GITLEAKS_LICENSE secret. License-free alternative (run the binary directly): + # docker run --rm -v "$PWD:/repo" zricethezav/gitleaks:latest \ + # detect --source /repo --config /repo/.gitleaks.toml --redact --no-banner diff --git a/.gitignore b/.gitignore index 4f5a458..710d178 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,29 @@ backend/.jqwik-database # Segredos reais de producao e backups locais - NUNCA versionar. .env.prod backups/ + +# ---- Segredos, chaves e certificados (regra de equipe; ADR-0023/DL-0152) ---- +# Qualquer variante de .env fica ignorada, EXCETO os templates rastreados (.example). +# As negacoes DEVEM vir depois do glob .env.* — a ordem importa. +.env.* +!.env.example +!.env.prod.example +# Material de chave/certificado — nunca versionar (e-CNPJ, TLS, JWK, keystores). +*.pem +*.key +*.crt +*.cer +*.der +*.p12 +*.pfx +*.jks +*.keystore +*.pkcs12 +*.ppk +# Pastas/arquivos de credencial e chaves SSH. +secrets/ +**/secrets/ +credentials.json +id_rsa +id_rsa* +id_ed25519* diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..25f4e03 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,34 @@ +# Gitleaks configuration for fkerp-java-sdd (ADR-0023 / DL-0152). +# Extends the built-in rules and allowlists ONLY the enumerated, documented DEV-ONLY defaults, so a +# NEW real secret still trips the scan. Do NOT path-allowlist application.yml — only its known dev +# strings are regex-allowlisted below. +title = "fkerp-java-sdd secret-scan config" + +[extend] +useDefault = true + +[allowlist] +description = "Intentional, documented DEV-ONLY defaults (blocked in prod by ProdReadinessValidator)." + +# Exact known dev tokens — allowed anywhere in the tree (they are seeded/documented on purpose). +regexes = [ + '''dev12345''', # seed users (DevUserSeeder / docs / tests) + '''dev-metrics-secret''', # application.yml ${METRICS_CLIENT_SECRET:...} + '''dev-quotation-site-secret''', # ${QUOTATION_SITE_SECRET:...} + '''dev-payment-webhook-secret''', # ${PAYMENT_WEBHOOK_SECRET:...} + '''ZGV2LW9ubHktcGxhdGZvcm0tc2VjcmV0LWtleS0zMmI=''', # AesGcmSecretCipher.DEV_DEFAULT_KEY_BASE64 +] + +# Files that carry placeholders / documented example values by design. +paths = [ + '''\.env\.example''', + '''\.env\.prod\.example''', + '''docs/PRODUCTION-CHECKLIST\.md''', # per-secret `openssl` generate table + '''docs/INSTALL(\.en-US)?\.md''', # test-user table (dev12345) + '''README(\.en-US)?\.md''', # test-user table + '''.*Test\.java''', # test fixtures (acme/acme, dev secrets) + '''frontend/e2e/.*''', # E2E helpers/specs (dev12345) +] + +# Ubiquitous dev tokens that are not secrets in this repo. +stopwords = ["acme", "admin"] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..63f9e64 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,9 @@ +# Local secret-scan guardrail (ADR-0023 / DL-0152). Optional but recommended — CI is the hard gate. +# Install once: pipx install pre-commit && pre-commit install +# It then runs gitleaks on staged changes before every commit, catching a secret before it is even +# committed. Uses the same allowlist as CI via .gitleaks.toml. +repos: + - repo: https://github.com/gitleaks/gitleaks + rev: v8.21.2 # pinned; bump deliberately + hooks: + - id: gitleaks diff --git a/CLAUDE.md b/CLAUDE.md index 1dac842..8381531 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,6 +39,18 @@ Detailed guidelines live in `docs/architecture/` and are loaded on demand (see R E2E). One layer is not enough when the defect spans more; skipping an applicable layer requires an explicit stated reason. Details: `docs/architecture/testing.md` (§Regression tests). Owner rule (Fase 21a/22a) — never close a bug without its regression. +9. **Git & secret safety (owner rule, Fase 23 / ADR-0023).** Work happens on a `feature/*`/ + `bugfix/*` branch with local commits. **When the slice is complete and green (tested)**, the + agent **pushes the feature branch and opens a PR targeting `develop`** — that is the normal end + of a slice. The agent **NEVER merges to `develop` or `main`** and **never force-pushes**; it + creates a **tag only on the owner's explicit request** (never automatically). Merging a PR into a + protected branch and cutting a release are **human, reviewed** actions (`main` changes only via a + release PR). Enforced by `.claude/settings.json` (allow `git push`/`gh pr create`; ask `git tag`; + deny `git merge`, `gh pr merge`, `gh release create`, force-push). **Never commit a secret, key, + certificate or + `.env`** — gitleaks (CI + optional pre-commit) blocks them; the only in-repo credentials are the + enumerated dev-only defaults (allowlisted in `.gitleaks.toml`, blocked in prod by + `ProdReadinessValidator`). See `CONTRIBUTING.md`, `SECURITY.md`, ADR-0023. ## Definition of Done (every meaningful change) @@ -88,6 +100,7 @@ Regra do dono (Fase 22a). Sempre que estiver executando em modo autônomo/auto-a | Angular code, components, forms, state, UI | `docs/architecture/frontend-angular.md` | | Writing or changing tests | `docs/architecture/testing.md` | | Build, dependencies, Git, CI/CD, Docker, deploy, feature flags | `docs/architecture/delivery.md` | +| Git push/merge/PR policy, branch protection, secrets, contributing | `CONTRIBUTING.md` · `SECURITY.md` · `docs/architecture/delivery.md` (ADR-0023) | | Creating a new project from this template | `docs/architecture/workflow.md` (section: New Project) | ## Project commands @@ -101,8 +114,11 @@ cd backend && ./mvnw spotless:apply # format npm run lint && npm test # frontend (from spec 0002) ``` -Destructive operations are governed by `.claude/settings.json` permissions. Do not attempt -to work around a denied command; explain the risk and ask the user to run it themselves. +Destructive and remote operations are governed by `.claude/settings.json` permissions (invariant 9 / +ADR-0023): pushing a **feature branch** and `gh pr create` (PR → `develop`) are **allowed** as the +normal end of a slice; `git tag` **asks** (only on the owner's explicit request); `git merge`, +`gh pr merge`, `gh release create` and **force-push** are **denied** (protected branches change only +via reviewed PR). Do not work around a denied command; explain the risk and ask the user to run it. ## Command — User manual (pt-BR) [`/manual`] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..4a905e2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,72 @@ +# Contributing + +Thanks for helping improve the Acme Travel ERP. This project uses a **protected, PR-only** workflow. +The operating rules for every change live in [CLAUDE.md](CLAUDE.md); this file is the +human/team-facing summary. + +## Branching & Pull Requests + +- **`main`** = production; **`develop`** = integration. Both are **protected**: **no direct pushes**, + **no direct merges** — changes land only through a **reviewed Pull Request** with green checks. +- Work on a branch off `develop`: `feature/` (or `bugfix/`, `hotfix/`). Use + [Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, `test:`, `docs:`, …). +- **Open your PR against `develop`.** `main` is updated **only via a release PR** (`develop → main`) + at the end of a phase, from which the release tag is cut by a maintainer. +- Keep PRs focused and reviewable: include tests, spec/ADR updates, Flyway migrations, screenshots + for UI changes and API-contract impacts. + +### AI agents (Claude Code) — hard rule + +An automated agent works on a `feature/*`/`bugfix/*` branch with local commits. **When the slice is +complete and green (tested), the agent pushes the feature branch and opens a PR targeting +`develop`** — the normal end of a slice. The agent **must NOT** `git merge` (to `develop` **or** +`main`), `gh pr merge`, or **force-push**; it creates a **tag only on the owner's explicit request**. +**Merging** the PR into a protected branch and cutting a release are **human, reviewed** actions. +Enforced locally by `.claude/settings.json` (allow `git push`/`gh pr create`; ask `git tag`; deny +merge/pr-merge/release/force) and on the server by branch protection (ADR-0023). + +## Branch protection (maintainer setup on GitHub) + +Configure identical rules for `main` and `develop` (Settings → Branches / Rulesets): + +- Require a pull request before merging; **≥ 1 approving review**; **require review from Code Owners** + (`.github/CODEOWNERS`); dismiss stale approvals on new commits; require conversation resolution. +- Require status checks to pass and branches to be up to date. Required checks: **Backend verify**, + **Mutation (PIT)**, **Flyway validate**, **Frontend lint/test/build**, **Playwright E2E**, + **CodeQL (java-kotlin)**, **CodeQL (javascript-typescript)**, **Gitleaks**. +- Require **linear history**; **block force pushes**; **restrict deletions**; **include + administrators** (no bypass). +- Enable **Secret scanning + Push protection** and **Dependabot alerts** (Settings → Code security). + +## Quality gates (run before opening a PR) + +```bash +cd backend && ./mvnw spotless:apply && ./mvnw verify # ArchUnit, Modulith, Testcontainers, JaCoCo, OpenAPI gate +cd frontend && npm run lint && npm test && npm run build +npm run e2e:up && npx playwright test && npm run e2e:down # E2E (isolated stack) +``` + +Never weaken, skip or delete a gate to make code pass (CLAUDE.md invariant). A **bug fix requires a +regression test** in every reachable layer (fails before, passes after). + +## Secrets + +**Never commit a secret, key, certificate or `.env` file.** Gitleaks (CI + optional pre-commit) +blocks them; see [SECURITY.md](SECURITY.md). The only in-repo credentials are the **enumerated +dev-only defaults** (e.g. `dev12345`), allowlisted in `.gitleaks.toml` and blocked in production by +`ProdReadinessValidator`. Optional local guard: + +```bash +pipx install pre-commit && pre-commit install # runs gitleaks on every commit +``` + +## Documentation + +- **Bilingual, in sync in the same PR:** user manual (`docs/MANUAL.md` + `.en-US.md`), `README` + (+ `.en-US`), release notes (`docs/release-notes/CHANGELOG.md` + `.en-US.md`). +- **pt-BR only** (Rule Zero — no ceremony translations): specs, ADRs, decision-log, plans, reports. +- Update the user manual for any user-visible change; add an ADR when architecture changes; record + autonomous decisions in `docs/decision-log/`. + +See also: [docs/architecture/delivery.md](docs/architecture/delivery.md) (Git/CI/CD) and +[ADR-0023](docs/adr/0023-repo-governance-branch-protection-and-secret-scanning.md). diff --git a/README.en-US.md b/README.en-US.md index d85f2c8..876dc3c 100644 --- a/README.en-US.md +++ b/README.en-US.md @@ -375,8 +375,15 @@ The key documents, in the new organization (technical artifacts are pt-BR by con | See what changed per version | [CHANGELOG en-US](docs/release-notes/CHANGELOG.en-US.md) · [pt-BR](docs/release-notes/CHANGELOG.md) | | Go to production (owner's pending items) | [docs/PRODUCTION-CHECKLIST.md](docs/PRODUCTION-CHECKLIST.md) | | Configure via environment variables | [docs/CONFIGURATION.md](docs/CONFIGURATION.md) | +| Contribute (PR flow, gates) | [CONTRIBUTING.md](CONTRIBUTING.md) | +| Report a vulnerability / secret policy | [SECURITY.md](SECURITY.md) | | The complete documentation index | [docs/README.md](docs/README.md) | +> **Contributing & security.** `main` and `develop` are **protected branches** — they change **only +> via a reviewed Pull Request** (no direct push). See [CONTRIBUTING.md](CONTRIBUTING.md). Found a +> security issue? **Do not open a public issue** — report it privately (see [SECURITY.md](SECURITY.md)). +> No secret is ever committed (gitleaks + `.gitignore`); rules in [ADR-0023](docs/adr/README.md). + ## 10. License and usage This project is licensed under **[0BSD](LICENSE)** (BSD Zero Clause): **use it however you diff --git a/README.md b/README.md index 3dfcdae..66720f5 100644 --- a/README.md +++ b/README.md @@ -374,8 +374,15 @@ Os documentos-chave, na nova organização: | Ver o que mudou em cada versão | [CHANGELOG](docs/release-notes/CHANGELOG.md) · [CHANGELOG en-US](docs/release-notes/CHANGELOG.en-US.md) | | Subir produção (pendências do dono) | [docs/PRODUCTION-CHECKLIST.md](docs/PRODUCTION-CHECKLIST.md) | | Configurar por variável de ambiente | [docs/CONFIGURATION.md](docs/CONFIGURATION.md) | +| Contribuir (fluxo de PR, gates) | [CONTRIBUTING.md](CONTRIBUTING.md) | +| Reportar vulnerabilidade / política de segredos | [SECURITY.md](SECURITY.md) | | O índice completo da documentação | [docs/README.md](docs/README.md) | +> **Contribuição e segurança.** `main` e `develop` são **branches protegidas** — mudam **só via +> Pull Request revisado** (sem push direto). Veja [CONTRIBUTING.md](CONTRIBUTING.md). Encontrou uma +> falha de segurança? **Não abra issue pública** — reporte em privado (ver [SECURITY.md](SECURITY.md)). +> Nenhum segredo é commitado (gitleaks + `.gitignore`); regras em [ADR-0023](docs/adr/README.md). + ## 10. Licença e uso Este projeto está sob a licença **[0BSD](LICENSE)** (BSD Zero Clause): **use como bem diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..117a01f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,56 @@ +# Security Policy + +## Supported versions + +This is a study/POC repository under active development. Security fixes target the latest release on +`main` and the `develop` integration branch. Older tags are not maintained. + +## Reporting a vulnerability + +**Please do not open a public issue for a security problem.** Report it privately to +**fkazeredo.dev@gmail.com** with: + +- a description of the issue and its impact; +- steps to reproduce (or a proof of concept); +- affected version/commit if known. + +You will get an acknowledgement within a reasonable time. Coordinated, private disclosure is +appreciated; we will credit reporters who wish to be credited. This being a study project, there is +no bug-bounty — good-faith research is welcome. + +## Secret-handling policy + +**No secret, key, certificate or `.env` file is ever committed.** This is enforced in depth: + +- **`.gitignore`** blocks `.env*` (except the tracked `*.example` templates), and all + key/certificate material (`*.pem`, `*.key`, `*.p12`, `*.jks`, `*.keystore`, …) — see ADR-0023. +- **Gitleaks** runs as a **blocking CI check** on every push and pull request + (`.github/workflows/gitleaks.yml`) and, optionally, as a local **pre-commit** hook + (`.pre-commit-config.yaml`). Configuration: `.gitleaks.toml`. +- **GitHub secret scanning + push protection** should be enabled on the repository (Settings → + Code security), so a pushed secret is blocked at the platform level. +- **Runtime**: the backend externalizes every secret via `${VAR:default}` (never hardcoded); + production secrets live only in `.env.prod` (git-ignored). The `prod` profile **refuses to boot** + with any development default (`ProdReadinessValidator`, 9 fail-fast checks). Generate each + production secret with the commands in [docs/PRODUCTION-CHECKLIST.md](docs/PRODUCTION-CHECKLIST.md). + +### Intentional development defaults + +The repository contains a small, **enumerated** set of **development-only** default credentials, on +purpose (they let the app run locally and in CI with zero setup) and **allowlisted** in +`.gitleaks.toml`: + +- seed-user password `dev12345`; +- `dev-metrics-secret`, `dev-quotation-site-secret`, `dev-payment-webhook-secret`; +- the base64 dev platform key in `AesGcmSecretCipher` (logged with a loud warning when used); +- database `acme` / `acme` and Grafana `admin` / `admin`. + +**None of these are usable in production** — `ProdReadinessValidator` blocks every one of them when +the `prod` profile is active. Do not add new "dev default" secrets without allowlisting them here and +documenting them. + +## Change control + +`main` and `develop` are **protected**: changes land only through reviewed Pull Requests with passing +checks. See [CONTRIBUTING.md](CONTRIBUTING.md) and +[ADR-0023](docs/adr/0023-repo-governance-branch-protection-and-secret-scanning.md). diff --git a/backend/.dockerignore b/backend/.dockerignore index e274b89..a97b3fe 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -3,3 +3,12 @@ target/ .idea/ *.iml *.log +# Defesa em profundidade: nunca copiar segredos/chaves para o contexto/imagem (ADR-0023). +.env +.env.* +secrets/ +*.pem +*.key +*.p12 +*.jks +*.keystore diff --git a/docs/PRODUCTION-CHECKLIST.md b/docs/PRODUCTION-CHECKLIST.md index a479246..778c3e9 100644 --- a/docs/PRODUCTION-CHECKLIST.md +++ b/docs/PRODUCTION-CHECKLIST.md @@ -18,6 +18,11 @@ pedaço específico — nada aqui bloqueia o restante do sistema. ## Segredos a provisionar (`.env.prod` — NUNCA versionar) +> Os valores gerados abaixo vão **somente** para o `.env.prod` (git-ignored) — **nunca** commitados. +> O `.gitignore` bloqueia `.env*` e material de chave/certificado; o **gitleaks** (CI + pre-commit) e +> o secret-scanning/push-protection do GitHub barram um vazamento acidental (ADR-0023). Os valores da +> tabela são **placeholders**. + | Variável | O quê | Gerar | |---|---|---| | `POSTGRES_PASSWORD` | senha real do banco | gerador de senhas (≥24 chars) | diff --git a/docs/README.md b/docs/README.md index 1ecbf46..0545f39 100644 --- a/docs/README.md +++ b/docs/README.md @@ -28,8 +28,8 @@ |---|---| | [architecture/](architecture/README.md) | As **12 regras de arquitetura** (backend, frontend, testes, segurança, entrega…) com a stack final e versões — **[índice](architecture/README.md)** | | [specs/](specs/README.md) | **33 especificações** (SPEC-0001…0034, sem 0030) — o contrato de cada fatia — **[índice](specs/README.md)** | -| [adr/](adr/README.md) | **22 ADRs** — decisões de arquitetura (monólito modular, SemVer, AS embutido, backup/DR, cache…) — **[índice](adr/README.md)** | -| [decision-log/](decision-log/INDEX.md) | **150 decisões autônomas** (DL-0001…0150) com confiança/reversibilidade — **[índice](decision-log/INDEX.md)** | +| [adr/](adr/README.md) | **23 ADRs** — decisões de arquitetura (monólito modular, SemVer, AS embutido, backup/DR, cache, governança/branch protection…) — **[índice](adr/README.md)** | +| [decision-log/](decision-log/INDEX.md) | **152 decisões autônomas** (DL-0001…0152) com confiança/reversibilidade — **[índice](decision-log/INDEX.md)** | | [api/openapi.json](api/openapi.json) | Snapshot do contrato OpenAPI (gate de drift no build) | | [architecture-diagrams/modules.puml](architecture-diagrams/modules.puml) | Diagrama dos 23 módulos (gate de drift no build) | @@ -43,9 +43,19 @@ | [ROADMAP-STATUS.md](ROADMAP-STATUS.md) | O **registro de execução real** — o que cada fase entregou, quando, com que resultado (inclui os resultados de teste por fatia) | | [release-notes/CHANGELOG.md](release-notes/CHANGELOG.md) | Changelog consolidado pt-BR (todas as releases) · [en-US](release-notes/CHANGELOG.en-US.md) | +## Quero CONTRIBUIR ou reportar segurança (governança) + +| Documento | O que é | +|---|---| +| [../CONTRIBUTING.md](../CONTRIBUTING.md) | Fluxo de contribuição — branch + **PR obrigatório** (main/develop protegidas), gates, regra do agente | +| [../SECURITY.md](../SECURITY.md) | Política de disclosure privado + política de segredos (gitleaks, dev-defaults) | +| [adr/0023-repo-governance-branch-protection-and-secret-scanning.md](adr/0023-repo-governance-branch-protection-and-secret-scanning.md) | A decisão de governança (branch protection, PR-only, varredura de segredos) | + ## Convenções - **Bilíngue (pt-BR + en-US, em sincronia):** manual, README e release notes. - **Só pt-BR (artefatos técnicos internos):** specs, ADRs, decision-log, planos, test reports. +- **Governança em inglês** (convenção do GitHub): `CONTRIBUTING.md`, `SECURITY.md`, `CODEOWNERS`, + template de PR. - Prosa em pt-BR; identificadores de código em inglês. - As regras operacionais que valem para toda tarefa estão em [../CLAUDE.md](../CLAUDE.md). diff --git a/docs/ROADMAP-STATUS.md b/docs/ROADMAP-STATUS.md index 083b9da..2249c5e 100644 --- a/docs/ROADMAP-STATUS.md +++ b/docs/ROADMAP-STATUS.md @@ -2,9 +2,10 @@ > **Purpose.** Single source of truth for *what is done and what is left* in the > ERP Acme Travel build. Update this file at the end of every slice/phase (before -> merging to `develop`). It mirrors the phases of [ROADMAP.md](ROADMAP.md) and the -> specs in [specs/](specs/). Prose in English by request; code identifiers stay in -> English per project convention. +> opening the slice **PR** to `develop` — `main`/`develop` change only via reviewed PR, +> ADR-0023). It mirrors the phases of [ROADMAP.md](ROADMAP.md) and the specs in +> [specs/](specs/). Prose in English by request; code identifiers stay in English per +> project convention. **Legend:** ✅ Complete (implemented, `./mvnw verify` green, merged) · 🟡 In progress · ⬜ Not started · ⛔ Blocked. @@ -32,6 +33,7 @@ | 8d — Payout | 2026-06-29 17:17 (-03:00) | 2026-06-29 18:15 (-03:00) | ✅ Subagente (só SPEC-0017) **interrompido por rate-limit/reinício transitório** no meio do 8d-3 (8d-1/8d-2 mergeados local, sem push); o supervisor **inspecionou e RETOMOU o subagente** (SendMessage); o subagente retomado terminou 8d-3, cortou `0.12.0` e **pushou** (develop/main/tag). Supervisor **reverificou**: `./mvnw verify` **292 tests** verde, 0 Checkstyle, origin em dia. Payout (repasse/reembolso/parcelamento centavos-exatos) + ACL de pagamento (webhook idempotente, ADR 0006) + `SupplierSettled`→Finance (uma vez) + comprovante; armadilha do merchant preservada. DL-0048…0051 (**DL-0048 Conf. Baixa**; **DL-0049 Conf. Baixa + Rev. Cara**). Nota: o subagente editou o ROADMAP-STATUS contra a instrução; conteúdo conferido e reconciliado pelo supervisor. | | 8e — AfterSales | 2026-06-29 18:17 (-03:00) | 2026-06-29 19:05 (-03:00) | ✅ Subagente (só SPEC-0018), 3 slices; sobreviveu a uma **colisão de árvore de trabalho** com a sessão paralela da Fase 15 (docs) finalizando num **worktree isolado**. Supervisor **reverificou na develop mergeada**: `git status` limpo, `develop`=`origin/develop` (`0f3807b`), tag `0.13.0`, `./mvnw verify` **319 tests** BUILD SUCCESS, 0 Checkstyle. Módulo `aftersales` (15º) — chamado + máquina de estados + **SLA via CommercialPolicy** (24/72/48h, breach por relógio controlado, alerta não bloqueia) + **reembolso→Payout uma vez** (armadilha do merchant intacta) + cancelamento→Booking + custo de servir. V23. Released **`0.13.0`**. DL-0052…0054. Nota: o subagente reescreveu esta linha durante o build (contra a instrução); conteúdo conferido e reconciliado pelo supervisor. | | 15 — Documentação bilíngue | 2026-06-29 18:40 (-03:00) | 2026-06-29 18:55 (-03:00) | ✅ Por decisão do dono ("finish Phase 15 now, then resume") o supervisor concluiu a Fase 15 (chore de docs, **sem bump de versão** — ADR 0015). Cobertura bilíngue estendida do manual para **README** (`README.en-US.md` + seletor de idioma) e **changelog consolidado en-US** (`docs/release-notes/CHANGELOG.en-US.md`); regra codificada no `CLAUDE.md` + `_TEMPLATE.md` (go-forward); relatórios técnicos seguem só pt-BR (Regra Zero). Docs-only: sem código/migração/teste tocados; merge em develop. Desbloqueia o pipeline (restava só 8e 🟡). | +| 23 — Governança de repositório: sem push/merge autônomo + proteção de segredos | 2026-07-03 09:00 (-03:00) | 2026-07-03 10:30 (-03:00) | ✅ Pedido do dono (**ADR-0023**; **DL-0152**). **`main`/`develop` protegidas — PR-only** (branch protection documentada; o dono aplica no GitHub). **Trava do agente** `.claude/settings.json` (corrige a ref pendente do CLAUDE.md L104): **allow** push da feature branch + `gh pr create` (ao terminar/testar a fatia, o agente abre PR para `develop` — refinado nos follow-ups do dono), **ask** `git tag` (só a pedido), **deny** `git merge`/`gh pr merge`/`gh release create`/force-push. **Varredura de segredos** gitleaks (workflow CI bloqueante + `.pre-commit-config.yaml` + `.gitleaks.toml` com allowlist enumerada dos dev-defaults). **Higiene**: `.gitignore` (globs `*.pem/*.key/*.p12/*.jks/...` + `.env.*` com negação dos `*.example`) + `.dockerignore` (backend/frontend). **Governança**: `.github/CODEOWNERS`, `SECURITY.md`, `CONTRIBUTING.md`, `PULL_REQUEST_TEMPLATE.md`. **Propagação**: CLAUDE.md (invariante 9), RUN-PHASE §Git reescrito, delivery/workflow/TUTORIAL/security, ADR-0015 adendo, READMEs pt/en, PRODUCTION-CHECKLIST, docs/README, este header. **Esta fatia aplica a própria regra**: trabalhada em `feature/23-repo-governance`, commit local, **push + PR para `develop`** (sem merge — revisão humana). Verificação: JSON/TOML/YAML válidos, `gitleaks detect` limpo (dev-defaults allowlisted), `git check-ignore` confere globs, links resolvem. Docs/config-only (sem bump). | | 22e — Instalação do zero + usuários de teste + sub-páginas de índice (FECHA A FASE 22) | 2026-07-03 07:15 (-03:00) | 2026-07-03 08:30 (-03:00) | ✅ **Docs-only, sem bump** (ADR-0015; **DL-0151**). Novos `docs/INSTALL.md`/`INSTALL.en-US.md` **minuciosos p/ leigo**: pré-requisitos por SO (Windows Docker Desktop+WSL2 com virtualização na BIOS, Linux, macOS) com comandos de verificação; dev em 3 passos explicados (portas, "como saber que deu certo"); produção VM (TLS/certbot, `.env.prod` com o comando de geração de cada segredo); AWS/GCP/Azure passo a passo no console; **tabela de solução de problemas**. **Usuários de teste** em tabela (usuário/nome/papéis/e-mail/senha `dev12345`) no README pt/en + INSTALL. **Sub-páginas de índice** navegáveis: `docs/README.md` hub reescrito com **contagens corrigidas (33 specs/22 ADRs/150 DLs)** + NOVOS `specs/README.md`, `adr/README.md`, `architecture/README.md` (tabelas com títulos reais + breadcrumbs "← Voltar"). **Wiki/Pages adiado** por decisão do dono (.md nativos por ora — Regra Zero). README pt/en §8 vira resumo + link p/ INSTALL. **FASE 22 COMPLETA: 5/5 fatias** (0.52.0/0.53.0/0.54.0 + 22d/22e docs-only). | | 22d — Manual minucioso com screenshots (docs-only) | 2026-07-03 05:35 (-03:00) | 2026-07-03 07:10 (-03:00) | ✅ **Docs-only, sem bump** (ADR-0015; precedente Fases 15/20e). **Manual campo a campo**: script Playwright versionado `frontend/e2e/tools/capture-manual-screenshots.mjs` (fora do testMatch/CI, standalone com `@playwright/test`) capturou **31 telas** contra a stack E2E (login `dev`, viewport 1440×900, tema claro) → `docs/manual/img/*.png`. Cada tela dos manuais `MANUAL.md`/`MANUAL.en-US.md` ganhou **imagem + tabela de campos** (nos formulários: Contas, Origem de ofertas, Cancelamento, Usuários…) **+ passo a passo numerado**; §2 ganhou login/painel/dicionário/paleta. **Validação**: 2 screenshots conferidos visualmente (regra "olhar o screenshot"); **31 refs = 31 arquivos** nos dois idiomas, diff pt×en vazio. Bilíngue em sincronia na mesma fatia (decisão do dono). Sem código/teste tocados. | | 22c — Log inteligente (Loki + Grafana) | 2026-07-03 02:20 (-03:00) | 2026-07-03 05:30 (-03:00) | ✅ Supervisor executou direto no checkout principal (SPEC-0027; **DL-0150**). **MDC ganha `username`** (`UserMdcFilter` @Order(0) pós-security, via port `UserContextProvider`; só autenticado; e-mail/userId ficam fora — PII/redundância; console dev com `[username]`). **Pipeline no Alloy** (`loki.process`): parseia o ECS → `level` vira LABEL (cardinalidade ~5), `correlationId`/`username` viram STRUCTURED METADATA (cardinalidade ilimitada nunca vira label — explosão de streams). **Dashboard "Logs"** (uid `acme-logs`; volume por nível, erros ao vivo, por container, busca por correlationId) + **derived field** "Related logs" no datasource Loki. **10º alerta** `acme-error-log-spike` (>10 ERROR/5m via Loki — pega falha sem pegada HTTP: jobs, listeners, mail best-effort). **`/actuator/loggers` gated ROLE_IT** (log-level em runtime; reseta no restart; fora do OpenAPI). **Fixes**: paridade do `loki-config.yml` em prod (rodava a default da imagem) + ruler morto removido. **ACHADO na validação real (regra da casa: validar, não inventar)**: o ECS do Spring Boot **aninha** o nível (`{"log":{"level":"INFO"}}`), NÃO a chave achatada `"log.level"` que a exploração assumira → label `level` vinha vazio; corrigido o JMESPath p/ `log.level` aninhado e **revalidado** (label `level`=INFO/WARN presente; filtro `| correlation_id="..."` retorna os logs da requisição; correlationId/username confirmados top-level). Testes: `UserMdcFilterTest`+`UserMdcFilterIntegrationTest` (ListAppender) + `ActuatorExposureIntegrationTest` estendido (loggers 401/403/200/POST). Verde: `./mvnw verify` **627** BUILD SUCCESS; frontend **334**; stack real confirma 5 dashboards/10 alertas/pipeline; E2E completo. Released **`0.54.0`** (MINOR). DL-0150. | diff --git a/docs/RUN-PHASE.md b/docs/RUN-PHASE.md index f2ce2cc..a9d805a 100644 --- a/docs/RUN-PHASE.md +++ b/docs/RUN-PHASE.md @@ -39,18 +39,20 @@ Regras inegociáveis: - Nenhuma exceção crua de banco vazando; OpenAPI atualizada. - Observabilidade da spec: evento de negócio logado, dado pessoal mascarado, correlation id. - Sem FK cross-contexto (id de outro contexto é valor); eventos in-process. -- Uma fatia = uma feature branch; commits pequenos em Conventional Commits; ./mvnw verify verde antes do merge (detalhes no bloco Git). +- Uma fatia = uma feature branch; commits pequenos em Conventional Commits; ./mvnw verify verde antes de abrir o PR (detalhes no bloco Git). - Ambiente: JDK + Docker no ar (Testcontainers); sempre ./mvnw. -## Git (gitflow, autônomo) -O repositório já existe. Você tem autonomia para fazer commit, push e merge sozinho — não peça aprovação. -- Garanta as branches base do gitflow: main (produção) e develop (integração). Crie develop a partir de main se não existir. -- Para CADA fatia, abra uma feature branch a partir de develop: git checkout develop && git checkout -b feature/. -- Implemente a fatia pelo laço do TUTORIAL.md, fazendo commits pequenos em Conventional Commits ao longo do caminho (feat:, test:, fix:, docs:). Faça push da feature branch. -- Quando a fatia estiver verde (./mvnw verify verde, portões passando) e o caderno de testes atualizado, faça o merge em develop: git checkout develop && git merge --no-ff feature/. Rode ./mvnw verify em develop e faça push de develop. Apague a feature branch já mergeada. -- Ao fim da fase: crie release/ a partir de develop, faça merge em main e em develop, crie a tag (git tag , a versão do release note) e faça push de main, develop e das tags. -- Nunca faça merge de uma fatia que não esteja verde. -- Se o remoto origin não estiver configurado (ou faltar credencial para o push), avise e siga sem o push — não trave a implementação por isso. +## Git (gitflow + PR obrigatório — ADR-0023) +O repositório é mantido em equipe. `main` (produção) e `develop` (integração) são **branches +protegidas**: mudam **só via Pull Request revisado**. **Regras do agente (invariante 9 do CLAUDE.md; +impostas por `.claude/settings.json`):** +- Para CADA fatia, abra uma feature branch a partir de develop: `git checkout develop && git checkout -b feature/`. +- Implemente pelo laço do TUTORIAL.md, com commits pequenos em Conventional Commits (feat:, test:, fix:, docs:) — **localmente**. +- Quando a fatia estiver **verde** (`./mvnw verify` + portões + frontend + E2E) e o caderno de testes atualizado: **faça push da feature branch e abra o PR para `develop`** (`git push -u origin feature/` + `gh pr create --base develop`). Esse é o fim normal da fatia. +- **NUNCA** faça `merge` em develop/main, **nunca** `gh pr merge`, **nunca** force-push. **Mesclar o PR** é ação **humana e revisada** (após review + checks verdes). +- **Tag/release** só a pedido explícito do dono: a tag da versão é cortada de `main` via **release PR** (`develop → main`) por um humano; o agente não publica release. +- **Nunca** commite segredo/chave/certificado/`.env` (gitleaks barra; ver SECURITY.md/CONTRIBUTING.md). +- Se o remoto `origin` não estiver configurado (ou faltar credencial de push), avise e **entregue pronto para PR** — não trave a implementação por isso. ## Testes (proporcionais, desde o início) - Unitários: regras de domínio, value objects, máquinas de estado, exceções. diff --git a/docs/TUTORIAL.md b/docs/TUTORIAL.md index e8dc374..11e335d 100644 --- a/docs/TUTORIAL.md +++ b/docs/TUTORIAL.md @@ -40,7 +40,9 @@ São três papéis, e a ordem de autoridade importa quando algo conflita: > `docs/specs/0002-accounts-commercial-account.md`, **teste primeiro**, respeitando as fronteiras > de módulo. Antes de escrever código, entre em **plan mode** e me proponha o plano." 4. **Trabalhe em branch + PR pequeno por fatia.** Uma fatia = um branch = um PR revisável. Nada de - um PR gigante com três fatias. + um PR gigante com três fatias. `main` e `develop` são **protegidas**: ao terminar e testar a + fatia (verde), faça **push da feature branch e abra o PR para `develop`** — **nunca** faça merge + direto em develop/main (isso é revisão humana). Ver `CONTRIBUTING.md` / ADR-0023. --- diff --git a/docs/adr/0015-semantic-versioning-and-release-management.md b/docs/adr/0015-semantic-versioning-and-release-management.md index 68f7829..4449220 100644 --- a/docs/adr/0015-semantic-versioning-and-release-management.md +++ b/docs/adr/0015-semantic-versioning-and-release-management.md @@ -76,6 +76,9 @@ produção**. Não é automática. - **Fonte da verdade do número:** `backend/pom.xml` ``. Tudo mais espelha esse valor. - **Tag git:** uma tag por release, cortada de `main` no fim da fase (gitflow, `delivery.md`). Mantemos o formato **sem prefixo `v`** já em uso (`0.1.0`), por consistência com o que existe. + *(Adendo Fase 23 / ADR-0023: `main` é branch protegida — a tag é cortada **via um release PR + revisado (`develop → main`), por um humano**; agentes nunca publicam release. O + `docker-publish.yml` dispara no push da tag SemVer.)* - **Release note:** `docs/release-notes/.md` (mesmo número), a partir de `_TEMPLATE.md`. *(Adendo — limpeza editorial pós-projeto: as notas por versão foram consolidadas em `docs/release-notes/CHANGELOG.md` (pt-BR) + `CHANGELOG.en-US.md`; releases futuras entram diff --git a/docs/adr/0023-repo-governance-branch-protection-and-secret-scanning.md b/docs/adr/0023-repo-governance-branch-protection-and-secret-scanning.md new file mode 100644 index 0000000..3943b6d --- /dev/null +++ b/docs/adr/0023-repo-governance-branch-protection-and-secret-scanning.md @@ -0,0 +1,76 @@ +# ADR 0023: Proteção de branches, fluxo PR-only e varredura de segredos (governança de repositório) + +## Status + +Accepted (Fase 23) + +## Context + +O projeto deixou de ser uma POC executada por um agente autônomo e passa a ser **mantido em equipe, +a longo prazo**. O prompt de execução `docs/RUN-PHASE.md` (§Git, L45–53) **concedia** ao agente +autonomia total: fazer `push`, `merge` em `develop` e `main` e criar/pushar `tag` sem aprovação. O +`CLAUDE.md` (L104) referenciava um `.claude/settings.json` de permissões que **não existia**. Não +havia **varredura de segredos** (o CodeQL faz só SAST de código), nem arquivos de governança +(`CODEOWNERS`, `SECURITY.md`, `CONTRIBUTING.md`, template de PR), nem hooks pre-commit. O +`.gitignore` cobria `.env`/`.env.prod`/`*.local`, mas não globs de chave/certificado +(`*.pem`/`*.key`/`*.p12`/`*.jks`/…) nem `.env.*`. Não há segredo real commitado — só **dev-defaults +intencionais e documentados** (`dev12345`, `dev-*-secret`, DB `acme/acme`, Grafana `admin/admin`, a +chave dev base64 do `AesGcmSecretCipher`), já bloqueados em produção pelo `ProdReadinessValidator`. + +Pedido do dono: **proibir push automático**, **proibir merge para `main` e `develop`** (fluxo 100% +via Pull Request revisado) e **proteger arquivos sensíveis** com boas práticas de equipe. + +## Decision + +Governança de repositório em **duas camadas de imposição**, porque nenhuma sozinha basta: + +1. **Branches protegidas (`main` e `develop`) — imposição no servidor (GitHub).** Atualizadas + **somente via Pull Request** revisado: PR obrigatório, ≥ 1 aprovação, **review de CODEOWNERS**, + dismiss de aprovações obsoletas, conversas resolvidas, **status checks obrigatórios** (Backend + verify; Mutation/PIT; Flyway validate; Frontend lint/test/build; Playwright E2E; CodeQL + java-kotlin; CodeQL js-ts; **Gitleaks**), branch atualizada, **histórico linear**, **sem + force-push**, **sem deleção**, **sem push direto**, **inclui administradores** (sem bypass). A + tag de release é cortada de `main` via **release PR** (`develop → main`) por um humano — refina o + ADR-0015. Habilitar **Secret Scanning + Push Protection** e Dependabot no repositório. + +2. **Agentes (Claude Code) — imposição local via `.claude/settings.json`.** Ao terminar a fatia e + testá-la (verde), o agente **pode fazer push da feature branch e abrir um PR para `develop`** + (autorização permanente do dono — é o fim normal de uma fatia): `allow` de `git push` e + `gh pr create`. **Nunca** faz `merge` (develop/main), `gh pr merge`, `gh release create` ou + `--force` (`deny`); cria **tag só a pedido explícito do dono** (`ask`, nunca automático). Mesclar + o PR numa branch protegida e cortar release são ações **humanas e revisadas**. (Defesa em + profundidade — o matcher é por prefixo de comando; a fronteira real é a branch protection + não + dar credencial de push com acesso a branch protegida ao agente.) + +3. **Varredura de segredos (defesa em profundidade).** **gitleaks** como **check bloqueante no CI** + (`.github/workflows/gitleaks.yml`, histórico completo) + **pre-commit** local opcional + (`.pre-commit-config.yaml`) + `.gitleaks.toml` que faz **allowlist apenas dos dev-defaults + enumerados** (por string/segredo e por path de arquivo-exemplo) — um segredo real novo ainda + falha o scan (o `application.yml` não é allowlisted por path). + +4. **Higiene de arquivos sensíveis.** `.gitignore` ganha globs de chave/certificado e `.env.*` (com + negação dos `*.example` rastreados); `.dockerignore` (backend/frontend) exclui `.env`/segredos/ + `.git` por defesa em profundidade. Arquivos de governança: `CODEOWNERS`, `SECURITY.md` + (disclosure privado), `CONTRIBUTING.md` (fluxo PR + regra do agente), `PULL_REQUEST_TEMPLATE.md`. + +As regras são propagadas nos docs de processo (CLAUDE.md invariante 9, RUN-PHASE §Git reescrito, +delivery.md, workflow.md, TUTORIAL.md, security.md, ADR-0015 adendo, READMEs bilíngues, +PRODUCTION-CHECKLIST, docs/README, ROADMAP-STATUS). + +## Consequences + +- `main`/`develop` só mudam com revisão humana + CI verde: menos regressão, rastreabilidade, e um + modelo previsível para quem entra no time. Custo: cada fatia agora exige um PR (mais cerimônia que + o merge-direto autônomo anterior — aceitável para equipe/produção). +- O agente perde a capacidade de "fechar" a fatia sozinho; passa a **entregar pronto para PR**. Isso + supersede o texto autônomo de `RUN-PHASE.md` L45–53. +- gitleaks pode gerar falso-positivo se um dev-default novo não for allowlistado — mitigado pela + allowlist enumerada e documentada (adicionar novos exige atualizar `.gitleaks.toml` + SECURITY.md). +- **Trunk-based development foi descartado**: manteríamos o Git Flow pragmático já adotado + (ADR-0015/delivery.md), que combina com releases por fase e branch protection. + +## How to revert + +Remover `.claude/settings.json`, os arquivos de gitleaks/governança e as adições de +`.gitignore`/`.dockerignore`; reverter as edições de doc; desligar a branch protection no GitHub. +Sem impacto de código de aplicação. diff --git a/docs/adr/README.md b/docs/adr/README.md index 1bf176e..6c91996 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -35,3 +35,4 @@ pt-BR/inglês técnico** (Regra Zero). | [0020](0020-multi-instance-ready.md) | Pronto para multi-instância (revisa o 0002) | deploy/HA | | [0021](0021-backup-restore-dr.md) | Backup, restore e disaster recovery | operação | | [0022](0022-in-process-caching-with-caffeine.md) | Cache em processo com Caffeine (cadastro + papéis) | performance | +| [0023](0023-repo-governance-branch-protection-and-secret-scanning.md) | Proteção de branches, fluxo PR-only e varredura de segredos (governança) | entrega/segurança | diff --git a/docs/architecture/delivery.md b/docs/architecture/delivery.md index 6f083df..3e401c4 100644 --- a/docs/architecture/delivery.md +++ b/docs/architecture/delivery.md @@ -19,13 +19,24 @@ Angular CLI 22.0.4. Exact library versions live in `backend/pom.xml` and ## Git Pragmatic Git Flow (`main`, `develop`, `feature/*`, `bugfix/*`, `release/*`, `hotfix/*`) and -Conventional Commits (`feat:`, `fix:`, `test:`, `docs:`). PRs are focused and reviewable, -including tests, specs, migrations, screenshots for UI changes, API impacts and ADR updates -when applicable. Semantic Versioning (`MAJOR.MINOR.PATCH`) is the official policy — see -**ADR 0015** for the per-digit criteria, reset rules, the `0.y.z` initial-development phase and -the mapping to ROADMAP phases / Conventional Commits. The version's source of truth is -`backend/pom.xml`; releases are tagged from `main`. Docs-only slices do **not** bump the -version (Fase 15/20e precedent). +Conventional Commits (`feat:`, `fix:`, `test:`, `docs:`). + +**Branch protection & PR-only (ADR-0023).** `main` and `develop` are **protected**: **no direct +push**, **no direct merge** — they change **only via a reviewed Pull Request**. Ruleset (both +branches): require a PR, **≥1 review + CODEOWNERS review**, dismiss stale approvals, conversation +resolution, **required status checks** (Backend verify · Mutation/PIT · Flyway validate · Frontend +lint/test/build · Playwright E2E · CodeQL ×2 · **Gitleaks**), branch up to date, **linear history**, +no force-push, no deletions, **include administrators**. Enable GitHub **Secret Scanning + Push +Protection** and Dependabot. PRs target `develop`; `main` is updated only via a **release PR** +(`develop → main`), from which a human cuts the tag. **AI agents never merge to protected branches, +never publish releases, and never force-push** (`.claude/settings.json`); on a green slice they push +the feature branch and open the PR to `develop`, and may tag only on explicit request. See +`CONTRIBUTING.md` and `SECURITY.md`. + +Semantic Versioning (`MAJOR.MINOR.PATCH`) is the official policy — see **ADR 0015** for the per-digit +criteria, reset rules, the `0.y.z` initial-development phase and the mapping to ROADMAP phases / +Conventional Commits. The version's source of truth is `backend/pom.xml`; the release tag is cut from +`main` **via the release PR**. Docs-only slices do **not** bump the version (Fase 15/20e precedent). Generated files are never edited manually — modify the generation source (OpenAPI snapshot via `-Dopenapi.snapshot.write=true`, module diagram via `-Dmodulith.diagram.write=true`). @@ -37,6 +48,7 @@ via `-Dopenapi.snapshot.write=true`, module diagram via `-Dmodulith.diagram.writ | `ci.yml` | backend `./mvnw verify` (tests + ArchUnit/Modulith + Spotless/Checkstyle + JaCoCo floors + snapshot gates), frontend lint/test+coverage/build, `npm audit --audit-level=critical` gate, PIT mutation job, artifacts (JaCoCo always, surefire on failure) | | `e2e.yml` | Playwright suite against the isolated `compose.e2e.yaml` stack | | `codeql.yml` | CodeQL static analysis (java manual build + ts), weekly + on PR | +| `gitleaks.yml` | **secret scanning** (blocking) on push/PR, full history, allowlisting the enumerated dev defaults (`.gitleaks.toml`) — ADR-0023 | | `docker-publish.yml` | backend/frontend images to **GHCR** tagged per release | Dependabot watches Maven, npm and GitHub Actions. Failed tests, broken builds, invalid diff --git a/docs/architecture/security.md b/docs/architecture/security.md index 4b9f057..a6317d6 100644 --- a/docs/architecture/security.md +++ b/docs/architecture/security.md @@ -57,6 +57,18 @@ exposing userId, username and roles; the `SecurityContextUserProvider` adapter r 32-byte master key (`PLATFORM_SECRET_KEY`, DL-0074). - Uploads to the document vault are validated by **magic bytes** and stored under an opaque `fileRef` UUID that is never exposed (DL-0124). +- **No secret is ever committed** (ADR-0023): `.gitignore` blocks `.env*` (except `*.example`) and + all key/cert material; **gitleaks** runs as a blocking CI check + optional pre-commit hook + (`.gitleaks.toml`); enable GitHub **secret scanning + push protection**. The only in-repo + credentials are the **enumerated dev-only defaults** (allowlisted, blocked in prod by + `ProdReadinessValidator`) — see `SECURITY.md`. + +## Branch protection & change control (ADR-0023) + +`main` and `develop` are **protected**: changes land **only via a reviewed Pull Request** (≥1 review ++ CODEOWNERS + required checks incl. Gitleaks + linear history, no force-push, no direct push, +include admins). AI agents push feature branches and open PRs but **never merge to protected +branches, never publish releases, never force-push** (`.claude/settings.json`). See `CONTRIBUTING.md`. ## Privacy and LGPD diff --git a/docs/architecture/workflow.md b/docs/architecture/workflow.md index ab732ad..c9ce66c 100644 --- a/docs/architecture/workflow.md +++ b/docs/architecture/workflow.md @@ -75,3 +75,7 @@ Sequence: read/create initial spec → identify initial domains → generate min backend/frontend → documentation entry points → local dev setup (`docker-compose`, `.env.example`) → basic tests → initial CI when appropriate. The first feature **MUST** be guided by a spec. + +Slices land via a **branch + Pull Request** to `develop`; `main`/`develop` are protected and change +only via reviewed PR — agents push the feature branch and open the PR but **never merge to protected +branches** (see `CONTRIBUTING.md` / ADR-0023). diff --git a/docs/decision-log/DL-0152-repo-governance-team-hardening.md b/docs/decision-log/DL-0152-repo-governance-team-hardening.md new file mode 100644 index 0000000..ca1d813 --- /dev/null +++ b/docs/decision-log/DL-0152-repo-governance-team-hardening.md @@ -0,0 +1,32 @@ +# DL-0152 — Governança de repositório para equipe: sem push/merge autônomo + varredura de segredos + +- **Fase:** 23 · **Spec(s):** — · **ADR:** ADR-0023 · **Data:** 2026-07-03 +- **Status:** DECIDIDO · **Confiança:** Alta · **Reversibilidade:** Barata + +## Lacuna + +O projeto passa a ser mantido em equipe. `docs/RUN-PHASE.md` (L45–53) concedia push/merge/tag +autônomos ao agente; `CLAUDE.md` (L104) apontava para um `.claude/settings.json` inexistente; não +havia varredura de segredos (CodeQL é só SAST), CODEOWNERS, SECURITY.md, CONTRIBUTING.md, template de +PR nem hooks pre-commit; o `.gitignore` não cobria globs de chave/certificado nem `.env.*`. + +## Decisão (com o dono) + +- **Fluxo PR-only** em `main` **e** `develop` (decisão do dono) — branch protection documentada + (aplicada pelo dono no GitHub) com review de CODEOWNERS + checks obrigatórios (inclui **Gitleaks**). +- **Trava do agente** (decisão do dono, refinada nas mensagens de follow-up): `.claude/settings.json` + — **allow** `git push`/`gh pr create` (ao terminar e testar a fatia, o agente faz push da feature + branch e abre PR para `develop` — fim normal da fatia); **ask** `git tag` (só a pedido do dono); + **deny** `git merge`/`gh pr merge`/`gh release create`/force-push. Mesclar em branch protegida e + cortar release são humanos. Corrige de quebra a referência pendente no CLAUDE.md L104. +- **Varredura de segredos completa** (decisão do dono): gitleaks no CI (bloqueante) + pre-commit + + `.gitleaks.toml` com allowlist **enumerada** dos dev-defaults (`dev12345`, `dev-*-secret`, + `acme`/`admin`, chave dev base64) — segredo real novo ainda falha. +- **Higiene**: `.gitignore`/`.dockerignore` com globs de cert/chave/`.env.*` (negando os `*.example` + rastreados); arquivos de governança (CODEOWNERS, SECURITY.md, CONTRIBUTING.md, PR template); + propagação das regras nos docs de processo; ADR-0023. + +## Como reverter + +Remover os arquivos novos e reverter as edições de doc; desligar a branch protection no GitHub. Sem +impacto de código de aplicação. diff --git a/docs/decision-log/INDEX.md b/docs/decision-log/INDEX.md index aad9154..cd9cb3a 100644 --- a/docs/decision-log/INDEX.md +++ b/docs/decision-log/INDEX.md @@ -320,3 +320,4 @@ conforme `docs/RUN-PHASE.md`. | [DL-0149](DL-0149-enterprise-monitoring-dashboards-alerts-smtp.md) | 22b | **Monitoramento enterprise** (pedido do dono): 3 dashboards provisionados (Application Health RED / JVM-Pool-Cache / Business & Jobs c/ vencimento do e-CNPJ), 4 alertas novos anti-ruído (heap, certificado <30d, spike de falhas de login, breaker OPEN) e **entrega por e-mail SMTP** (contact point + policy reusando `SPRING_MAIL_*`); métricas novas: `acme_outbound_breaker_state`, `acme_nfse_transmission{operation,outcome}`, falhas/lockouts de login, dias p/ vencer o e-CNPJ (gauge cacheado @Scheduled). Retenção: dev volume+15d, prod 60d+4GB. **Nãos justificados c/ gatilho de revisão**: postgres_exporter, OTel, HealthIndicators externos (+ `health.mail.enabled=false` anti-cascata) | Alta | Barata | | [DL-0150](DL-0150-intelligent-logging-loki-pipeline-runtime-levels.md) | 22c | **Log inteligente** (pedido do dono): MDC ganha `username` (`UserMdcFilter` @Order(0) pós-security, via port, só autenticado — e-mail/userId ficam fora); **pipeline no Alloy** parseia o ECS (`level`→LABEL ~5 valores; `correlationId`/`username`→STRUCTURED METADATA — cardinalidade ilimitada nunca vira label); **dashboard Logs** (volume por nível, erros ao vivo, por container, busca por correlationId) + **derived field** ("Related logs"); alerta `acme-error-log-spike` via Loki (falhas sem pegada HTTP); **paridade prod do loki-config** corrigida (rodava a default da imagem) + ruler morto removido; **`/actuator/loggers` gated ROLE_IT** (log-level em runtime, reseta no restart) | Alta | Barata | | [DL-0151](DL-0151-install-guide-and-doc-index-subpages.md) | 22e | **Guia de instalação do zero + sub-páginas de índice** (pedido do dono): `docs/INSTALL.md`/`INSTALL.en-US.md` minuciosos (pré-req por SO — Windows/Docker Desktop+WSL2, Linux, macOS; dev/prod/nuvem; troubleshooting); **usuários de teste** em tabela no README pt/en; **sub-páginas de índice** navegáveis (`docs/README.md` hub + `specs/README.md` + `adr/README.md` + `architecture/README.md`) com contagens corrigidas (33 specs/22 ADRs/150 DLs) e breadcrumbs. **Wiki/Pages adiado** (decisão do dono — .md nativos por ora; Regra Zero) | Alta | Barata | +| [DL-0152](DL-0152-repo-governance-team-hardening.md) | 23 | **Governança de repositório para equipe** (pedido do dono; ADR-0023): fluxo **PR-only em main E develop** (branch protection documentada); **trava do agente** `.claude/settings.json` — allow push da feature branch + `gh pr create` (abre PR para develop ao terminar/testar), ask `git tag` (só a pedido), deny merge/`gh pr merge`/release/force (corrige a ref pendente do CLAUDE.md L104); **varredura de segredos** gitleaks (CI bloqueante + pre-commit + `.gitleaks.toml` com allowlist enumerada dos dev-defaults); higiene de `.gitignore`/`.dockerignore` (globs cert/chave/`.env.*`); CODEOWNERS/SECURITY.md/CONTRIBUTING.md/PR template; regras propagadas nos docs de processo | Alta | Barata | diff --git a/frontend/.dockerignore b/frontend/.dockerignore index d46281c..3135e0f 100644 --- a/frontend/.dockerignore +++ b/frontend/.dockerignore @@ -10,3 +10,8 @@ blob-report .playwright e2e playwright.config.ts +# Defesa em profundidade: nunca copiar .git/segredos para o contexto/imagem (ADR-0023). +.git +.env +.env.* +secrets/ From 6460370d2f80d4009e18946859fef2690e7e9a33 Mon Sep 17 00:00:00 2001 From: "franklin.azeredo" Date: Fri, 3 Jul 2026 10:44:25 -0300 Subject: [PATCH 2/3] fix(23): gitleaks-action exige GITHUB_TOKEN no evento de pull_request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit O check Gitleaks do PR #14 falhou por configuração da action (não por segredo — o run do evento push passou limpo): a gitleaks-action@v2 lê o range de commits do PR via API e exige o GITHUB_TOKEN. Adicionado o token padrão (contents: read basta). Co-Authored-By: Claude Opus 4.8 --- .github/workflows/gitleaks.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/gitleaks.yml b/.github/workflows/gitleaks.yml index 653e92d..cbb5769 100644 --- a/.github/workflows/gitleaks.yml +++ b/.github/workflows/gitleaks.yml @@ -27,6 +27,9 @@ jobs: - name: Run gitleaks uses: gitleaks/gitleaks-action@v2 env: + # Required by gitleaks-action to scan pull_request events (it reads the PR's commit + # range via the API). The default GITHUB_TOKEN with `contents: read` is enough. + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITLEAKS_CONFIG: .gitleaks.toml # Personal/public repo: no license needed. If this repo ever moves under a GitHub ORG, # set the GITLEAKS_LICENSE secret. License-free alternative (run the binary directly): From ef14952fe605b972e08a8c4736f93c95075795e3 Mon Sep 17 00:00:00 2001 From: "franklin.azeredo" Date: Fri, 3 Jul 2026 11:00:18 -0300 Subject: [PATCH 3/3] fix(23): isola PointClockCrawlerIntegrationTest (limpa o banco ANTES de cada teste) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Falha do CI (verde no Windows local, vermelha no runner Linux do GitHub, desde a Fase 20b): breakerOpens...:151 esperava 0 snapshots mas via 1. A asserção 148 (não bateu no portal) passava, provando que o crawler curto-circuitou correto — o snapshot fantasma era RESÍDUO no Postgres singleton COMPARTILHADO por todas as classes de integração (AbstractPostgresIntegrationTest). A limpeza só rodava em @AfterEach, deixando a PRIMEIRA asserção exposta a resíduo de outra classe. Fix: @BeforeEach além do @AfterEach — cada teste começa com point_snapshots/ point_crawl_runs limpos, imune a resíduo. Não mascara bug de produto (o comportamento do breaker é provado pela asserção 148). Repro em container Linux: 627 testes verdes, PointClockCrawler 5/5. Co-Authored-By: Claude Opus 4.8 --- .../fksoft/pointclock/PointClockCrawlerIntegrationTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/src/test/java/com/fksoft/pointclock/PointClockCrawlerIntegrationTest.java b/backend/src/test/java/com/fksoft/pointclock/PointClockCrawlerIntegrationTest.java index 2e783f2..cdc11b2 100644 --- a/backend/src/test/java/com/fksoft/pointclock/PointClockCrawlerIntegrationTest.java +++ b/backend/src/test/java/com/fksoft/pointclock/PointClockCrawlerIntegrationTest.java @@ -14,6 +14,7 @@ import com.fksoft.system.AbstractPostgresIntegrationTest; import java.time.Clock; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; @@ -37,6 +38,11 @@ class PointClockCrawlerIntegrationTest extends AbstractPostgresIntegrationTest { @Autowired private Clock clock; @Autowired private JdbcTemplate jdbcTemplate; + // Clean BEFORE and AFTER each test: the integration suite shares one Postgres, and these crawler + // tests assert absolute counts on point_snapshots/point_crawl_runs. Cleaning only afterwards left + // the FIRST assertion exposed to any residue from another integration class (green on Windows, + // red on the slower CI Linux runner — the real cause of the long-standing CI failure). + @BeforeEach @AfterEach void cleanUp() { jdbcTemplate.execute("DELETE FROM point_snapshots");