Skip to content

Sync master from upstream - #120

Merged
vanshk141999 merged 110 commits into
masterfrom
sync/master-20260806
Aug 6, 2026
Merged

vanshk141999 merged 110 commits into
masterfrom
sync/master-20260806

Conversation

@vanshk141999

Copy link
Copy Markdown
Collaborator

Summary

Syncs brainstormforce/sureforms@master (private) into this public mirror.

  • Upstream range: a57d81840..23f96be46 — 563 commits
  • Internal-only paths stripped (see below); the diff below is computed against public/master via a merge-cap, so it shows only real upstream changes — no internal-file deletions.

Highlights

  • Security hardening batch: stored XSS fix in the entries admin view (CVE-2026-18406), CSV formula-injection neutralization, unique-field/field-key validation hardening, {all_data} email escaping, generate-form-markup endpoint capability gating, and a verified-payment requirement for payment-enabled forms
  • 2.12.3 changelog + version bump
  • i18n/translation updates
  • @since tag cleanup and docblock hygiene sweep

Strip applied

Removed (not part of the public plugin): .claude/, .scripts/git-hooks/, internal-docs/, docs/, ARCHITECTURE.md, COMPREHENSIVE_ANALYSIS.md, PRODUCT_ANALYSIS.md, TECHNICAL_OVERVIEW.md, all nested CLAUDE.md files, tests/play/specs/TODO.md, and 5 internal-only CI workflow files (push-to-deploy.yml, push-asset-readme-update.yml, release-tag-draft.yml, update-translations.yml, release-pr-template.yml), plus 3 bin/ release scripts.

vanshk141999 and others added 30 commits July 15, 2026 15:35
…ycle

Addresses the WPML compatibility team's review of the 2.11.0 integration:

1. Backfill existing forms — new String_Backfill runs String_Collector::collect()
   once per plugin version (Action Scheduler, one job per form) when a provider
   becomes active, so forms created before the integration register their String
   Packages without a manual re-save.
2. Form title is now registered + translated. Previously post_title rendered as
   the form heading / instant-form banner but was never a translatable string.
3. Associate each String Package with its form post (post_id in the descriptor)
   so WPML 'Translate Everything Automatically' queues it with the post.
4. Delete the String Package when a form is permanently deleted — new
   Provider::delete_package() + before_delete_post handler, so orphaned packages
   and translations don't linger.

Adds unit coverage for the backfill guard/idempotency, title registration +
translation, package deletion, and the post_id association.
- Add the per-function tests the coverage check requires: delete_package()
  for the Provider interface, Null and WPML providers, and title_name() for
  String_Translator.
- WPML_Provider::delete_package() uses ?? instead of isset()-ternary (PHP
  Insights style).
…, test hardening

- Backfill done-marker keyed to a dedicated SCHEMA_VERSION constant instead of
  SRFM_VER, so ordinary releases no longer re-enqueue a job per form on every
  update; marker stored with autoload=false (admin-only).
- Enqueue with the Action Scheduler unique flag so concurrent/interrupted
  passes dedupe by (hook, args, group) instead of double-queuing.
- Backfill query includes the 'future' post status; drop redundant int casts.
- on_form_delete() builds a lightweight { name, kind } descriptor instead of
  the full form_package() (no get_the_title/get_edit_post_link on delete).
- form_package() docblock lists post_id; note cms_id as the WPML fallback
  pending live verification (tracked in #2942).
- Tests: assert one Action Scheduler job per form (via as_has_scheduled_action,
  shim fallback); backfill_one() actually registers the form's strings;
  permanent-delete-vs-trash through the real before_delete_post hook;
  provider-inactive delete bail; empty-title skip; WPML delete_package
  missing-kind branch. Backfill marker tests assert SCHEMA_VERSION.
WPML's package-level Translate Everything Automatically (TEA) gate reads
the wpml_active_string_package_kinds filter to decide which string-package
kinds to auto-translate. We registered one package per form (kind
'SureForms Form') and associated it via post_id, but never declared the
kind on that filter, so TEA skipped SureForms form packages.

Declare it from String_Collector, keyed by sanitize_title( PACKAGE_KIND )
= 'sureforms-form' — the same slug WPML derives for our packages, so the
declaration matches already-registered packages. post_id is retained.

Verified: filter output shape + registration (live wp eval), PHPCS,
PHPStan L9 clean, plus two unit tests.
The admin Entries search ("Search entries...") only built two WHERE
conditions: an exact ID match (numeric terms only) and form_id IN (...)
resolved from form titles. form_data was never searched, and a non-numeric
term matching no form title forced an always-empty ID = 0 condition - so
text a respondent submitted could never be found. Both the React admin REST
endpoint (/sureforms/v1/entries/list) and the Abilities/MCP list-entries
funnel into the same Entries::get_entries(), so both missed.

Fix: add a form_data LIKE condition to the search OR-group for every
non-empty term (numeric too - phone numbers, zip codes), and drop the dead
forced-empty fallback. form_data stores plain JSON (encode_json uses
JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), so LIKE matches submitted
values textually including emails, URLs and non-ASCII input. The Base query
compiler already supports LIKE and wraps the value in "%...%" itself;
esc_like() neutralizes user-typed wildcard characters. Also corrects the
list-entries ability description ("by entry ID" -> ID, form title, or
submitted form data).

Verified end-to-end on a live install: searching a submitted name returns
its entry; a gibberish term returns zero; numeric ID and form-title search
behavior unchanged. Adds unit tests for the new condition, the removed
forced-empty fallback, and wildcard escaping.
The form_data LIKE cannot use an index, so guard it for low-resource hosts:

- Backend (authoritative, covers REST + MCP ability): skip the form_data LIKE
  for text terms under 3 characters (mb_strlen, so multibyte input counts
  correctly). Numeric terms are exempt - they resolve to an exact, indexed
  entry-ID lookup. A short text term matching no form title forces an empty
  result again (an OR-group with no conditions would be dropped by the query
  compiler and return every entry).
- Admin UI: Enter-to-search ignores non-numeric terms under 3 characters;
  empty submit still clears the search.
- Ability description documents the minimum.

Tests: short text term produces no form_data condition and forces an empty
result; short numeric term still matches the entry ID without scanning.
Verified live: 3+ char text matches, 2-char text returns 0, numeric ID exact
match works at any length.
With a search term active, the pagination COUNT includes the unindexed
form_data LIKE - a full scan of candidate rows re-executed on every
pagination click for an answer that cannot change between clicks. Cache it
for 30 seconds keyed on the exact WHERE conditions, halving the cost of
every search request and making pagination clicks scan-once. Unsearched
listings stay uncached so totals reflect trash/delete mutations immediately.

Verified live: page 2 of a search serves the total from the transient;
unsearched listings compute fresh.
normalizeCSSVariablesForDarkBackground() appended the dark-dropdown CSS
variable to the form container's inline <style> via an unguarded
querySelector('style').innerHTML. Forms rendered with default styling
disabled (#2925) omit that inline <style> block entirely, and with the
derived variables gone the --srfm-color-input-text luminance check
degenerates so the inject branch runs every time - an
"Uncaught TypeError: Cannot read properties of null (reading 'innerHTML')"
on every de-skinned form, which also aborts the forEach over form
containers and breaks initialization of every later form on the page.

Fix:
- Skip the variable adjustment for containers carrying the
  srfm-styling-none marker class - the default stylesheet that consumes
  --srfm-expandable-menu-background is not enqueued for them, so the
  injection was pointless as well as crashy. The srfm-has-dark-bg class
  hook still applies (works without the default stylesheet; documented
  for custom styling).
- Create the <style> tag when missing instead of dereferencing null, so
  no other markup shape (e.g. custom-CSS-only variant) can resurface the
  TypeError.
Front-loads Contact Form Builder in the title while retaining AI Forms,
Payment Form, Survey and Quiz. Separates SureForms Free vs Business form
types, retains expanded feature explanations, all 16 blocks, FAQs,
compatibility, third-party disclosures, changelog and Upgrade Notice.
Only readme.txt changed.

Recreates PR #2964 on a clean branch off the latest master.
The repo lagged at 7.0.1 while the live wp.org page already shows 7.0.2
(bumped directly on wp.org, never mirrored to git). Aligning here so this
readme deploy does not overwrite/regress the live compatibility value.
Mirrors the AI-first readme.txt rewrite and Tested up to 7.0.2 into the
GitHub-facing README.md via the canonical wp_readme_to_markdown task.
…efresh

SureForms - Contact Form Builder, AI Forms, Payment Form, Survey & Quiz
…write)

The `language` column added in 2.11.0 shipped a combined migration:
  ALTER TABLE wp_srfm_entries ADD COLUMN language VARCHAR(20),
                              ADD INDEX idx_form_id_language (form_id, language)
On some database engines the ADD INDEX cannot see the column being added in
the same statement, so the ALTER fails ("column does not exist: language")
and re-runs on every init/plugins_loaded, flooding the error log. Where the
column ends up absent, the submission INSERT (which supplied a `language`
value) fails too and the form returns "Unable to submit form. Please try
again."

Remove the column from the submission path entirely:
- entries table: drop `language` from the schema, the CREATE definition, the
  `idx_form_id_language` index, the new-columns migration, and the orderby
  allowlist. Existing installs keep the (now-unused) column and its data; the
  failing ALTER simply stops running.
- form-submit: stop writing `language` in the entry data. The local language
  detection is kept only where it renders the confirmation/email in the
  visitor's language at submit time — that never needed the stored column.
- list-entries ability: drop `language` from the orderby enum, the output
  schema, and the per-entry output mapping.
- tests: assert the column/index are absent from the migration and the ability
  no longer exposes `language`.

Note: the separate `wp_srfm_draft_submissions` migration errors in the same
log (session_id / drop-primary-key) originate in SureForms Pro and are out of
scope here.
The Payment History stylesheet (srfm-payment-history) was enqueued on every
front-end page regardless of whether the block/shortcode was present. Gate the
CSS (and JS) on actual presence so it no longer loads site-wide, while keeping
the render-time enqueue so page-builder / FSE / widget placements still load it.

Includes the review follow-ups from #2971:
- Withhold the script + localized nonce from logged-out visitors (CSS still
  loads so the login message stays styled); the cancel handler re-checks auth
  server-side and has no wp_ajax_nopriv_ endpoint.
- Cover the has_block() gate with a hook-time test using real block markup.
- Make the test helper exception-safe (try/finally around the filter stack).
- Add a hook-time no-WP_Post early-return test + logged-in/out JS-gate coverage.

Recreated on a clean branch off the latest dev (the previous branch could not
be synced with dev because dev carries a pre-ruleset unsigned commit that the
"require verified signatures" rule blocks from propagating).
Adi's review: the backend dropped the entries `language` column but the React
admin still rendered a sortable Language column that would always show "-" and
send a now-rejected orderby=language on header click.

- EntriesTable.js: remove the Language column definition.
- useEntriesSort.js: drop `language` from columnToApiFieldMap so its header
  can no longer emit orderby=language.
- entryHelpers.js: stop mapping entry.language in transformEntry.
- form-submit.php: refresh the now-stale comments — $entry_language only drives
  the submit-time switch_language() for confirmation/email rendering; it is no
  longer persisted to any column.

Pro read-paths checked (finding #2): no SureForms Pro code reads or orders by
the entries `language` column, and no srfm_before_entry_data callback injects
it, so fresh installs (where the column is never created) are safe. The
partial-entries RootLayout.js "language" reference is a docblock about visual
styling, not the column.
…cks widgets

Adi's review: gating the payment-history stylesheet on has_block()/has_shortcode()
regressed the Elementor and Bricks payment-history widgets, whose content lives in
postmeta and so is never detected on wp_enqueue_scripts. The CSS then loaded only at
render() time (footer), so the dashboard rendered unstyled then restyled (FOUC).

1. HIGH — register the handle + declare it as a widget style dependency so both
   builders enqueue it in the <head>:
   - payment-history-shortcode.php: new register_assets() (wp_enqueue_scripts,
     priority 1) registers the srfm-payment-history style + script handles
     unconditionally; enqueue_assets() now enqueues by handle. The conditional
     block/shortcode gate is unchanged, so the CSS still stays off unrelated pages.
   - elementor/payment-history-widget.php: get_style_depends() → ['srfm-payment-history'].
   - bricks/elements/payment-history-widget.php: enqueue_scripts() enqueues the handle.
   The render()-time enqueue remains only as the last-resort FSE template-part /
   block-widget fallback (footer CSS, acceptable there).

3. LOW — enqueue_assets() now takes an explicit $from_render flag instead of
   sniffing doing_action('wp_enqueue_scripts') (which would also match a nested
   do_shortcode() run inside a wp_enqueue_scripts callback). render() passes true.

Tests: added test_register_assets, Elementor test_get_style_depends, Bricks
test_enqueue_scripts; updated the render-fallback cases to the explicit-flag call
and simplified the on-hook helper (the gate is param-driven now).
…ice)

The Turnstile API script was enqueued with an invalid $args array:

    [ false, 'defer' => true ]

which WordPress reads as unrecognized keys `0` and `defer`. On WordPress
7.0 this triggers a front-end "wp_enqueue_script was called incorrectly"
notice (Unrecognized keys in the $args parameter: 0, defer).

Replace it with the correct, documented form `[ 'strategy' => 'defer' ]`
(matching the existing hCaptcha enqueues). Behaviour is unchanged — the
script still loads deferred in the head — the invalid keys are removed.

Fixed in both enqueue sites: get_cf_turnstile_script() in
generate-form-markup.php and the inline-button captcha path in
inlinebutton-markup.php.
…older)

The number field's `.srfm-input-content` wrapper set its top margin from
`var(--srfm-input-field-margin-top)` while every other field's input uses the
shared `srfm-input-margin-styles` expression
`var(--srfm-input-label-gap, var(--srfm-input-field-margin-top))`. The number
mixin simply never picked up the `--srfm-input-label-gap` fallback that was
added later, so when a form's label gap differs from the size preset's
field-margin-top, a number field placed next to another field (e.g. date) at
50% width sat a few px off — visible once labels become placeholders and the
top margin becomes the field's leading space.

Align the number wrapper to the same expression so it respects
`--srfm-input-label-gap` like the rest. No change when the variable is unset
(the fallback resolves to the same value as before).

Pairs with the sureforms-pro date-field fix (date field now honors
"Use label as placeholder"), which removes the larger label-height offset.
On the frontend, when a logged-in user who can view entries is on a page that
contains a SureForms form, add an "Entries" node to the WP admin bar that
deep-links to the Entries admin page pre-filtered to that form. With multiple
forms on the page, a submenu lists one item per form and the parent links to the
unfiltered Entries page.

- Generate_Form_Markup::get_form_markup() (the single render choke point for both
  the block and the shortcode) records each rendered form ID in a static registry.
  The admin bar renders on wp_footer — after the_content — so the list is fully
  populated by the time the node is built.
- add_entries_admin_bar_node() (admin_bar_menu, priority 100, mirroring the
  existing "Edit Form" node) builds the node(s), gated to is_admin_bar_showing()
  and Helper::current_user_can() (manage_options — the same capability the Entries
  admin page and REST endpoints require). Falls back to the singular form ID on
  Instant Form / single-form pages.
- Deep link: admin.php?page=sureforms_entries#/?form=<id> — the format the React
  entries app's `form` search param already understands.

No new class or loader change: the behavior lives on the already-registered
Generate_Form_Markup singleton. Frontend-only (is_admin() guard).

Closes #2988
…pages

Show the admin-bar "Entries" deep-link only on a form's own Instant Form
page (is_singular( SRFM_FORMS_POST_TYPE ) with enable_instant_form set — the
same setting srfm_instant_form_redirect() gates the page on), not on every
page that happens to render a form via the srfm/form block or [sureforms]
shortcode.

Removes the now-unused per-request rendered-form registry and the multi-form
submenu; the node is always a single deep-link to the current form's filtered
entries. Test updated to simulate the singular Instant Form page and assert
the node is absent when Instant Form is disabled.
…-data

Fix: entries search never matched submitted form data
…styling-none

Fix: frontend TypeError on forms with default styles disabled
…uage-column

Remove the entries language column (fixes failing migration + submission failures)
…e-wp7-notice-v2

Fix: correct Cloudflare Turnstile wp_enqueue_script $args (WP 7.0 notice)
vanshk141999 and others added 29 commits August 4, 2026 13:31
…ies-stored-xss

fix(security): stored XSS in entries admin view (CVE-2026-18406)
…epeater rows (#2993)

Addresses the blocking review findings on #3005.

H1 — repeater child keys bypassed the guard entirely. Pro submits rows as
`repeaterKey[index][childKey]`, which PHP collapses into one top-level key, so
the top-level-only loop never saw the child keys; `process_repeater_field()`
copies them verbatim and they are label-decoded downstream. An attacker could
copy a real repeater block_id out of page source and nest forged child keys
inside it, re-obtaining exactly what #2993 describes. Rows are now walked; the
allowlist already contained repeater children via the innerBlocks recursion.

H2 — the guard rejected, and the error masked every other validation error.
The allowlist is derived from post_content at submit time while the visitor's
HTML was rendered earlier, so full-page caching or an editor-side block_id
reassignment made a valid form unsubmittable. Worse, `field_errors` has no
consumer and `reset()` in the caller promotes the first error to *the* message,
hiding real per-field errors. Unknown keys are now dropped via a new
`strip_unknown_field_keys()` called before validation, which meets the same
security goal — the invented key never reaches storage, email or export.

M2 — `@since 2.12.3` was a guessed version; now the `x.x.x` placeholder.

M3 — the collector stored `sanitize_text_field( $block_id )` while the lookup
used the raw id from `Helper::get_block_id_from_key()`. Any id the sanitiser
altered was filed under a different string than the one looked up, making a
legitimate field permanently unsubmittable. Both sides now use the raw id;
these are comparison-only map keys and nothing is echoed from there.

Also, from the non-blocking notes: the filter no longer advertises itself as a
way to switch the check off, coerces a list return into a map and ignores a
non-array return in favour of the walked set; only the pre-filter walk is
memoised so a bad filter return cannot poison the request; and the collector
takes a depth cap.

M5 is resolved structurally — the strip now runs before
`srfm_field_validation_data` is applied, so that filter's synthetic keys are no
longer racing a memoised allowlist.

Tests: repeater nesting (verified failing without the row walk), fail-open on an
underivable block set, filter list/garbage coercion, and the existing
non-alphanumeric block-id regression repointed at the new API.
…ed-payment

fix(security): require a verified payment on payment-enabled forms (#2998)
…ate-form-markup-endpoint

fix(security): gate generate-form-markup on capability and form type (#2995)
…ort-header

fix(security): neutralise CSV formula injection in entries-export header (#2996)
…a-email-label

fix(security): escape {all_data} email label as text (#2991)
…-keys-against-form

fix(security): reject undefined field keys on submission (#2993)
…e-field-validation

fix(security): restrict nopriv unique check to the form's unique fields (#2997)
…994)

Addresses the comment-level review findings on #3006.

M1 — `@since 0.0.1` was factually wrong on `encode()`/`decode()`. Both are new
symbols; calling either against any released SureForms is a fatal, and the
two-line `@since` form documents a change to an existing element, not the
introduction of one. `composer gen-stubs` copies these docblocks verbatim into
the stubs Pro consumes, so a Pro dev reading `@since 0.0.1` would reasonably
skip a version guard. Now a single `@since x.x.x`, with the rename note moved
into the prose so the "behaviour unchanged since 0.0.1" fact is not lost. The
alias `@since 0.0.1` tags are correct and are left alone.

M2 — finished the encrypt/decrypt vocabulary sweep in files this PR already
edited, where the stale wording undercuts the whole point of the rename:

- `inc/fields/email-markup.php` — "Encrypted label" → "Encoded label", eight
  lines from an edit this PR already made.
- `inc/abilities/entries/entry-parser.php` — "form data decryption" →
  "form data decoding"; the inline comment six lines below was already updated.
- `inc/abilities/CLAUDE.md` — "decrypted form_data" → "decoded form_data"; the
  sibling line in `ABILITIES.md` was already updated.
- `inc/rest-api.php` — the `srfm_entry_value` filter docblock told integrators
  they "may want to decrypt" the value. Reworded, and it now states plainly that
  nothing reaching that filter is encrypted by SureForms.

Low — moved the escape-at-the-sink warning onto `get_field_label_from_key()`.
That is what every unescaped sink actually calls, and it is strictly more
dangerous than `decode()` alone: it additionally applies `html_entity_decode()`,
which expands entities and so undoes any `htmlspecialchars()`-on-store defence.

Left deliberately untouched: `AI_Auth` and the `openssl_*` paths (real AES), and
the `test_encrypt*` method names, which test the alias and are accurately named.

Comment-only change — no executable lines touched. PHPStan L9, PHPCS and PHP
Insights clean; `Test_Helper` shows the same 4 pre-existing local failures
before and after.
…-decrypt-encoding

fix(security): rename misleading encrypt/decrypt to encode/decode (#2994)
Auto-generated by /i18n command on PR #3002
…ngelog

Addresses the four code/metadata carry-overs @adi3890 raised on #3002.

Docblocks (inc/ ships in the wp.org ZIP — .distignore excludes tests and src
but not inc/, and the repo is private, so the ZIP is the first public
exposure). Five docblocks narrated the pre-patch behaviour in enough detail
to reconstruct the issue. Rewritten to state the invariant and why it must
hold, dropping the "here is what used to happen" story, following the house
style at inc/email/email-template.php:322:

- inc/payments/front-end.php
- inc/field-validation.php
- inc/form-submit.php
- inc/database/tables/entries.php
- inc/generate-form-markup.php

The behaviour and the reasons a future change must not relax these checks are
preserved in full — only the exploit narrative is gone.

Gruntfile.js: the @SInCE placeholder sweep globbed PHP only, so 20 files
under src/ kept "x.x.x" through every release, and one built bundle
(assets/js/payment-history.js) SHIPS with the placeholder — slightly wider
than reported. Added src/**/*.js and assets/js/**/*.js.

Also escaped the dots in the match pattern: /x.x.x/ig matched any character
between the x's, which would rewrite incidental strings like "x1x2x". Latent
before, but a real risk now that minified JS is in scope.

readme.txt:
- CVE-2026-18406 now has its own Fix: line with props to daroo and Wordfence,
  instead of being folded into the generic "Hardened security" improvement.
- == Upgrade Notice == was an empty dangling section at EOF on a release
  carrying a 7.2 CVE; filled in for 2.12.3.

Verified: PHPCS clean, PHPStan level 9 "No errors", Gruntfile parses.
…-overs

Addresses @adi3890's review on #3018.

MEDIUM — the sweep was fixed but never run. Ran it: 33 placeholders removed
across 24 files. Verified the diff contains nothing but the substitution —
every changed line is `@since x.x.x` -> `@since 2.12.3` and no other line
differs. Gruntfile.js is deliberately skipped: it holds the search pattern
itself.

LOW — sweep scope. Accepted the point that `src` is the source of truth, but
narrowing to `src/**/*.js` alone would have left two placeholders in the
wordpress.org ZIP:

  inc/page-builders/elementor/assets/elementor-preview-styling.js:8
  assets/js/payment-history.js:8

`inc/` is not in .distignore at all, and .distignore excludes only
`assets/js/unminified`, so `assets/js/*.js` ships. Added `inc/**/*.js` and
kept `assets/js/**/*.js`, with a comment recording why both are in scope so
the next person does not narrow it back. Neither file was reachable by the
previous PHP-only glob either, so both have shipped with a placeholder.

Also reworded the new comment so it does not contain the literal placeholder:
this task rewrites its own file, so the comment would have been substituted on
the next bump.

LOW — inc/field-validation.php:208 named the wrong consumer and the wrong
verb. Confirmed get_known_field_block_ids() has exactly one caller,
strip_unknown_field_keys() at :300, and that it unset()s the key at :315
rather than rejecting the submission. Docblock corrected and pointed at the
note explaining why dropping beats rejecting.

LOW — added the changelog line for the Helper::encrypt()/decrypt()
deprecation. Confirmed the @deprecated tags exist (inc/helper.php:565, :604)
and that sureforms-pro still calls the old names in 9 places, so add-on
authors need the notice.

Not in this PR: #3002's own body is still missing 4 changelog lines. That is a
description edit, not code.

Verified: 33/33 placeholders gone (only Gruntfile.js's own pattern remains),
all swept JS parses, eslint clean on the swept src/ files, PHPCS clean on
inc/field-validation.php, PHPStan level 9 "No errors".
The @SInCE sweep in the previous commit stamped @SInCE 2.12.3 on all 33
JS tags, but only the 8 in sanitizeEntryValue.js are new this cycle. The
other 25 document code released earlier (each already carried x.x.x in
the v2.12.2 tag), so 2.12.3 is wrong and @SInCE is the field add-on
authors read for compatibility.

Backfilled each tag with the version its code actually shipped in, dated
by the earliest release tag containing the file (per-symbol where the
file predates the tagged symbol):

- Helpers.js: lockBlockSlugByBlockId 2.7.1, lockBlockSlugBySlug 2.7.0
- textarea.js Quill toolbar localization: 2.8.0 (file is v0.0.2)
- payment-history block + assets: 2.8.0
- settings/migration/*: 2.11.0
- QuizEmptyState/upgrade-modal/styling-utils/preview-styling/elementor: 2.7.0
- SurveyEmptyState: 2.8.0, PartialEntriesEmptyState: 2.9.0

sanitizeEntryValue.js left at 2.12.3 (correct). No x.x.x reintroduced.
fix(2.12.3): release hygiene — docblock disclosure, @SInCE sweep, CVE changelog
@vanshk141999
vanshk141999 merged commit dc4caf4 into master Aug 6, 2026
8 checks passed
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.

1 participant