Skip to content

Keep submitted shortcodes and merge tags encoded on WordPress 7.2 - #1972

Merged
jakejackson1 merged 1 commit into
developmentfrom
fix/kses-keep-encoded-tags-wp72
Oct 27, 2026
Merged

jakejackson1 merged 1 commit into
developmentfrom
fix/kses-keep-encoded-tags-wp72

Conversation

@zamoragregory

@zamoragregory zamoragregory commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Gravity PDF encodes [ ] { } in submitted values as [ ] { }, so shortcodes and merge tags in them aren't processed in a PDF. The HTML then goes through Kses::parse(). WordPress 7.2 rebuilds wp_kses() on the HTML API, which decodes those character references, so Kses::parse() turned them back into real tags. On WordPress 7.2-beta1, a submitted {admin_email} printed the site admin's email address in the PDF, and a submitted [gravitypdf] shortcode printed a link.

encode_tags() now puts a zero-width no-break space (U+FEFF) after each [, { and : instead. Kses keeps it on both parsers, it stops shortcodes and merge tags matching, and mPDF strips it before drawing, so the PDF shows the text as typed. HTML attributes keep their URL-encoding, and their colons are neutralised too, so a merge tag opened in the text can't end inside one. $form_data values get the same.

Try it

  1. On WordPress 7.2-beta1, submit a form with a Paragraph field containing {admin_email} [gravitypdf id="<PDF ID>"].
  2. View the entry's PDF. The field shows the text as it was typed. Before this change it showed the admin's email address and a PDF link.

Test plan

  • yarn test:php -- --filter 'test_encode_tags|test_html_structure_keeps_user_tags_encoded'
  • On WordPress 7.2-beta1, the Try it steps above
More info
  • Found in the 6.18.0-RC3 review (C1).
  • It doesn't depend on WordPress keeping the legacy parser, unlike an earlier version that set wp_kses_force_legacy_parser.
  • Why after : too: Gravity Forms' field merge tags ({Label:1}, {Label:1:value}, {:1}) and Gravity PDF's {Label:pdf:ID} allow any text after {, so a joiner there alone doesn't stop them.
  • Why U+FEFF: mPDF removes it before drawing, whatever the font, so it can't show as a box or a gap, and text copied out of the PDF doesn't include it. A zero-width space (U+200B) showed as a space in some fonts.
  • How it's applied:
    • Joiners go on the whole value, then the HTML tag processor takes them back out of attribute values. Text that only looks like a tag, such as I <3 {admin_email} >_<, is covered too.
    • In attributes, [ ] { } are URL-encoded as before. A colon gets %3A in a URL after the scheme, stays as a style declaration's property colon, and gets the joiner anywhere else, so a rich-text value like {x <b title=":3:">} can't print field 3's value.
    • It's added once only, so a value encoded twice still matches the trusted administrative defaults, which are mapped back from both attribute forms.
    • Values without [ ] { } don't change.
  • Trade-offs:
    • A URL in $form_data that contains brackets also gets a joiner after https:. It prints the same, but a custom template that uses it as a link would need to strip U+FEFF.
    • In a value that also contains [ ] { }, a link with a port, like https://example.org:8080/, gets %3A for the port's colon, which breaks the link.
  • Tests:
    • test_encode_tags covers 11 cases. Each is checked encoded twice, and against Gravity PDF's and Gravity Forms' merge tag patterns after kses.
    • test_default_tags_are_restored_in_attributes is new.
    • test_html_structure_keeps_user_tags_encoded runs on both kses parsers, and covers a {Text:1} field tag and I <3 {admin_email} >_<.
    • Four other tests' expected values change from &#91; to U+FEFF.
    • The entry-tags and signed-url Playwright specs look for U+FEFF instead of &#91;.
  • Integration suite, run natively on WordPress 7.2-beta1 (Laragon, PHP 8.3) rather than in wp-env, against this branch's base on the same setup:
    • No new failures.
    • 2 tests that failed on the base under 7.2 now pass.
    • The 14 new tests pass.
    • The other failures are WordPress 7.2's kses output changes, and they fail on the base too.
  • End to end on a WordPress 7.2-beta1 site, on both parsers: forged Text and rich-text entries print as typed, U+FEFF is invisible in seven fonts with the same line breaks, and copied text has no joiners.
  • The source change is the same as Keep submitted shortcodes and merge tags encoded on WordPress 7.2 (6.18.0) #1974 on hot-patch-6.18.0.

🤖 Generated with AI

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Coverage report for commit: 7d39622
File: ./tmp/jest-coverage/clover.xml

Cover ┌─────────────────────────┐ Freq.
   0% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  10% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  20% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  30% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  40% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  50% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  60% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  70% │ █░░░░░░░░░░░░░░░░░░░░░░ │  1.5%
  80% │ ██░░░░░░░░░░░░░░░░░░░░░ │  6.1%
  90% │ ███████░░░░░░░░░░░░░░░░ │ 21.2%
 100% │ ███████████████████████ │ 71.2%
      └─────────────────────────┘
 *Legend:* █ = Current Distribution 
Summary - Lines: 93.30% | Methods: 88.89% | Branches: 82.27%
FilesLinesMethodsBranches
src/assets/js/react/actions
   coreFonts.js100.00%100.00%100.00%
   fontManager.js100.00%100.00%100.00%
   templates.js100.00%100.00%100.00%
src/assets/js/react/components/Alert
   Alert.js100.00%100.00%100.00%
src/assets/js/react/components/CoreFonts
   CoreFontContainer.js100.00%100.00%91.43%
   CoreFontCounter.js100.00%100.00%100.00%
   CoreFontListResults.js100.00%100.00%85.71%
   CoreFontListSpacer.js100.00%100.00%100.00%
src/assets/js/react/components
   CustomHashRouter.js100.00%100.00%100.00%
   Empty.js100.00%100.00%100.00%
   ShowMessage.js80.65%80.00%68.75%
   Spinner.js100.00%100.00%100.00%
src/assets/js/react/components/FontManager
   AddFont.js100.00%100.00%100.00%
   AddUpdateFontFooter.js85.37%50.00%88.89%
   AdvancedButton.js100.00%100.00%100.00%
   FontList.js100.00%50.00%65.22%
   FontListAlertMessage.js100.00%100.00%100.00%
   FontListHeader.js100.00%100.00%100.00%
   FontListIcon.js100.00%100.00%100.00%
   FontListItems.js85.39%64.00%68.66%
   FontListSkeleton.js100.00%100.00%100.00%
   FontManager.js77.78%57.14%50.00%
   FontManagerBody.js94.20%96.43%90.29%
   FontManagerHeader.js100.00%100.00%100.00%
   FontVariant.js90.00%60.00%70.00%
   FontVariantLabel.js100.00%100.00%100.00%
   InitialAddUpdateState.js100.00%100.00%100.00%
   SearchBox.js90.00%66.67%69.23%
   TemplateTooltip.js100.00%75.00%100.00%
   UpdateFont.js75.00%50.00%75.00%
src/assets/js/react/components/Modal
   CloseDialog.js93.33%66.67%70.59%
src/assets/js/react/components/Template
   TemplateActivateButton.js100.00%100.00%100.00%
   TemplateButton.js85.71%66.67%100.00%
   TemplateContainer.js71.43%66.67%25.00%
   TemplateDeleteButton.js100.00%100.00%70.00%
   TemplateFooterActions.js100.00%100.00%100.00%
   TemplateHeaderNavigation.js82.35%85.71%70.00%
   TemplateHeaderTitle.js100.00%100.00%100.00%
   TemplateList.js100.00%100.00%100.00%
   TemplateListItem.js100.00%100.00%92.86%
   TemplateListItemComponents.js100.00%100.00%100.00%
   TemplateManagerClosed.js100.00%100.00%100.00%
   TemplateScreenshot.js100.00%100.00%100.00%
   TemplateScreenshots.js100.00%100.00%50.00%
   TemplateSearch.js93.75%88.89%50.00%
   TemplateSingle.js100.00%100.00%100.00%
   TemplateSingleComponents.js100.00%100.00%75.00%
   TemplateUploader.js100.00%100.00%94.44%
   TemplateUploaderContext.js100.00%100.00%100.00%
   TemplateUploaderTile.js100.00%100.00%100.00%
src/assets/js/react/reducers
   coreFontReducer.js95.65%100.00%88.00%
   fontManagerReducer.js87.21%75.00%75.00%
   index.js100.00%100.00%100.00%
   templateReducer.js100.00%100.00%100.00%
src/assets/js/react/sagas
   coreFonts.js91.67%100.00%75.00%
   fontManager.js86.96%90.00%83.33%
   index.js100.00%100.00%100.00%
   templates.js90.91%100.00%100.00%
src/assets/js/react/selectors
   getTemplates.js91.11%100.00%83.33%
src/assets/js/react/utilities/FontManager
   adjustFontListHeight.js100.00%100.00%100.00%
   associatedFontManagerSelectBox.js94.44%100.00%66.67%
   fontManagerReducer.js100.00%100.00%100.00%
   getTabLocation.js100.00%100.00%100.00%
   toggleUpdateFont.js100.00%100.00%100.00%
src/assets/js/react/utilities/Template
   hasPendingUpload.js100.00%100.00%100.00%
src/assets/js/react/utilities
   withRouterHooks.js100.00%100.00%100.00%

🤖 Jest coverage report

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Coverage report for commit: 7d39622
File: tmp/coverage/report-xml/merged.xml

Cover ┌─────────────────────────┐ Freq.
   0% │ ██████░░░░░░░░░░░░░░░░░ │  8.8%
  10% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  20% │ █░░░░░░░░░░░░░░░░░░░░░░ │  0.5%
  30% │ █░░░░░░░░░░░░░░░░░░░░░░ │  1.4%
  40% │ █░░░░░░░░░░░░░░░░░░░░░░ │  0.5%
  50% │ ██████░░░░░░░░░░░░░░░░░ │  8.8%
  60% │ █░░░░░░░░░░░░░░░░░░░░░░ │  1.4%
  70% │ ██░░░░░░░░░░░░░░░░░░░░░ │  2.3%
  80% │ ████████████░░░░░░░░░░░ │ 17.5%
  90% │ ███████████████████████ │ 34.6%
 100% │ █████████████████░░░░░░ │ 24.4%
      └─────────────────────────┘
 *Legend:* █ = Current Distribution 
Summary - Lines: 88.36% | Methods: 95.14%
FilesLinesMethodsBranches
/var/www/html/wp-content/plugins/gravity-pdf
   api.php98.06%100.00%100.00%
   gravity-pdf-updater.php53.97%100.00%100.00%
   pdf.php59.82%81.25%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Controller
   Controller_Actions.php99.07%100.00%100.00%
   Controller_Activation.php95.65%100.00%100.00%
   Controller_Custom_Fonts.php94.04%100.00%100.00%
   Controller_Debug.php100.00%100.00%100.00%
   Controller_Export_Entries.php96.77%100.00%100.00%
   Controller_Form_Settings.php87.37%90.00%100.00%
   Controller_Install.php100.00%100.00%100.00%
   Controller_Mergetags.php100.00%100.00%100.00%
   Controller_PDF.php81.46%100.00%100.00%
   Controller_Pdf_Cache.php100.00%100.00%100.00%
   Controller_Pdf_Queue.php85.27%87.50%100.00%
   Controller_Save_Core_Fonts.php88.89%100.00%100.00%
   Controller_Settings.php86.90%100.00%100.00%
   Controller_Shortcodes.php100.00%100.00%100.00%
   Controller_System_Report.php100.00%100.00%100.00%
   Controller_Templates.php100.00%100.00%100.00%
   Controller_Uninstaller.php83.33%77.78%100.00%
   Controller_Upgrade_Routines.php93.75%100.00%100.00%
   Controller_Webhooks.php100.00%100.00%100.00%
   Controller_Zapier.php100.00%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Exceptions
   GravityPdfDatabaseUpdateException.php50.00%100.00%100.00%
   GravityPdfDomainException.php50.00%100.00%100.00%
   GravityPdfException.php50.00%100.00%100.00%
   GravityPdfFontNotFoundException.php50.00%100.00%100.00%
   GravityPdfIdException.php50.00%100.00%100.00%
   GravityPdfModelNotUpdatedException.php50.00%100.00%100.00%
   GravityPdfRuntimeException.php50.00%100.00%100.00%
   GravityPdfShortcodeEntryIdException.php50.00%100.00%100.00%
   GravityPdfShortcodePdfConditionalLogicFailedException.php50.00%100.00%100.00%
   GravityPdfShortcodePdfConfigNotFoundException.php50.00%100.00%100.00%
   GravityPdfShortcodePdfInactiveException.php50.00%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Helper/Fields
   Field_Address.php92.86%100.00%100.00%
   Field_Chainedselect.php66.67%75.00%100.00%
   Field_Checkbox.php94.44%100.00%100.00%
   Field_Consent.php94.74%100.00%100.00%
   Field_Coupon.php--100.00%
   Field_Creditcard.php83.33%100.00%100.00%
   Field_Date.php83.33%100.00%100.00%
   Field_Default.php83.33%100.00%100.00%
   Field_Discount.php44.00%75.00%100.00%
   Field_Email.php83.33%100.00%100.00%
   Field_Fg_Ls_Consent.php92.86%100.00%100.00%
   Field_Fg_Ls_Signature.php73.08%66.67%100.00%
   Field_Fileupload.php95.83%100.00%100.00%
   Field_Form.php90.91%100.00%100.00%
   Field_Hidden.php81.82%100.00%100.00%
   Field_Html.php89.47%100.00%100.00%
   Field_Image_Choice.php86.67%100.00%100.00%
   Field_Likert.php97.22%100.00%100.00%
   Field_List.php92.41%100.00%100.00%
   Field_Multi_Choice.php50.00%100.00%100.00%
   Field_Multiselect.php92.59%100.00%100.00%
   Field_Name.php84.62%100.00%100.00%
   Field_Number.php83.33%100.00%100.00%
   Field_Option.php96.15%100.00%100.00%
   Field_Page.php83.33%100.00%100.00%
   Field_Phone.php94.55%100.00%100.00%
   Field_Poll.php93.75%100.00%100.00%
   Field_Post_Category.php85.00%100.00%100.00%
   Field_Post_Content.php82.35%100.00%100.00%
   Field_Post_Custom_Field.php50.00%100.00%100.00%
   Field_Post_Excerpt.php81.82%100.00%100.00%
   Field_Post_Image.php94.00%100.00%100.00%
   Field_Post_Tags.php90.91%100.00%100.00%
   Field_Post_Title.php81.82%100.00%100.00%
   Field_Product.php92.31%100.00%100.00%
   Field_Products.php84.32%100.00%100.00%
   Field_Quantity.php84.62%100.00%100.00%
   Field_Quiz.php89.74%100.00%100.00%
   Field_Radio.php94.74%100.00%100.00%
   Field_Rank.php95.24%100.00%100.00%
   Field_Rating.php95.24%100.00%100.00%
   Field_Repeater.php97.39%100.00%100.00%
   Field_Section.php90.74%100.00%100.00%
   Field_Select.php94.12%100.00%100.00%
   Field_Shipping.php92.86%100.00%100.00%
   Field_Signature.php71.11%100.00%100.00%
   Field_Slim.php82.35%100.00%100.00%
   Field_Slim_Post.php91.30%100.00%100.00%
   Field_Subtotal.php65.38%75.00%100.00%
   Field_Survey.php95.00%100.00%100.00%
   Field_Tax.php32.00%50.00%100.00%
   Field_Text.php81.82%100.00%100.00%
   Field_Textarea.php90.63%100.00%100.00%
   Field_Time.php81.82%100.00%100.00%
   Field_Tos.php92.68%100.00%100.00%
   Field_Total.php88.00%100.00%100.00%
   Field_Website.php85.71%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Helper/Fonts
   FlushCache.php92.31%100.00%100.00%
   LocalFile.php88.89%100.00%100.00%
   LocalFilesystem.php66.67%100.00%100.00%
   SupportsOtl.php87.50%100.00%100.00%
   TtfFontValidation.php84.62%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Helper
   Helper_Abstract_Addon.php94.09%100.00%100.00%
   Helper_Abstract_Config_Settings.php75.00%100.00%100.00%
   Helper_Abstract_Controller.php-100.00%100.00%
   Helper_Abstract_Field_Products.php89.47%66.67%100.00%
   Helper_Abstract_Fields.php96.08%95.83%100.00%
   Helper_Abstract_Fields_Input_Type.php84.00%100.00%100.00%
   Helper_Abstract_Form.php--100.00%
   Helper_Abstract_Model.php100.00%100.00%100.00%
   Helper_Abstract_Options.php77.13%79.66%100.00%
   Helper_Abstract_Pdf_Shortcode.php94.08%95.65%100.00%
   Helper_Abstract_View.php96.77%100.00%100.00%
   Helper_Async_Notifications.php64.29%100.00%100.00%
   Helper_Data.php98.29%100.00%100.00%
   Helper_Field_Container.php92.31%100.00%100.00%
   Helper_Field_Container_Gf25.php92.00%100.00%100.00%
   Helper_Field_Container_Void.php16.67%-100.00%
   Helper_Form.php80.65%64.29%100.00%
   Helper_Interface_Actions.php-100.00%100.00%
   Helper_Interface_Config.php50.00%100.00%100.00%
   Helper_Interface_Config_Settings.php50.00%100.00%100.00%
   Helper_Interface_Deprecated_Features.php-100.00%100.00%
   Helper_Interface_Extension_Settings.php-100.00%100.00%
   Helper_Interface_Extension_Uninstaller.php-100.00%100.00%
   Helper_Interface_Field_Pdf_Config.php50.00%100.00%100.00%
   Helper_Interface_Filters.php-100.00%100.00%
   Helper_Interface_Setup_TearDown.php50.00%100.00%100.00%
   Helper_Interface_Url_Signer.php-100.00%100.00%
   Helper_Logger.php-100.00%100.00%
   Helper_Misc.php89.17%100.00%100.00%
   Helper_Mpdf.php50.00%100.00%100.00%
   Helper_Notices.php92.98%100.00%100.00%
   Helper_Options_Fields.php98.13%100.00%100.00%
   Helper_PDF.php88.89%97.50%100.00%
   Helper_PDF_List_Table.php84.50%92.31%100.00%
   Helper_Pdf_Queue.php84.31%100.00%100.00%
   Helper_QueryPath.php80.00%100.00%100.00%
   Helper_Render_Health.php85.71%100.00%100.00%
   Helper_Sha256_Url_Signer.php90.91%100.00%100.00%
   Helper_Singleton.php100.00%100.00%100.00%
   Helper_Templates.php97.27%100.00%100.00%
   Helper_Trait_Logger.php25.00%-100.00%
   Helper_Trait_Removed_Methods.php100.00%100.00%100.00%
   Helper_Url_Signer.php84.82%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Helper/Licensing
   EDD_SL_Plugin_Updater.php94.83%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Helper/Log
   Context_Logger.php80.00%100.00%100.00%
   Logger.php86.89%100.00%100.00%
Table truncated to fit comment

🤖 PHPUnit coverage report

@gravitypdf-bot
gravitypdf-bot force-pushed the fix/kses-keep-encoded-tags-wp72 branch 2 times, most recently from 55efccf to 8fb2921 Compare October 27, 2026 05:47
WordPress 7.2 rebuilds wp_kses() on the HTML API, which decodes the `&#91;`/`&#123;` references encode_tags() used, so
Kses::parse() turned submitted `{admin_email}` and `[gravitypdf]` back into live tags. encode_tags() now puts a
zero-width no-break space (U+FEFF) after each `[`, `{` and `:` instead. Kses keeps it, and mPDF strips it before drawing,
so it's invisible in every font (U+2060 showed as a box or a gap in most).

Joiners go on the whole value and the HTML tag processor takes them back out of attribute values, which are
URL-encoded as before. An attribute's colons are neutralised too, so a merge tag opened in the text can't end inside
one: a URL keeps its scheme and gets %3A, a style declaration keeps a lone property colon, and anything else gets the
joiner. Trusted default tags are mapped back from both attribute forms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gravitypdf-bot
gravitypdf-bot force-pushed the fix/kses-keep-encoded-tags-wp72 branch from 8fb2921 to 7d39622 Compare October 27, 2026 05:54
zamoragregory pushed a commit that referenced this pull request Oct 27, 2026
…olon handling

Same encode_tags() change as #1972. A zero-width no-break space (U+FEFF) replaces the word joiner, as mPDF strips it
before drawing. Joiners now go on the whole value and come back out of attribute values, so text that only looks like
a tag is covered, and an attribute's colons are neutralised so a merge tag opened in the text can't end inside one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jakejackson1
jakejackson1 merged commit 9756bef into development Oct 27, 2026
19 checks passed
@jakejackson1
jakejackson1 deleted the fix/kses-keep-encoded-tags-wp72 branch October 27, 2026 21:59
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.

3 participants