Proteger dados sensíveis e atualizar andamento do plano

- Caminho do compartimento pai e CIDR do SSH saem do código para
  .secrets/oci-settings.json (fora do Git), com override por DMB_*.
- Relatórios de sanidade deixam de gravar o hostname local.
- tests/test_sensitive.py falha se OCIDs, chaves, IPs públicos ou valores
  de .secrets aparecerem em arquivos versionados.
- tools/plan_progress.py gera o bloco de andamento por fase no plano.
- Plano: status reais das tarefas de F0, F3 e F6; README e docs/05 com
  GPU em sa-saopaulo-1, runtime CUDA do Rizzo e pipeline sintético.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Marcos Lohmann
2026-10-07 14:08:44 -03:00
parent ce544639ac
commit 171595f4c9
13 changed files with 193 additions and 29 deletions

View File

@@ -12,7 +12,7 @@ def test_external_connection_is_blocked():
s = socket.socket()
try:
with pytest.raises(NetworkAccessBlocked):
s.connect(("93.184.216.34", 443))
s.connect(("203.0.113.10", 443)) # TEST-NET-3 (RFC 5737)
finally:
s.close()

52
tests/test_sensitive.py Normal file
View File

@@ -0,0 +1,52 @@
"""Nada sensível nos arquivos versionados: OCIDs, chaves, IPs públicos, namespace ou dados de .secrets/."""
import ipaddress
import json
import re
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
PATTERNS = [
re.compile(r"ocid1\.[a-z0-9]+\.oc1\."),
re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----"),
re.compile(r"\bfingerprint\s*=\s*[0-9a-f:]{20,}"),
]
IPV4 = re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b")
def tracked_files():
out = subprocess.run(["git", "-C", str(ROOT), "ls-files", "-co", "--exclude-standard"],
capture_output=True, text=True, check=True).stdout.split()
return [ROOT / f for f in out if (ROOT / f).is_file() and not f.startswith(".secrets/")]
def local_secrets() -> list[str]:
"""Valores de .secrets/oci-settings.json não podem aparecer em arquivos versionados."""
path = ROOT / ".secrets" / "oci-settings.json"
if not path.is_file():
return []
values = []
for v in json.loads(path.read_text()).values():
values += [v] + [part for part in str(v).replace("/", " ").split() if len(part) > 5 and part != "root"]
return values
def test_no_sensitive_content():
secrets = local_secrets()
problems = []
for f in tracked_files():
try:
text = f.read_text()
except UnicodeDecodeError:
continue
rel = f.relative_to(ROOT)
problems += [f"{rel}: {p.pattern}" for p in PATTERNS if p.search(text)]
problems += [f"{rel}: valor de .secrets/oci-settings.json" for s in secrets if s and s in text]
for ip in IPV4.findall(text):
try:
addr = ipaddress.ip_address(ip)
except ValueError:
continue
if addr.is_global:
problems.append(f"{rel}: IP público {ip}")
assert not problems, "\n".join(problems)