Skip to content

Security: add a CSRF nonce to the setup/installer AJAX action#1374

Open
vuckro wants to merge 2 commits into
Ultimate-Multisite:mainfrom
vuckro:security/setup-wizard-csrf
Open

Security: add a CSRF nonce to the setup/installer AJAX action#1374
vuckro wants to merge 2 commits into
Ultimate-Multisite:mainfrom
vuckro:security/setup-wizard-csrf

Conversation

@vuckro

@vuckro vuckro commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

wu_setup_install runs privileged installers (plugin install/activation, network
creation, wp-config writes) gated only by a capability check, with no nonce.
A logged-in admin could be induced by a cross-site request to execute installer
steps (CSRF).

Changes

Introduce a wu_setup_install nonce, following the existing
ajax_network_activate pattern:

  • created in the shared wu_setup_settings localization,
  • sent by the installer AJAX call (both setup-wizard.js and its built
    setup-wizard.min.js),
  • verified in both setup_install() handlers (network setup wizard and the
    pre-multisite installer).

Compatibility

The wizard JS now sends the nonce, so the normal setup flow is unaffected. Both
handlers verify the same action.


Part of a small series of focused security hardening PRs. Full technical detail
is available privately to the maintainers on request (coordinated disclosure).

Summary by CodeRabbit

  • Bug Fixes

    • Implemented nonce token verification for setup wizard AJAX requests to enhance security during the installation process and prevent unauthorized requests.
  • Chores

    • Enhanced CI/CD workflow reliability by adding defensive file existence checks before executing environment management commands, ensuring proper error handling when expected files or directories are missing.

wu_setup_install ran privileged installers (plugin install/activation,
network creation, wp-config writes) gated only by a capability check, with
no nonce. A logged-in admin could be tricked by a cross-site request into
executing installer steps.

Add a 'wu_setup_install' nonce: created in the shared wu_setup_settings
localization, sent by the installer AJAX call (source and minified JS), and
verified in both setup_install() handlers (network setup wizard and the
pre-multisite installer), mirroring the existing ajax_network_activate
nonce flow.

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

coderabbitai Bot commented Jun 10, 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: 543ffeda-3e1d-44fe-86ff-3565d1c67d7c

📥 Commits

Reviewing files that changed from the base of the PR and between 1310078 and 3abe05c.

⛔ Files ignored due to path filters (1)
  • assets/js/setup-wizard.min.js is excluded by !**/*.min.js
📒 Files selected for processing (5)
  • .github/workflows/e2e.yml
  • assets/js/setup-wizard.js
  • inc/admin-pages/class-multisite-setup-admin-page.php
  • inc/admin-pages/class-setup-wizard-admin-page.php
  • inc/admin-pages/class-wizard-admin-page.php

📝 Walkthrough

Walkthrough

This PR adds CSRF nonce protection to the setup wizard AJAX endpoints and hardens the CI workflow with defensive path checks. The nonce is generated server-side, localized to the client, included in installer requests, and validated on both setup endpoints before processing continues.

Changes

Setup Wizard CSRF Nonce Protection

Layer / File(s) Summary
Nonce generation and client localization
inc/admin-pages/class-wizard-admin-page.php
The render_installation_steps() method creates a nonce token using wp_create_nonce('wu_setup_install') and passes it to the client-side script via the wu_setup_settings localized object.
Client AJAX request with nonce
assets/js/setup-wizard.js
The setup wizard AJAX POST request now includes _wpnonce: wu_setup_settings.install_nonce in the payload alongside action, installer, and dry-run fields.
Server-side nonce verification on both endpoints
inc/admin-pages/class-setup-wizard-admin-page.php, inc/admin-pages/class-multisite-setup-admin-page.php
Both setup_install() handlers verify the AJAX nonce via check_ajax_referer('wu_setup_install', '_wpnonce', false); nonce validation failure returns a JSON error with bad-nonce and exits early, preventing further processing.

CI Workflow Defensive Checks

Layer / File(s) Summary
Workflow conditional path checks
.github/workflows/e2e.yml
The "Fix permissions for Cypress output" step now checks for tests/e2e/cypress before running chown -R, and the "Stop WordPress Environment" step checks for package.json before running npm run env:stop; missing paths log a skip message and continue.

Sequence Diagram

sequenceDiagram
  participant Wizard as Wizard Page
  participant Client as Setup Wizard JS
  participant Server as Setup Install Handler
  Wizard->>Wizard: wp_create_nonce('wu_setup_install')
  Wizard->>Client: localize install_nonce
  Client->>Server: AJAX POST with _wpnonce
  Server->>Server: check_ajax_referer()
  alt Nonce Valid
    Server->>Server: process installer step
  else Nonce Invalid
    Server->>Client: JSON error (bad-nonce)
  end
Loading

🎯 2 (Simple) | ⏱️ ~8 minutes

review-feedback-scanned, status:available

🐰 A wizard guards each door with token divine,
Nonces checked and paths confirmed in line,
Each step secured with cryptographic care,
While workflows skip with humble flair,
Protection flows through CI's mountain air!

🚥 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 clearly and specifically describes the main change: adding a CSRF nonce to the setup/installer AJAX action for security purposes.
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 unit tests (beta)
  • Create PR with unit tests

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.

@superdav42

Copy link
Copy Markdown
Collaborator

Permission check failed for this PR (HTTP 200 from collaborator permission API). Unable to determine if @vuckro is a maintainer or external contributor. A maintainer must review and merge this PR manually. This is a fail-closed safety measure — the pulse will not auto-merge until the permission API succeeds.


aidevops.sh v3.20.46 automated scan.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants