Add cross-OS install/uninstall CI + fix 2 SUSE bugs found along the way - #40
Merged
Merged
Conversation
The installer chowns /var/www/simplerisk (including cron/cron.php) to the web-server account, then registered the backup cron in root's crontab. Any attacker with write access as that web account could overwrite cron.php and get root code execution on the next minute tick, defeating the containment the web account is supposed to provide. set_up_backup_cronjob() now installs a /etc/cron.d/simplerisk entry with the web-server account in the user column instead of appending to root's crontab, so cron.php only ever executes with the privileges the web account already has. remove_backup_cronjob() also cleans up any legacy root-crontab entry left by older installs. Reported via HackerOne #3761952. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
exec_cmd_nobail appends its own `> /dev/null 2>&1` to every command (to suppress non-debug output), which collided with the literal `>` redirect in set_up_backup_cronjob() and truncated /etc/cron.d/simplerisk to 0 bytes instead of writing the cron entry - silently disabling the scheduled backup cron entirely. Piping through tee instead avoids the collision (matching the existing convention already used elsewhere in this script for writing files via exec_cmd, e.g. the apt source list entries). Caught by running the actual patched function against a real cron daemon in Docker across all three supported OS families. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- setup_suse(): the SUSE mysql-community-server package's /etc/my.cnf has no `!includedir /etc/my.cnf.d`, so the sql_mode drop-in written there is silently never read and STRICT_TRANS_TABLES stays live. Apply the same setting via SET GLOBAL as a safety net, matching how the CentOS/RHEL path already handles this same MySQL 8.4+ behavior. - uninstall_suse(): `zypper -n autoremove` isn't a valid zypper subcommand (unlike apt-get/dnf), so it always failed with "Unknown command" and aborted the rest of the function under set -e before the MySQL repo, firewall rules, and password file were ever removed. Removed the call; zypper has no built-in equivalent. Found and verified via a real install/uninstall run against openSUSE Leap 15.6 in Docker while building the install-test CI workflow. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ports and extends the Docker-based install/uninstall test suite that previously lived on the abandoned feature/uninstall-support branch (never merged; simplerisk-setup.sh has diverged substantially since). - .github/workflows/install-test.yml: on every push/PR, builds a container per supported OS, runs the real simplerisk-setup.sh --yes, verifies the install, runs --uninstall, and verifies that too. - tests/verify-install.sh / verify-uninstall.sh: updated the cron checks to assert the backup cron runs via /etc/cron.d as the web-server account (not root's crontab) - the old checks asserted the pre-fix, vulnerable behavior from HackerOne #3761952. - Added openSUSE/SLES coverage (previously missing entirely): real SLES requires a live SUSE Customer Center subscription that a public CI container can't have (validate_os_and_version() calls `suseconnect --list-extensions`), so tests/run-suse-function-test.sh instead sources the script and calls setup_suse()/uninstall_suse() directly against openSUSE Leap 15.6, exercising the real apache2/mysql/php8/cron install logic without the subscription-gated entry point. - Dropped Debian 12 from the matrix: current validate_os_and_version() only accepts Debian 13. Matrix: ubuntu-22.04, ubuntu-24.04, debian-13, centos-stream-9, centos-stream-10, opensuse-leap-15. Locally verified end-to-end (install -> verify -> uninstall -> verify) against current main plus the cron fix for ubuntu-22.04 (the "normal" setup()-entry-point path) and opensuse-leap-15 (the new function-level path); the remaining matrix entries reuse Dockerfiles/shims already proven working on the prior branch and will get their first run in CI. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…s CI start_mysqld() pre-created /var/log/mysqld.log (flat), copied verbatim from the CentOS shim, but setup_suse() passes /var/log/mysql/mysqld.log (with subdirectory) to set_up_database() - matching the package's own my.cnf log-error= directive. Without that directory existing (no live systemd-tmpfiles), set_up_database's grep for the startup temp-password line failed with "No such file or directory", aborting the rest of setup_suse under set -e. Passed locally against Docker Desktop because /var/log/mysql already existed there for unrelated reasons; failed on the real GitHub Actions runner, which is how this was caught. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
jsokol
enabled auto-merge (squash)
September 19, 2026 20:36
- validate_os_and_version(): accept 26.04 (current LTS, "Resolute Raccoon", already released) instead of the 25.* wildcard. Interim (non-LTS) Ubuntu releases get ~9 months of upstream support and churn every 6 months; the intent is to support LTS releases specifically (22.04/24.04/26.04), not track every interim point release. - uninstall_ubuntu_debian(): also purge sensible-mda alongside sendmail/sendmail-bin. sensible-mda is a sendmail dependency with its own hard Depends on the mail-transport-agent virtual package; left behind, apt keeps that dependency satisfied by auto-installing a replacement MTA (courier-mta, pulling in ~40 packages including a full C build toolchain) instead of just removing it. Reproduced against a clean Ubuntu 26.04 install with nothing but sendmail/sendmail-bin present - not specific to 26.04, but only surfaced once 26.04's test container (no live systemd) turned an apt side-effect into a hard uninstall failure via a separate, unrelated postinst issue. Verified end-to-end (install -> verify -> uninstall -> verify) against Ubuntu 26.04 in Docker, 28/28 and 14/14 checks passing, zero courier-mta packages touched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- tests/dockerfiles/Dockerfile.ubuntu-26.04 + mysql-init-ubuntu-26.04.sh:
Ubuntu 26.04's mysql-server package ships no /etc/init.d/mysql (systemd
units only). Verified via /usr/sbin/service's own source
(`[ -d /run/systemd/system ]` gates delegation to systemctl) that a real,
systemd-booted 26.04 server handles `service mysql start` fine via its
live mysql.service unit - this is purely a no-init-system container gap,
not a simplerisk-setup.sh issue, so the fix is test-only (same category
as the existing Debian 12/13 shim).
- tests/verify-install.sh: fixed the HTTP check added in the prior commit.
It grepped for the login form (name="authenticate"), but a genuinely
fresh database has zero users, so index.php's own gate
(`if ($count == 0) { create_default_admin_account(); exit(); }`) shows
the "Default Admin Account Creation" wizard first and never reaches the
login form - that page, not the login screen, is what a truly fresh
install is supposed to show. Now checks for that page instead
(verify_create_default_admin_account, its submit button's literal name).
- .github/workflows/install-test.yml: added ubuntu-26.04 to the matrix.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
SimpleRisk's current release requires PHP >= 8.3 (Composer platform check). Debian and CentOS/RHEL already pin a modern PHP via a third-party repo (Sury / Remi), but Ubuntu just ran `apt-get install lamp-server^` and took whatever PHP version Ubuntu's own archive defaults to for that release. Ubuntu 24.04 (8.3.6) and 26.04 (8.5) happen to be fine; Ubuntu 22.04 only offers 8.1 - every fresh install completed "successfully" per the script's own output, but silently served a PHP fatal error (Composer's platform_check.php) instead of the app. Fix: for Ubuntu, still install via lamp-server^ (unchanged - keeps Ubuntu's own MySQL/MariaDB provisioning as-is, only the PHP version was the problem), but also add the same Sury PHP8 repo Debian uses, install the pinned 8.5 packages, and switch Apache's active PHP module to it (scanning /etc/apache2/mods-enabled/php*.load rather than assuming a specific old version, since it varies by release). Also extended uninstall_ubuntu_debian's repo cleanup to remove the Sury repo/key for Ubuntu too, matching install. This was previously invisible because verify-install.sh's HTTP check never followed the http->https redirect, so it only ever saw the redirect's 301/302 status and never reached the page where the fatal error actually occurs. Verified end-to-end (install -> verify -> uninstall -> verify) on all three supported Ubuntu versions in Docker: 22.04 (28/28, 14/14 - PHP correctly pinned to 8.5.10 via Sury), 24.04 (28/28, 14/14), and 26.04 (28/28, 14/14 - confirmed no conflict between the newly-added Sury repo and the native php8.5 package it already ships). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
SimpleRisk's current release requires PHP >= 8.3 (Composer platform check). SLES 15's own repositories cap out at PHP 8.2 (the php8 package) with no upgrade path: openSUSE's community devel:languages:php OBS project, which sometimes backports a newer PHP to older releases, has dropped 15.6 support entirely and only targets the next major release (16.0), not yet generally available for SLES. Shipping an install path that silently serves a PHP fatal error instead of the app (as Ubuntu 22.04 did until the previous commit) serves no one, so validate_os_and_version() now rejects SLES outright with a clear explanation instead of attempting the install. setup_suse()/uninstall_suse() are left in place (unreachable for now) as a starting point: openSUSE Leap 16.0 already ships PHP 8.4 natively, so SLES 16 (once released) should be a viable target, but those functions are written entirely around SLES 15's package names, module structure, and MySQL repo RPM naming (mysql84-community-release-sl15) - supporting SLES 16 needs its own dedicated pass, verified against a real SLES 16/Leap 16.0 environment, not just relaxing this version check. Also removes the SLES/openSUSE CI matrix entry and its run-suse-function-test.sh workaround (it bypassed the now-intentional rejection above to exercise setup_suse() directly) and updates the required status checks on main's branch protection to match the current matrix (ubuntu-22.04/24.04/26.04, debian-13, centos-stream-9/10). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Supported versions: 22.04/24.04/26.04 LTS only (matches validate_os_and_version() as of this branch - Ubuntu interim releases and SLES 15 are no longer accepted), with a brief note on why SLES isn't currently supported. - Fixed the install one-liners pointing at a 'master' branch that no longer exists (the repo's default branch is 'main'; raw.githubusercontent.com happens to still resolve the old ref today, but that's not something to document as if intentional). - Added a CI status badge for the new install-test workflow. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Doc-only commits (README, etc.) were triggering the full ~25-minute matrix for no reason. Scoped both push and pull_request triggers to simplerisk-setup.sh, tests/**, and the workflow file itself (shared via a YAML anchor so the two trigger blocks can't drift out of sync). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Builds on #39 (this branch includes those commits — merge #39 first, or this PR's diff will show both until then).
.github/workflows/install-test.yml, which on every push/PR builds a Docker image per supported OS, runs the realsimplerisk-setup.sh --yes, verifies the install, runs--uninstall, and verifies that too. Matrix:ubuntu-22.04,ubuntu-24.04,debian-13,centos-stream-9,centos-stream-10,opensuse-leap-15.feature/uninstall-supportbranch (never merged —simplerisk-setup.shhas diverged ~700 lines since).tests/verify-install.sh/verify-uninstall.sh's cron checks: they previously asserted the cron job lives in root's crontab (the pre-fix, vulnerable behavior from Run backup cron as web-server account instead of root #39 / HackerOne #3761952). Now they assert it's in/etc/cron.d/simpleriskrunning as the web-server account.validate_os_and_version()callssuseconnect --list-extensions), sotests/run-suse-function-test.shsources the script and callssetup_suse()/uninstall_suse()directly against openSUSE Leap 15.6 — exercising the real apache2/mysql/php8/cron install logic without going through the subscription-gated entry point.validate_os_and_version()only accepts Debian 13.simplerisk-setup.shbugs found and fixed while validating this against openSUSE Leap in Docker (separate commit,47d7a29):setup_suse(): SUSE'smysql-community-serverpackage ships/etc/my.cnfwith no!includedir /etc/my.cnf.d, so thesql_modedrop-in written there was silently never read andSTRICT_TRANS_TABLESstayed live. Added aSET GLOBALsafety net, matching how the CentOS/RHEL path already handles this exact MySQL 8.4+ behavior.uninstall_suse():zypper -n autoremoveisn't a valid zypper subcommand (unlikeapt-get/dnf), so it always failed with "Unknown command" and aborted the rest of the function underset -e— before the MySQL repo, firewall rules, and password file were ever removed. Removed the call (zypper has no built-in equivalent).Test plan
bash -n simplerisk-setup.sh— syntax check passesmain+ Run backup cron as web-server account instead of root #39's fix:setup()-entry-point path used by 5 of 6 matrix entries): 27/27 install checks, 14/14 uninstall checks.httpd2vs actualhttpd-prefork; apgrep -fself-matching-its-own-invocation false positive).ubuntu-24.04,debian-13,centos-stream-9,centos-stream-10reuse Dockerfiles/shims already proven working on the prior (abandoned) branch, but haven't been re-run locally against currentmainin this session — first real run will be this PR's own CI once it's live on GitHub Actions.