You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Due for payment 2026-03-25] [$250] Tax - Angle brackets in tax name create duplicate tax rates & reverts on selection #83183
Version Number: V9.3.24-1 Reproducible in staging?: Yes Reproducible in production?: Yes If this was caught during regression testing, add the test name, ID and link from BrowserStack:https://test-management.browserstack.com/projects/2219752/folder/13176868/test-cases/41237324 Email or phone of affected tester (no customers):dreamnitethedragon@gmail.com Issue reported by: Applause Internal Team Bug source: Exploratory - Significant User Experience Deterioration Device used: Redminote 10S Android 13 App Component: Workspace Settings
Action Performed:
Launch app
Create workpace
Enable taxes
Add rate and save
Note invalid character error is shown
Enter tax name GST<10%>, tax value and save
Note no error is shown but two rates are created GST<10%> and GST.
Go to workspace chat
Create a manual expense selecting GST as tax rate
Open the expense
Tap tax and select GST<10%>
Note selection gets reverted back to GST
Expected Result:
If < > are allowed:
Tax rate should be saved exactly as entered (GST<10%>).
Only one rate should be created.
Selecting that rate in an expense should work normally.
Selection should not revert.
If < > are invalid characters:
System should show validation error consistently for all cases.
No tax rate should be created.
Actual Result:
Saving tax name as :
→ Shows "invalid character" error.
GST<10%> → No error shown.
But two tax rates are created:
GST
GST<10%>
When selecting GST<10%> in an expense:
Selection reverts back to GST.
🚨 Edited by proposal-police: This proposal was edited at 2026-03-04 23:18:00 UTC.
Proposal
Please re-state the problem that we are trying to solve in this issue.
When creating or editing workspace tax rates, inputs containing angle brackets (for example GST<10%>) are not handled consistently. In some flows, the form allows save attempts and can lead to two visible tax entries after sync (for example GST<10%> and GST). Later, selecting the intended tax in an expense can revert to a different tax, which breaks expected behavior.
What is the root cause of that problem?
The tax name forms currently rely on the default HTML tag validation behavior in FormProvider, which is not strict enough for cases like GST<10%>. Because the frontend check is inconsistent with backend handling for angle-bracket patterns, invalid names can pass in some paths and create inconsistent post-save behavior.
What changes do you think we should make in order to solve the problem?
Add shouldUseStrictHtmlTagValidation to the FormProvider in both tax name creation and tax name editing forms. This ensures frontend validation consistently rejects angle-bracket patterns like GST<10%>, matching backend behavior and preventing invalid tax creation.
This makes the Invalid character error appear immediately for values like GST<10%> and avoids the inconsistent flow where duplicate rates can appear and selected tax can revert.
What alternative solutions did you explore? (Optional)
Alternative 1 - Allow < and > in tax names and escape everywhere:
This requires escaping/encoding in validation, storage, matching, rendering, and search/selection flows.
Risk is higher because any missed path can reintroduce duplicate keys or selection mismatches.
Decision: avoid this for now and keep strict rejection behavior.
Alternative 2 - Add extra domain validation in tax actions:
This also works but duplicates validation responsibility across layers.
Decision: first align form-level validation using strict HTML tag validation in both tax name forms.
Summary: Tax names containing certain angle bracket patterns (e.g., GST<10%>) bypass frontend HTML-tag validation but get stripped by the backend WAF, creating duplicate tax rates and causing selection reversion.
Root Cause: The frontend uses VALIDATE_FOR_HTML_TAG_REGEX (/<\/?\w*((\s+\w+...)+\s*|\s*)\/?>/g) which only catches valid HTML-like tags — those where the content after < starts with word characters (e.g., <Shahrukh>, <div>). Patterns like <10%> start with a digit followed by %, which doesn't match the regex's attribute/whitespace expectations after the tag name, so they pass validation. The backend WAF applies stricter stripping that removes any angle-bracketed content, creating a mismatch: the frontend optimistically saves GST<10%> while the backend creates GST, resulting in duplicate rates and broken selection.
The codebase already has STRICT_VALIDATE_FOR_HTML_TAG_REGEX (/<([^>\s]+)(?:[^>]*?)>/g) which catches any content in angle brackets, and a shouldUseStrictHtmlTagValidation prop on FormProvider to enable it. This prop is already used on merchant-related forms but is not enabled on tax name forms.
Recommended Fix: Add shouldUseStrictHtmlTagValidation to the FormProvider in:
WorkspaceTaxesSettingsCustomTaxName.tsx (custom tax name setting) and WorkspaceTaxCodePage.tsx (tax code editing) also use FormProvider without strict validation. Depending on whether the backend applies the same WAF stripping to these fields, they may need the same fix. The reported issue specifically covers tax rate names, so the primary fix targets the two pages above.
Next Steps for Contributor+ team: Reply with @MelvinBot implement this to create a draft PR, or @MelvinBot <your feedback> to refine this analysis.
The solution for this issue has been 🚀 deployed to production 🚀 in version 9.3.39-3 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:
@sobitneupane The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the BugZero Checklist from here into a new comment on this GH and complete it. If you have the K2 extension, you can simply click: [this button]
--- /dev/null+++ b/src/libs/TaxRateUtils.js@@ -0,0 +1,8 @@+/**+ * @param {string} taxName+ * @returns {boolean} True if the tax name is valid, false otherwise.+ */+function isValidTaxName(taxName) {+ // Angle brackets (< and >) are invalid characters in tax names as they can cause parsing issues,+ // data corruption, or inconsistent display/selection behavior within the application.+ return !/[<>]/.test(taxName);+}++export {+ isValidTaxName,+};
EVM Wallet for Payout: 0x0c67cbE9e30c66267975eE2D74Eb88036CA65e9F Solana Wallet for Payout: 3hLpMvUUS685bZz2PzR6vWeA37UrdKq924s7yLMB47eZ XRPL Wallet for Payout: 0x0c67cbE9e30c66267975eE2D74Eb88036CA65e9F (EVM Sidechain)
Activity
abbasifaizan70 commentedon Feb 23, 2026
🚨 Edited by proposal-police: This proposal was edited at 2026-03-04 23:18:00 UTC.
Proposal
Please re-state the problem that we are trying to solve in this issue.
When creating or editing workspace tax rates, inputs containing angle brackets (for example
GST<10%>) are not handled consistently. In some flows, the form allows save attempts and can lead to two visible tax entries after sync (for exampleGST<10%>andGST). Later, selecting the intended tax in an expense can revert to a different tax, which breaks expected behavior.What is the root cause of that problem?
The tax name forms currently rely on the default HTML tag validation behavior in
FormProvider, which is not strict enough for cases likeGST<10%>. Because the frontend check is inconsistent with backend handling for angle-bracket patterns, invalid names can pass in some paths and create inconsistent post-save behavior.Relevant code (permalinks):
App/src/components/Form/FormProvider.tsx
Line 162 in c3b6820
App/src/pages/workspace/taxes/WorkspaceCreateTaxPage.tsx
Lines 92 to 101 in c3b6820
App/src/pages/workspace/taxes/NamePage.tsx
Lines 86 to 95 in c3b6820
What changes do you think we should make in order to solve the problem?
Add
shouldUseStrictHtmlTagValidationto theFormProviderin both tax name creation and tax name editing forms. This ensures frontend validation consistently rejects angle-bracket patterns likeGST<10%>, matching backend behavior and preventing invalid tax creation.Location 1:
App/src/pages/workspace/taxes/WorkspaceCreateTaxPage.tsxOriginal code (permalink):
App/src/pages/workspace/taxes/WorkspaceCreateTaxPage.tsx
Lines 92 to 101 in c3b6820
Suggested change (TypeScript):
Location 2:
App/src/pages/workspace/taxes/NamePage.tsxOriginal code (permalink):
App/src/pages/workspace/taxes/NamePage.tsx
Lines 86 to 95 in c3b6820
Suggested change (TypeScript):
This makes the
Invalid charactererror appear immediately for values likeGST<10%>and avoids the inconsistent flow where duplicate rates can appear and selected tax can revert.What alternative solutions did you explore? (Optional)
Alternative 1 - Allow
<and>in tax names and escape everywhere:Alternative 2 - Add extra domain validation in tax actions:
Demo
melvin-bot commentedon Mar 3, 2026
Triggered auto assignment to @dylanexpensify (
Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.MelvinBot commentedon Mar 3, 2026
🔍 Issue Analysis
Summary: Tax names containing certain angle bracket patterns (e.g.,
GST<10%>) bypass frontend HTML-tag validation but get stripped by the backend WAF, creating duplicate tax rates and causing selection reversion.Root Cause: The frontend uses
VALIDATE_FOR_HTML_TAG_REGEX(/<\/?\w*((\s+\w+...)+\s*|\s*)\/?>/g) which only catches valid HTML-like tags — those where the content after<starts with word characters (e.g.,<Shahrukh>,<div>). Patterns like<10%>start with a digit followed by%, which doesn't match the regex's attribute/whitespace expectations after the tag name, so they pass validation. The backend WAF applies stricter stripping that removes any angle-bracketed content, creating a mismatch: the frontend optimistically savesGST<10%>while the backend createsGST, resulting in duplicate rates and broken selection.The codebase already has
STRICT_VALIDATE_FOR_HTML_TAG_REGEX(/<([^>\s]+)(?:[^>]*?)>/g) which catches any content in angle brackets, and ashouldUseStrictHtmlTagValidationprop onFormProviderto enable it. This prop is already used on merchant-related forms but is not enabled on tax name forms.Recommended Fix: Add
shouldUseStrictHtmlTagValidationto theFormProviderin:src/pages/workspace/taxes/WorkspaceCreateTaxPage.tsx(tax rate creation form)src/pages/workspace/taxes/NamePage.tsx(tax rate rename form)This aligns frontend validation with backend behavior, consistently rejecting names like
GST<10%>before submission.Detailed Investigation
Root Cause Analysis
The validation flow in
FormProvider.tsx:162selects which regex to use:Why
<Shahrukh>is caught but<10%>is not:VALIDATE_FOR_HTML_TAG_REGEXmatches<\w*—Shahrukhis all word characters, so<Shahrukh>matches<10%>,\w*matches10, then%>fails to match the expected attribute/whitespace pattern, so the whole match failsWhy
STRICT_VALIDATE_FOR_HTML_TAG_REGEXcatches both:<([^>\s]+)— any one or more characters that aren't>or whitespace — so<10%>,<✓>,<Shahrukh>all matchEvidence Chain
GST<10%>→ frontendVALIDATE_FOR_HTML_TAG_REGEXdoesn't match → no error shownGST<10%>locally<10%>→ creates rate namedGSTGST<10%>(optimistic) andGST(server)GST<10%>sends a tax code the backend doesn't recognize → reverts toGSTRelevant Code
App/src/CONST/index.ts:2066-2071— Both regex definitionsApp/src/components/Form/FormProvider.tsx:155-162— Regex selection logicApp/src/pages/workspace/taxes/WorkspaceCreateTaxPage.tsx:92-101— Create form missing strict validationApp/src/pages/workspace/taxes/NamePage.tsx:86-95— Rename form missing strict validationExisting Usage Pattern
shouldUseStrictHtmlTagValidationis already used in:src/pages/workspace/rules/MerchantRules/AddMerchantToMatchPage.tsxsrc/pages/iou/request/step/IOURequestStepMerchant.tsxsrc/components/Rule/TextBase.tsxAdditional Consideration
WorkspaceTaxesSettingsCustomTaxName.tsx(custom tax name setting) andWorkspaceTaxCodePage.tsx(tax code editing) also useFormProviderwithout strict validation. Depending on whether the backend applies the same WAF stripping to these fields, they may need the same fix. The reported issue specifically covers tax rate names, so the primary fix targets the two pages above.Next Steps for Contributor+ team: Reply with
@MelvinBot implement thisto create a draft PR, or@MelvinBot <your feedback>to refine this analysis.25 remaining items
melvin-bot commentedon Mar 18, 2026
Reviewinglabel has been removed, please complete the "BugZero Checklist".melvin-bot commentedon Mar 18, 2026
Triggered auto assignment to @mallenexpensify (
Awaiting Payment)melvin-bot commentedon Mar 18, 2026
The solution for this issue has been 🚀 deployed to production 🚀 in version 9.3.39-3 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:
If no regressions arise, payment will be issued on 2026-03-25. 🎊
For reference, here are some details about the assignees on this issue:
melvin-bot commentedon Mar 18, 2026
@sobitneupane The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the BugZero Checklist from here into a new comment on this GH and complete it. If you have the K2 extension, you can simply click: [this button]
allornothingai commentedon Mar 18, 2026
EVM Wallet for Payout: 0x0c67cbE9e30c66267975eE2D74Eb88036CA65e9F
Solana Wallet for Payout: 3hLpMvUUS685bZz2PzR6vWeA37UrdKq924s7yLMB47eZ
XRPL Wallet for Payout: 0x0c67cbE9e30c66267975eE2D74Eb88036CA65e9F (EVM Sidechain)
melvin-bot commentedon Mar 25, 2026
Payment Summary
Upwork Job
BugZero Checklist (@mallenexpensify)
mallenexpensify commentedon Mar 26, 2026
Payment Summary
Contributor+: @sobitneupane due $250 via NewDot
JmillsExpensify commentedon May 24, 2026
$250 approved for @sobitneupane