93 lines
8.6 KiB
Python
93 lines
8.6 KiB
Python
import csv
|
|
import hashlib
|
|
import json
|
|
from collections import Counter
|
|
from pathlib import Path
|
|
|
|
csv.field_size_limit(20_000_000)
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
DOC = ROOT / 'docs/investmoz/v2/auditoria-portal'
|
|
PRIVATE = ROOT / 'storage/app/private/investmoz-v2/portal-audit'
|
|
PACKET = PRIVATE / 'country-activation-stopped-20261011'
|
|
OUTPUT = ROOT / 'outputs/investmoz-country02-activation-20261011'
|
|
packet = json.loads((PACKET / 'csv-edits.json').read_text(encoding='utf-8'))
|
|
preservation = []
|
|
for edit in packet['files']:
|
|
file = DOC / edit['filename']
|
|
rows = list(csv.reader(file.open(encoding='utf-8-sig', newline='')))
|
|
assert len(rows) == len(edit['rows'])
|
|
by_row = {p['row']: p['values'] for p in edit['patches']}
|
|
for i, row in enumerate(rows):
|
|
expected = list(edit['rows'][i])
|
|
for field, value in by_row.get(i, {}).items():
|
|
expected[rows[0].index(field)] = value
|
|
assert row == expected, 'A non-authorized cell changed'
|
|
preservation.append({'file': edit['filename'], 'all_other_cells_preserved': True, 'changed_rows': len(by_row), 'sha256': hashlib.sha256(file.read_bytes()).hexdigest()})
|
|
requirements = list(csv.DictReader((DOC / 'matriz-requisitos.csv').open(encoding='utf-8-sig', newline='')))
|
|
counts = {'rows': len(requirements), 'unique_ids': len({r['ID'] for r in requirements}), 'source_occurrences': sum(int(r['Quantidade de linhas de origem']) for r in requirements), 'states': dict(Counter(r['Estado'] for r in requirements))}
|
|
assert counts['rows'] == counts['unique_ids'] == 3393
|
|
assert counts['source_occurrences'] == 6845
|
|
assert counts['states']['concluído e demonstrado'] == 7
|
|
candidate_dir = ROOT / 'outputs/investmoz-country02-candidate-20261011'
|
|
envelope = json.loads((candidate_dir / 'manifesto-pacote.json').read_text(encoding='utf-8'))
|
|
candidate = json.loads((candidate_dir / 'manifesto-candidata.json').read_text(encoding='utf-8'))
|
|
assert hashlib.sha256((candidate_dir / envelope['archive']).read_bytes()).hexdigest() == envelope['archive_sha256']
|
|
assert hashlib.sha256((candidate_dir / 'manifesto-candidata.json').read_bytes()).hexdigest() == envelope['manifesto_candidata_sha256']
|
|
for name, sha in candidate['files'].items():
|
|
assert hashlib.sha256((ROOT / name).read_bytes()).hexdigest() == sha, name
|
|
stage = json.loads((PRIVATE / 'country-production-stage-latest.json').read_text(encoding='utf-8'))
|
|
for name, sha in stage['active_assets'].items():
|
|
assert hashlib.sha256((ROOT / 'public/build' / name).read_bytes()).hexdigest() == sha, name
|
|
for name, sha in candidate['production']['files'].items():
|
|
if name != 'manifest.json':
|
|
assert hashlib.sha256((ROOT / 'public/build' / name).read_bytes()).hexdigest() == sha, name
|
|
|
|
summary = '''**Ativação COUNTRY02 interrompida antes do POST.** A autorização exclusiva da entrega `c68ceef9d4a3`, código `36f7722ce5a5`, foi recebida. Backup/restauro de 06:09:56 UTC revalidado às 06:50 UTC: alvo `investemoz:5432`, chave estável, código, bundle e dados das 288 tabelas correspondentes. As 22 fontes, 157 ficheiros do bundle e 187 entradas do ZIP foram conferidas.
|
|
|
|
**C110:** no endereço ativo HTTP `portal-investidor.test`, Chromium informa `isSecureContext=false` e `crypto.randomUUID` indisponível. O componente compilado do instalador chama esse método ao inicializar e lança `TypeError: crypto.randomUUID is not a function`. O ensaio anterior em HTTP `127.0.0.1` não cobria esta diferença. A prova atual é de inicialização isolada do componente com propriedades de ensaio. A prévia autenticada não foi obtida e não houve POST nem login de gestor.
|
|
|
|
O bundle foi disponibilizado com a flag desabilitada e depois revertido atomicamente para o manifesto anterior `75352185c2a85`, conservando os ativos antigos e novos. Leitura final de 06:53:28 UTC: 050000 e as duas colunas continuam ausentes, nenhum evento de instalação, zero alterações de dados de negócio, contas ou representações. Mantêm-se 13 utilizadores, zero organizações/vínculos, 79 migrations e 126 eventos de auditoria. Apenas duas sessões de visita foram criadas. Todas as flags permanecem desabilitadas.
|
|
|
|
**Próxima ação:** corrigir a inicialização de UUID em QA com alternativa criptográfica `getRandomValues` ou UUID do servidor, mantendo MFA, CSRF e idempotência. Ensaiar num hostname HTTP equivalente que não seja loopback, recompilar e fixar uma nova revisão. Revalidar a prova e a prévia autenticada dessa entrega antes de nova aplicação autorizada. A autorização recebida para hashes exatos não abrange uma entrega substituta. Dados reais de organizações e representação permanecem fora desta operação.
|
|
|
|
Só os requisitos AUD-COUNTRY-02 e AUD-OPS-01 receberam novas evidências. COUNTRY02 passou de implementado mas não ativado para com falha de compatibilidade C110. A matriz mantém 3.393 requisitos e linhas, 6.845 ocorrências de origem e 3.386 requisitos abertos: 3.234 parciais, 145 não implementados, 5 dependentes de decisão, 1 implementado mas não ativado, 1 com falha e 7 concluídos. A/B e C37 continuam encerrados no âmbito QA aceito; nenhum outro pacote foi ativado.
|
|
|
|
Este resultado prevalece sobre os retratos anteriores, conservados como histórico. [Relatório e recibos sanitizados](../../../../outputs/investmoz-country02-activation-20261011/resultado.md).
|
|
'''
|
|
section = '<!-- country-activation-20261011:begin -->\n\n## COUNTRY02 autorizado — execução interrompida por C110\n\n'+summary+'\n<!-- country-activation-20261011:end -->'
|
|
for name in ['relatorio-executivo.md','backlog.md','plano-ativacao.md']:
|
|
file = DOC / name
|
|
text = file.read_text(encoding='utf-8')
|
|
begin = '<!-- country-activation-20261011:begin -->'
|
|
end = '<!-- country-activation-20261011:end -->'
|
|
assert text.count(begin) == text.count(end) == 1
|
|
prefix, rest = text.split(begin,1)
|
|
_, suffix = rest.split(end,1)
|
|
file.write_text(prefix+section+suffix, encoding='utf-8')
|
|
cases = DOC / 'casos-reproducao.md'
|
|
case_text = cases.read_text(encoding='utf-8')
|
|
separator = str(OUTPUT / 'resultado.md')+'\n\n'
|
|
assert case_text.count(separator) == 1
|
|
_, older_cases = case_text.split(separator,1)
|
|
case = '''## C110 — formulário do instalador COUNTRY02 em hostname HTTP
|
|
|
|
**Operação afetada:** inicialização da interface da prévia em `http://portal-investidor.test/management/opportunity-resources-install/organization-country`. Revisão `c68ceef9d4a3` / código `36f7722ce5a5`.
|
|
|
|
**Reprodução:** depois de conferir os hashes, carregar o bundle no endereço ativo como visitante. GET do instalador redireciona para login, conforme a política de acesso. Na mesma origem, conferir a disponibilidade de `crypto.randomUUID` e carregar o módulo compilado do instalador. Inicializar o componente isoladamente com propriedades de ensaio, sem fazer login ou chamar a API de aplicação.
|
|
|
|
**Esperado:** inicialização do componente e chave UUID de repetição válida, permitindo a posterior prévia autenticada.
|
|
|
|
**Observado:** `isSecureContext=false`, `crypto.randomUUID=undefined`, `getRandomValues=function`. O módulo real, com hash conferido, lança `TypeError: crypto.randomUUID is not a function` antes de `useForm` ou do layout. A prévia autenticada não foi obtida. Esta prova não simula autorização ou autenticação.
|
|
|
|
**Causa:** chamada incondicional de `crypto.randomUUID()` na inicialização de `idempotency_key` em `resources/js/pages/management/opportunity-install.tsx:31`. A QA em HTTP loopback não reproduzia o contexto do hostname HTTP ativo.
|
|
|
|
**Correção e aceitação restantes:** UUIDv4 criptográfico com alternativa `getRandomValues` ou UUID do servidor. Ensaio em QA equivalente ao hostname HTTP ativo, preservando CSRF, MFA recente, confirmação, motivo, hash e repetição. Recompilar e fixar nova revisão antes de retomar uma prévia e aplicação autorizadas correspondentes. Não alterar chave, perfis ou origem para contornar a falha.
|
|
|
|
**Resultado seguro:** interrompido antes do POST, manifesto anterior restaurado atomicamente, 050000 e duas colunas ausentes, flag desabilitada. Zero alterações de negócio e zero eventos de instalação; duas sessões públicas de visita. [Relatório e recibos](../../../../outputs/investmoz-country02-activation-20261011/resultado.md). A/B e C37 preservados.
|
|
|
|
'''
|
|
cases.write_text(case+older_cases, encoding='utf-8')
|
|
proof = {'result':'passed','targeted_csv_preservation':preservation,'counts':counts,'approved_source_archive_unchanged':True,'old_assets_and_manifest_restored':True,'new_immutable_assets_preserved':True,'business_state_receipt':'estado-final.json'}
|
|
(OUTPUT / 'verificacao-final.json').write_text(json.dumps(proof,ensure_ascii=False,indent=2),encoding='utf-8')
|
|
print(json.dumps(proof,ensure_ascii=False))
|