generated from jric11/baseProject
ChatGPT's update to harden software and run db updates.
This commit is contained in:
+86
-1
@@ -4,6 +4,7 @@ require_once __DIR__ . '/../classes/Base.php';
|
||||
$dsn = getenv('HCR_TEST_DSN');
|
||||
if (!$dsn) throw new RuntimeException('Set HCR_TEST_DSN to an isolated test server.');
|
||||
$db = new PDO($dsn, getenv('HCR_TEST_USER') ?: 'root', getenv('HCR_TEST_PASSWORD') ?: '', [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
|
||||
$db->exec('SET NAMES utf8mb4');
|
||||
$action = $argv[1] ?? '';
|
||||
$name = $argv[2] ?? ('hcr_http_test_' . bin2hex(random_bytes(6)));
|
||||
if (!preg_match('/\Ahcr_http_test_[a-f0-9]{12}\z/', $name)) throw new RuntimeException('Only generated test databases may be used.');
|
||||
@@ -22,11 +23,60 @@ if ($action === 'create') {
|
||||
putenv('HCR_DB_NAME=' . $name);
|
||||
require __DIR__ . '/../bin/migrate-auth.php';
|
||||
require __DIR__.'/../bin/migrate-production.php';
|
||||
putenv('HCR_SUPER_ADMIN_EMAIL=admin@example.test');
|
||||
require __DIR__.'/../bin/migrate-tenants.php';
|
||||
putenv('HCR_SUPER_ADMIN_EMAIL');
|
||||
require __DIR__.'/../bin/migrate-security-capacity.php';
|
||||
// Idempotent migration is verified separately by the HTTP test runner.
|
||||
echo json_encode(['database' => $name]);
|
||||
exit;
|
||||
}
|
||||
$db->exec("USE `{$name}`");
|
||||
if($action==='memberSummarySeed'){
|
||||
$a=(int)$argv[3];$b=(int)$argv[4];$own=(int)$argv[5];$neighbor=(int)$argv[6];$foreign=(int)$argv[7];$first=0;
|
||||
$season=$db->prepare('INSERT INTO club_seasons(club_serial,season_name,starts_on,ends_on) VALUES(?,?,?,?)');
|
||||
$due=$db->prepare('INSERT INTO club_dues(club_serial,membership_serial,season_serial,amount_cents,recorded_by) VALUES(?,?,?,?,1)');
|
||||
for($i=1;$i<=31;$i++){$season->execute([$a,'Member season '.sprintf('%02d',$i),'2026-01-01','2026-12-31']);$due->execute([$a,$own,(int)$db->lastInsertId(),10000]);if(!$first)$first=(int)$db->lastInsertId();}
|
||||
$pay=$db->prepare("INSERT INTO club_payments(due_serial,amount_cents,paid_on,request_key,payment_method,payment_reference,recorded_by) VALUES(?,100,'2026-01-01',?,'cash',?,1)");
|
||||
for($i=1;$i<=32;$i++)$pay->execute([$first,bin2hex(random_bytes(32)),'OWN-RECEIPT-'.sprintf('%02d',$i)]);
|
||||
$reversed=(int)$db->lastInsertId();
|
||||
$stmt=$db->prepare('INSERT INTO club_payment_reversals(payment_serial,reason,request_key,recorded_by) VALUES(?,?,?,1)');$stmt->execute([$reversed,'Own reversed receipt',bin2hex(random_bytes(32))]);
|
||||
$stmt=$db->prepare('INSERT INTO club_due_adjustments(due_serial,amount_cents,corrected_amount_cents,reason,request_key,recorded_by) VALUES(?,5000,15000,?,?,1)');$stmt->execute([$first,'Own correction <script>unsafe</script>',bin2hex(random_bytes(32))]);
|
||||
foreach([[$a,$neighbor,'NEIGHBOR-PRIVATE'],[$b,$foreign,'FOREIGN-PRIVATE']] as [$club,$membership,$marker]){$season->execute([$club,'Additional season '.$membership,'2026-01-01','2026-12-31']);$due->execute([$club,$membership,(int)$db->lastInsertId(),99999]);$pay->execute([(int)$db->lastInsertId(),bin2hex(random_bytes(32)),$marker]);}
|
||||
$season->execute([$a,'Available new season','2026-01-01','2026-12-31']);
|
||||
echo '{}';exit;
|
||||
}
|
||||
if($action==='sessionAge'){
|
||||
$id=$argv[3]??'';if(!preg_match('/\A[a-zA-Z0-9,-]{16,128}\z/',$id))throw new RuntimeException('Invalid test session.');
|
||||
session_name('HUNTINGCLUBROSTER');session_id($id);session_start();
|
||||
if(!isset($_SESSION[Base::USER_SERIAL]))throw new RuntimeException('Missing test session.');
|
||||
foreach(['authenticated_at','last_activity','recent_auth'] as $index=>$field){$age=$argv[4+$index]??'';if($age!=='')$_SESSION[$field]=time()-(int)$age;}
|
||||
session_write_close();echo '{}';exit;
|
||||
}
|
||||
if($action==='clearTestAttempts'){$db->exec('DELETE FROM auth_attempts');echo '{}';exit;}
|
||||
if($action==='mfaTestSession'){
|
||||
$id=$argv[3]??'';if(!preg_match('/\A[a-zA-Z0-9,-]{16,128}\z/',$id))throw new RuntimeException('Invalid test session.');
|
||||
session_name('HUNTINGCLUBROSTER');session_id($id);session_start();
|
||||
$user=(int)($_SESSION[Base::USER_SERIAL]??0);$stmt=$db->prepare('SELECT 1 FROM users WHERE user_serial=?');$stmt->execute([$user]);if(!$stmt->fetchColumn())throw new RuntimeException('Owned test account required.');
|
||||
if(($argv[4]??'')==='expire'&&isset($_SESSION['mfa_replacement']))$_SESSION['mfa_replacement']['expires']=time()-1;
|
||||
$state=$_SESSION;session_write_close();echo json_encode($state);exit;
|
||||
}
|
||||
if($action==='mfaTestAuditFailure'){
|
||||
if(($argv[3]??'')==='on')$db->exec("CREATE TRIGGER reject_mfa_test_audit BEFORE INSERT ON journal FOR EACH ROW SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Test audit failure'");
|
||||
else $db->exec('DROP TRIGGER IF EXISTS reject_mfa_test_audit');echo '{}';exit;
|
||||
}
|
||||
if($action==='mfaResetToken'){
|
||||
$token=bin2hex(random_bytes(32));$stmt=$db->prepare('INSERT INTO password_resets(token_hash,user_serial,expires_at) VALUES(?,1,DATE_ADD(UTC_TIMESTAMP(),INTERVAL 1 HOUR))');$stmt->execute([hash('sha256',$token)]);echo json_encode(['token'=>$token]);exit;
|
||||
}
|
||||
if($action==='mfaState'){
|
||||
$stmt=$db->prepare('SELECT user_serial FROM users WHERE user_email=?');$stmt->execute([$argv[3]]);$user=(int)$stmt->fetchColumn();
|
||||
$stmt=$db->prepare('SELECT * FROM user_mfa WHERE user_serial=?');$stmt->execute([$user]);$factor=$stmt->fetch();
|
||||
$stmt=$db->prepare('SELECT code_hash FROM mfa_recovery WHERE user_serial=?');$stmt->execute([$user]);echo json_encode(['factor'=>$factor,'recovery'=>$stmt->fetchAll(PDO::FETCH_COLUMN)]);exit;
|
||||
}
|
||||
if($action==='mfaCode'){
|
||||
$stmt=$db->prepare('SELECT user_serial FROM users WHERE user_email=?');$stmt->execute([$argv[3]]);$user=(int)$stmt->fetchColumn();
|
||||
$code=bin2hex(random_bytes(16));$stmt=$db->prepare('INSERT INTO mfa_recovery(user_serial,code_hash) VALUES(?,?)');$stmt->execute([$user,hash('sha256',$code)]);echo json_encode(['code'=>$code]);exit;
|
||||
}
|
||||
if($action==='fingerprints'){
|
||||
$result=[];foreach($db->query('SHOW FULL TABLES WHERE Table_type="BASE TABLE"')->fetchAll(PDO::FETCH_COLUMN) as $table){
|
||||
$rows=array_map(static fn($row)=>json_encode($row,JSON_THROW_ON_ERROR),$db->query('SELECT * FROM `'.str_replace('`','``',$table).'`')->fetchAll(PDO::FETCH_ASSOC));sort($rows,SORT_STRING);$result[$table]=hash('sha256',implode("\n",$rows));
|
||||
@@ -36,11 +86,46 @@ if($action==='verifyEmail'){
|
||||
$stmt=$db->prepare('UPDATE users SET user_email_verified_at=UTC_TIMESTAMP() WHERE user_name=?');$stmt->execute([$argv[3]??'']);echo json_encode(['verified'=>true]);exit;
|
||||
}
|
||||
if($action==='inspect'){
|
||||
$tables=['users','dropdowntypes','dropdowns','statuses','notes','journal','clubs','club_memberships','club_seasons','club_dues','club_payments','club_due_adjustments','club_payment_reversals'];$result=[];
|
||||
$tables=['users','dropdowntypes','dropdowns','statuses','notes','journal','clubs','club_memberships','club_seasons','club_dues','club_payments','club_due_adjustments','club_payment_reversals','club_invitations','club_tickets','club_ticket_messages'];$result=[];
|
||||
foreach($tables as $table)$result[$table]=$db->query('SELECT * FROM '.$table)->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach($result['users'] as &$user)unset($user['user_password_hash']);unset($user);
|
||||
echo json_encode($result);exit;
|
||||
}
|
||||
if($action==='legacyTenantSchema'){$db->exec('ALTER TABLE club_memberships DROP COLUMN membership_role');$db->exec('ALTER TABLE clubs DROP INDEX club_name, ADD UNIQUE KEY club_name (club_name)');echo '{}';exit;}
|
||||
if($action==='restoreTestSuper'){$db->exec("UPDATE users SET user_status='active' WHERE user_serial=1");echo '{}';exit;}
|
||||
if($action==='seedTenantInvitation'){
|
||||
$stmt=$db->prepare("INSERT INTO club_invitations(club_serial,invited_by,invite_email,expires_at,sent_at) VALUES(?,?,?,DATE_ADD(UTC_TIMESTAMP(),INTERVAL 1 DAY),UTC_TIMESTAMP())");$stmt->execute([(int)$argv[3],(int)$argv[4],$argv[5]]);echo json_encode(['invitation'=>(int)$db->lastInsertId()]);exit;
|
||||
}
|
||||
if($action==='demoteTenantAdmin'){
|
||||
$stmt=$db->prepare("UPDATE club_memberships SET membership_role='member' WHERE membership_serial=?");$stmt->execute([(int)($argv[3]??0)]);echo '{}';exit;
|
||||
}
|
||||
if($action==='repairConcurrencySeed'){
|
||||
$club=(int)$argv[3];$user=(int)$argv[4];$inviter=(int)$argv[5];
|
||||
$stmt=$db->prepare("INSERT INTO club_memberships(club_serial,user_serial,membership_status,joined_on) VALUES(?,?,'inactive','2026-01-01')");$stmt->execute([$club,$user]);$membership=(int)$db->lastInsertId();
|
||||
$stmt=$db->prepare("INSERT INTO club_invitations(club_serial,invite_email,invited_by,expires_at,sent_at) SELECT ?,user_email,?,DATE_ADD(UTC_TIMESTAMP(),INTERVAL 1 DAY),UTC_TIMESTAMP() FROM users WHERE user_serial=?");$stmt->execute([$club,$inviter,$user]);echo json_encode(['membership'=>$membership,'invitation'=>(int)$db->lastInsertId()]);exit;
|
||||
}
|
||||
if($action==='ticketPagingSeed'){
|
||||
$a=(int)$argv[3];$b=(int)$argv[4];$owner=(int)$argv[5];$neighbor=(int)$argv[6];$foreign=(int)$argv[7];$oldest=0;
|
||||
$stmt=$db->prepare('INSERT INTO club_tickets(club_serial,user_serial,subject) VALUES(?,?,?)');
|
||||
for($n=1;$n<=101;$n++){$stmt->execute([$a,$owner,'Archive ticket '.sprintf('%03d',$n)]);if($n===1)$oldest=(int)$db->lastInsertId();}
|
||||
$stmt->execute([$a,$owner,'Literal %_! <b>needle</b>']);$literal=(int)$db->lastInsertId();
|
||||
$stmt->execute([$a,$neighbor,'Private neighbor ticket']);$private=(int)$db->lastInsertId();
|
||||
$stmt->execute([$b,$foreign,'Archive foreign club ticket']);$foreignTicket=(int)$db->lastInsertId();
|
||||
echo json_encode(['oldest'=>$oldest,'literal'=>$literal,'private'=>$private,'foreign'=>$foreignTicket]);exit;
|
||||
}
|
||||
if($action==='tenantSeed'){
|
||||
$ids=[];$hash=password_hash('Tenant test password 123!',PASSWORD_DEFAULT);
|
||||
$insert=$db->prepare("INSERT INTO users(user_name,user_first_name,user_last_name,user_email,user_password_hash,user_role,user_email_verified_at) VALUES(?,?,?,?,?,?,UTC_TIMESTAMP())");
|
||||
foreach(['admina','admina2','adminb','membera','memberb','shared','invitee','unverified'] as $label){$insert->execute(['tenant'.$label,'Tenant',ucfirst($label),$label.'@example.test',$hash,in_array($label,['admina','adminb'],true)?'Administrator':'User']);$ids[$label]=(int)$db->lastInsertId();}
|
||||
$db->exec("UPDATE users SET user_email_verified_at=NULL WHERE user_name='tenantunverified'");
|
||||
$db->exec("INSERT INTO clubs(club_name) VALUES('Tenant Alpha')");$ids['clubA']=(int)$db->lastInsertId();$db->exec("INSERT INTO clubs(club_name) VALUES('Tenant Beta')");$ids['clubB']=(int)$db->lastInsertId();
|
||||
$stmt=$db->prepare("INSERT INTO club_memberships(club_serial,user_serial,membership_role,joined_on) VALUES(?,?,?,'2026-01-01')");
|
||||
foreach([['clubA','admina','administrator'],['clubA','admina2','administrator'],['clubB','adminb','administrator'],['clubA','membera','member'],['clubB','memberb','member'],['clubA','shared','member'],['clubB','shared','member'],['clubB','admina','member']] as [$club,$who,$role]){$stmt->execute([$ids[$club],$ids[$who],$role]);$ids[$club.'_'.$who]=(int)$db->lastInsertId();}
|
||||
echo json_encode($ids);exit;
|
||||
}
|
||||
if($action==='maintenance'){
|
||||
$out=[];foreach(['alerts','popovers'] as $table)$out[$table]=$db->query('SELECT * FROM '.$table.' ORDER BY 1')->fetchAll(PDO::FETCH_ASSOC);echo json_encode($out);exit;
|
||||
}
|
||||
if($action==='unsafeRule'){
|
||||
$db->exec("INSERT INTO dropdowntypes(dropdowntype_name,dropdowntype_title,dropdowntype_table,dropdowntype_column) VALUES('Unsafe legacy','Unsafe legacy','*function','getSettings')");$type=(int)$db->lastInsertId();
|
||||
$stmt=$db->prepare("INSERT INTO dropdowns(dropdown_dropdowntype,dropdown_value) VALUES(?,'Legacy')");$stmt->execute([$type]);echo json_encode(['dropdown'=>(int)$db->lastInsertId()]);exit;
|
||||
|
||||
Reference in New Issue
Block a user