Skip to content

fix(security): escape product name/category in admin vendor-detail ProductCard (stored XSS)#3304

Open
MdAsifHossainNadim wants to merge 1 commit into
developfrom
fix/vendor-single-productcard-xss
Open

fix(security): escape product name/category in admin vendor-detail ProductCard (stored XSS)#3304
MdAsifHossainNadim wants to merge 1 commit into
developfrom
fix/vendor-single-productcard-xss

Conversation

@MdAsifHossainNadim

@MdAsifHossainNadim MdAsifHossainNadim commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

All Submissions:

  • My code follows the WordPress coding standards
  • My code satisfies feature requirements
  • My code is tested
  • My code passes the lint tests
  • My code has proper inline documentation

Changes proposed in this Pull Request:

This PR fixes L5 from the Dokan security audit (getdokan/plugin-internal-tasks#1994): latent stored XSS: admin vendor-detail ProductCard renders product name/category via dangerouslySetInnerHTML (currently KSES-mitigated).

The problem

The admin vendor-detail ProductCard renders vendor-controlled product data via React's dangerouslySetInnerHTML instead of as escaped JSX text. product.name (lines 28-30) and product.category (lines 35-37) are strings from products a vendor fully controls; a name/category containing markup (e.g. an img tag with an onerror handler) is injected raw into the admin dashboard DOM = stored XSS in an admin context. product.sold (lines 42-45) also uses dangerouslySetInnerHTML with an @ts-ignore; numeric so not currently exploitable but the same unsafe pattern. Latent/KSES-mitigated because the REST payload may currently be sanitized server-side, but the client enforces no trust boundary of its own. React auto-escaping is the correct self-contained fix.

The fix

Removes dangerouslySetInnerHTML for product.name; React escapes the string, closing the stored-XSS vector while keeping 30-char truncation. Removes dangerouslySetInnerHTML for product.category; React escapes the string, keeping 20-char truncation. Removes the last dangerouslySetInnerHTML and its @ts-ignore; numeric sold renders as escaped JSX, eliminating the unsafe pattern from this component.

Implementation scope

  • Edit only src/admin/dashboard/pages/vendors-single/components/ProductCard.tsx — replace the three dangerouslySetInnerHTML usages (name h4, category p, sold div) with escaped JSX text children.
  • No changes to the Product interface, the truncate utility, or the REST payload — only render escaping changes.
  • Rebuild admin dashboard assets (npm run build) so the compiled bundle reflects the change.
  • No server-side/PHP changes required; purely a client-side output-encoding fix.

How to test the changes in this Pull Request:

Security — the vulnerability is now closed.

Before (reproduces the issue on develop):

Attack blocked: as a vendor, create a product whose name is an img tag with an onerror alert and a category name containing a script tag, then as admin open the vendor-single detail page rendering ProductCard; confirm the literal text is shown and no script/onerror fires (pre-fix the payload executes).

After (this PR): the attack is rejected; the legitimate flow and admin access are unaffected.

Regression — verify each of these still holds:

  • Legit flow: a normal product with plain name/category and numeric sold count still renders, truncated to 30/20 chars, with benign ampersand/angle-bracket characters shown literally.
  • Admin/no-regression: confirm the numeric sold count still shows the correct value after removing dangerouslySetInnerHTML, and card layout/styling is unchanged.
  • Build check: run tsc/npm build to confirm removing the @ts-ignore on product.sold surfaces no type error (sold is typed number, so rendering it as a JSX child type-checks).

Changelog entry

Title: Fix escape product name/category in admin vendor-detail ProductCard

Fixed a cross-site scripting security issue: latent stored XSS: admin vendor-detail ProductCard renders product name/category via dangerouslySetInnerHTML (currently KSES-mitigated).

Related Pull Request(s)

  • Security audit: getdokan/plugin-internal-tasks#1994 (finding(s) L5)

  • Closes getdokan/plugin-internal-tasks#2077

Summary by CodeRabbit

  • Bug Fixes
    • Improved product card rendering to safely display product name, category, and sold count as text.
    • Correctly shows encoded characters like & in product details without exposing raw markup.
    • Reduced the risk of unintended HTML injection in vendor product listings.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6ad4fa2b-4d58-4a2f-8de5-df9c45e50906

📥 Commits

Reviewing files that changed from the base of the PR and between e2fbd83 and 265f900.

📒 Files selected for processing (1)
  • src/admin/dashboard/pages/vendors-single/components/ProductCard.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/admin/dashboard/pages/vendors-single/components/ProductCard.tsx

📝 Walkthrough

Walkthrough

ProductCard.tsx now decodes and renders product name, category, and sold count as escaped JSX text instead of using dangerouslySetInnerHTML.

Changes

ProductCard XSS Fix

Layer / File(s) Summary
Escape product name, category, and sold rendering
src/admin/dashboard/pages/vendors-single/components/ProductCard.tsx
Imports decodeEntities and renders product.name, product.category, and product.sold directly as escaped text, removing HTML injection and the prior @ts-ignore.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Suggested reviewers: mrabbani

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: fixing stored XSS in the vendor ProductCard by escaping product fields.
Description check ✅ Passed The description covers the change, testing, changelog, and related PRs, though the Before/After Changes sections are not filled in.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/vendor-single-productcard-xss

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…oductCard (stored XSS)

Render vendor-controlled product name/category as decoded, auto-escaped JSX
text (decodeEntities + React escaping) instead of dangerouslySetInnerHTML, so
HTML entities still display correctly while injected markup can no longer
execute in the admin dashboard.

Fixes finding L5 from the Dokan security audit (getdokan/plugin-internal-tasks#1994).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@MdAsifHossainNadim MdAsifHossainNadim force-pushed the fix/vendor-single-productcard-xss branch from e2fbd83 to 265f900 Compare July 3, 2026 14:27
@MdAsifHossainNadim MdAsifHossainNadim self-assigned this Jul 3, 2026
@MdAsifHossainNadim MdAsifHossainNadim added Needs: Testing This requires further testing Needs: Dev Review It requires a developer review and approval Dev Review Done and removed Needs: Dev Review It requires a developer review and approval labels Jul 3, 2026
@dev-shahed dev-shahed added 🎉 QA Approved This PR is approved by the QA team and removed Needs: Testing This requires further testing labels Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Dev Review Done 🎉 QA Approved This PR is approved by the QA team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants