-
Notifications
You must be signed in to change notification settings - Fork 466
fix: Prevent printed scripts from breaking GraphQL responses #3413
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Prevent printed scripts from breaking GraphQL responses #3413
Conversation
…eaking GraphQL responses Fixes wp-graphql#3397 where plugins calling wp_print_inline_script_tag() or similar output functions during the 'wp_enqueue_scripts' action would break GraphQL JSON responses by mixing HTML with JSON output. The issue occurred when: 1. A GraphQL query includes 'enqueuedScripts' or 'enqueuedStylesheets' fields 2. WPGraphQL fires the 'wp_enqueue_scripts' action to determine enqueued assets 3. Plugins output HTML during this action (e.g., wp_print_inline_script_tag()) 4. The HTML output corrupts the JSON response, causing parsing errors This fix implements comprehensive output buffering at the Router level during GraphQL HTTP request execution. This approach: - Captures and discards any unwanted output during GraphQL execution - Provides comprehensive protection beyond just wp_enqueue_scripts - Uses a single point of control for cleaner architecture - Automatically handles future sources of unwanted output - Includes proper cleanup in exception handling I initally explored output buffering at the Model layer (i.e. Post.php, etc near the do_action('wp_enqueue_script'); calls, but had an epiphany that the Router-level approach would help fix other bugs like this that have been reported (but were always tricky to track down) in the past. This router approach is superior to resolver-level fixes as it protects the entire GraphQL execution context rather than individual action calls. 👏 Changes: - Add output buffering around GraphQL execution in Router::process_http_request() - Add test case to prevent regression - Ensure proper cleanup in error conditions
…-if-script-is-printed
…tput buffering Adds testContentRenderingWorksWithRouterOutputBuffering() to ensure that the Router-level output buffering fix for wp-graphql#3397 does not interfere with legitimate content rendering that uses WordPress filters like 'the_content'. The test verifies that: - Content filters still work correctly (apply_filters returns processed content) - GraphQL responses remain valid with Router-level output buffering - HTML content from filters appears properly in the response This test complements the existing fix validation and ensures no regression in content rendering functionality.
…-enqueued-scripts-breaks-response-if-script-is-printed
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements router-level output buffering to prevent HTML output from corrupting GraphQL JSON responses. The fix addresses issues where plugins calling functions like wp_print_inline_script_tag() during GraphQL execution would output HTML directly into the response stream, breaking JSON parsing on the client side.
- Router-level output buffering wrapped around the entire GraphQL HTTP execution
- Comprehensive test coverage for various sources of unwanted HTML output during GraphQL execution
- Protection against HTML output from WordPress hooks, WPGraphQL-specific hooks, and plugin behaviors
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Router.php | Added output buffering around GraphQL HTTP request execution with proper exception handling |
| tests/wpunit/EnqueuedScriptsTest.php | Added regression test for the original issue with wp_print_inline_script_tag during wp_enqueue_scripts |
| tests/wpunit/RouterTest.php | Added extensive test coverage for various scenarios where HTML output could break GraphQL responses |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
Co-authored-by: Copilot <[email protected]>
🐛 Problem
When plugins call
wp_print_inline_script_tag()or similar output functions during thewp_enqueue_scriptsaction, it causes HTML to be output directly into the response stream. This breaks GraphQL JSON responses because HTML markup gets mixed with JSON, causing parsing errors on the client side.Root Cause
WPGraphQL calls
do_action( 'wp_enqueue_scripts' )in multiple places to determine which scripts/styles are enqueued for specific nodes:enqueuedScriptsorenqueuedStylesheetsfields on posts, pages, terms, users, etc.registeredScriptsorregisteredStylesheetsfrom the root queryIf any plugin outputs HTML during this action (which is not uncommon), it corrupts the JSON response.
Example Error
{ "errors": [ { "message": "Unexpected token '<', \"\t<script>\nw\"... is not valid JSON", "stack": "SyntaxError: Unexpected token '<', \"\t<script>\nw\"... is not valid JSON" } ] }🔧 Solution
This PR implements Router-level output buffering during GraphQL HTTP request execution.
✅ Advantages of Router-Level Fix
🔧 Implementation Details
The fix wraps the entire GraphQL execution in output buffering:
🧪 Testing
Test Case Added
testPrintInlineScriptDoesNotBreakEnqueuedScriptsWithFix()verifies that callingwp_print_inline_script_tag()duringwp_enqueue_scriptsdon't break GraphQL responsesTest Results
Manual Testing
The fix was tested with the exact scenario from the issue:
Before fix: GraphQL response broken with HTML mixed in JSON
After fix: Clean JSON response with proper enqueued scripts data
📁 Files Changed
src/Router.php- Added comprehensive output buffering around GraphQL executiontests/wpunit/EnqueuedScriptsTest.php- Added regression test🔄 Backward Compatibility
This change is fully backward compatible:
graphql()calls)🎯 Impact
This fix protects against:
wp_print_inline_script_tag()calls duringwp_enqueue_scripts(original issue)enqueuedScriptsfield can lead to broken output if scripts are printed in this action #3397NOTE: I can't point to specific issues, but I've helped folks debug issues in the past where responses to certain queries would break the response in the IDE and we were never able to holistically determine a solution. I have a hunch that this will solve a lot of edge cases that were previously difficult to reproduce consistently.
🚀 Alternative Approaches Considered
I initially explored adding output buffering to the Post, Term, User, etc Model where they call the wp_enqueue_scripts action. However, this would lead to:
Instead of duplicating the model-level output buffering effort, I implemented it in the Router which covers these cases and likely other misc edge cases as noted above.