Skip to content

ci: fix PR #1382 E2E cleanup failure#1413

Merged
superdav42 merged 5 commits into
mainfrom
fix/pr-1382-e2e-cleanup
Jun 12, 2026
Merged

ci: fix PR #1382 E2E cleanup failure#1413
superdav42 merged 5 commits into
mainfrom
fix/pr-1382-e2e-cleanup

Conversation

@superdav42

@superdav42 superdav42 commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

Testing

  • git diff --check
  • php -l inc/functions/sunrise.php && php -l inc/class-sunrise.php && php -l tests/WP_Ultimo/Functions/Sunrise_Functions_Test.php
  • vendor/bin/phpcs inc/functions/sunrise.php inc/class-sunrise.php tests/WP_Ultimo/Functions/Sunrise_Functions_Test.php
  • python3 - <<'PY' ... yaml.safe_load(.github/workflows/e2e.yml) ... PY

For #1382


Summary by CodeRabbit

  • Security

    • Strengthened security-mode unlock validation with defensive checks and timing-safe key comparison
    • Replaced deterministic keys with persisted high-entropy security keys and added a legacy fallback for migration
  • Chores

    • CI workflow updated to use a pinned service image and to perform conditional cleanup only when appropriate
  • Tests

    • Updated tests to validate the new high-entropy key behavior, persistence, and fallback paths

vuckro and others added 3 commits June 10, 2026 14:24
The unauthenticated ?wu_secure=KEY query string that turns the network-wide
recovery "security mode" off used substr(md5(admin_email), 0, 6) as the key —
only ~24 bits and derived from a commonly public/guessable value, so an attacker
could compute or brute-force it and remotely disable the admin's safe-mode
lockdown.

Generate a 128-bit random key once (random_bytes, since this runs from sunrise
before pluggable.php) and store it as a network option, and compare it with
hash_equals(). The key is already displayed on the settings screen, so the
documented "copy this URL to disable security mode" workflow is unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0a93e8f9-2247-4ad3-bd79-93a5149b6e39

📥 Commits

Reviewing files that changed from the base of the PR and between 0398978 and 749f1d7.

📒 Files selected for processing (3)
  • .github/workflows/e2e.yml
  • inc/functions/sunrise.php
  • tests/WP_Ultimo/Functions/Sunrise_Functions_Test.php
🚧 Files skipped from review as they are similar to previous changes (3)
  • .github/workflows/e2e.yml
  • tests/WP_Ultimo/Functions/Sunrise_Functions_Test.php
  • inc/functions/sunrise.php

📝 Walkthrough

Walkthrough

Upgrades the security-mode key to a persisted 32-char hex secret with legacy fallback, makes Sunrise unlock timing-safe, adds tests for generation/persistence/legacy behavior, and hardens the E2E workflow by pinning Mailpit and conditionally skipping cleanup steps when checkout artifacts are missing.

Changes

Security Mode Key Upgrade to High-Entropy Persistent Keys

Layer / File(s) Summary
High-entropy key generation and persistence
inc/functions/sunrise.php
wu_get_security_mode_key($generate = true) now reads wu_security_mode_key, generates a 16-byte random secret (hex-encoded, 32 chars) and persists it when missing; supports $generate=false to return the legacy value. wu_get_legacy_security_mode_key() added.
Timing-safe security-mode unlock verification
inc/class-sunrise.php
Sunrise::load() captures wu_secure into $provided_key, checks it's a string, and uses hash_equals() against wu_get_security_mode_key(false) for timing-safe comparison.
Test coverage for high-entropy and legacy behavior
tests/WP_Ultimo/Functions/Sunrise_Functions_Test.php
Tests assert generated keys are 32-char lowercase hex, stable after persistence, that wu_get_security_mode_key(false) returns the persisted key when present, and that $generate=false returns the legacy md5-derived key without persisting when no stored key exists.

CI Workflow Robustness Improvements

Layer / File(s) Summary
Service image pinning and conditional cleanup
.github/workflows/e2e.yml
Mailpit image pinned to ghcr.io/axllent/mailpit@sha256:.... The workflow now only chowns tests/e2e/cypress if the directory exists and only runs npm run env:stop if package.json is present; otherwise logs and skips cleanup steps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

review-feedback-scanned, testing, origin:interactive

Poem

🐰 I burrowed through keys tonight,

From hashed crumbs to shining byte,
Twenty-four plus eight, now thirty-two,
Safe compares and tests to prove it's true,
CI skips when checkouts take flight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'ci: fix PR #1382 E2E cleanup failure' directly addresses the primary fix of the E2E workflow cleanup issue mentioned in the PR objectives, which is the main change focus in the .github/workflows/e2e.yml file modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/pr-1382-e2e-cleanup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown

🔨 Build Complete - Ready for Testing!

📦 Download Build Artifact (Recommended)

Download the zip build, upload to WordPress and test:

🌐 Test in WordPress Playground (Very Experimental)

Click the link below to instantly test this PR in your browser - no installation needed!
Playground support for multisite is very limitied, hopefully it will get better in the future.

🚀 Launch in Playground

Login credentials: admin / password

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
inc/class-sunrise.php (1)

338-340: ⚡ Quick win

Sanitize the incoming recovery key before comparing it.

$provided_key now comes straight from $_GET. Please unslash and sanitize it before the hash_equals() check so this path follows the repo’s input-handling rule without changing valid hex keys.

Suggested fix
 				$provided_key = wu_get_isset($_GET, 'wu_secure'); // phpcs:ignore WordPress.Security.NonceVerification
