Skip to content

Slash post content before writing in publish-post - #211

Merged
jasperf merged 2 commits into
mainfrom
fix/publish-post-wp-slash
Sep 3, 2026
Merged

Slash post content before writing in publish-post#211
jasperf merged 2 commits into
mainfrom
fix/publish-post-wp-slash

Conversation

@jasperf

@jasperf jasperf commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Fixes #210.

Problem

publish-post passed post_content to wp_update_post() / wp_insert_post() unslashed. Both call wp_unslash() internally and are documented as expecting slashed data, so every literal backslash in a draft was eaten on the way into the database.

Prose posts were unaffected, which is why this survived review. Posts carrying code were silently corrupted: \", \', \\, \n, \b and backreferences like \2 are load-bearing inside preg_replace patterns and printf/sprintf calls, and simply disappeared.

Hit in practice on a WooCommerce MU-plugin post — 17701 bytes in, 17688 stored, the regex and format strings mangled beyond use.

Scope

Four call sites, both entry points, both the update and the create path:

File Call
mcp-server/src/tools/publishPost.ts wp_update_post, wp_insert_post
wp-cli/content-creation/publish-post.sh wp_update_post, wp_insert_post

Creating a new post was affected identically to updating one.

Fix

One wp_slash( $body ) per worker, placed after the transit-length guard and before either write, so a single statement covers both paths. Keeping it after the guard means that check still measures the real payload rather than the slashed one.

Why the existing guards missed it

The transit check runs before the write:

if ( strlen( $body ) !== $bytes ) { echo "ABORT: body length changed in transit\n"; return; }

The body arrived intact; the loss happened inside WordPress afterwards. The post-write byte verification did catch it — it reported the length mismatch that led here — but only as a generic count, with no hint that backslashes were the cause. With this change that verification round-trips instead of failing.

Verification

  • bash -n wp-cli/content-creation/publish-post.sh passes
  • tsc --noEmit passes; the built dist/tools/publishPost.js emits $body = wp_slash( $body ); intact (the worker is a String.raw literal, so no escaping surprises)
  • The shell worker was generated with build_php and linted with php -l under PHP 8.5 — valid, with wp_slash correctly placed ahead of both writes and $body correctly unescaped through the heredoc
  • wp_slash() was already confirmed as the working counterpart out-of-band: writing the same content via wp eval with wp_slash() applied stored 17153 bytes with all 16 backslashes preserved, byte-identical to source

Not addressed here

On the update path publish-post sets SEO meta, tags and terms but does not rewrite post_title, so a title typo survives an update that changes everything else. Noted at the end of #210; left alone since it may well be deliberate.

wp_update_post() and wp_insert_post() call wp_unslash() internally and
expect slashed input. The body was passed unslashed, so every literal
backslash was eaten on the way into the database.

Prose was unaffected, which is why this went unnoticed. Posts carrying
code were silently corrupted: escapes and regex backreferences such as
\" \' \\ \n \b and \2 are load-bearing in preg_replace patterns and
printf calls, and simply vanished.

Applies wp_slash() to the body immediately before the write, covering
the update and create paths in both the MCP tool and the CLI script.
The slash lands after the transit-length guard so that guard still
measures the real payload, and the post-write byte verification now
round-trips instead of reporting a length mismatch.

Fixes #210
Same defect as the body: wp_insert_post() unslashes its input, so a
backslash in the title or meta description was dropped on write. Rare
in practice, but the create path writes both fields today, so it is a
live bug rather than a latent one.
@jasperf

jasperf commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Added a second commit: post_title and post_excerpt are slashed on the create path too.

Same defect — wp_insert_post() unslashes its input, so a backslash in a title or meta description was dropped on write. Rare in practice, but the create path writes both fields today, so it is live rather than latent. Regenerated the shell worker with a \"-containing title and re-ran php -l plus tsc --noEmit; both clean.

@jasperf
jasperf merged commit 9124634 into main Sep 3, 2026
1 check passed
@jasperf
jasperf deleted the fix/publish-post-wp-slash branch September 3, 2026 04:47
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.

publish-post silently strips backslashes from post content (missing wp_slash)

1 participant