From b8b665f86a2e2fd278a7c62d00fc046261739989 Mon Sep 17 00:00:00 2001 From: Liraz Siri Date: Tue, 25 Aug 2026 01:45:22 +0000 Subject: [PATCH 1/8] Port Drupal 10 to Trixie and 10.6.14 Move the appliance to PHP 8.4 and an exact maintained Drupal, Drush, and contributed-module set. Pin Guzzle 7.15.2 because the upstream Drupal lock selected a release affected by two current advisories, and replace schema-dependent welcome-page inserts and the retired Drush security command with supported APIs. Add functional acceptance coverage for firstboot login, authenticated web page creation, database persistence, service restarts, bundled modules, management endpoints, and non-mutating update paths. A disposable Trixie Composer preflight verified the selected dependency set and a clean advisory audit. --- README.rst | 23 ++- changelog | 17 ++ conf.d/main | 66 +++----- docs/v19.0-testing.md | 58 +++++++ overlay/usr/lib/inithooks/bin/drupal10.py | 18 ++- .../usr/lib/inithooks/firstboot.d/40drupal10 | 2 +- .../usr/local/sbin/drush-mail-securityupdates | 25 ++- tests/v19.sh | 153 ++++++++++++++++++ 8 files changed, 295 insertions(+), 67 deletions(-) create mode 100644 docs/v19.0-testing.md create mode 100755 tests/v19.sh diff --git a/README.rst b/README.rst index b7d10c2..7cbd4ed 100644 --- a/README.rst +++ b/README.rst @@ -16,14 +16,13 @@ and on top of that: default eases management (especially with tools such as the turnkey script wrappers) but also has potential security implications. - **Security note**: Updates to Drupal may require supervision so + **Security note**: Updates to Drupal require supervision so they **ARE NOT** configured to install automatically. See below for - updating Drupal. And/or see `Drupal 8 documentation`_ + updating Drupal. See the `Drupal update documentation`_ for details. - - Includes drush_, drupal-console_ and composer_ for command line - administration, configuration and development. Also includes relevant - wrapper scripts; turnkey-drush_, turnkey-drupal_ & turnkey-composer_ (run - the relevant command, but as www-data user). + - Includes drush_ and composer_ for command line administration, + configuration and development. The turnkey-drush_ and turnkey-composer_ + wrappers run these tools as the www-data user. - Drupal security update alerts delivered to your inbox - requires `Security Alerts`_ ('secalerts') be enabled on firstboot with a valid @@ -35,7 +34,7 @@ and on top of that: swap - a temporary swap file is acceptable). - Bundled Drupal 10 modules and dependencies (installed to - /var/www/drupal10/web/modules - new default for Drupal10): + /var/www/drupal10/web/modules/contrib): - `Field group`_: Allows fields to be grouped together. - `Google analytics`_: Adds Google Analytics js tracking code to all @@ -69,9 +68,7 @@ details, please consult the `Drupal Upgrade docs`_, we recommend using `Composer to update from the commandline`_ (via turnkey-composer_):: cd /var/www/drupal10 - # update composer - not strictly necessary - composer self-update - turnkey-composer update drupal/core --with-dependencies + turnkey-composer update "drupal/core-*" --with-all-dependencies turnkey-drush updatedb -y turnkey-drush cr @@ -97,12 +94,10 @@ Credentials *(passwords set at first boot)* .. _TurnKey Core: https://www.turnkeylinux.org/core .. _composer: https://getcomposer.org/ .. _drush: https://www.drush.org/ -.. _drupal-console: https://drupalconsole.com/ -.. _turnkey-drush: https://github.com/turnkeylinux-apps/drupal8/blob/master/overlay/usr/local/bin/turnkey-drush -.. _turnkey-drupal: https://github.com/turnkeylinux-apps/drupal8/blob/master/overlay/usr/local/bin/turnkey-drupal +.. _turnkey-drush: https://github.com/turnkeylinux-apps/drupal10/blob/master/overlay/usr/local/bin/turnkey-drush .. _turnkey-composer: https://github.com/turnkeylinux/common/blob/master/overlays/composer/usr/local/bin/turnkey-composer .. _Security Alerts: https://www.turnkeylinux.org/docs/automatic-security-alerts -.. _Drupal 8 documentation: https://www.drupal.org/docs/8/update +.. _Drupal update documentation: https://www.drupal.org/docs/updating-drupal .. _Field group: https://www.drupal.org/project/field_group .. _Google analytics: https://www.drupal.org/project/google_analytics .. _Honeypot: https://www.drupal.org/project/honeypot diff --git a/changelog b/changelog index f9f3949..2c25032 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,20 @@ +turnkey-drupal10-19.0 (1) turnkey; urgency=low + + * Upgrade the base distribution to Debian 13/Trixie and PHP 8.4. + + * Update Drupal to 10.6.14 with Drush 13.7.6 and compatible pinned versions + of the bundled contributed modules. + + * Update Guzzle to 7.15.2 to include the upstream fixes for CVE-2026-69245 + and CVE-2026-69246. + + * Create the welcome page through Drupal's entity API and update the + security advisory check for current Drush. + + * See the TurnKey Core 19.0 changelog for changes common to all appliances. + + -- TurnKey Linux release engineering Tue, 25 Aug 2026 00:00:00 +0000 + turnkey-drupal10-18.1 (1) turnkey; urgency=low * v18.1 rebuild - includes latest Debian & TurnKey packages. diff --git a/conf.d/main b/conf.d/main index 8df9d4f..6036325 100755 --- a/conf.d/main +++ b/conf.d/main @@ -7,11 +7,10 @@ DB_PASS=$(mcookie) ADMIN_NAME=admin ADMIN_PASS=turnkey -SRC=/usr/local/src WEBROOT=/var/www/drupal10 -PHP_V=8.2 -USER=www-data +PHP_V=8.4 +WEB_USER=www-data # set proxy settings [ "$FAB_HTTP_PROXY" ] && export HTTP_PROXY=$FAB_HTTP_PROXY @@ -27,43 +26,34 @@ export COMPOSER_MEMORY_LIMIT=-1 # temporarily disable composer memory limits dur # install drupal10 mkdir -p $WEBROOT -chown $USER:$USER $WEBROOT -turnkey-composer create-project drupal/recommended-project:10.x $WEBROOT --no-dev --no-interaction +chown $WEB_USER:$WEB_USER $WEBROOT +turnkey-composer create-project drupal/recommended-project:10.6.14 $WEBROOT --no-dev --no-interaction cd $WEBROOT -# install drush -cd $WEBROOT -turnkey-composer require drush/drush --no-interaction - CONF=$WEBROOT/web/sites/default/settings.php cp $WEBROOT/web/sites/default/default.settings.php $CONF LIBRARIES_PATH=$WEBROOT/libraries mkdir -p $LIBRARIES_PATH/ -chown -R $USER:$USER $WEBROOT +chown -R $WEB_USER:$WEB_USER $WEBROOT service mysql start -mysqladmin create $DB_NAME -mysql --batch --execute "grant all privileges on $DB_NAME.* to $DB_USER@localhost identified by '$DB_PASS'; flush privileges;" +mysql --batch --execute "CREATE DATABASE $DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" +mysql --batch --execute "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" +mysql --batch --execute "GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost';" mysql --batch --execute "set global transaction isolation level read committed;" -# download and install modules -MODULES="drupal/ctools \ - drupal/field_group \ - drupal/google_analytics \ - drupal/imce \ - drupal/pathauto \ - drupal/token" - -# drupal/honeypot -# -# currently doesn't work on drupal10, if you want this re-included upstream -# would appreciate help, read the thread -# https://www.drupal.org/project/honeypot/issues/2943526 - -for module in $MODULES; do - turnkey-composer require $module --no-interaction -done +# Install the maintained command-line tools and bundled modules at the versions +# validated for the v19 appliance. +turnkey-composer require --no-interaction --update-no-dev \ + drush/drush:13.7.6 \ + drupal/ctools:4.1.1 \ + drupal/field_group:4.0.0 \ + drupal/google_analytics:4.0.3 \ + drupal/imce:3.1.5 \ + drupal/pathauto:1.15.0 \ + drupal/token:1.17.0 \ + guzzlehttp/guzzle:7.15.2 turnkey-drush site-install standard -y \ --account-name=$ADMIN_NAME \ @@ -83,8 +73,8 @@ turnkey-drush cache-rebuild # configure apache a2dissite 000-default a2ensite drupal10 -a2enmod rewrite - +a2enmod rewrite + # insert "www.example.com" as default trusted_host (inithook will adjust) echo "\$settings['trusted_host_patterns'] = ['^www\.example\.com\$'];" >> $CONF @@ -120,17 +110,11 @@ DRUSH="/usr/local/bin/turnkey-drush" EOF chmod +x $CRON_DRUPAL -# create welcome page -MYSQL_BATCH="mysql --batch --execute" - +# Create the welcome page through Drupal so entity revisions and field storage +# remain consistent with the installed schema. WELCOME='

