Files
huntingclubroster/PRODUCTION-FIXES.md
jric11 1e63076123
Security and workflow regressions / regression (push) Has been cancelled
Chat's updates for security, db and workflow
2026-10-20 06:45:30 -04:00

16 KiB

Production safeguard changes — October 19, 2026

This implements the production review's security defects, financial history and corrections, protected audit writes, mailbox verification, and deployment tooling. It does not deploy to a public host or silently change the development database.

Apply the new schema before running this code against an existing database

Back up the target database, verify that the backup restores, and use a maintenance window. With separate migration credentials and the target environment configured:

php bin/migrate-auth.php
php bin/migrate-roster.php
php bin/migrate-production.php

All three migrations are now applied to development. The production-safeguards migration was applied there on October 20, 2026, after a fresh verified backup; see the rollout record below. It has not been applied to a production host here. It adds mailbox verification, financial correction/reversal tables, and payment metadata; it enlarges journal entries, ensures transactional audit storage, and refreshes the user view. It can be rerun. Existing financial records retain their amounts; their previously unknown actors remain null and appear as historical records. Schema changes can lock tables; they are CLI operations, never web requests.

Existing addresses are not automatically marked verified. Ordinary users must verify their email before viewing club data or receiving/reactivating membership. They can use the new verification flow or complete a password setup/reset link. Existing administrators retain access to administer this transition. User records, memberships, and financial history are preserved.

Account security and browser dependency

  • Password-reset issuance now locks the account and compares the originally resolved email, status, and account version. Account edits and status changes delete outstanding reset tokens under the same account lock. This handles both interleavings: an edit before token creation, and an edit during email delivery.
  • Username/email/role/status changes invalidate reset links and verification links. Changing an email also clears its verified status. Disable/reactivate cannot revive a reset link. Existing session revocation remains intact.
  • Replaced jQuery 3.2.0 with official jQuery 3.7.1 on current and legacy templates. Removed the old asset. Its downloaded SHA-256 matches the official integrity value recorded in browser-dependencies.json. Existing jQuery 3.x plugin behavior is retained; a major-version migration is not necessary for this fix.
  • Browser tests cover account creation, deletion safeguards, the alert and popover editors, rosters, financial corrections, downloads, and verification requests.
  • Fixed the garbled username validation message and normalized newly added view text to UTF-8.

Financial history, corrections, and audit

  • New LedgerService coordinates dues, payments, corrections, and reversals. Charges and payments record their actor; payments also store method/reference.
  • Itemized payment history shows date, amount, member, method/reference, actor, and reversal details. Members see only their own financial history.
  • Correcting dues appends an adjustment with the previous/effective amounts, actor, reason, and request key. It preserves the original charge. A correction cannot reduce dues below received payments; reverse an erroneous payment first.
  • Reversing a payment appends one reversal with an actor and reason. It restores the amount owed and retains the original payment. This records a bookkeeping correction; it does not send a refund or move money.
  • Repeated payment, correction, and reversal requests are idempotent. Changed payloads with a reused request key are rejected. Concurrent writes use account, club, and dues locks as appropriate; overpayment is still rejected.
  • Financial CSV includes effective dues, paid/balance cents, individual payments, corrections, reversal reasons, and actors. User text is protected against spreadsheet formula injection. Monetary calculations use integer cents.
  • Financial, club, membership, season, and administrative account changes write audit entries in the same transaction. An audit failure rolls back the change. Entries include entity IDs and relevant before/after values, amounts, or reasons. Passwords and tokens are never audit payloads.
  • Removed the journal-clearing route, button, and handler. The legacy PHP helper also refuses clearing. Accounts with recorded journal history are retained; disable them instead of deleting their identity.
  • Runtime grant examples allow journal inserts but deny journal update/deletion and financial update/deletion. Migrations and backup/restore use separate roles. Authentication audit errors continue to go to server logs without preventing login; configure alerts on those errors.

