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

139 lines
23 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Seal one bounded QA candidate, with destination blockers distinct from clone evidence."""
from pathlib import Path
from datetime import datetime,timezone
import json,hashlib,zipfile,shutil,csv,collections,xml.etree.ElementTree as ET
csv.field_size_limit(20000000)
R=Path(__file__).resolve().parent.parent;P=R/'storage/app/private/investmoz-v2/portal-audit';D=R/'docs/investmoz/v2/auditoria-portal'
read=lambda n:json.loads((P/n).read_text(encoding='utf-8'))
sha=lambda p:hashlib.sha256(Path(p).read_bytes()).hexdigest()
code=read('workflow-upgrade-code-20261011.json');build=read('workflow-upgrade-production-build-20261011.json');backup=read('workflow-upgrade-backup-restoration-qa-20261011.json');target=read('workflow-upgrade-target-backup-20261011.json')
deps=read('workflow-upgrade-final-previews-chromium-20261011.json');counts=read('workflow-upgrade-finalize-20261011/counts.json');source=Path(read('workflow-upgrade-latest.json')['source'])
oldFolder=R/'outputs/investmoz-opportunity-workflow-scope-20261011';old=json.loads((oldFolder/'manifesto.json').read_text(encoding='utf-8'));oldPackage=json.loads((oldFolder/'pacote.json').read_text(encoding='utf-8'))
oldZip=next(oldFolder.glob('*.zip'));assert sha(oldZip)==oldPackage['sha256']
for p in [backup,target]:
assert p['result']=='passed'
for field in ['dump_path','files_path']:assert sha(p[field])==p[field.replace('_path','_sha256')]
assert code['code_revision']==backup['code_revision']==target['candidate_code_revision']
assert build['manifest_sha256']==code['production_manifest_sha256']==backup['production_manifest_sha256']==target['candidate_qa_manifest_sha256']
assert deps['result']=='passed' and deps['resources_preview']['code_hash']==backup['resource_fingerprint'] and deps['profiles_preview']['code_hash']==backup['profile_fingerprint']
names=['workflow-upgrade-fixed-install-chromium-20261011.json','workflow-upgrade-document-pipeline-chromium-20261011.json','workflow-upgrade-office-chromium-20261011.json','workflow-upgrade-final-ui-chromium-20261011.json','workflow-upgrade-final-previews-chromium-20261011.json']
for n in names:assert read(n)['result']=='passed'
for n,h in code['code_files'].items():assert sha(source/n)==h
assert len(code['code_files'])==17 and len(build['assets'])==158
for n,h in code['active_base_hashes'].items():assert (sha(R/n)if(R/n).is_file()else None)==h
for n,h in read('functional-active-files-before-20261011.json').items():assert sha(R/n)==h
blocked=[
{'id':'B1','condition':'Atualizar/validar o ClamD real do destino e a atualização periódica das definições','evidence':'Leitura15:05Z:ClamD3310 versão1.5.4,assinaturas09out08:25Z,signatures_stale;QA13310assinaturas11out08:26Z.','success':'Health disponível dentro do limite48h e scan real de um ficheiro autorizado no destino;sem herdarem clean daQA.'},
{'id':'B2','condition':'Configurar/verificar worker supervisionado e runtime documental efetivo no destino','evidence':'Destino tem QUEUE_CONNECTION=database; isso não comprova worker. Snapshot com0jobs/0failed_jobs. Clone demonstra worker,210s,repetição,GD/WebP e recuperar bytes.','success':'PHP/extensões/limites/serviço real e reinício controlado verificados;worker processa envio autorizado e scans das derivadas,sem acesso indevido. Sem ingerir dados sintéticosQA.'},
{'id':'B3','condition':'Designar titulares legítimos, aprovador independente e âmbitos funcionais','evidence':'No destino há2gestores verificados comMFA;bootstrap exige3pessoas distintas. Zero organizações/representações. Só contas sintéticas foram preparadas nosclones.','success':'Responsável identifica2titulares+terceiro gestorMFA e perfis/tipos/organizações legítimos;cerimónia e concessões autenticadas. Não é necessário inventar organização para instalar.'}]
payload={**code['code_files'],**{'public/build/'+n:h for n,h in build['assets'].items()}}
core={k:v for k,v in old.items()if k not in ['revision','sealed_at']}
core.update({'format':'investmoz-workflow-upgrade-candidate-v1','previous_candidate':old['revision'],'code_revision':code['code_revision'],'payload':payload,'base_hashes':{n:sha(R/n)if(R/n).is_file()else None for n in payload},
'additional_changes':{'changed_from_previous':code['changed_from_previous'],'superuser':'Perfil operacional no âmbito + elevação independente de consulta,registo15min/MFA;sem acesso administrativo universal.','images':'Derivadas reutilizadas porhash/original;repetição sem novosstreams/versões/decisões.','UI':'Privacidade explícita e ação de consulta identificada.','after_installer_rehearsal':code['changed_since_authenticated_installer'],'after_installer_effect':code['installer_delta_scope']},
'production_manifest_sha256':build['manifest_sha256'],'recompilation_required':True,'profile_fingerprint':backup['profile_fingerprint'],'resources_fingerprint':backup['resource_fingerprint'],
'effects':{'code_files':17,'build_assets':158,'configuration':'Só flags do workflow após autorização/designações;preservarCOUNTRY02,HTTPS,cookie e chaves. Nenhuma configuração ativa alterada neste ensaio.','services':'ClamD e worker do destino ainda necessários;não demonstrados por configurarQA.','schema':'030000:3tabelas+documents.storage_encryption;040000:4tabelas+users.portal_access_revision+3colunasemopportunities. Total7tabelas/5colunas;020000jáexiste;050000preservada;190000fora.','data':'Não importaQA,cria contas,organizações,representações ou converte donos/estados históricos. Instalações acrescentam ledger/auditoria;bootstrap/grants são atos separados.'},
'privacy_policy':'Anexos privados seguem autorização vigente da oportunidade,tipo/organização e estado. Superuseradministrativo isolado não consulta;precisa de âmbito e elevação independente deconsulta para o registo. Auditor autorizado conserva leitura e restrição operacional. Classificação/publicação de anúncio não concede acesso ao ficheiro. Partilha individual permanece indisponível.',
'QA_evidence':{n:sha(P/n)for n in names},'reused_evidence':{'candidate':old['revision'],'criteria':old['reused_evidence'],'closures':['A/B','C37'],'not_repeated':'Coedição,contratos/workflowdas seis categorias e demais critérios válidos;repetidos apenas instalação/processamento/ACL/UIafetados.'},
'quality':{'production_build':True,'typescript_no_emit_exit':0,'affected_tests':'3testesafetados passaram no primeiro lote;teste novo corrigido após contrariar a própria constraint de autoaprovação,passou29assertions. Erro do roteiro/teste preservado;não foi falha do portal.','policy_validity_xml_sha256':sha(Path(read('portal-stability-latest.json')['source'])/'storage/workflow-upgrade-policy-validity-20261011.xml'),'image_and_previous_permissions_xml_sha256':sha(Path(read('portal-stability-latest.json')['source'])/'storage/workflow-upgrade-affected-20261011.xml')},
'QA_backup':{k:backup[k]for k in ['verified_at','target','restored','dump_sha256','files_sha256','table_count','sequence_count','file_count','MFA_envelopes_readable','original_non_transient_tables_preserved']},
'destination_backup':{k:target[k]for k in ['verified_at','target','restored_target','dump_sha256','files_sha256','file_count','MFA_envelopes_readable','active_originals_backed_up']},
'COUNTRY02_compatibility':{'active_ON':True,'050000_not_reapplied':True,'original_rows_cipher_history_preserved':True,'organizations_still_zero':'No real master-country write demonstration without legitimate organization/representation;not an installer block.'},
'destination_inventory':read('workflow-upgrade-target-readonly-20261011.json'),'destination_services_read_only':read('workflow-upgrade-destination-services-readonly-20261011.json'),
'activation_ready':False,'activation_authorized':False,'activation_blocks':blocked,'new_acervo_import_or_publication_included':False,'QA_fixtures_in_delivery':False,'audit_counts':counts})
revision=hashlib.sha256(json.dumps(core,sort_keys=True,separators=(',',':'),ensure_ascii=False).encode()).hexdigest();manifest={**core,'revision':revision,'sealed_at':datetime.now(timezone.utc).isoformat()}
out=R/'outputs/investmoz-workflow-upgrade-20261011';assert not(out/'manifesto.json').exists(),'Previous fixed candidate must not be overwritten';out.mkdir(exist_ok=True)
(out/'manifesto.json').write_text(json.dumps(manifest,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
report=f'''# Ensaio de atualização e candidata {revision[:12]}
**Código preparado e critérios abaixo demonstrados em QA; não pronta para disponibilização ativa.** Código `{code['code_revision']}`. A candidata `e871f8aadb59` fica preservada como referência. COUNTRY02 ativo e fechos A/B/C37 não foram alterados nem repetidos. Sem escrita no destino.
## Provas do clone e verificações do destino
| Critério | Demonstração do clone | Destino, somente leitura |
| --- | --- | --- |
| 030000→040000 | Chromium HTTPS/MFA normal; prévia divergente409 semDDL; repetição030000 exige renovar prévia,sem duplicação;040000 mantém chave/mesmo recibo e rejeita conteúdo divergente. Instalação não concede perfis. Bootstrap distinto com duas contas sintéticas e terceiro aprovador. | 020000/050000 instaladas;030000/040000 ausentes;COUNTRY02ON,demaisflagsOFF. |
| Preservação | Snapshot288tabelas; adição prevista7→295. Up/down/rollback vazio conferido. Após uso,285tabelas originais não transitórias preservadas pelos campos originais; originais,permissões,histórico e doisMFA antigos conservados. Zero organizações/representações em todosestesclones. | 288tabelas,13contas,0organizações/representações/geometrias;nenhuma alteração ativa. |
| Envio/fila/scan | PDF e PNG enviados pelo navegador ao servidorQA. Quarentena423. Falha **injetada e identificada**:worker sai70 após reservar umjob;original/fila preservados. Mesmojob retoma após o prazo real210s,sem reenvio. | Configuração database e0jobs/0failed_jobs no snapshot não comprovam worker supervisionado. |
| ClamD/derivadas | ClamD1.5.4/13310,assinaturas11out08:26UTC;GD/WebP real. TrêsWebP320/640/800 com scans independentes. Repetição200 mantémUUIDs,hashes,cifra,versões e decisões. Originais cifrados preservados;recuperação exata porhash. | ClamD3310,assinaturas09out08:25UTC,stale/indisponível. Não foi atualizado neste âmbitoQA. |
| Tipos/documentos | PDF/PNG/3WebP eDOCX/XLSX recuperáveis; Office não é convertido/publicado automaticamente. Provas anteriores de contratos/documentos das seis categorias conservadas. | Nenhum ficheiroQA importado ou publicado. Outros consumidores/exportações normativas não encerrados por estes testes. |
| Superutilizador | Sem âmbito403;com âmbito ainda403 sem elevação. Outro titular aprova consulta para umdossier15min:200/hash;autoaprovação/outro dossier403. Revogação invalida sessão/URL anterior e novo login403. Expiração e Auditor acumulado recusados no teste afetado. | Não há titulares novos escolhidos ou privilégios alterados. |
| Interface/bundle | Aviso de privacidade e derivada visíveis390px;opção «Consultar dossier e anexos»;hash doJS servido confere com compilação final. Roteiros inicialmente pararam por erros de rota/parser/seleção da aba;corrigidos,separados de falhas do portal. | Bundle/HTTPS/cookie/chaves ativos inalterados. |
Atualização final do rótulo de consulta alterou apenas`resources/js/pages/profiles/administration.tsx` após o ensaio de instalação;pedidos,autorização,DDL,transações e política iguais. A interface/bundle afetados foram reensaiados e as prévias/backup foram renovados para os fingerprints finais. Não se apresenta o fingerprint anterior como o final.
## Âmbito incluído e indisponível
Mantém as seis categorias base e as variantes delimitadas da candidata anterior:projeto;venda/arrendamento imobiliário concluído,desocupado,sem encargos;fundiário;venda de negócio;parceria;franquia. Provas30–35/54,renda4500MZN/mês,materialidade/revisões independentes e coedição são reutilizadas. Este lote reforça instalação,anexos e processamento;não certifica toda variante ou decisão externa.
**Indisponíveis:** restauração de oportunidade;ACL/partilha por destinatário individual;Response/§27normativo integral;variantes não ensaiadas;módulos/ofertas/SLA;legado;percurso integrado190000;importação/divulgação de camadas;dispositivos físicos/acessibilidade integral. Os limites permanecem visíveis e as flags própriasOFF. Cartografia do destino tem1990registos e zero geometrias: cadastro não comprova mapas com limites. Não instalar/importar camadas para contornar isso neste pacote.
R-ARCHIVE continua proposta separada:Publicador autorizado/independente comMFA,motivo/auditoria→`needs_information` não público,nunca republicação automática. R-DRAFT tem regra própria:autor/coeditorPromotor autorizado→`draft` privado,só se nunca submetido/publicado. Restauro de anexos anterior é distinto. Nenhuma proposta foi ativada.
## Lista única de bloqueios restantes à disponibilização
1. **B1 — ClamD do destino:** atualizar definições oficiais,validarhealth/scan real e periodicidade. Prova do13310QA não é prova do3310ativo.
2. **B2 — execução documental do destino:** worker supervisionado,PHP/extensões/GD/WebP/limites e recuperação real;configuração de fila isoladamente não basta. Rever fila antes de iniciar;atualmente0jobs/0falhas no snapshot.
3. **B3 — designações legítimas:** dois titulares,terceiro gestor independente comemail/MFAconfirmados e perfis/âmbitos funcionais. Hoje só2gestoresMFA são elegíveis;ninguém foi escolhido. Organizações podem ser preenchidas posteriormente pelo fluxo legítimo;instalar não exige criar uma organização fictícia.
Depois destas condições,a revisão completa continua a precisar de autorização própria antes de disponibilização. Restauração/partilha e demais exclusões são decisões/frentes separadas,sem bloquear artificialmente o workflow delimitado.
## Designações concretas a fornecer pelo responsável
- TitularA e TitularB:duas contas legítimas distintas,verificadas eMFAconfirmado.
- Gestor da cerimónia:terceira conta distinta,legitimamente autorizada para instalação,verificada eMFAconfirmado;aprova o bootstrap.
- Promotores:identidade legítima,individual ou representação real existente,tipo autorizado;pedido documental e decisão independente.
- Validador e Publicador:contas/âmbitos/tipos concretos,independentes do autor/editor/representante e entre aprovação/publicação. Coordenador/revisores/recurso só para responsabilidades efetivamente designadas;nenhum nome,SLA ou suplência inventado.
## Backup/restauro correspondente
Destino READONLY `{target['target']['database']}:{target['target']['port']}`,verificado`{target['verified_at']}`:288tabelas/31seq/799ficheiros/4originaisprivados/2MFA,restauro exclusivoQA. Inclui configuração/chaves/certificado/vhosts/bundle ativos em arquivo privado,**não na entrega**. Pós-usoQA`{backup['verified_at']}`:295tabelas/31seq/188ficheiros/9MFA. Diferença288→295 são exatamente7tabelas030000+040000. BackupQA nunca substitui o snapshot do alvo. Renovar/revalidar após atualização de serviços,alteração do alvo/revisão,expiração ou antes da intervenção futura.
## Matriz e evidências
Mesma matriz:{counts['requirements']}requisitos/{counts['rows']}linhas,{counts['source_occurrences']}ocorrências. Só seteIDs atualizados,zero novos requisitos. Estados preservados:{counts['states']}. C127 identifica leitura administrativa global encontrada por código;C128 identifica geração repetida incondicional. As provasHTTP posteriores são explicitamente distinguíveis das constatações de código.
Ver`evidencias-por-criterio.json`,`manifesto.json`e`procedimento.md`. Testes/hashes sustentam os resultados visíveis e recuperáveis;não substituem a verificação do destino. Nenhuma ativação incluída.
'''
(out/'relatorio.md').write_text(report,encoding='utf-8')
procedure=f'''# Procedimento único — candidata {revision[:12]}; ativação ainda não autorizada
1. Conferir pacoteSHA-256/manifesto/revisão`{revision}`/código`{code['code_revision']}`e baseline485ee04c40b2. Resolver B1–B3 antes de disponibilizar. Preservar a referência anterior;nenhuma migração global/seed/importação.
2. Agrupar intervenções necessárias:responsável designa2titulares+terceiro gestor+âmbitos;operador configura scanner/atualizador eworker supervisionado,certifica PHP/extensões/GD/WebP. Qualquer atualização destes serviços do ativo e disponibilização desta revisão requer autorização própria. Não pedir ao investidor para descobrir a infraestrutura incompleta.
3. Revalidar alvo READONLY,COUNTRY02ON/050000,ledger/schema020000/030000/040000 eprivilegios. Renovar backup/restauro físico do alvo/chaves/originais/TLS/vhosts e conferir hashes desta revisão após os ajustes. Hoje:288tabelas,030/040ausentes,13contas/0org/0representações,0jobs;parar se houver divergência. Após uso,não executar down/drop/restauro completo automático.
4. Sob futura autorização:disponibilizar17ficheiros+158assets com workflowOFF. ManterCOUNTRY02ON,HTTPS,cookie,chaves,histórico. Este delta não substituiDLL nem configuraHTTPS. Se umserviço precisar de reinício,usar controlo normal doLaragon/conferirvhosts;não terminar processos indiscriminadamente. Verificarhashes doasset efetivamente servido.
5. Gestor existente com email verificado faz login/MFA normalmente. GET`https://portal-investidor.test/management/opportunity-resources-install`:prévia alvo/fingerprint`{backup['resource_fingerprint']}`/backup/installed/ready esperados. POST na mesma rota **com`hash,confirm`**;030000 não utiliza o reciboUUID de040000. Aplicar somente030000 se ausente;prévia antiga409 exigeGETnovo. Se já instalada,recuperar prévia atual e não reinstalar;a repetição com prévia atual não duplica ledger/auditoria. Parar antes de aplicar em divergência/readinessausente.
6. GET`https://portal-investidor.test/management/profile-install`:030000 íntegra,020000 e identidade/auditoria existentes;190000 não é dependência. Conferir fingerprint`{backup['profile_fingerprint']}`,target/hash/schema/backup. POST **`hash,confirm,idempotency_key,reason`**,UUID criptográfico estável para a mesma tentativa;mesma chave/conteúdo devolve o mesmo recibo,conteúdo divergente409. Aplicar somente040000. Zero contas/grants/organizações/representações automáticos;050000 não reaplicada.
7. Bootstrap separado POST`/management/profile-install/bootstrap`,prévia atual,`hash,confirm,idempotency_key,first_user_id,second_user_id,reason`;terceiro gestor distinto comMFA recente. Só titulares legitimamente designados,exatamente2. Atribuições pelo pedido documental/perfis existentes,sem autoconcessão. Instalar é independente de preencher posteriormente cadastros legítimos.
8. Habilitar só`PORTAL_PROFILE_ACCESS_ENABLED`,`PORTAL_OPPORTUNITY_DASHBOARD_ACCESS_ENABLED`,`PORTAL_OPPORTUNITY_SPECIALIZED_DRAFTS_ENABLED`no âmbito futuro autorizado. COUNTRY02 continuaON. Modulares,legado,190000,ofertas/SLA,restauração/partilha individual continuam indisponíveis. Publicação de oportunidade exige ação própria,não decorre da instalação.
9. Critérios de sucesso no destino:030/040ledger/schema coerentes e uma auditoria por instalação;050/Country eoriginais/histórico intactos;loginMFA/sessão;perfil legítimo→rascunho→guardar/recuperar→envio/quarentena→worker/scan/derivadas→hash/preview autorizado. Super sem âmbito/elevação,autoaprovação,fora do dossier e grant revogado recusados nas rotas próprias/genéricas/assinadas. Conferirbundle e resultado visível. ReutilizarA/B,C37 e provas anteriores;não repetir sem alteração relevante. Gravação real de país mestre permanece pendente até existir organização legítima representada;isso não impede instalar030/040.
10. Contenção:desligar somente flags do workflow e parar novos processamentos desse âmbito pelo controlo do serviço,preservando scanner seguro. **Conservar C126/C127 e a correção comum dos binários**;não repor leitura universal. PreservarHTTPS,cookie,COUNTRY02,colunas,originais/cifra,histórico,auditoria. DDL falhado deve reverter transação/ledger. Após utilização,nenhum down/drop;restauro completo ou recuperação destrutiva exige decisão separada e reconciliação das escritas posteriores. Restauração de oportunidades nunca repõe publicação automaticamente.
'''
(out/'procedimento.md').write_text(procedure,encoding='utf-8')
evidence={n:read(n)for n in names};evidence['worker_fault']=read('workflow-upgrade-worker-fault-20261011.json');evidence['reused_previous_criteria']=old['reused_evidence'];evidence['scope_note']='Clones55441;no active mutation;failure deliberately injected. Credentials/cookies/MFA codes and signed query omitted.'
(out/'evidencias-por-criterio.json').write_text(json.dumps(evidence,ensure_ascii=False,indent=2),encoding='utf-8')
zipPath=out/('investmoz-workflow-'+revision[:12]+'.zip')
with zipfile.ZipFile(zipPath,'w',zipfile.ZIP_DEFLATED)as z:
for n,h in payload.items():assert sha(source/n)==h;z.write(source/n,n)
for n in ['manifesto.json','relatorio.md','procedimento.md','evidencias-por-criterio.json']:z.write(out/n,'review/'+n)
with zipfile.ZipFile(zipPath)as z:
for n,h in payload.items():assert hashlib.sha256(z.read(n)).hexdigest()==h
assert not any(n.startswith(('storage/','vendor/','node_modules/')) or n.endswith(('.env','.dump'))for n in z.namelist())
package={'revision':revision,'code_revision':code['code_revision'],'filename':zipPath.name,'sha256':sha(zipPath),'manifest_sha256':sha(out/'manifesto.json'),'payload_files':len(payload),'code_files':17,'build_assets':158,'activation_ready':False,'activation_authorized':False,'active_writes':0}
(out/'pacote.json').write_text(json.dumps(package,indent=2),encoding='utf-8')
marker='workflow-upgrade-qa-20261011'
brief=f'''\n<!-- {marker} -->
## Atualização do clone do destino — {revision[:12]}
**QA demonstrada; ativo não atualizado.**030000→040000 autenticadas no clone real288→295;050000/COUNTRY02,originais,permissões,cifra/histórico preservados. PDF/PNG/DOCX/XLSX e3WebP com scanClamDreal/hash/recuperação;worker sai após reservar e recupera mesmojob210s depois;repetições sem duplicação. C127:Super requer perfil operacional no âmbito+elevação de consulta independente15min,sem aprovação própria;revogação/novo login e outro dossier recusados. C128:derivadas reutilizadas. UI390px e asset carregado conferidos. A/B,C37 e provas válidas conservadas.
Lista única de bloqueios:B1ClamD do destino stale;B2worker/PHPdocumental supervisionados por comprovar no destino;B3dois titulares+terceiro gestorMFA+âmbitos legítimos por designar. Instalações só sob futura autorização;190000 não bloqueia workflow isolado. Restauração e partilha individual permanecem desabilitadas. Candidata/código:{revision[:12]}/{code['code_revision'][:12]},17ficheiros/158assets,`activation_ready=false`. Relatório e procedimento:`outputs/investmoz-workflow-upgrade-20261011/`.
Mesma matriz3401requisitos/linhas,7739ocorrências;seteIDs atualizados,zero novos. Backup/restauro REAL do alvo `{target['verified_at']}`:288tabelas/31seq/799ficheiros/2MFA;QA pós-uso `{backup['verified_at']}`:295/31/188/9MFA. Não transformar este restauroQA em prova de funcionamento ativo. Renovar após ajustes/revisão/expiração/antes da intervenção.
*As secções seguintes conservam evidências e estados históricos;este bloco contém o estado atual.*
<!-- /{marker} -->
'''
for n in ['relatorio-executivo.md','backlog.md','workflow-criterios-saida-20261011.md','plano-ativacao.md']:
f=D/n;t=f.read_text(encoding='utf-8');assert marker not in t;first,rest=t.split('\n',1);f.write_text(first+'\n'+brief+rest,encoding='utf-8')
print(json.dumps(package,ensure_ascii=False))