Please follow these steps to set up and start using your website:

  1. Configure your website: To get started, log in as admin and visit the administration section, where you can customize and configure all aspects of your website.
  2. Start posting content: When ready, delete this welcome and create content for your website.

For more information, please refer to the TurnKey Drupal10 release notes, help section, or the online Drupal handbooks. You may also post at the Drupal forum, or view the wide range of other support options available.

' - -$MYSQL_BATCH "INSERT INTO $DB_NAME.node (nid, vid, type, uuid, langcode) VALUES (1,1,'page','dbab9403-2ebd-4fb9-8a00-e764ce980ecd','en');" -$MYSQL_BATCH "INSERT INTO $DB_NAME.node__body (bundle, deleted, entity_id, revision_id, langcode, delta, body_value, body_summary, body_format) VALUES ('page',0,1,1,'en',0,'$WELCOME','$WELCOME','basic_html');" -$MYSQL_BATCH "INSERT INTO $DB_NAME.node_field_data (nid, vid, type, langcode, title, uid, status, created, changed, promote, sticky, revision_translation_affected, default_langcode) VALUES (1,1,'page','en','Welcome to TurnKey Drupal',1,1,1435831165,1435831165,1,0,1,1);" -$MYSQL_BATCH "INSERT INTO $DB_NAME.node_field_revision (nid, vid, langcode, title, uid, status, created, changed, promote, sticky, revision_translation_affected, default_langcode) VALUES (1,1,'en','Welcome to TurnKey Drupal',1,1,1435831165,1435831165,1,0,1,1);" -$MYSQL_BATCH "INSERT INTO $DB_NAME.node_revision (nid, vid, langcode, revision_timestamp, revision_uid, revision_log) VALUES (1,1,'en',1435831535,1,'');" -$MYSQL_BATCH "INSERT INTO $DB_NAME.node_revision__body (bundle, deleted, entity_id, revision_id, langcode, delta, body_value, body_summary, body_format) VALUES ('page',0,1,1,'en',0,'$WELCOME','$WELCOME','basic_html');" +WELCOME_B64=$(printf '%s' "$WELCOME" | base64 -w0) +turnkey-drush php:eval "\$node = \\Drupal\\node\\Entity\\Node::create(['type' => 'page', 'title' => 'Welcome to TurnKey Drupal', 'body' => ['value' => base64_decode('$WELCOME_B64'), 'format' => 'basic_html'], 'status' => 1, 'promote' => 1]); \$node->save();" service mysql stop diff --git a/docs/v19.0-testing.md b/docs/v19.0-testing.md new file mode 100644 index 0000000..ba74d82 --- /dev/null +++ b/docs/v19.0-testing.md @@ -0,0 +1,58 @@ +# Drupal 10 19.0 testing + +## Scope and source decision + +Debian 13 Trixie does not package the Drupal application. Drupal 10.6.14, +Drush 13.7.6 and the contributed modules therefore come from their maintained +official Composer packages. The build requests exact application versions and +Composer records the resolved distributions in `composer.lock`. Guzzle is +pinned to 7.15.2 because Drupal 10.6.14's initial lock file selected 7.15.1, +which is affected by CVE-2026-69245 and CVE-2026-69246. + +PHP 8.4, Apache, MariaDB, Postfix and Composer come from signed Debian Trixie +repositories. Drupal 10.6 supports PHP 8.4. The supported update path is a +supervised Composer update followed by Drupal database updates and a cache +rebuild, as documented in the README. + +## Acceptance command + +```sh +/sandboxed-git/turnkey/tools/test-v19-appliance drupal10 \ + --source /home/agent/.local/worktrees/turnkey-apps/drupal10/wish-drupal10-v19-trixie +``` + +This command builds the Trixie root filesystem, imports it into Docker, boots +the normal systemd and firstboot path, then runs `tests/v19.sh`. + +## README crosswalk + +| README contract | Focused check | Required result | Evidence | +| --- | --- | --- | --- | +| Drupal is served over TLS | Request the landing page through Apache | The welcome page is returned over HTTPS | `tests/v19.sh` | +| Firstboot configures the administrator | Log in through Drupal's real web form using the preseeded password | Drupal creates an authenticated administrator session | `tests/v19.sh` | +| Drupal supports normal content management | Create a Basic page through the authenticated web form, read it from MariaDB, restart Apache and MariaDB, then request it again | The page is visible through Drupal and persists across service restarts | `tests/v19.sh` | +| Drush and Composer support command-line administration | Query versions, run cron, validate the Composer lock and inspect the current security command | Drupal 10.6.14 and Drush 13.7.6 run successfully; Composer and cron pass | `tests/v19.sh` | +| The bundled contributed modules are installed | Query enabled modules and the IMCE installation path | CTools, Field Group, Google Analytics, Pathauto and Token are enabled; IMCE is installed | `tests/v19.sh` | +| Drupal security alerts use current tooling | Ask Drush for help on `pm:security` and audit the lock file | The current command exists and the installed dependency set has no known advisory | `tests/v19.sh` | +| Webmin, Adminer and local application mail are available | Request both management endpoints and inspect the loopback SMTP listener | Webmin and Adminer answer over HTTPS; Postfix listens locally | `tests/v19.sh` | +| Debian and Drupal components have maintained update paths | Refresh APT metadata and perform a non-mutating Composer core update resolution | Trixie candidates remain eligible; the Composer update resolves without changing the deployment | `tests/v19.sh` | +| Root SSH, backup and other base services are inherited from Core | Cite the unchanged Core layer | Core 19 baseline passes | Core run `20260824t010251z-1634-32241`, source `24c82ee3540ce545422742b0e28ba6b687c53ec2`, verdict `PASS` | + +## Cheap compatibility preflight + +A disposable `debian:trixie-slim` container installed Drupal 10.6.14 and the +exact selected module set on PHP 8.4.24. Composer's platform checks passed. +The initial audit exposed the vulnerable Guzzle 7.15.1 lock, so the appliance +now requires the fixed 7.15.2 release. The full appliance run is responsible +for the installed-site and firstboot behavior. + +## Deferred issues + +The focused test confirms the local Postfix listener and Drupal mail +configuration but does not deliver mail through an external relay. External +mail delivery depends on site-specific relay credentials and is not required +to establish the appliance's main flow. + +The Docker runtime does not repeat installer, kernel or bootloader checks. +Drupal adds no appliance-specific behavior at those boundaries, so the proven +Core 19 baseline supplies that inherited evidence. diff --git a/overlay/usr/lib/inithooks/bin/drupal10.py b/overlay/usr/lib/inithooks/bin/drupal10.py index 78cc037..dfb5a6b 100755 --- a/overlay/usr/lib/inithooks/bin/drupal10.py +++ b/overlay/usr/lib/inithooks/bin/drupal10.py @@ -86,10 +86,18 @@ def main(): m = MySQL() m.execute('UPDATE drupal10.users_field_data SET mail=%s WHERE name=\"admin\";', (email,)) m.execute('UPDATE drupal10.users_field_data SET init=%s WHERE name=\"admin\";', (email,)) - subprocess.run(['turnkey-drush', '-y', 'config-set', 'contact.form.feedback', 'recipients', email]) - subprocess.run(['turnkey-drush', '-y', 'config-set', 'update.settings', 'notification.emails.0', email]) - subprocess.run(['turnkey-drush', '-y', 'config-set', 'system.site', 'mail', email]) - subprocess.run(['turnkey-drush', 'user-password', 'admin', password]) + subprocess.run([ + 'turnkey-drush', '-y', 'config-set', 'contact.form.feedback', + 'recipients', email + ], check=True) + subprocess.run([ + 'turnkey-drush', '-y', 'config-set', 'update.settings', + 'notification.emails.0', email + ], check=True) + subprocess.run([ + 'turnkey-drush', '-y', 'config-set', 'system.site', 'mail', email + ], check=True) + subprocess.run(['turnkey-drush', 'user-password', 'admin', password], check=True) conf = '/var/www/drupal10/web/sites/default/settings.php' conf_tmp = f'{conf}.tmp' shutil.move(conf, conf_tmp) @@ -113,7 +121,7 @@ def main(): os.chown(conf, uid, gid) os.chmod(conf, 0o444) print('Data updated; clearing caches') - subprocess.run(['/etc/cron.hourly/drupal10']) + subprocess.run(['/etc/cron.hourly/drupal10'], check=True) if __name__ == "__main__": main() diff --git a/overlay/usr/lib/inithooks/firstboot.d/40drupal10 b/overlay/usr/lib/inithooks/firstboot.d/40drupal10 index 0ca9fb9..de2047a 100755 --- a/overlay/usr/lib/inithooks/firstboot.d/40drupal10 +++ b/overlay/usr/lib/inithooks/firstboot.d/40drupal10 @@ -1,5 +1,5 @@ #!/bin/bash -e -# set drupal9 admin password and email +# set Drupal 10 admin password, email and domain . /etc/default/inithooks diff --git a/overlay/usr/local/sbin/drush-mail-securityupdates b/overlay/usr/local/sbin/drush-mail-securityupdates index f2c7345..d1a7ed6 100755 --- a/overlay/usr/local/sbin/drush-mail-securityupdates +++ b/overlay/usr/local/sbin/drush-mail-securityupdates @@ -1,10 +1,23 @@ -#!/bin/bash -e +#!/bin/bash +set -e cd /var/www/drupal10 TMPFILE=$(mktemp) -trap "rm -f $TMPFILE" INT TERM EXIT -/usr/local/bin/drush pm-updatestatus --security-only > $TMPFILE +trap 'rm -f "$TMPFILE"' INT TERM EXIT -if grep -q 'SECURITY UPDATE' $TMPFILE; then - cat $TMPFILE | mail -s 'Drupal security updates available' root -fi +set +e +/usr/local/bin/turnkey-drush pm:security >"$TMPFILE" 2>&1 +status=$? +set -e + +case $status in + 0) + ;; + 3) + mail -s 'Drupal security updates available' root <"$TMPFILE" + ;; + *) + cat "$TMPFILE" >&2 + exit "$status" + ;; +esac diff --git a/tests/v19.sh b/tests/v19.sh new file mode 100755 index 0000000..abf5f7d --- /dev/null +++ b/tests/v19.sh @@ -0,0 +1,153 @@ +#!/bin/bash +set -Eeuo pipefail +umask 077 + +result=${TKL_TEST_RESULT:?TKL_TEST_RESULT is required} +app_password=${TKL_TEST_APP_PASS:?TKL_TEST_APP_PASS is required} +base=https://localhost +cookie=/tmp/tkl-drupal10-cookie.$$ +page=/tmp/tkl-drupal10-page.$$ +headers=/tmp/tkl-drupal10-headers.$$ +policy=/tmp/tkl-drupal10-policy.$$ + +report_error() { + printf 'test_failure line=%s status=%s command=%q\n' \ + "$1" "$2" "$3" >&2 + exit "$2" +} +trap 'report_error "$LINENO" "$?" "$BASH_COMMAND"' ERR +trap 'rm -f -- "$cookie" "$page" "$headers" "$policy"' EXIT + +input_value() { + local name=$1 + local file=$2 + sed -n "s/.*name=\"$name\"[^>]*value=\"\([^\"]*\)\".*/\1/p" \ + "$file" | head -n 1 +} + +systemctl --quiet is-active apache2.service mariadb.service postfix.service \ + multi-user.target +systemctl --quiet is-enabled apache2.service mariadb.service postfix.service +apache2ctl -t +php_version=$(php -r 'echo PHP_MAJOR_VERSION, ".", PHP_MINOR_VERSION, ".", PHP_RELEASE_VERSION;') +[[ $php_version == 8.4.* ]] + +cd /var/www/drupal10 +drupal_version=$(turnkey-drush status --field=drupal-version) +drush_version=$(turnkey-composer show drush/drush --format=json | \ + php -r '$j=json_decode(stream_get_contents(STDIN), true); echo preg_replace("/^[*v ]+/", "", $j["versions"][0]);') +test "$drupal_version" = 10.6.14 +test "$drush_version" = 13.7.6 +turnkey-composer validate --no-check-publish --no-interaction >/dev/null +turnkey-composer install --dry-run --no-dev --no-interaction >/dev/null +turnkey-composer audit --locked --no-interaction | grep -Fq \ + 'No security vulnerability advisories found' + +for package_version in \ + 'drupal/ctools 4.1.1' \ + 'drupal/field_group 4.0.0' \ + 'drupal/google_analytics 4.0.3' \ + 'drupal/imce 3.1.5' \ + 'drupal/pathauto 1.15.0' \ + 'drupal/token 1.17.0' \ + 'guzzlehttp/guzzle 7.15.2'; do + read -r package expected <<<"$package_version" + actual=$(turnkey-composer show "$package" --format=json | \ + php -r '$j=json_decode(stream_get_contents(STDIN), true); echo preg_replace("/^[*v ]+/", "", $j["versions"][0]);') + test "$actual" = "$expected" +done + +enabled_modules=$(turnkey-drush pm:list --type=module --status=enabled --format=list) +for module in ctools field_group google_analytics pathauto token; do + grep -Fxq "$module" <<<"$enabled_modules" +done +test -d web/modules/contrib/imce +turnkey-drush help pm:security >/dev/null +turnkey-drush cron >/dev/null + +curl --insecure --fail --silent --show-error --location \ + "$base/" >"$page" +grep -Fq 'Welcome to TurnKey Drupal' "$page" + +curl --insecure --fail --silent --show-error \ + -c "$cookie" "$base/user/login" >"$page" +form_build_id=$(input_value form_build_id "$page") +test -n "$form_build_id" +curl --insecure --fail --silent --show-error --location \ + -b "$cookie" -c "$cookie" "$base/user/login" \ + --data-urlencode 'name=admin' \ + --data-urlencode "pass=$app_password" \ + --data-urlencode "form_build_id=$form_build_id" \ + --data-urlencode 'form_id=user_login_form' \ + --data-urlencode 'op=Log in' >"$page" +grep -Eq 'Log out|user/logout' "$page" +grep -q 'SSESS' "$cookie" + +curl --insecure --fail --silent --show-error \ + -b "$cookie" "$base/node/add/page" >"$page" +form_build_id=$(input_value form_build_id "$page") +form_token=$(input_value form_token "$page") +test -n "$form_build_id" +test -n "$form_token" +curl --insecure --fail --silent --show-error --location \ + -b "$cookie" -c "$cookie" -D "$headers" \ + "$base/node/add/page" \ + --data-urlencode 'title[0][value]=TurnKey v19 acceptance page' \ + --data-urlencode 'body[0][value]=Created through the Drupal web interface' \ + --data-urlencode 'body[0][format]=basic_html' \ + --data-urlencode 'status[value]=1' \ + --data-urlencode "form_build_id=$form_build_id" \ + --data-urlencode "form_token=$form_token" \ + --data-urlencode 'form_id=node_page_form' \ + --data-urlencode 'op=Save' >"$page" +grep -Fq 'TurnKey v19 acceptance page' "$page" +grep -Fq 'Created through the Drupal web interface' "$page" + +nid=$(mariadb --batch --skip-column-names drupal10 --execute \ + "SELECT nid FROM node_field_data WHERE title='TurnKey v19 acceptance page' ORDER BY nid DESC LIMIT 1") +test -n "$nid" +mariadb --batch --skip-column-names drupal10 --execute \ + "SELECT body_value FROM node__body WHERE entity_id=$nid" | \ + grep -Fxq 'Created through the Drupal web interface' + +systemctl restart mariadb.service apache2.service +curl --insecure --fail --silent --show-error \ + "$base/node/$nid" >"$page" +grep -Fq 'TurnKey v19 acceptance page' "$page" +grep -Fq 'Created through the Drupal web interface' "$page" + +dpkg-query -W webmin-apache webmin-mysql >/dev/null +curl --insecure --fail --silent --show-error --head \ + https://127.0.0.1:12321/ >/dev/null +curl --insecure --fail --silent --show-error --head \ + https://127.0.0.1:12322/ >/dev/null +ss -ltn | grep -Eq '127\.0\.0\.1:25[[:space:]]' + +lock_before=$(sha256sum composer.lock | awk '{print $1}') +turnkey-composer update 'drupal/core-*' --with-all-dependencies \ + --dry-run --no-interaction >/dev/null +lock_after=$(sha256sum composer.lock | awk '{print $1}') +test "$lock_after" = "$lock_before" + +apt-get update >/dev/null +for package in apache2 mariadb-server php8.4; do + apt-cache policy "$package" >"$policy" + candidate=$(awk '/Candidate:/ {print $2}' "$policy") + test -n "$candidate" + test "$candidate" != '(none)' + grep -Eq 'trixie|deb13' "$policy" +done +grep -Rqs '^Suites: trixie' /etc/apt/sources.list.d +! grep -Rqi bookworm /etc/apt/sources.list.d + +apache_version=$(dpkg-query -W -f='${Version}' apache2) +mariadb_version=$(dpkg-query -W -f='${Version}' mariadb-server) +cat >"$result" < Date: Wed, 26 Aug 2026 05:18:17 +0000 Subject: [PATCH 2/8] Use Composer audit status for Drupal acceptance Composer 2.8 writes its successful advisory summary outside the stdout stream consumed by the old wording assertion. The exact appliance had already built, booted, completed firstboot, and reached this audit check. Rely on the command exit status, which is the supported machine-readable pass or fail contract, and keep the migration lane focused on appliance behavior. --- tests/v19.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/v19.sh b/tests/v19.sh index abf5f7d..8c4fdb6 100755 --- a/tests/v19.sh +++ b/tests/v19.sh @@ -40,8 +40,7 @@ test "$drupal_version" = 10.6.14 test "$drush_version" = 13.7.6 turnkey-composer validate --no-check-publish --no-interaction >/dev/null turnkey-composer install --dry-run --no-dev --no-interaction >/dev/null -turnkey-composer audit --locked --no-interaction | grep -Fq \ - 'No security vulnerability advisories found' +turnkey-composer audit --locked --no-interaction >/dev/null for package_version in \ 'drupal/ctools 4.1.1' \ From 567a9767ca94e543ec1110056334f35ac0fcd65c Mon Sep 17 00:00:00 2001 From: Liraz Siri Date: Fri, 28 Aug 2026 12:33:35 +0000 Subject: [PATCH 3/8] Update Drupal candidate to 10.6.15 Drupal 10.6.15 supersedes the previously tested patch and is the current supported production release in the 10.6 line. Build and acceptance expectations now target that release. Let core-recommended retain its tested Guzzle 7.15.3 lock instead of downgrading it with a direct requirement. Clarify the official Composer source channels and record the prior exact run separately from the current candidate preflight. Verified on Debian Trixie with PHP 8.4.24 using Composer platform checks, lock validation, audit, and a dry-run update. Shell and Python syntax, required test inputs, and Trixie package-plan availability also pass. --- changelog | 6 ++--- conf.d/main | 5 ++-- docs/v19.0-testing.md | 59 ++++++++++++++++++++++++++++++++++--------- tests/v19.sh | 10 ++++---- 4 files changed, 57 insertions(+), 23 deletions(-) diff --git a/changelog b/changelog index 2c25032..e0c9b8c 100644 --- a/changelog +++ b/changelog @@ -2,11 +2,11 @@ turnkey-drupal10-19.0 (1) turnkey; urgency=low * Upgrade the base distribution to Debian 13/Trixie and PHP 8.4. - * Update Drupal to 10.6.14 with Drush 13.7.6 and compatible pinned versions + * Update Drupal to 10.6.15 with Drush 13.7.6 and compatible pinned versions of the bundled contributed modules. - * Update Guzzle to 7.15.2 to include the upstream fixes for CVE-2026-69245 - and CVE-2026-69246. + * Use Drupal core-recommended's Guzzle 7.15.3 dependency, which includes the + upstream fixes for CVE-2026-69245 and CVE-2026-69246. * Create the welcome page through Drupal's entity API and update the security advisory check for current Drush. diff --git a/conf.d/main b/conf.d/main index 6036325..b157047 100755 --- a/conf.d/main +++ b/conf.d/main @@ -27,7 +27,7 @@ export COMPOSER_MEMORY_LIMIT=-1 # temporarily disable composer memory limits dur # install drupal10 mkdir -p $WEBROOT chown $WEB_USER:$WEB_USER $WEBROOT -turnkey-composer create-project drupal/recommended-project:10.6.14 $WEBROOT --no-dev --no-interaction +turnkey-composer create-project drupal/recommended-project:10.6.15 $WEBROOT --no-dev --no-interaction cd $WEBROOT @@ -52,8 +52,7 @@ turnkey-composer require --no-interaction --update-no-dev \ drupal/google_analytics:4.0.3 \ drupal/imce:3.1.5 \ drupal/pathauto:1.15.0 \ - drupal/token:1.17.0 \ - guzzlehttp/guzzle:7.15.2 + drupal/token:1.17.0 turnkey-drush site-install standard -y \ --account-name=$ADMIN_NAME \ diff --git a/docs/v19.0-testing.md b/docs/v19.0-testing.md index ba74d82..818b21e 100644 --- a/docs/v19.0-testing.md +++ b/docs/v19.0-testing.md @@ -2,12 +2,14 @@ ## Scope and source decision -Debian 13 Trixie does not package the Drupal application. Drupal 10.6.14, -Drush 13.7.6 and the contributed modules therefore come from their maintained -official Composer packages. The build requests exact application versions and -Composer records the resolved distributions in `composer.lock`. Guzzle is -pinned to 7.15.2 because Drupal 10.6.14's initial lock file selected 7.15.1, -which is affected by CVE-2026-69245 and CVE-2026-69246. +Debian 13 Trixie does not package the Drupal application. Drupal 10.6.15 and +the contributed modules therefore come from Drupal's official Composer +repository. Drush and transitive PHP dependencies come from Packagist. The +build requests exact application and module versions, and Composer records the +resolved distributions in `composer.lock`. +Drupal 10.6.15's `core-recommended` lock selects Guzzle 7.15.3, which includes +the fixes for CVE-2026-69245 and CVE-2026-69246 without overriding Drupal's +tested dependency set. PHP 8.4, Apache, MariaDB, Postfix and Composer come from signed Debian Trixie repositories. Drupal 10.6 supports PHP 8.4. The supported update path is a @@ -31,7 +33,7 @@ the normal systemd and firstboot path, then runs `tests/v19.sh`. | Drupal is served over TLS | Request the landing page through Apache | The welcome page is returned over HTTPS | `tests/v19.sh` | | Firstboot configures the administrator | Log in through Drupal's real web form using the preseeded password | Drupal creates an authenticated administrator session | `tests/v19.sh` | | Drupal supports normal content management | Create a Basic page through the authenticated web form, read it from MariaDB, restart Apache and MariaDB, then request it again | The page is visible through Drupal and persists across service restarts | `tests/v19.sh` | -| Drush and Composer support command-line administration | Query versions, run cron, validate the Composer lock and inspect the current security command | Drupal 10.6.14 and Drush 13.7.6 run successfully; Composer and cron pass | `tests/v19.sh` | +| Drush and Composer support command-line administration | Query versions, run cron, validate the Composer lock and inspect the current security command | Drupal 10.6.15 and Drush 13.7.6 run successfully; Composer and cron pass | `tests/v19.sh` | | The bundled contributed modules are installed | Query enabled modules and the IMCE installation path | CTools, Field Group, Google Analytics, Pathauto and Token are enabled; IMCE is installed | `tests/v19.sh` | | Drupal security alerts use current tooling | Ask Drush for help on `pm:security` and audit the lock file | The current command exists and the installed dependency set has no known advisory | `tests/v19.sh` | | Webmin, Adminer and local application mail are available | Request both management endpoints and inspect the loopback SMTP listener | Webmin and Adminer answer over HTTPS; Postfix listens locally | `tests/v19.sh` | @@ -40,11 +42,44 @@ the normal systemd and firstboot path, then runs `tests/v19.sh`. ## Cheap compatibility preflight -A disposable `debian:trixie-slim` container installed Drupal 10.6.14 and the -exact selected module set on PHP 8.4.24. Composer's platform checks passed. -The initial audit exposed the vulnerable Guzzle 7.15.1 lock, so the appliance -now requires the fixed 7.15.2 release. The full appliance run is responsible -for the installed-site and firstboot behavior. +A disposable `debian:trixie-slim` container installed Drupal 10.6.15 and the +exact selected module set on PHP 8.4.24. Composer's platform, lock validation +and security audit checks passed. The resolved Drupal dependency set retained +Guzzle 7.15.3. The full appliance run is responsible for the installed-site +and firstboot behavior. + +The preflight ran these commands after installing Debian's Composer and PHP +packages: + +```sh +composer create-project --no-dev --no-interaction \ + drupal/recommended-project:10.6.15 /tmp/drupal +composer require --no-interaction --update-no-dev \ + drush/drush:13.7.6 drupal/ctools:4.1.1 \ + drupal/field_group:4.0.0 drupal/google_analytics:4.0.3 \ + drupal/imce:3.1.5 drupal/pathauto:1.15.0 drupal/token:1.17.0 +composer check-platform-reqs +composer validate --no-check-publish --no-interaction +composer install --dry-run --no-dev --no-interaction +composer audit --locked --no-interaction +composer update "drupal/core-*" --with-all-dependencies \ + --dry-run --no-interaction +``` + +## Evidence status + +The previous 10.6.14 candidate at source commit +`55c1571b1d5cb4ce1978fe0b448fdb04d651c260` passed the exact Docker run +`20260826t054036z-631-7386`. It established firstboot, administrator login, +content creation and database readback, service restart persistence, cron, +module, audit and updater behavior. The report SHA-256 is +`326d53f8dce93d3171c26a4b4f74128a76d59c76765893a078bed24b67e0bd7a`, and +the run log SHA-256 is +`29db486d16f526fc5bdb69bb77c00ca41cfa14c8c390c9eacaabd2dcb272a72a`. + +The 10.6.15 candidate requires its queued exact run before release acceptance. +The compatibility preflight above passed for the updated source and dependency +set. ## Deferred issues diff --git a/tests/v19.sh b/tests/v19.sh index 8c4fdb6..a6dbf58 100755 --- a/tests/v19.sh +++ b/tests/v19.sh @@ -36,7 +36,7 @@ cd /var/www/drupal10 drupal_version=$(turnkey-drush status --field=drupal-version) drush_version=$(turnkey-composer show drush/drush --format=json | \ php -r '$j=json_decode(stream_get_contents(STDIN), true); echo preg_replace("/^[*v ]+/", "", $j["versions"][0]);') -test "$drupal_version" = 10.6.14 +test "$drupal_version" = 10.6.15 test "$drush_version" = 13.7.6 turnkey-composer validate --no-check-publish --no-interaction >/dev/null turnkey-composer install --dry-run --no-dev --no-interaction >/dev/null @@ -49,7 +49,7 @@ for package_version in \ 'drupal/imce 3.1.5' \ 'drupal/pathauto 1.15.0' \ 'drupal/token 1.17.0' \ - 'guzzlehttp/guzzle 7.15.2'; do + 'guzzlehttp/guzzle 7.15.3'; do read -r package expected <<<"$package_version" actual=$(turnkey-composer show "$package" --format=json | \ php -r '$j=json_decode(stream_get_contents(STDIN), true); echo preg_replace("/^[*v ]+/", "", $j["versions"][0]);') @@ -142,11 +142,11 @@ grep -Rqs '^Suites: trixie' /etc/apt/sources.list.d apache_version=$(dpkg-query -W -f='${Version}' apache2) mariadb_version=$(dpkg-query -W -f='${Version}' mariadb-server) cat >"$result" < Date: Sat, 29 Aug 2026 16:14:42 +0000 Subject: [PATCH 4/8] Seal Drupal 10 v19 exact acceptance Record the passing exact run for the Drupal 10.6.15 release candidate so the documented README crosswalk is backed by reproducible appliance evidence. The run covered build, boot, firstboot, administrator authentication, content persistence, management endpoints, audit and updater behavior without requiring another product fix loop. Verified with exact run 20260829t160411z-5113-11051, shell syntax checks, Python AST parsing, and git diff validation. --- docs/v19.0-testing.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/v19.0-testing.md b/docs/v19.0-testing.md index 818b21e..d7bcf1e 100644 --- a/docs/v19.0-testing.md +++ b/docs/v19.0-testing.md @@ -68,18 +68,19 @@ composer update "drupal/core-*" --with-all-dependencies \ ## Evidence status -The previous 10.6.14 candidate at source commit -`55c1571b1d5cb4ce1978fe0b448fdb04d651c260` passed the exact Docker run -`20260826t054036z-631-7386`. It established firstboot, administrator login, -content creation and database readback, service restart persistence, cron, -module, audit and updater behavior. The report SHA-256 is -`326d53f8dce93d3171c26a4b4f74128a76d59c76765893a078bed24b67e0bd7a`, and -the run log SHA-256 is -`29db486d16f526fc5bdb69bb77c00ca41cfa14c8c390c9eacaabd2dcb272a72a`. - -The 10.6.15 candidate requires its queued exact run before release acceptance. -The compatibility preflight above passed for the updated source and dependency -set. +The Drupal 10.6.15 candidate at source commit +`567a9767ca94e543ec1110056334f35ac0fcd65c` passed exact Docker run +`20260829t160411z-5113-11051`. It established the complete README crosswalk: +build and import, normal systemd boot, firstboot, administrator login, content +creation with MariaDB readback, service restart persistence, cron, bundled +modules, management endpoints, security audit and both supported updater +channels. The report SHA-256 is +`da515816c2abaf90288a8b21052666c20f58548b60a18f3d7228d132d3a95523`, the +run log SHA-256 is +`26794562d18786c81d4f06032be0674cca124eb0a6e6845b12bc65b01dd27c9a`, and +the `SHA256SUMS` manifest SHA-256 is +`a9a70f1eebd41c773ebc97cd115d0159edb9ba0464d97d4a1b0509af58613187`. +The harness reported `verdict=PASS` and verified cleanup. ## Deferred issues From 8aeea0171e92280374b507d88ab63fa8c21e1515 Mon Sep 17 00:00:00 2001 From: Liraz Siri Date: Sat, 29 Aug 2026 16:21:33 +0000 Subject: [PATCH 5/8] Run Drupal security alerts with required privilege The scheduled helper called turnkey-drush as www-data, but turnkey-drush itself must start as root so it can switch to the web account. Run the cron entry as root and exercise the helper through that configured identity during acceptance. This is product fix loop 2/6. Verified with shell syntax checks, a disposable Trixie fixture using the real helper and wrapper, and git diff validation. --- docs/v19.0-testing.md | 2 +- overlay/etc/cron.d/drush-mail-securityupdates | 2 +- tests/v19.sh | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/v19.0-testing.md b/docs/v19.0-testing.md index d7bcf1e..3dfe367 100644 --- a/docs/v19.0-testing.md +++ b/docs/v19.0-testing.md @@ -35,7 +35,7 @@ the normal systemd and firstboot path, then runs `tests/v19.sh`. | Drupal supports normal content management | Create a Basic page through the authenticated web form, read it from MariaDB, restart Apache and MariaDB, then request it again | The page is visible through Drupal and persists across service restarts | `tests/v19.sh` | | Drush and Composer support command-line administration | Query versions, run cron, validate the Composer lock and inspect the current security command | Drupal 10.6.15 and Drush 13.7.6 run successfully; Composer and cron pass | `tests/v19.sh` | | The bundled contributed modules are installed | Query enabled modules and the IMCE installation path | CTools, Field Group, Google Analytics, Pathauto and Token are enabled; IMCE is installed | `tests/v19.sh` | -| Drupal security alerts use current tooling | Ask Drush for help on `pm:security` and audit the lock file | The current command exists and the installed dependency set has no known advisory | `tests/v19.sh` | +| Drupal security alerts use current tooling | Run the scheduled helper as its configured cron identity, ask Drush for help on `pm:security` and audit the lock file | Root starts the helper so `turnkey-drush` can drop to the web user; the current command exists and the installed dependency set has no known advisory | `tests/v19.sh` | | Webmin, Adminer and local application mail are available | Request both management endpoints and inspect the loopback SMTP listener | Webmin and Adminer answer over HTTPS; Postfix listens locally | `tests/v19.sh` | | Debian and Drupal components have maintained update paths | Refresh APT metadata and perform a non-mutating Composer core update resolution | Trixie candidates remain eligible; the Composer update resolves without changing the deployment | `tests/v19.sh` | | Root SSH, backup and other base services are inherited from Core | Cite the unchanged Core layer | Core 19 baseline passes | Core run `20260824t010251z-1634-32241`, source `24c82ee3540ce545422742b0e28ba6b687c53ec2`, verdict `PASS` | diff --git a/overlay/etc/cron.d/drush-mail-securityupdates b/overlay/etc/cron.d/drush-mail-securityupdates index 8b7c7bc..acb244e 100644 --- a/overlay/etc/cron.d/drush-mail-securityupdates +++ b/overlay/etc/cron.d/drush-mail-securityupdates @@ -1 +1 @@ -0 0 * * * www-data [ -x /usr/local/sbin/drush-mail-securityupdates ] && /usr/local/sbin/drush-mail-securityupdates +0 0 * * * root [ -x /usr/local/sbin/drush-mail-securityupdates ] && /usr/local/sbin/drush-mail-securityupdates diff --git a/tests/v19.sh b/tests/v19.sh index a6dbf58..e9b0378 100755 --- a/tests/v19.sh +++ b/tests/v19.sh @@ -63,6 +63,11 @@ done test -d web/modules/contrib/imce turnkey-drush help pm:security >/dev/null turnkey-drush cron >/dev/null +security_cron_user=$(awk '/drush-mail-securityupdates/ {print $6}' \ + /etc/cron.d/drush-mail-securityupdates) +test "$security_cron_user" = root +runuser -u "$security_cron_user" -- \ + /usr/local/sbin/drush-mail-securityupdates curl --insecure --fail --silent --show-error --location \ "$base/" >"$page" From 1075abdd1beac65e8fcd1c07fb35407620022b71 Mon Sep 17 00:00:00 2001 From: Liraz Siri Date: Sat, 29 Aug 2026 16:31:05 +0000 Subject: [PATCH 6/8] Assert Drupal wrapper runtime identity Extend exact acceptance to prove that the real turnkey-drush wrapper reaches Drupal as www-data after the scheduled helper starts through its root cron identity. This is an evidence correction and consumes no product fix loop. Verified with shell syntax and git diff validation. --- docs/v19.0-testing.md | 2 +- tests/v19.sh | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/v19.0-testing.md b/docs/v19.0-testing.md index 3dfe367..f7319d6 100644 --- a/docs/v19.0-testing.md +++ b/docs/v19.0-testing.md @@ -35,7 +35,7 @@ the normal systemd and firstboot path, then runs `tests/v19.sh`. | Drupal supports normal content management | Create a Basic page through the authenticated web form, read it from MariaDB, restart Apache and MariaDB, then request it again | The page is visible through Drupal and persists across service restarts | `tests/v19.sh` | | Drush and Composer support command-line administration | Query versions, run cron, validate the Composer lock and inspect the current security command | Drupal 10.6.15 and Drush 13.7.6 run successfully; Composer and cron pass | `tests/v19.sh` | | The bundled contributed modules are installed | Query enabled modules and the IMCE installation path | CTools, Field Group, Google Analytics, Pathauto and Token are enabled; IMCE is installed | `tests/v19.sh` | -| Drupal security alerts use current tooling | Run the scheduled helper as its configured cron identity, ask Drush for help on `pm:security` and audit the lock file | Root starts the helper so `turnkey-drush` can drop to the web user; the current command exists and the installed dependency set has no known advisory | `tests/v19.sh` | +| Drupal security alerts use current tooling | Run the scheduled helper as its configured cron identity, verify the wrapper's effective user, ask Drush for help on `pm:security` and audit the lock file | Root starts the helper and `turnkey-drush` drops to the web user; the current command exists and the installed dependency set has no known advisory | `tests/v19.sh` | | Webmin, Adminer and local application mail are available | Request both management endpoints and inspect the loopback SMTP listener | Webmin and Adminer answer over HTTPS; Postfix listens locally | `tests/v19.sh` | | Debian and Drupal components have maintained update paths | Refresh APT metadata and perform a non-mutating Composer core update resolution | Trixie candidates remain eligible; the Composer update resolves without changing the deployment | `tests/v19.sh` | | Root SSH, backup and other base services are inherited from Core | Cite the unchanged Core layer | Core 19 baseline passes | Core run `20260824t010251z-1634-32241`, source `24c82ee3540ce545422742b0e28ba6b687c53ec2`, verdict `PASS` | diff --git a/tests/v19.sh b/tests/v19.sh index e9b0378..af80621 100755 --- a/tests/v19.sh +++ b/tests/v19.sh @@ -68,6 +68,8 @@ security_cron_user=$(awk '/drush-mail-securityupdates/ {print $6}' \ test "$security_cron_user" = root runuser -u "$security_cron_user" -- \ /usr/local/sbin/drush-mail-securityupdates +test "$(turnkey-drush php:eval 'echo posix_geteuid();')" = \ + "$(id -u www-data)" curl --insecure --fail --silent --show-error --location \ "$base/" >"$page" From 3c1fb0daf8267eb730bc59703457af57d45d140e Mon Sep 17 00:00:00 2001 From: Liraz Siri Date: Sat, 29 Aug 2026 16:42:31 +0000 Subject: [PATCH 7/8] Use Composer for Drupal security alerts Drush 13 removed pm:security, causing the scheduled alert helper to fail even after its cron privilege was corrected. Audit the locked dependency set with Composer, ignore abandoned-package status for alert decisions, and mail root when Trixie's Composer reports a security finding. This is product fix loop 3/6. Verified with shell syntax, diff validation, and disposable Trixie fixtures covering clean and advisory exit paths through the real turnkey-composer wrapper. --- docs/v19.0-testing.md | 2 +- overlay/usr/local/sbin/drush-mail-securityupdates | 5 +++-- tests/v19.sh | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/v19.0-testing.md b/docs/v19.0-testing.md index f7319d6..2187788 100644 --- a/docs/v19.0-testing.md +++ b/docs/v19.0-testing.md @@ -35,7 +35,7 @@ the normal systemd and firstboot path, then runs `tests/v19.sh`. | Drupal supports normal content management | Create a Basic page through the authenticated web form, read it from MariaDB, restart Apache and MariaDB, then request it again | The page is visible through Drupal and persists across service restarts | `tests/v19.sh` | | Drush and Composer support command-line administration | Query versions, run cron, validate the Composer lock and inspect the current security command | Drupal 10.6.15 and Drush 13.7.6 run successfully; Composer and cron pass | `tests/v19.sh` | | The bundled contributed modules are installed | Query enabled modules and the IMCE installation path | CTools, Field Group, Google Analytics, Pathauto and Token are enabled; IMCE is installed | `tests/v19.sh` | -| Drupal security alerts use current tooling | Run the scheduled helper as its configured cron identity, verify the wrapper's effective user, ask Drush for help on `pm:security` and audit the lock file | Root starts the helper and `turnkey-drush` drops to the web user; the current command exists and the installed dependency set has no known advisory | `tests/v19.sh` | +| Drupal security alerts use current tooling | Run the scheduled helper as its configured cron identity, verify the wrappers' effective application user and audit the lock file | Root starts the helper, the wrappers drop to the web user and Composer finds no known advisory | `tests/v19.sh` | | Webmin, Adminer and local application mail are available | Request both management endpoints and inspect the loopback SMTP listener | Webmin and Adminer answer over HTTPS; Postfix listens locally | `tests/v19.sh` | | Debian and Drupal components have maintained update paths | Refresh APT metadata and perform a non-mutating Composer core update resolution | Trixie candidates remain eligible; the Composer update resolves without changing the deployment | `tests/v19.sh` | | Root SSH, backup and other base services are inherited from Core | Cite the unchanged Core layer | Core 19 baseline passes | Core run `20260824t010251z-1634-32241`, source `24c82ee3540ce545422742b0e28ba6b687c53ec2`, verdict `PASS` | diff --git a/overlay/usr/local/sbin/drush-mail-securityupdates b/overlay/usr/local/sbin/drush-mail-securityupdates index d1a7ed6..be3a237 100755 --- a/overlay/usr/local/sbin/drush-mail-securityupdates +++ b/overlay/usr/local/sbin/drush-mail-securityupdates @@ -6,14 +6,15 @@ TMPFILE=$(mktemp) trap 'rm -f "$TMPFILE"' INT TERM EXIT set +e -/usr/local/bin/turnkey-drush pm:security >"$TMPFILE" 2>&1 +/usr/local/bin/turnkey-composer audit --locked --no-interaction \ + --abandoned=report >"$TMPFILE" 2>&1 status=$? set -e case $status in 0) ;; - 3) + 1) mail -s 'Drupal security updates available' root <"$TMPFILE" ;; *) diff --git a/tests/v19.sh b/tests/v19.sh index af80621..5631d46 100755 --- a/tests/v19.sh +++ b/tests/v19.sh @@ -61,7 +61,6 @@ for module in ctools field_group google_analytics pathauto token; do grep -Fxq "$module" <<<"$enabled_modules" done test -d web/modules/contrib/imce -turnkey-drush help pm:security >/dev/null turnkey-drush cron >/dev/null security_cron_user=$(awk '/drush-mail-securityupdates/ {print $6}' \ /etc/cron.d/drush-mail-securityupdates) From ea71a1ed5d4aedbee2be695f809d612529fbfdbe Mon Sep 17 00:00:00 2001 From: Liraz Siri Date: Sat, 29 Aug 2026 16:53:20 +0000 Subject: [PATCH 8/8] Seal corrected Drupal v19 exact acceptance Record the passing exact run after replacing the removed Drush security command with the supported Composer audit path. The evidence now covers the real root cron entry, application-user wrapper, all prior Drupal main flows, supported updater channels, and the accurate product fix count of three out of six. Verified with exact run 20260829t164241z-6730-30442, shell syntax checks, Python AST parsing, evidence hash matching, and git diff validation. --- docs/v19.0-testing.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/v19.0-testing.md b/docs/v19.0-testing.md index 2187788..0a1c3fc 100644 --- a/docs/v19.0-testing.md +++ b/docs/v19.0-testing.md @@ -69,17 +69,18 @@ composer update "drupal/core-*" --with-all-dependencies \ ## Evidence status The Drupal 10.6.15 candidate at source commit -`567a9767ca94e543ec1110056334f35ac0fcd65c` passed exact Docker run -`20260829t160411z-5113-11051`. It established the complete README crosswalk: +`3c1fb0daf8267eb730bc59703457af57d45d140e` passed exact Docker run +`20260829t164241z-6730-30442`. It established the complete README crosswalk: build and import, normal systemd boot, firstboot, administrator login, content creation with MariaDB readback, service restart persistence, cron, bundled -modules, management endpoints, security audit and both supported updater -channels. The report SHA-256 is -`da515816c2abaf90288a8b21052666c20f58548b60a18f3d7228d132d3a95523`, the +modules, management endpoints, the scheduled Composer security audit through +its root cron entry and web-user wrapper, and both supported updater channels. +This candidate used three of six product fix loops. The report SHA-256 is +`bc286beada670bb799e2624dfb8efd9e2e2d7ef5cb698516b117de19e048bb72`, the run log SHA-256 is -`26794562d18786c81d4f06032be0674cca124eb0a6e6845b12bc65b01dd27c9a`, and +`35758cc8d0e74a1e721a59cc5c353df8d5c59294e41fe7543e3369edf3d0299e`, and the `SHA256SUMS` manifest SHA-256 is -`a9a70f1eebd41c773ebc97cd115d0159edb9ba0464d97d4a1b0509af58613187`. +`b9de86de60a8f59002e8bfffab92f57e7157b59c4e386007401f8c4deac2581e`. The harness reported `verdict=PASS` and verified cleanup. ## Deferred issues