Mailbox verification

  • Signup requests an email verification link and explains how to resend it. Public requests return neutral messages for unknown, verified, rate-limited, or undeliverable accounts.
  • Links contain 256-bit random values, stored only as hashes, expire after 24 hours, and are bound to the account email/version. Failed delivery removes the unsent token. Expired records are handled by the cleanup CLI.
  • Opening a link does not consume it. Confirmation requires POST, CSRF protection, a valid token, and the account password. This prevents a mailbox owner from accidentally activating an account whose password was chosen by someone else.
  • Successful password setup/reset also proves ownership of its current mailbox. Changing a known password without a delivered token does not verify an email.
  • Active membership assignment requires an active, verified account. Regular members also need verified email to read club data. Admin summaries show the verification state and retain the password setup-link action.

Production configuration and release tooling

  • Remote production database connections require HCR_DB_TLS_CA. Certificate validation is enabled and the negotiated TLS cipher is checked. Localhost/ loopback database connections remain available; explicitly protect any local tunnel. Production TLS must still be tested against the actual server.
  • Set HCR_ENVIRONMENT=Production explicitly. That prevents automatic loading of settings.local.xml; environment variables or an explicitly selected external HCR_SETTINGS_FILE supply production secrets. Never package development secrets.
  • HTTPS is recognized directly or from X-Forwarded-Proto only when REMOTE_ADDR is in HCR_TRUSTED_PROXIES, a comma-separated list of exact IP addresses. The proxy must overwrite forwarded headers and direct access to its backend must be restricted. Untrusted and ambiguous forwarding headers are rejected.
  • Application responses hide PHP errors and send CSP, frame/referrer/content-type protections and Permissions-Policy. Production HTTPS sends HSTS. Inline scripts and JavaScript links were replaced so script-src does not need unsafe-inline. Inline styles remain permitted for Bootstrap/editor compatibility.
  • Apache protections now require mod_rewrite rather than silently skipping the private-directory rules. Apache needs AllowOverride support. IIS additionally blocks sensitive file extensions. Nginx and PHP configuration examples are in deploy/. These examples must be adapted and tested on the chosen server.
  • python bin/build-release.py /protected/releases/hcr.zip creates a clean zip and SHA-256 manifest from the workspace, including new files. It excludes local settings, backups, tests, Git metadata, and IDE files. Use locked Composer dependencies and review the archive before deployment; it is not published.
  • python bin/check-production.py https://app.myhuntingclubroster.com performs read-only HTTPS, header, cookie, and private-path checks after deployment. It sends no email and changes no accounts. The actual production host was not contacted in this change.
  • php bin/check-health.php exits nonzero on database/schema failures. Run it from the server monitor with production settings and alert on failures.

Operational setup on the hosting server

The project now supplies the entry points; the server administrator must install their schedule, permissions, storage, and alerts on the actual production host. No hosting credentials or host details were supplied for that work.

  1. Provision migration, runtime, and backup accounts separately. Adapt deploy/runtime-grants.sql.example. Keep secrets outside the document root and restrict file access to the service/job accounts. Test the runtime role.
  2. Schedule php bin/cleanup-auth.php daily. It removes expired reset/verification links and stale rate-limit buckets without deleting audit history.
  3. Schedule php bin/backup-database.php /protected/backups using backup-specific credentials. Set HCR_DUMP_BINARY to the installed mariadb-dump executable if it is not on PATH. The directory must exist outside the application tree. The job writes a complete transactional SQL dump and checksum metadata and removes its temporary credentials file. All tables must be InnoDB. It exits on failure.
  4. Restrict the backup directory before running the job, especially with Windows ACLs. Use encrypted storage and protected off-host copies, define retention and recovery objectives, and regularly restore-test backups. The script does not implement encryption, retention, or off-host storage by itself.
  5. Alert on failed backups/cleanup/health jobs, PHP errors, audit failures, SMTP failures, disk pressure, and database failures. Configure log rotation and protected audit retention/archival; there is no web audit-deletion facility.
  6. Run Composer platform checks and the isolated regression runner before release. The GitHub workflow supplies a disposable MariaDB and browser; it runs when pushed to a repository with Actions enabled. This change does not push code or claim that GitHub CI has run.
  7. Deploy the complete release during the migration window. Run host checks, then send a real invitation/reset to a controlled mailbox and verify inbox delivery and link completion. Local tests deliberately use simulated delivery.
  8. Keep the prior release and the verified pre-migration backup. Recovery after new financial writes needs reconciliation; do not blindly restore an old dump and discard subsequently received entries.

