82 lines
7.5 KiB
Python
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}))
|