185 lines
21 KiB
Python
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))
|