Verification and remaining scope

The final local run passed 308 checks plus 5 concurrency scenarios:

Suite Passed
Authentication service/templates 49
Maintenance validation/pagination 52
Authentication HTTP 27
Project HTTP, ledger, exports, and access boundaries 99
Reset invalidation, verification, corrections, audit rollback, transport 42
Restricted runtime database permissions 5
Browser workflows, editors, corrections, downloads, and CSP 21
Migration, release, backup, health, and private-path tooling 13
Concurrent defaults, payments, reversal retries, correction retries, final admin 5 scenarios

Source checks passed for 41 PHP files, 55 JavaScript files, 39 compiled XSL stylesheets, and the jQuery integrity hash. IIS configuration is well-formed XML. Composer's final locked dependency audit reported no advisories or abandoned packages. The GitHub workflow and actual Apache/IIS/Nginx production hosts have not been executed or contacted here.

The verified development backup was also restored locally. After bringing the copy to the existing roster schema, the new migration was run twice. Every original column value across all 17 existing tables was unchanged. The isolated restored copy was removed. That October 19 implementation run did not migrate development; the subsequently authorized rollout is recorded below.

Tests use generated disposable databases; they do not send real mail, move money, or modify existing development accounts. Temporary databases and the restricted test account were removed, and the local test server was stopped. Production TLS certificate negotiation and real inbox delivery require the actual hosting setup.

Global site administrators continue to manage all clubs. Active club members continue to see their club's active contact directory. These are existing product policies, not newly introduced club-admin roles or contact-consent settings. Delegated club administration, MFA enrollment/recovery, online payments/refunds, and large-scale pagination/background exports remain separate enhancements. The production-specific TLS, web-server configuration, monitoring schedules, off-host backup policy, and real mail delivery remain deployment acceptance work.

Development rollout — October 20, 2026

At the user's request, bin/migrate-production.php was applied to 192.168.1.190/huntingclubroster at 6:32 a.m. Eastern. The previously approved unencrypted development connection was used. The target was checked explicitly; no production host was changed.

Before migration, a complete 43,100-byte backup was saved at .tools/backups/20261020-063143/huntingclubroster.sql. Its SHA-256 is 526113c2575927d33680cb696a398f59352554a5892c23c1a8ff414fe6f1e3ca. The backup was restored locally and its checksum and all 17 table counts matched. Every existing column value in the original 17 development tables was checked before and after migration and was preserved. The database now has 20 base tables.

48 acceptance checks passed against development, using temporary accounts and an owned temporary club through a local application server. They verified:

  • Verification link generation, trusted URL, POST confirmation, correct-password enforcement, single use, stored verification state, and membership gating.
  • Password reset, old-password rejection, new-password login, consumed-token rejection, session revocation, and invalidation after an email change.
  • Clearing verified state on email change and restoring club access only after a fresh bound verification link is confirmed.
  • Dues corrections preserving original charges, safe retries, correct balances, and rejection of corrections below received payments or excess payments.
  • Reversals preserving original payments, safe retries, duplicate-reversal rejection, restored balances, and settlement with a replacement payment.
  • Ordinary-member restrictions, detailed financial CSV, and audit events.

Email issuance used the real application services and development database, with only the delivery boundary replaced by local capture. No real emails were sent; SMTP/inbox delivery was not retested. The public URL was checked in generated links without requiring development to be served at that public hostname. All payments were manual test ledger entries; no money moved.

The first helper attempts encountered a Windows temporary-session-path parsing issue. The helper was corrected and those temporary records were removed. During the first complete run, an independent administrator login changed existing login timestamps, a journal entry, and rate counters. The differences were proved to match that login and were preserved; no original data was restored over user activity. A final complete run finished with a clean baseline comparison across all 20 tables. Existing accounts were not used as test accounts or reset by the verification scripts.

All temporary accounts, memberships, seasons, charges, payments, adjustments, reversals, tokens, test audit/rate-limit entries, and temporary sessions were removed. Auto-increment sequences may have advanced. A final CLI health check passed. The local restore copy was removed and its database server stopped. Private migration results and the check-by-check report remain beside the backup.