Files
Investidor-V2/tools/portal-stability-activation-results.py
T
nala 287e991486
tests / ci (push) Waiting to run
oportunidades 7
2026-10-13 11:40:29 +02:00

185 lines
21 KiB
Python

"""Document actual authorized activation and prepare targeted Artifact Tool CSV edits. No DB access."""
from pathlib import Path
from datetime import datetime, timezone
import json, csv, hashlib, io, re, shutil
ROOT = Path(__file__).resolve().parent.parent
PRIVATE = ROOT / 'storage/app/private/investmoz-v2/portal-audit'
DOC = ROOT / 'docs/investmoz/v2/auditoria-portal'
OUT = ROOT / 'outputs/investmoz-portal-stability-activation-20261011'
PACKET = PRIVATE / 'stability-active-results-20261011'
PACKET.mkdir(exist_ok=True)
read = lambda p: json.loads(p.read_text(encoding='utf-8-sig'))
enabled = read(PRIVATE / 'stability-active-check-enabled-20261011.json')
browser = read(PRIVATE / 'stability-active-browser-status.json')
final = read(PRIVATE / 'stability-active-browser-final-check.json')
transport = read(PRIVATE / 'stability-active-transport.json')
stage = read(PRIVATE / 'stability-active-stage-latest.json')
backup = read(Path(read(PRIVATE / 'stability-active-backup-latest.json')['path']))
manifest = read(ROOT / 'outputs/investmoz-portal-stability-20261011/manifesto-candidata.json')
assert enabled['result'] == final['result'] == transport['result'] == 'passed'
assert enabled['country_enabled'] and enabled['business_rows_preserved']
assert enabled['installed_packages'] == ['2026_10_10_050000_extend_organization_country']
assert enabled['installation_audits'] == 1 and not any(enabled['other_flags'].values())
assert enabled['counts_after']['users'] == 13 and enabled['counts_after']['organizations'] == enabled['counts_after']['memberships'] == 0
assert browser['criteria']['authenticated_UI_application_visible']
assert browser['repetition'] == {'same_status': 200, 'changed_status': 409, 'credentials_or_payload_recorded': False}
assert all(final['criteria'].values())
assert all(v['matches'] for v in browser['assets'].values())
assert not browser['mixed_requests'] and not browser['javascript_errors']
archive = ROOT / 'outputs/investmoz-portal-stability-20261011/portal-stability-485ee04c40b2-revisao.zip'
archive_sha = hashlib.sha256(archive.read_bytes()).hexdigest()
assert archive_sha == '033368b9532c94e303de07330abe6ab4afa63d9d6f2879044bfd3fe680364704'
for field in ['dump', 'files']:
assert hashlib.sha256(Path(backup[field + '_path']).read_bytes()).hexdigest() == backup[field + '_sha256']
at = datetime.now(timezone.utc).isoformat()
assert (datetime.now(timezone.utc) - datetime.fromisoformat(backup['verified_at'])).total_seconds() < 86400
audit = enabled['installation_audit_reference'][0]
criteria = {**browser['criteria'], **final['criteria']}
result = {
'at': at, 'phase': 'COUNTRY02_active_authorized_scope_verified',
'delivery_revision': manifest['delivery_revision'], 'code_hash': enabled['code_hash'],
'authorized_archive_sha256': archive_sha, 'production_manifest_sha256': manifest['production_manifest_sha256'],
'origin': transport['origin'], 'installed_packages': enabled['installed_packages'],
'country_enabled': True, 'other_flags': enabled['other_flags'],
'tables': enabled['tables'], 'counts_before': enabled['counts_before'], 'counts_after': enabled['counts_after'],
'columns': enabled['columns'], 'installation_audits': 1, 'installation_audit_reference': audit,
'business_rows_preserved': True, 'authentication_metadata_changes': enabled['authentication_metadata_changes'],
'key_cipher_original_MFA_preserved': True, 'original_MFA_envelopes_recovered': 2,
'backup_verified_at': backup['verified_at'], 'backup_dump_sha256': backup['dump_sha256'],
'backup_private_files_sha256': backup['files_sha256'], 'backup_tables': 288, 'backup_sequences': 31,
'backup_private_original_files': backup['file_count'], 'criteria': criteria,
'new_session_visual_evidence': 'Manual confirmation by responsible user plus actual new-session MFA redirect and profile GET200/no-store. Initial session profile/refresh/history automated.',
'loaded_assets_verified': len(browser['assets']), 'mixed_content_requests': 0, 'application_javascript_errors': 0,
'certificate': transport['certificate'], 'session_cookie': next(c for c in transport['cookies'] if c['name'] == 'portal_investidor_https'),
'PHP': transport['PHP'], 'HTTP_GET_status': 308, 'HTTP_POST_status': 405, 'default_vhost_status': 200,
'shared_DLL_sha256': stage['shared_DLL_sha256'], 'effective_vhost_path': stage['effective_vhost_path'],
'vhost_config_sha256': stage['active_vhost_sha256'],
'additional_operational_adjustments': ['C118: approved HTTPS bytes in persistent vhost without auto. prefix', 'Single direct existing Apache start explicitly accepted by user'],
'actual_master_save_demonstrated': False,
'remaining_criterion': 'Real organization and authorized representation are absent (0/0); actual active master-country save/reuse remains pending legitimate master data. QA evidence preserved.',
'containment': 'Only COUNTRY02 OFF, preserving HTTPS, columns, values, provenance, ledger, audit and cipher. Full restore requires separate decision.',
'A_B_C37_closed_scopes_preserved': True, 'secrets_or_credentials_exported': False,
'evidence': ['stability-active-controlled-start.json', 'stability-active-transport.json', 'stability-active-browser-status.json', 'stability-active-browser-final-check.json', 'stability-active-check-enabled-20261011.json', 'stability-active-user-delta-readonly.json'],
}
snapshots = PACKET / 'documents-before'
snapshots.mkdir(exist_ok=True)
for file in ['resultado.json', 'resultado.md']:
source = OUT / file
if source.exists() and not (snapshots / file).exists(): shutil.copy2(source, snapshots / file)
(OUT / 'resultado.json').write_text(json.dumps(result, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
report = f'''# Resultado da disponibilização COUNTRY02
Atualizado em {at}. **COUNTRY02 está instalado e habilitado em https://portal-investidor.test.** Revisão autorizada `485ee04c40b2`, código `162daff6ce08`. Somente a extensão `050000` foi aplicada pela interface autenticada existente.
## Efeitos conferidos
| Elemento | Resultado ativo |
| --- | --- |
| Prévia | Destino `investemoz:5432`, código, backup e dependências correspondentes; hash antes da aplicação `04afa2b70495fb6bb81c6f238bba71330b040a30d40a292523197ea17d5eaf27` |
| Aplicação autenticada | POST 302, recibo visível e instalação indicada na interface |
| Schema | Duas colunas opcionais em `organizations`: `incorporation_country_code` varchar(2) e `incorporation_country_provenance` jsonb |
| Preservação | 288 tabelas, 13 contas, zero organizações e zero representações; nenhuma nova tabela, identidade ou representação |
| Ledger e auditoria | 79→80 migrations, 126→127 eventos; uma instalação e uma auditoria de COUNTRY02 |
| Repetição | Mesmo conteúdo/chave: 200 e mesmo recibo; conteúdo diferente/mesma chave: 409; sem nova DDL/auditoria |
| Flags | Somente COUNTRY02 ON; restantes flags OFF; sem 030000, 040000 ou 190000 |
| HTTPS e runtime | Chromium reconhece o certificado sem ignorar TLS; contexto seguro; PHP 8.5.11/Apache e cURL 8.22.0; vhost predefinido preservado |
| Cookies e recursos | `portal_investidor_https`, host-only, `/`, Secure/HttpOnly/Lax; {len(browser['assets'])} ativos carregados com hashes aprovados; zero conteúdo misto e zero erros JS da aplicação |
| Autenticação e perfil | Login/MFA real, perfil 200 e estado vazio explicado; atualização e voltar/avançar com histórico cifrado |
| Nova sessão | Segundo login/MFA e perfil GET200/no-store; confirmação visual manual «entrei e validei sem problemas» |
| Logout e proteção | POST302, API privada401 e Voltar regressa ao login sem recuperar o perfil privado |
Auditoria: `{audit['id']}`, ator existente `{audit['actor_user_id']}`, correlação `{audit['correlation_id']}`, em {audit['created_at']}. A verificação final de preservação passou em {enabled['at']}, por transações READ ONLY. A única alteração adicional em `users` foi `remember_token` do ator autenticado, rotação normal de logout no Laravel. Todos os outros campos, privilégios, palavras-passe e MFA são iguais ao snapshot restaurado. Chave e cipher foram preservados; os dois envelopes MFA originais continuam recuperáveis.
## Backup e revisão
Backup/restauro do destino anterior verificado em {backup['verified_at']}: 288 tabelas, 31 sequências, {backup['file_count']} ficheiros privados e zero ficheiros em falta. Dump SHA-256 `{backup['dump_sha256']}`; arquivo privado `{backup['files_sha256']}`. Ambos os hashes foram conferidos novamente nesta conclusão e a prova está dentro da validade de 24 horas. Os originais e configurações permanecem privados.
ZIP autorizado: `{archive_sha}`. Produção: `{manifest['production_manifest_sha256']}`. Conferidos os 262 membros antes da disponibilização, os ficheiros/ativos instalados na verificação final e 83 snapshots de reversão. Nenhuma recompilação ou alteração do ZIP selado nesta execução.
O arranque direto único do executável Apache existente foi aceite pelo responsável após a dificuldade no controlo normal. C118 foi resolvido conservando os bytes HTTPS aprovados num vhost sem prefixo `auto.`, para o Laragon não os regenerar. Configuração, certificado, código e bundle mantêm os hashes aprovados. [Causa, ajuste operacional e cuidados de reposição](ajustes-execucao.md).
## Critério ainda pendente
**Não foi demonstrada gravação real do país mestre no ativo:** não existem organizações nem representações legítimas. O perfil mostra «Sem representação organizacional registada para esta conta.». Quando existir cadastro legítimo e representação autorizada, verificar guardar país ISO, proveniência/versão, recuperação e reutilização sem alterar os valores das submissões anteriores. A ausência não impediu a instalação; não foram criados dados de demonstração no ativo. A gravação e permissões já demonstradas em QA permanecem como prova QA.
O certificado é local: outra máquina/conta de navegador precisa de confiança própria. Expiração do certificado apresentado: {datetime.fromtimestamp(transport['certificate']['validTo'], timezone.utc).isoformat()}. Não representa disponibilização pública na Internet. Cartografia, perfis, ofertas, SLA, restantes consumidores documentais/workflow, acessibilidade integral e dispositivos reais mantêm critérios próprios. A/B e C37 conservam os fechos nos respetivos âmbitos.
## Contenção e reversão
Para contenção, desabilitar apenas COUNTRY02. Preservar HTTPS, correção de logout, duas colunas, dados/proveniência, ledger, auditoria e cifragem. Não executar `down/drop` ou `key:generate`. A reposição integral requer decisão separada e proteção dos dados posteriores; considerar o vhost personalizado ao repor o exemplar original, sem manter dois vhosts concorrentes. Backups e snapshots continuam disponíveis.
[Resultado estruturado](resultado.json). Evidências sanitizadas individuais permanecem no acervo privado da auditoria. A recolha não grava passwords, códigos MFA, cookies, tokens ou chaves. O pacote original e os relatórios históricos são conservados; este resultado prevalece sobre os estados de suspensão/preparação anteriores apenas no âmbito autorizado.
'''
(OUT / 'resultado.md').write_text(report, encoding='utf-8')
current = f'''<!-- portal-stability-authorized-activation-20261011:begin -->
## Estado mais recente — COUNTRY02 ativo, revisão 485ee04c40b2
Somente `050000` foi aplicado pela interface autenticada em 11/10/2026 às 10:39:39 UTC e COUNTRY02 habilitado às 10:39:51 UTC. Prévia correspondente à revisão/código/backup/destino `investemoz:5432`; aplicação302, repetição200 com mesmo recibo e conteúdo diferente/mesma chave409. Verificação READ ONLY final em {enabled['at']}: duas colunas opcionais, 288 tabelas, 13 contas, zero organizações/representações, 80 migrations e 127 audits. Uma auditoria de instalação; nenhuma nova tabela, conta, organização ou representação. Restantes flags OFF e pacotes030/040/190 ausentes.
Chromium confirmou HTTPS reconhecido, login/MFA, perfil/atualização/histórico cifrado/voltar-avançar. Nova sessão com segundo MFA e GETperfil200/no-store, acompanhada de confirmação manual. Logout302/API401 e Voltar ao login sem perfil privado. Chave/cipher e MFA originais preservados; rotação normal de `remember_token` do ator13 reconciliada por comparação campo a campo, sem exportar valores.
Backup/restauro real correspondente verificado às10:03:46UTC (288tabelas/31sequências/{backup['file_count']}ficheiros/2MFA); hashes do pacote/backup e código/bundle instalados conferidos. Certificado/cookie/runtime/vhost predefinido e {len(browser['assets'])}ativos carregados aprovados. C118: Laragon regenerava o vhost `auto.`; bytes HTTPS aprovados conservados no ficheiro personalizado sem esse prefixo. Arranque direto único aceite expressamente pelo responsável; nenhum hash do pacote mudou.
**Gravação ativa do país mestre permanece pendente de uma organização legítima e representação autorizada (0/0).** Esse critério mantém AUD-COUNTRY-02 parcial, embora a extensão esteja ativa. AUD-OPS-01 permanece parcial pelos restantes consumidores/pacotes. A/B e C37 preservados; não se encerram a auditoria global nem outras decisões.
Mesma matriz: 3393 requisitos/linhas e 6845 referências; 7 concluídos, 3386 abertos (3235parciais,145não implementados,1implementado mas não ativado,5decisões concretas). Apenas AUD-COUNTRY-02/AUD-OPS-01 atualizados. [Resultado, auditoria e limites](../../../../outputs/investmoz-portal-stability-activation-20261011/resultado.md) e [ajustes operacionais](../../../../outputs/investmoz-portal-stability-activation-20261011/ajustes-execucao.md). Contenção somente COUNTRY02OFF preserva HTTPS/schema/dados/histórico; restauro completo continua sujeito a decisão separada. Blocos abaixo são histórico anterior.
<!-- portal-stability-authorized-activation-20261011:end -->'''
begin='<!-- portal-stability-authorized-activation-20261011:begin -->'
end='<!-- portal-stability-authorized-activation-20261011:end -->'
for filename in ['relatorio-executivo.md','backlog.md','plano-ativacao.md','execucao-percurso-i1-i3.md']:
file=DOC/filename;text=file.read_text(encoding='utf-8')
assert text.count(begin)==text.count(end)==1
if not (snapshots/filename).exists():shutil.copy2(file,snapshots/filename)
text=text[:text.index(begin)]+current+text[text.index(end)+len(end):]
file.write_text(text,encoding='utf-8')
csv.field_size_limit(20_000_000)
edits=[]
tag='[Disponibilização ativa COUNTRY02 11/10/2026] '
active='COUNTRY02 instalado e habilitado em https://portal-investidor.test; somente050000/2colunas,288tabelas/13contas/0organizações/0representações; restantes flagsOFF e030/040/190ausentes. Revisão485ee04c40b2/código162daff6ce08 autorizados. Estados anteriores de suspensão/preparação preservados como histórico.'
evidence='outputs/investmoz-portal-stability-activation-20261011/resultado.md e resultado.json; ajustes-execucao.md/C118. Prévia200 correspondente; POST302; replay200/alterado409; 1audit/1migration; perfil200/atualização/Back-Forward cifrados; 2MFA em sessões distintas, recuperação visual manual e GET200/no-store; logout302/API401/Backlogin. Leitura final preserva todas as linhas de negócio, chave/cipher/MFA; remember_token do ator13 roda normalmente no logout, restantes campos idênticos ao clone original.'
remaining='Guardar e reutilizar país mestre REAL no ativo depende de organização legítima e representação autorizada, atualmente0/0. Não inventar vínculos. QA de país/versões/permissões preservada. Instalação concluída; não falta030/040/190 nemClamD paraCOUNTRY02.'
ops_remaining='Restantes consumidores de contratos/workflow/documentos/cartografia/acessibilidade/dispositivos reais e pacotes separados mantêm os critérios próprios. Ofertas/competências modulares/SLA continuam decisões separadas. COUNTRY02 ativo não encerra a auditoria integral. Não repetirA/B/C37 sem alteração pertinente.'
for filename in ['matriz-requisitos.csv','backlog-priorizado.csv']:
file=DOC/filename;data=file.read_bytes();rows=list(csv.reader(io.StringIO(data.decode('utf-8-sig'))));head=rows[0];idcol=head.index('ID');patches=[]
assert len(rows)==3394
shutil.copy2(file,PACKET/(filename+'.before'))
for index,row in enumerate(rows[1:],1):
req=row[idcol]
if req not in ['AUD-COUNTRY-02','AUD-OPS-01']:continue
old=dict(zip(head,row))
if filename=='matriz-requisitos.csv':
versions=json.loads(old['Critérios e versões por origem (JSON)'])
for version in versions:
version['disponibilizacao_country02_ativa_20261011']={'at':at,'revision':manifest['delivery_revision'],'code_hash':enabled['code_hash'],'ativo':True,'criterios_demonstrados':criteria,'mestre_ativo_gravado':False,'criterio_restante':remaining if req=='AUD-COUNTRY-02' else ops_remaining,'evidencia':evidence,'C118':'vhostpersistente;bytesHTTPSiguais;arranquediretounicoaceite','alteracao_autenticacao_normal':'remember_token somenteator13; semmudançadosoutroscampos','preservar_fechos':['A/B','C37']}
values={
'Disponibilidade ativa':active,'Estado':'parcial','Lacuna':remaining if req=='AUD-COUNTRY-02' else ops_remaining,
'Dependência':remaining if req=='AUD-COUNTRY-02' else 'Backups/snapshots preservados; contençãoCOUNTRY02OFF semdrop/down. Outras ativações dependem de revisão/autorização próprias. QA isolada para correções independentes. Certificado local exige confiança por máquina/conta; expira09/01/2027.',
'Evidência funcional':old['Evidência funcional']+'\n'+tag+evidence,
'Critério de aceitação':old['Critério de aceitação']+'\n'+tag+'Critérios de instalação/transporte/sessão demonstrados no ativo; falta gravação/recuperação/reutilização num mestre legítimo quando houver organização/representação. Outros pacotes/consumidores não encerrados.',
'Implementação encontrada':old['Implementação encontrada']+'\n'+tag+'Mesmo pacote485ee04c40b2/código162daff6ce08 e bundle250ae618d716 instalados; C118 resolvido conservandoHTTPSaprovado em vhostsem auto. Sem nova revisão da aplicação.',
'Versões divergentes':old['Versões divergentes']+'\n'+tag+'Autorização final485ee04c40b2 e execução ativa supersedem estados históricos136/preparação/suspensão somenteCOUNTRY02. C118 é ajuste operacional de caminho, bytes iguais.',
'Critérios e versões por origem (JSON)':json.dumps(versions,ensure_ascii=False,separators=(',',':')),
'Interface':old['Interface']+'\n'+tag+'Instalador/recibo/perfilvazio/atualização/Back-Forward vistos no Chromium; recuperação nova sessão manual, logout/Backlogin verificado.',
'API':old['API']+'\n'+tag+'Prévia200/POST302/replay200/alterado409; perfil200; APIpóslogout401.',
'Gravação':old['Gravação']+'\n'+tag+'050000:2colunasopcionais/1audit/1migration;0tabelas/contas/org/vínculos novos; nenhuma gravação de país mestre real.',
'Recuperação':old['Recuperação']+'\n'+tag+'Perfil ativo200/F5/Back-Forward cifrados; perfilGET200/no-store após segundoMFA e confirmação visualmanual. País mestre real ainda indisponível0org.',
'Resultado apresentado':old['Resultado apresentado']+'\n'+tag+'Extensão instalada e COUNTRY02ON; perfil mostra ausência de representação; logoutnegaAPI401 e Backlogin. Não declara mestre real gravado.',
'Consolidação':old['Consolidação']+'\n'+tag+'MesmoID/fontes/variantes; apenas critérios com prova própria. AUD-COUNTRY-02 passa de implementado mas nãoativado para parcial porque mestre ativo0/0; nenhum fechoA/B/C37 reaberto.'
}
else:
values={'Estado':'parcial','Pendência':remaining if req=='AUD-COUNTRY-02' else ops_remaining,
'Correção/componentes':old['Correção/componentes']+'\n'+tag+active+' '+evidence,
'Dependência/pergunta':remaining if req=='AUD-COUNTRY-02' else 'Autorizações próprias para outros pacotes; ofertas/competências/SLA não decididos. QA isolada para continuar. ContençãoOFFpreservaHTTPS/schema/dados.',
'Aceitação':old['Aceitação']+'\n'+tag+'Instalação/runtime/sessão demonstrados; manter pendente o mestre ativo legítimo e os consumidores independentes.',
'Ordem do próximo lote':'COUNTRY02: quando existir organização/representação legítima pelo fluxo autorizado, demonstrar gravar/recuperar/reutilizar país sem reescrever submissões. Restante loteQA por critério já identificado; nenhuma ativação adicional.'}
patches.append({'row':index,'values':values})
assert len(patches)==2
edits.append({'filename':filename,'input_sha256':hashlib.sha256(data).hexdigest(),'rows':rows,'allowed_columns':list(dict.fromkeys(k for p in patches for k in p['values'])),'patches':patches})
(PACKET/'csv-edits.json').write_text(json.dumps({'files':edits,'proof_filename':'csv-authoring-proof.json'},ensure_ascii=False),encoding='utf-8')
print(json.dumps({'document_result':'active_COUNTRY02_verified','CSV_packet_prepared':True,'CSV_files_not_yet_changed':2,'new_session_and_logout_verified':True,'master_active_save_pending':True,'database_access':False},ensure_ascii=False))