Skip to content

Escape the database settings written into wp-tests-config.php - #355

Merged
kittenkamala merged 3 commits into
WordPress:masterfrom
ekamran:shifteq/escape-db-credentials
Oct 22, 2026
Merged

kittenkamala merged 3 commits into
WordPress:masterfrom
ekamran:shifteq/escape-db-credentials

Conversation

@ekamran

@ekamran ekamran commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #354.

Problem

prepare.php copies the database settings into single-quoted PHP strings in wp-tests-config.php without escaping them. A password with a single quote, or one ending with a backslash, makes the file a parse error. A double backslash is silently written as one, and a password containing localhost gets that part replaced with WPT_DB_HOST, because str_replace() runs each placeholder over the result of the previous one. Details in #354.

Fix

  • Escape WPT_DB_NAME, WPT_DB_USER, WPT_DB_PASSWORD, WPT_DB_HOST, WPT_TABLE_PREFIX and WP_PHP_BINARY with addcslashes( $value, "\\'" ), as WordPress does when it writes wp-config.php (setup-config.php).
  • Replace the placeholders with strtr(), which works in a single pass and never rewrites a value it has already written.

For values that contain no ', no \ and no placeholder name such as localhost, the output does not change: the generated wp-tests-config.php for a plain password, with the default table prefix and with host_, is byte-identical to the one master writes.

CI

The CI job now uses the password wp'te\st-localhost and checks, right after prepare.php, that DB_PASSWORD in the generated file equals WPT_DB_PASSWORD. Run against master's prepare.php, that check fails with exit 255 (parse error). The mysql image puts MYSQL_PASSWORD into SQL without escaping it, so the job sets the password with ALTER USER in its own step.

Verification

Config-writing code of prepare.php, master and this branch:

WPT_DB_PASSWORD master this branch
plainpass correct correct, same file as master
p'q"r$s#t u parse error correct
endswith\ parse error correct
two\\back two\back correct
my-localhost-pw my-127.0.0.1:33348-pw correct

Against a real database, MariaDB 12.3 and MySQL 8.0: on master the generated config does not load, and cleanup-db.php stops with "could not parse the database settings", so the test tables stay. On this branch the config connects, and cleanup-db.php drops the tables.

Full flow on this branch with WPT_DB_PASSWORD set to p'q"r$s#t u, against MariaDB 12.3 with PHP 8.5: prepare.php succeeds, the new CI check passes, test.php with --group option runs 413 tests with no failures, report.php succeeds, and cleanup.php drops all 12 test tables.

php -l passes on PHP 7.4, 8.0 and 8.2, and phpcs is clean.

Notes

Use of AI

AI assistance: Yes
Tool(s): Claude Code and Codex
Used for: Investigation, implementation, verification harness, and PR wording. I reviewed the reasoning and test results, and I take responsibility for the contribution.

prepare.php copies WPT_DB_NAME, WPT_DB_USER, WPT_DB_PASSWORD, WPT_DB_HOST and
WPT_TABLE_PREFIX into single-quoted PHP strings in wp-tests-config.php without
escaping them.
A password with a single quote, or one ending with a backslash, made the file
a parse error, so the test run failed and cleanup-db.php could not read the
settings. A double backslash was silently written as one.

The values are now escaped with addcslashes( $value, "\\'" ), as WordPress
does when it writes wp-config.php in wp-admin/setup-config.php, and the same
goes for WP_PHP_BINARY. The placeholders are replaced with strtr() in a
single pass: str_replace() ran each pair over the previous result, so a
password containing "localhost" had that part replaced with WPT_DB_HOST.

The CI job now uses a password with a quote, a backslash and the word
"localhost", and checks that DB_PASSWORD in the generated file equals
WPT_DB_PASSWORD. The mysql image puts MYSQL_PASSWORD into SQL unescaped, so a
step sets the password with ALTER USER instead.
@github-actions

github-actions Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @jazzsequence.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: jazzsequence.

Co-authored-by: ekamran <ekamran@git.wordpress.org>
Co-authored-by: kittenkamala <amykamala@git.wordpress.org>
Co-authored-by: chrisdavidmiles <chrisdavidmiles@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@kittenkamala
kittenkamala merged commit 6b55379 into WordPress:master Oct 22, 2026
6 checks passed
@ekamran
ekamran deleted the shifteq/escape-db-credentials branch October 22, 2026 07:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A database password with a quote or a backslash breaks wp-tests-config.php

4 participants