+
+				if (is_string($provided_key)) {
+					$provided_key = sanitize_text_field(wp_unslash($provided_key));
+				}

 				if (is_string($provided_key) && hash_equals(wu_get_security_mode_key(false), $provided_key)) {

As per coding guidelines, "Use wu_clean() or WordPress sanitization functions for input sanitization; custom sanitizers are registered in .phpcs.xml.dist".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@inc/class-sunrise.php` around lines 338 - 340, The incoming recovery key
stored in $provided_key must be unslashed and sanitized before comparison;
update the assignment of $provided_key (used in the hash_equals check against
wu_get_security_mode_key(false)) to call wp_unslash on the raw GET value and
then sanitize it (preferably with wu_clean() per project guidelines, e.g.
$provided_key = wu_clean(wp_unslash(wu_get_isset($_GET, 'wu_secure')))) so only
cleaned input reaches the hash_equals() check.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/e2e.yml:
- Line 22: Replace the mutable tag used for the Mailpit service by pinning
services.mailpit.image to an immutable sha256 digest instead of
ghcr.io/axllent/mailpit:latest; update the value of services.mailpit.image to
the specific image@sha256:<digest> for a known-good build (obtain the digest
from the registry or CI artifact, e.g., via the registry manifest or docker
pull/inspect) so E2E runs are reproducible and supply-chain stable.

In `@inc/functions/sunrise.php`:
- Around line 98-105: The branch that generates a new $key must be made
first-write-wins: after generating $key (using bin2hex(random_bytes(16))) call
update_network_option(null, 'wu_security_mode_key', $key) but do not immediately
return the generated value; instead re-read the stored option (e.g., via the
corresponding network/site option getter) into $key and return that stored value
so that if another request beat us or the write failed we return the actual
persisted key; keep the existing fallback to wu_get_legacy_security_mode_key()
when $generate is false.

In `@tests/WP_Ultimo/Functions/Sunrise_Functions_Test.php`:
- Around line 144-153: Add a test that covers the persisted-key false path by
first generating and storing the key, then verifying
wu_get_security_mode_key(false) returns the stored 32-character value (not the
6-char legacy fallback). Concretely: call wu_get_security_mode_key(true) to
generate/persist the key (or manually update the network option
'wu_security_mode_key'), fetch the stored value via get_network_option(null,
'wu_security_mode_key'), then assert wu_get_security_mode_key(false) === stored
value and that the stored value has length 32; ensure you clean up/reset the
network option afterwards. Reference: wu_get_security_mode_key and the unlock
flow in inc/class-sunrise.php.

---

Nitpick comments:
In `@inc/class-sunrise.php`:
- Around line 338-340: The incoming recovery key stored in $provided_key must be
unslashed and sanitized before comparison; update the assignment of
$provided_key (used in the hash_equals check against
wu_get_security_mode_key(false)) to call wp_unslash on the raw GET value and
then sanitize it (preferably with wu_clean() per project guidelines, e.g.
$provided_key = wu_clean(wp_unslash(wu_get_isset($_GET, 'wu_secure')))) so only
cleaned input reaches the hash_equals() check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8e30d079-fc89-4a0b-8031-9242dd3cd0f0

📥 Commits

Reviewing files that changed from the base of the PR and between 4c6ceed and 0398978.

📒 Files selected for processing (4)
  • .github/workflows/e2e.yml
  • inc/class-sunrise.php
  • inc/functions/sunrise.php
  • tests/WP_Ultimo/Functions/Sunrise_Functions_Test.php

Comment thread .github/workflows/e2e.yml Outdated
Comment thread inc/functions/sunrise.php Outdated
Comment thread tests/WP_Ultimo/Functions/Sunrise_Functions_Test.php
@github-actions

Copy link
Copy Markdown

🔨 Build Complete - Ready for Testing!

📦 Download Build Artifact (Recommended)

Download the zip build, upload to WordPress and test:

🌐 Test in WordPress Playground (Very Experimental)

Click the link below to instantly test this PR in your browser - no installation needed!
Playground support for multisite is very limitied, hopefully it will get better in the future.

🚀 Launch in Playground

Login credentials: admin / password

@superdav42

Copy link
Copy Markdown
Collaborator Author

CLAIM_RELEASED reason=worker_complete runner=superdav42 ts=2026-06-12T11:09:06Z aidevops_version=3.20.56 opencode_version=1.17.3

@superdav42 superdav42 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approved

@github-actions

Copy link
Copy Markdown

🔨 Build Complete - Ready for Testing!

📦 Download Build Artifact (Recommended)

Download the zip build, upload to WordPress and test:

🌐 Test in WordPress Playground (Very Experimental)

Click the link below to instantly test this PR in your browser - no installation needed!
Playground support for multisite is very limitied, hopefully it will get better in the future.

🚀 Launch in Playground

Login credentials: admin / password

@superdav42 superdav42 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approved

@superdav42 superdav42 merged commit bc4d4bb into main Jun 12, 2026
13 checks passed
@superdav42

Copy link
Copy Markdown
Collaborator Author

Summary

Testing

  • git diff --check
  • php -l inc/functions/sunrise.php && php -l inc/class-sunrise.php && php -l tests/WP_Ultimo/Functions/Sunrise_Functions_Test.php
  • vendor/bin/phpcs inc/functions/sunrise.php inc/class-sunrise.php tests/WP_Ultimo/Functions/Sunrise_Functions_Test.php
  • python3 - <<'PY' ... yaml.safe_load(.github/workflows/e2e.yml) ... PY
    For Security: use a high-entropy random key for the security-mode disable URL #1382


Merged via PR #1413 to main.
Merged by deterministic merge pass (pulse-wrapper.sh).


aidevops.sh v3.20.57 spent 10m on this as a headless bash routine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status:available Task is available for claiming

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant