Skip to content

Sync master from upstream#108

Merged
vanshk141999 merged 42 commits into
masterfrom
sync/master-20260619-v2
Jun 19, 2026
Merged

Sync master from upstream#108
vanshk141999 merged 42 commits into
masterfrom
sync/master-20260619-v2

Conversation

@vanshk141999

Copy link
Copy Markdown
Collaborator

Syncs brainstormforce/sureforms@master into the public mirror.

Upstream range: 4ad4d6541a44..00d513cb89aa (315 commits)
Strip applied: yes — internal-only paths removed: .claude, CLAUDE.md (all, incl. nested), internal-docs/, docs/** (wiki + dev/migration docs), tests/play/specs/TODO.md, internal release-CI workflows, and bin/ build scripts.
Kept (legitimately public): README.md, inc/abilities/ABILITIES.md, modules/gutenberg/readme.md, tests/play/README.md, third-party inc/lib/** READMEs.
Diff base: merge-capped against mirror/master, so this PR shows only real upstream changes — no internal-file deletions.
Signatures: all synced commits re-signed; verified by GitHub.

Supersedes #107 (closed — its v1 branch still contained internal docs/).

Highlights (merge commits)

00d513cb8 Merge pull request #2875 from brainstormforce/readme-light-trim
a4dec511c Merge pull request #2871 from brainstormforce/seo/readme-keyword-optimization
0a83920c0 Merge pull request #2867 from brainstormforce/next-release
e22a299dd Merge pull request #2866 from brainstormforce/version-bump-2.11.1
4cbb10696 Merge pull request #2865 from brainstormforce/dev-nr-2.11.1-pre
7ef546374 Merge branch 'dev' of https://github.com/brainstormforce/sureforms into dev-nr-2.11.1-pre
d98e4bccd Merge pull request #2864 from brainstormforce/master-dev-2.11.1-pre
7f57706b0 Merge branch 'master' of https://github.com/brainstormforce/sureforms into master-dev-2.11.1-pre
e0ed33af2 Merge pull request #2858 from brainstormforce/ci/harden-i18n-workflow
ea609c051 Merge pull request #2862 from brainstormforce/chore/node-24-bump-v2
94f568b3a Merge pull request #2855 from brainstormforce/fix/payment-amount-bypass
382a5203d Merge pull request #2863 from brainstormforce/feat/suredonation-integration-banner
806556d97 Merge pull request #2838 from brainstormforce/fix/phone-auto-country-per-visitor
b06b7539f Merge pull request #2853 from brainstormforce/fix/turnstile-get-keys-url
5ba53a240 Merge pull request #2845 from brainstormforce/dev-nr-2.11.0
2fc07d1f0 Merge pull request #2844 from brainstormforce/master-dev-2.11.0
c621ce372 Merge pull request #2837 from brainstormforce/next-release
836b51dca Merge pull request #2842 from brainstormforce/i18n/next-release
2fe0a45d4 Merge pull request #2841 from brainstormforce/fix/i18n-verified-commits-master
8113d3dfa Merge pull request #2840 from brainstormforce/chore/release-date-2-11-0

vanshk141999 and others added 30 commits June 9, 2026 16:08
Detect the visitor's country server-side and apply it to the phone field:

- Helper::get_geo_country(): prefer a CDN/server country header
  (Cloudflare/CloudFront/mod_geoip) — free, instant, cache-safe and
  independent of the connecting IP — before any API call; then ipapi.co
  with an optional filterable key (srfm_ipapi_api_key) for reliable IP
  detection; degrade to the field's configured default country.
- Reject private/reserved IPs, detect ipapi 200-with-error rate-limit
  bodies, short failure-cache TTL (srfm_geo_failure_ttl) so a blip
  self-heals, and a site-wide hourly cap on outbound calls.
- Public REST route sureforms/v1/geo-country so detection is per-visitor
  and correct on full-page-cached sites (not baked into cached HTML).
- Frontend applyAutoCountry() fetches the endpoint and applies the country
  via intl-tel-input, cached in sessionStorage per session.
- phone-markup.php emits data-auto-country and passes the configured
  default as the fallback; frontend-assets.php localizes the endpoint URL.
Server-side IP detection can't work on localhost (loopback) and ipapi.co's
keyless tier is rate-limited, so auto-country fell back to US. Add a layered,
privacy-safe fallback:

- REST geo-country endpoint now returns { country, detected }; detected is
  false when the server has no confident result (no CDN header, no successful
  IP lookup).
- Frontend detectCountryFromBrowser() derives the ISO region from the browser
  locale via Intl.Locale — purely on-device, no network/IP/third-party, so it
  works offline, on localhost and on full-page-cached pages (none of the
  CORS/rate-limit/privacy issues that moved the IP lookup server-side).
- applyAutoCountry() applies the local guess immediately and only lets a
  confident server result override it; sessionStorage key bumped so stale
  fallback values don't mask the local guess.
Intl.Locale region reflects the browser *language*, not physical location, so
an en-US browser in India guessed US. Make the device timezone the primary
on-device signal via an IANA timezone -> country map (e.g. Asia/Kolkata -> in),
falling back to the locale region only when the timezone is unknown. Still
purely on-device: no network, no IP, no third party.
Reorder the new private static geo helpers after the public methods
(PHP Insights OrderedClassElements), and cover the two flagged
functions: Helper::get_geo_country() — CDN header preference with no
outbound call, placeholder rejection with private-IP fallback,
srfm_cdn_country filter short-circuit, API resolution with transient
caching, and rate-limit error-body fallback with failure caching —
and Rest_Api::get_geo_country() — detected and not-detected response
shapes.
The 'Get Keys' button pointed to https://www.cloudflare.com/en-gb/products/turnstile/,
a marketing page whose locale path now returns a 404 and never led to key creation.
Point it to the Cloudflare dashboard Turnstile page where users can create a widget
and retrieve their site and secret keys.
Closes an unauthenticated payment amount bypass (WPScan, CVSS 5.9) on
variable-amount payment forms.

- Variant B (legacy/empty source): validate_payment_intent_amount() no
  longer returns valid unconditionally when the amount-source field is
  unidentifiable; it now always falls through to the configured
  minimum-amount floor.
- Hidden / calculation-driven sources: the expected amount is derived
  server-side via the new 'srfm_server_side_variable_amount' filter
  (implemented in Pro) or the field's stored default value, never from
  the value submitted with the request. Plain 'name your price' number
  fields remain user-determined and floor-protected.
- Defense-in-depth: the amount Stripe actually charged/invoiced is
  re-validated against the form configuration on both the one-time and
  subscription paths.

Fixed-price, dropdown and multi-choice sources are unchanged (already
validated server-side). Capture is only requested after validation, so
a rejected submission never charges.
Strengthen the Variant B (legacy form) fix. Falling back to the
minimum-amount floor was insufficient: legacy forms often have a zero
floor, so an underpayment was still accepted (verified end-to-end).

Now, when the variable amount source field is not recorded in the
stored config, first refresh the block config from the form's current
content to recover it (self-heals legacy forms whose source field still
exists), then validate normally. If the source still cannot be
identified, reject the payment instead of accepting an attacker-supplied
amount.
fix: update Cloudflare Turnstile 'Get Keys' link to dashboard URL
- Store calculationRound as null when unset and round only when a
  precision is configured, so a non-integer calculated total (e.g.
  29.99) is no longer rounded up and rejected as a mismatch.
…ests

- Reorder Payment_Helper class elements (PHP Insights ordered-elements)
- Add test_get_submitted_value_by_slug and test_validate_amount_against_config
  to satisfy check-test-coverage for the new public methods
Version is filled in on the release PR; do not hardcode 2.11.1.
…le source

When a variable-amount form's amount-source field can no longer be identified
(e.g. deleted while amount_type stays 'variable'), enforce the configured minimum
amount as the authoritative lower bound instead of hard-rejecting every payment.
Only reject when no positive floor is configured (the genuinely unsafe case),
keeping the underpayment bypass closed while letting legacy forms keep working.

Also document the hidden-field amount-source behavior: smart-tag (non-numeric)
defaults fall through to the floor (dynamic prefill keeps working); a literal
numeric default is authoritative — custom JS-driven pricing must use the
srfm_server_side_variable_amount filter or a calculation field.

Adds test_validate_amount_against_config_unrecoverable_source_floor.
- phone.js: don't let the async REST refine override an explicit dropdown
  country pick (track lastAutoApplied; bail if selection changed). (High #1)
- phone.js validateCountryWithFilters: normalize + ISO2-validate fallbacks;
  include -> first valid included country; exclude -> first non-excluded
  candidate instead of a hardcoded gb/us that may itself be excluded. (High #2)
- phone.js setCountry: /^[a-z]{2}$/ guard before setCountry() so a malformed
  REST country value can't reach iti. (Medium #4)
- helper.php get_geo_country: don't write a per-IP transient in the quota-cap
  branch (the only write path not bounded by the cap; prevents spoofed
  X-Forwarded-For cache churn). (Medium #3)
- helper.php: bump per-IP cache key srfm_geo_ -> srfm_geo_v2_ so stale failure
  transients from older code don't shadow the new logic; note CDN header
  spoofability in get_cdn_country docblock. (Low)
- phone-markup.php: update stale constructor comment to the per-visitor REST
  flow (baked default + client-side Intl guess + REST refine). (Medium #5)
- tests: bump geo cache key to v2; add cap-reached no-cache test.
Mirrors the pro hardening (sureforms-pro#1298):
- Pass the PR head_ref via env (HEAD_REF) and reference it quoted in the
  create-branch and create-PR run blocks, so a branch name with shell
  metacharacters can't achieve RCE / exfiltrate the workflow secrets.
- Add an author_association gate (OWNER/MEMBER/COLLABORATOR) to the /i18n
  trigger so only trusted users can run the secret-bearing build job.
…ned bypass)

- HIGH: srfm/number branch now rejects when enableCalculation is set and the
  server amount is null, instead of falling through to name-your-price and
  trusting the client-submitted amount (the re-opened underpayment bypass).
- Medium: dropdown branch rejects a non-numeric/null expected amount, so
  abs($payment_amount - null) can never coerce to 0.
- Test: calc-number with an unresolvable formula (server null) is rejected,
  isolated with minimum_amount=0 so it can't pass via the floor.
Squashes chore/node-24-bump onto current dev as a single signed commit (the
original branch couldn't merge dev in: signed-commits rule + 5 unsigned dev
commits + no force-push). Same changes: Node 24.16.0 (Volta/CI/docs), drop
--legacy-peer-deps, and gpt-po 1.1.1 -> 1.3.0 with --model gpt-4o on all 20
i18n:gptpo:* scripts (drops vulnerable axios; avoids 1.3.0's gpt-5-nano 400).
…per-visitor

Phone: reliable per-visitor auto country detection
Adds a SureDonation entry as the first item in
Helper::sureforms_get_integration(), so it leads the rotating recommended-plugin
banner on the SureForms Extend dashboard. Ships the suredonation.svg brand asset
and follows the existing integration entry shape (title, descriptions, status via
get_plugin_status(), slug/path, encoded SVG logo).
…ration-banner

feat: add SureDonation to the integrations banner
fix(payments): unauthenticated payment amount bypass on variable-amount forms
chore: standardize Node to 24.16.0 + gpt-po 1.3.0 (replaces #2821)
fix(ci): harden /i18n workflow — block head_ref injection + gate trigger
vanshk141999 and others added 12 commits June 16, 2026 18:06
…mization

Optimize readme.txt for form / contact form search ranking
…lists, drop filler)

Collapses the integrations, themes, and plugins bullet lists into inline
sentences and removes pain-point/closing filler. Feature descriptions are
unchanged. Description: 2,291 -> 2,141 words. Note: this is a tidy-up only
and does not by itself bring the section under WordPress.org's 1,500-word
limit. Regenerate README.md.
Light readme cleanup — inline compatibility/integration lists
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatednpm/​gpt-po@​1.1.1 ⏵ 1.3.073 +110099 +587 +7100

View full report

@socket-security

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm @protobufjs/float is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: package-lock.jsonnpm/[email protected]npm/@protobufjs/[email protected]

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at [email protected].

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@protobufjs/[email protected]. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@vanshk141999 vanshk141999 merged commit 10103ca into master Jun 19, 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