125 lines
9.3 KiB
PHP
125 lines
9.3 KiB
PHP
<?php
|
|
|
|
// Guarded READ ONLY destination check for the specifically authorized fixed revision.
|
|
ini_set('zend.exception_ignore_args', '1');
|
|
$root = dirname(__DIR__);
|
|
$out = $root.'/outputs/investmoz-internal-designation-20261011';
|
|
$private = $root.'/storage/app/private/investmoz-v2/portal-audit';
|
|
set_exception_handler(static function (Throwable $e) use ($out): void {
|
|
file_put_contents($out.'/disponibilizacao-preflight.json', json_encode([
|
|
'result'=>'stopped_before_application', 'at'=>gmdate('c'), 'type'=>$e::class,
|
|
'file'=>basename($e->getFile()), 'line'=>$e->getLine(), 'active_writes'=>0,
|
|
'reason'=>$e instanceof LogicException ? $e->getMessage() : 'Sanitized technical failure; no application performed.',
|
|
], JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
|
|
fwrite(STDERR, 'Preflight stopped before application; sanitized evidence retained.'.PHP_EOL);
|
|
exit(1);
|
|
});
|
|
$read = static fn ($path) => json_decode(file_get_contents($path), true, flags: JSON_THROW_ON_ERROR);
|
|
$require = static function (bool $test, string $message): void {
|
|
if (! $test) throw new LogicException($message);
|
|
};
|
|
$m = $read($out.'/manifesto.json');
|
|
$final = $read($out.'/verificacao-final.json');
|
|
$link = $read($private.'/internal-designation-target-backup-link.json');
|
|
$b = $read($link['path']);
|
|
$require($final['revision']==='2876cdfa900a' && $m['delta_sha256']===$final['code_revision'], 'Candidate correspondence differs.');
|
|
$require(hash_file('sha256',$out.'/manifesto.json')===$final['manifest_sha256']
|
|
&& hash_file('sha256',$out.'/'.$m['package'])===$final['package_sha256'], 'Package or manifest differs.');
|
|
$require($b['candidate_code_revision']===$m['delta_sha256'] && $b['candidate_zip_sha256']===$final['package_sha256']
|
|
&& $b['candidate_manifest_sha256']===$final['manifest_sha256'], 'Backup belongs to a different revision.');
|
|
$require($b['restore_verified'] && $b['files_verified'] && $b['sequences_verified']
|
|
&& empty($b['missing_files']), 'Restore evidence incomplete.');
|
|
// The backup receipt covers restoration only; QA/deployment reversal are distinct receipts.
|
|
$rehearsal=$read($out.'/ensaio-disponibilizacao-reversao.json');
|
|
$browser=$read($out.'/navegador-qa.json');
|
|
$require($final['QA_code_deployment_and_reversal_passed'] && $rehearsal['result']==='passed'
|
|
&& $browser['result']==='passed' && $browser['code_revision']===$m['delta_sha256'], 'Fixed QA or deployment-reversal evidence incomplete.');
|
|
$require(time()<strtotime($b['valid_until']), 'Backup recorded validity expired.');
|
|
$require(hash_file('sha256',$b['dump_path'])===$b['dump_sha256']
|
|
&& hash_file('sha256',$b['files_path'])===$b['files_sha256'], 'Protected backup artifact differs.');
|
|
foreach ($m['files'] as $item) {
|
|
$path=$root.'/'.$item['path'];
|
|
$require((is_file($path)?hash_file('sha256',$path):null)===$item['before_sha256'], 'Prepared destination baseline differs: '.$item['path']);
|
|
}
|
|
$require(hash_file('sha256',$root.'/public/build/manifest.json')===$b['active_manifest_sha256'], 'Active bundle baseline differs.');
|
|
$require(hash_file('sha256',$root.'/.env')===$b['active_files_manifest']['runtime/.env'], 'Destination configuration changed since backup.');
|
|
foreach ($b['active_files_manifest'] as $entry=>$hash) {
|
|
if (str_starts_with($entry,'private/')) {
|
|
$require(hash_file('sha256',$root.'/storage/app/'.$entry)===$hash, 'An original private document changed.');
|
|
}
|
|
}
|
|
require $root.'/vendor/autoload.php';
|
|
$app = require $root.'/bootstrap/app.php';
|
|
(new Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables)->bootstrap($app);
|
|
(new Illuminate\Foundation\Bootstrap\LoadConfiguration)->bootstrap($app);
|
|
$c=config('database.connections.'.config('database.default'));
|
|
$require($c['driver']==='pgsql' && empty($c['url']) && in_array($c['host'],['127.0.0.1','localhost'],true)
|
|
&& (int)$c['port']===5432 && $c['database']==='investemoz', 'Effective destination refused.');
|
|
$require(config('app.url')==='https://portal-investidor.test'
|
|
&& config('portal.organization_country_enabled') && ! config('portal.profile_access_enabled')
|
|
&& ! config('portal.opportunity_dashboard_access_enabled') && ! config('portal.opportunity_specialized_drafts_enabled')
|
|
&& ! config('portal.profile_administration_enabled',false)
|
|
&& ! config('portal.notifications_enabled') && ! config('portal.opportunity_modular_profiles_qa_enabled')
|
|
&& ! config('portal.legacy_content_qa_enabled') && ! config('investor-path.enabled'), 'Destination flags do not match authorized scope.');
|
|
$require(! $app->configurationIsCached(), 'Cached configuration differs from rehearsed procedure.');
|
|
$pdo=new PDO('pgsql:host='.$c['host'].';port='.$c['port'].';dbname='.$c['database'],$c['username'],$c['password'],[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION]);
|
|
$pdo->exec('BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY');
|
|
$require($pdo->query('SELECT current_database()')->fetchColumn()==='investemoz'
|
|
&& (int)$pdo->query('SELECT inet_server_port()')->fetchColumn()===5432
|
|
&& $pdo->query("SELECT current_setting('transaction_read_only')")->fetchColumn()==='on', 'Actual destination identity refused.');
|
|
$actualTables=$pdo->query("SELECT schemaname,tablename FROM pg_tables WHERE schemaname NOT IN ('pg_catalog','information_schema') ORDER BY schemaname,tablename")->fetchAll(PDO::FETCH_ASSOC);
|
|
$changed=[];
|
|
foreach ($actualTables as $table) {
|
|
$name='"'.str_replace('"','""',$table['schemaname']).'"."'.str_replace('"','""',$table['tablename']).'"';
|
|
$rows=$pdo->query('SELECT row_to_json(t)::text FROM '.$name.' t ORDER BY row_to_json(t)::text')->fetchAll(PDO::FETCH_COLUMN);
|
|
if (!isset($b['tables'][$name]) || $b['tables'][$name]!==['count'=>count($rows),'sha256'=>hash('sha256',implode("\n",$rows))]) $changed[]=$name;
|
|
unset($rows);
|
|
}
|
|
$require(count($actualTables)===count($b['tables']), 'Destination schema changed since verified restore.');
|
|
$volatile=['"public"."cache"','"public"."cache_locks"','"public"."sessions"'];
|
|
$durableChanged=array_values(array_diff($changed,$volatile));
|
|
$require($durableChanged===[], 'Permanent destination data changed since backup; renew before release: '.implode(', ',$durableChanged));
|
|
$sequences=$pdo->query("SELECT * FROM pg_sequences WHERE schemaname NOT IN ('pg_catalog','information_schema') ORDER BY schemaname,sequencename")->fetchAll(PDO::FETCH_ASSOC);
|
|
$require($sequences===$b['sequences'], 'Destination sequences changed since backup.');
|
|
$counts=[];
|
|
foreach(['users','organizations','memberships','portal_role_assignments','portal_profile_requests','jobs','failed_jobs'] as $table) {
|
|
$counts[$table]=(int)$pdo->query('SELECT count(*) FROM "'.$table.'"')->fetchColumn();
|
|
}
|
|
$pair=$pdo->query('SELECT first_user_id,second_user_id,version FROM portal_superuser_pair WHERE id=1')->fetch(PDO::FETCH_ASSOC);
|
|
$require(array_map('intval',[$pair['first_user_id'],$pair['second_user_id']])===[13,2], 'Initialized owner pair differs.');
|
|
$require((int)$pdo->query("SELECT count(*) FROM migrations WHERE migration IN ('2026_10_09_030000_extend_opportunity_resources','2026_10_10_040000_create_portal_profile_authorization','2026_10_10_050000_extend_organization_country')")->fetchColumn()===3, 'Existing prerequisite installations differ.');
|
|
$mfaReadable=0;
|
|
$keys=array_values(array_unique(array_filter([config('app.key'),...config('app.previous_keys',[])])));
|
|
foreach($pdo->query('SELECT two_factor_secret,two_factor_recovery_codes FROM users WHERE two_factor_secret IS NOT NULL')->fetchAll(PDO::FETCH_ASSOC) as $record) {
|
|
foreach(['two_factor_secret','two_factor_recovery_codes'] as $field) {
|
|
if($record[$field]===null) continue;
|
|
$readable=false;
|
|
foreach($keys as $key) {
|
|
try {
|
|
$crypt=new Illuminate\Encryption\Encrypter(str_starts_with($key,'base64:')?base64_decode(substr($key,7),true):$key,config('app.cipher'));
|
|
$plain=$crypt->decrypt($record[$field],false);
|
|
$readable=is_string($plain)&&$plain!=='';
|
|
unset($plain);
|
|
if($readable) break;
|
|
} catch(Illuminate\Contracts\Encryption\DecryptException) {}
|
|
}
|
|
$require($readable,'Existing encrypted MFA envelope unreadable.');
|
|
}
|
|
$mfaReadable++;
|
|
}
|
|
$pdo->exec('ROLLBACK');
|
|
$proof=[
|
|
'result'=>'ready_for_authorized_code_and_administration_only', 'at'=>gmdate('c'),
|
|
'revision'=>'2876cdfa900a','manifest_sha256'=>$final['manifest_sha256'],'package_sha256'=>$final['package_sha256'],
|
|
'database'=>'investemoz','port'=>5432,'read_only'=>true,'tables_unchanged'=>count($actualTables),
|
|
'sequences_unchanged'=>count($sequences),'counts'=>$counts,'pair'=>$pair,'encrypted_MFA_readable'=>$mfaReadable,
|
|
'backup_archive_hashes_verified'=>true,'backup_revision_corresponds'=>true,'backup_permanent_source_data_unchanged'=>true,
|
|
'volatile_tables_changed_since_snapshot'=>$changed,'permanent_tables_changed_since_snapshot'=>$durableChanged,
|
|
'backup_valid_until'=>$b['valid_until'],'existing_originals_unchanged'=>true,'source_hashes_match'=>true,
|
|
'COUNTRY02'=>true,'workflow'=>false,'administration_before'=>false,
|
|
'active_writes'=>0,'no_migration_required'=>true,'no_service_change_required'=>true,
|
|
'authorization'=>'User explicitly confirmed fixed revision 2876cdfa900a administration-only release in this turn',
|
|
];
|
|
file_put_contents($out.'/disponibilizacao-preflight.json',json_encode($proof,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR).PHP_EOL);
|
|
echo json_encode($proof,JSON_THROW_ON_ERROR).PHP_EOL;
|