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

82 lines
7.5 KiB
Python

"""Verify the exact authorized archive and prepare private snapshots; do not stage active code."""
from pathlib import Path
from datetime import datetime, timezone
import json, hashlib, zipfile, shutil
from cryptography import x509
from cryptography.hazmat.primitives import hashes, serialization
ROOT=Path(__file__).resolve().parent.parent
D=ROOT/'storage/app/private/investmoz-v2/portal-audit'
OUT=ROOT/'outputs/investmoz-portal-stability-20261011'
read=lambda p:json.loads(Path(p).read_text(encoding='utf-8-sig'))
sha=lambda p:hashlib.sha256(Path(p).read_bytes()).hexdigest()
manifest=read(OUT/'manifesto-candidata.json'); envelope=read(OUT/'manifesto-pacote.json')
REV='485ee04c40b2754ab668f48fd84d8559fb19a8b98f4b9af50fc0517bb488ca76'
ARCHIVE='033368b9532c94e303de07330abe6ab4afa63d9d6f2879044bfd3fe680364704'
assert manifest['delivery_revision']==envelope['delivery_revision']==REV
assert sha(OUT/envelope['archive'])==envelope['archive_sha256']==ARCHIVE
assert hashlib.sha256(json.dumps(manifest['revision_input'],sort_keys=True,separators=(',',':')).encode()).hexdigest()==REV
directory=D/('stability-active-'+datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S'));directory.mkdir(exist_ok=False)
package=directory/'verified-package';package.mkdir()
with zipfile.ZipFile(OUT/envelope['archive']) as z:
assert len(z.namelist())==envelope['entries'] and set(z.namelist())==set(envelope['verified_members_sha256'])
for name,digest in envelope['verified_members_sha256'].items():
assert ':' not in name and not name.startswith(('/', '\\')) and not any(p in ['.','..'] for p in name.split('/'))
b=z.read(name);assert hashlib.sha256(b).hexdigest()==digest
target=package/name;assert target.resolve().is_relative_to(package.resolve());target.parent.mkdir(parents=True,exist_ok=True);target.write_bytes(b)
changed=manifest['revision_input']['application_files']
for name,record in changed.items():
target=ROOT/name
assert (sha(target) if target.exists() else None)==record['active_before'], 'Active source differs: '+name
assert sha(package/'application'/name)==record['after']
for name,digest in manifest['production_inputs'].items():
if name not in changed:assert (ROOT/name).is_file() and sha(ROOT/name)==digest, 'Unchanged application input differs: '+name
baseline=read(D/'portal-stability-latest.json')
for filename in ['country02-interface.json','country02-persistencia.json','consumidores-persistencia.json','runtime-qa.json','backup-pos-utilizacao-qa.json']:
assert read(package/filename)['result']=='passed'
assert not (ROOT/'bootstrap/cache/config.php').exists(),'Unexpected config cache'
fresh=read(D/'country-candidate-backup-latest.json');backup=read(fresh['path'])
assert backup['restore_verified'] and backup['files_verified'] and backup['sequences_verified'] and not backup['source_installations']['country_flag']
assert backup['source_installations']['migrations']==[] and backup['source_installations']['country_columns']==[]
assert backup['installer_eligibility_counts']['verified_managers_with_mfa']>0,'No existing verified manager with normal MFA'
assert sha(backup['dump_path'])==backup['dump_sha256'] and sha(backup['files_path'])==backup['files_sha256']
assert 0<=(datetime.now(timezone.utc)-datetime.fromisoformat(backup['verified_at'])).total_seconds()<86400
old=read(read(D/'country-https-backup-latest.json')['path'])
volatile={'"public"."sessions"','"public"."cache"','"public"."cache_locks"'}
assert set(old['tables'])==set(backup['tables']) and old['sequences']==backup['sequences']
assert all(state==old['tables'][name] for name,state in backup['tables'].items() if name not in volatile),'Business baseline differs from demonstrated QA'
config=read(ROOT/'outputs/investmoz-country02-https-20261011/configuracao-ativa-leitura.json')
for name in ['active_vhost','apache_config','ssl_include']:
assert sha(config[name]['path'])==config[name]['sha256'],'Apache baseline differs'
apache=Path('C:/laragon/bin/apache/httpd-2.4.54-win64-VS16/bin/httpd.exe')
dll=apache.parent/'nghttp2.dll'
assert sha(dll)=='27dbfd55cbff5dc0f580b4e109f18e651c804322382c04dd9588c733517a34f6'
assert sha(package/'runtime/nghttp2.dll')=='4bee6c3aa3270c49395ff528db1ebd1e3d78c211503a7b9ea6c23aa9ad2dec85'
tls=Path('C:/Users/macbook/.config/investmoz/tls')
leaf,ca=x509.load_pem_x509_certificates((tls/'portal-investidor.test-fullchain.pem').read_bytes())
key=serialization.load_pem_private_key((tls/'portal-investidor.test-key.pem').read_bytes(),None)
assert ca.fingerprint(hashes.SHA1()).hex().upper()=='7D30D7FB226481B6243051309818CF426AF6B6B7'
assert leaf.fingerprint(hashes.SHA256()).hex()=='df368329204106b6a182c79e7e5c98a8e73890479cb7bca7c3d0928076a60ed5'
assert leaf.not_valid_before_utc<=datetime.now(timezone.utc)<leaf.not_valid_after_utc
assert leaf.extensions.get_extension_for_class(x509.SubjectAlternativeName).value.get_values_for_type(x509.DNSName)==['portal-investidor.test']
assert key.public_key().public_bytes(serialization.Encoding.DER,serialization.PublicFormat.SubjectPublicKeyInfo)==leaf.public_key().public_bytes(serialization.Encoding.DER,serialization.PublicFormat.SubjectPublicKeyInfo)
leaf.verify_directly_issued_by(ca)
files={};absent=[]
def preserve(file,label):
file=Path(file)
if not file.exists():absent.append(str(file));return
saved=directory/'snapshots'/label;saved.parent.mkdir(parents=True,exist_ok=True);shutil.copyfile(file,saved)
assert sha(file)==sha(saved);files[str(file)]={'backup':str(saved),'sha256':sha(saved)}
for name in changed:preserve(ROOT/name,'sources/'+name)
for name in ['.env','public/build/manifest.json']:preserve(ROOT/name,'runtime/'+name)
for label in ['active_vhost','apache_config','ssl_include']:preserve(config[label]['path'],'apache/'+label)
for file in Path('C:/laragon/etc/apache2/sites-enabled').glob('*.conf'):preserve(file,'apache/vhosts/'+file.name)
preserve('C:/laragon/etc/apache2/mod_php.conf','apache/mod_php.conf')
preserve('C:/laragon/bin/php/php-8.5.11-Win32-vs17-x64/php.ini','runtime/php.ini')
preserve(dll,'runtime/nghttp2.dll')
preserve(ROOT/'storage/app/private/investmoz-v2/organization-country-installation/readiness.json','runtime/readiness.json')
receipt={'result':'verified_snapshots_ready_before_Laragon_stop','at':datetime.now(timezone.utc).isoformat(),'delivery_revision':REV,'archive_sha256':ARCHIVE,'package':str(package),'directory':str(directory),'backup_link':fresh,'code_hash':manifest['country_installation_fingerprint'],'production_manifest_sha256':manifest['production_manifest_sha256'],'production_input_hash':manifest['application_input_hash'],'application_files':changed,'backup_files':files,'previously_absent_files':absent,'apache':str(apache),'dll':str(dll),'active_vhost':config['active_vhost']['path'],'certificate_verified':True,'existing_manager_MFA_available':True,'country_enabled':False,'installed':False,'active_database_writes':0,'active_source_configuration_runtime_writes':0,'QA_reuse_basis':'Exact final code/bundle and existing demonstrated QA receipts; fresh restored business rows/schema/sequences match prior QA baseline except allowed volatile session/cache tables'}
(directory/'preparation.json').write_text(json.dumps(receipt,indent=2),encoding='utf-8')
(D/'stability-active-preparation-latest.json').write_text(json.dumps(receipt,indent=2),encoding='utf-8')
print(json.dumps({'result':receipt['result'],'revision':REV[:12],'package_sha256':ARCHIVE,'fresh_backup':backup['verified_at'],'tables':len(backup['tables']),'files':backup['file_count'],'snapshot_files':len(files),'source_files_prepared':len(changed),'existing_manager_MFA_available':True,'active_changes':0}))