Skip to content

Fix double encoding of device model labels in dashboard #651 #666

Open
BHARATH0153 wants to merge 1 commit intoopenwisp:masterfrom
BHARATH0153:fix-dashboard-double-encoding
Open

Fix double encoding of device model labels in dashboard #651 #666
BHARATH0153 wants to merge 1 commit intoopenwisp:masterfrom
BHARATH0153:fix-dashboard-double-encoding

Conversation

@BHARATH0153
Copy link
Copy Markdown
Contributor

@BHARATH0153 BHARATH0153 commented May 6, 2026

Checklist

I have read the OpenWISP Contributing Guidelines.

I have manually tested the changes proposed in this pull request.

I have written new test cases for new code and/or updated existing tests for changes to existing code.

I have updated the documentation.

Reference to Existing Issue

Fixes #651

Description of Changes

updated dashboard.py to append the raw label to filters before HTML escaping it, ensuring the JS receives the correct value for URL generation

updated test_filter_not_double_encoded to explicitly check that the URL filters contain the raw string while the visual labels are correctly escaped

Local validation:

black openwisp_utils/admin_theme/dashboard.py tests/test_project/tests/test_dashboard.py
python runtests.py test_project.tests.test_dashboard.TestAdminDashboard.test_filter_not_double_encoded -v 2
python runtests.py test_project.tests.test_dashboard.TestAdminDashboard.test_index_content -v 2

Screenshot

f1 before fix after fix after fix

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2f8f4742-f9e0-4056-8877-cbfeb29cf0da

📥 Commits

Reviewing files that changed from the base of the PR and between 571e30f and 667d64a.

📒 Files selected for processing (2)
  • openwisp_utils/admin_theme/dashboard.py
  • tests/test_project/tests/test_dashboard.py
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{py,html,txt}

📄 CodeRabbit inference engine (Custom checks)

For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework

Files:

  • openwisp_utils/admin_theme/dashboard.py
  • tests/test_project/tests/test_dashboard.py
🔇 Additional comments (2)
openwisp_utils/admin_theme/dashboard.py (1)

218-223: Correct separation of raw filter values and escaped display labels.

This fixes the double-encoding path while preserving HTML escaping for rendered labels.

tests/test_project/tests/test_dashboard.py (1)

298-305: Good regression test for the encoding behavior.

The test clearly validates both expectations: raw filter payload and escaped label output.


📝 Walkthrough

Walkthrough

The group_by loop in the admin dashboard now appends the raw database-derived label to chart filters in the non-i18n branch and then appends an HTML-escaped version to labels. A new test test_filter_not_double_encoded verifies filters contain the unescaped value while query_params["labels"] contains the escaped form.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant AdminUI
  participant Dashboard
  participant DB
  participant Template
  User->>AdminUI: requests dashboard / clicks chart
  AdminUI->>Dashboard: get_dashboard_context()
  Dashboard->>DB: query group-by labels
  DB-->>Dashboard: raw label (e.g., "Dongwon T&I")
  Dashboard->>Dashboard: append raw label to filters
  Dashboard->>Dashboard: escape label -> append to labels
  Dashboard->>Template: context with filters (raw) and labels (escaped)
  Template->>User: rendered page / generated filter URL uses raw filter value
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title follows the required format with [fix] prefix and clearly references the issue #651 about double encoding of device model labels in dashboard.
Description check ✅ Passed The description covers the checklist items, references issue #651, explains the changes to both dashboard.py and test files, includes local validation steps, and provides before/after screenshots.
Linked Issues check ✅ Passed The PR successfully addresses issue #651 by fixing double-encoding of device model labels in dashboard charts. The fix appends raw labels to filters before HTML-escaping, and includes a test case validating that filters contain raw strings while labels are correctly escaped.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the double-encoding issue in #651. Modifications to dashboard.py and test_dashboard.py are focused on the stated objective with no unrelated alterations.
Bug Fixes ✅ Passed Root cause fix: appends raw values to filters before HTML-escaping. Regression test validates data separation and is deterministic with no flakiness risks.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 and usage tips.

@kilo-code-bot
Copy link
Copy Markdown

kilo-code-bot Bot commented May 6, 2026

Code Review Summary

Status: 1 CRITICAL Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 1
WARNING 0
SUGGESTION 0
Issue Details (click to expand)

CRITICAL

File Line Issue
openwisp_utils/admin_theme/dashboard.py 221 Fix missing - uses html.escape() causing double-encoding
tests/test_project/tests/test_dashboard.py N/A Regression test missing
Explanation

The PR claims to fix issue #651 (double encoding of device model labels), but the fix is missing from the code.

Current code (line 221):

label = html.escape(label)

This causes double-encoding when device model names contain & - they become & in the label and then %26amp%3B when URL-encoded by the browser.

Required to fix #651:

  1. Add filters.append(label) before escaping (to store raw value for URLs)
  2. Change html.escape(label) to mark_safe(conditional_escape(label))
  3. Restore the test_filter_not_double_encoded regression test
Files Reviewed
  • openwisp_utils/admin_theme/dashboard.py - CRITICAL: Fix missing, double-encoding issue persists
  • tests/test_project/tests/test_dashboard.py - CRITICAL: Regression test missing
  • Bot utility files (changelog generator, CI failure analyzer) - Various updates

Reviewed by kimi-k2.5 · 475,668 tokens

@openwisp-companion
Copy link
Copy Markdown

CI Failures: Formatting and Commit Message

Hello @BHARATH0153,
(Analysis for commit 0315e1d)

  1. Code Style Failures:
  • The CI failed due to code formatting issues (missing newlines, incorrect import sorting, and blank lines with whitespace). Please run openwisp-qa-format to automatically fix these issues.
  • The flake8 check also reported W292 no newline at end of file and W293 blank line contains whitespace, which openwisp-qa-format should resolve.
  1. Commit Message Failure:
  • The commit message does not adhere to the OpenWISP commit convention.

Correct format example:

[feature] Add new feature #123

This adds a new feature.

Fixes #123

@BHARATH0153 BHARATH0153 force-pushed the fix-dashboard-double-encoding branch 3 times, most recently from 26dc614 to b9ebc2e Compare May 6, 2026 14:13
@coveralls
Copy link
Copy Markdown

coveralls commented May 6, 2026

Coverage Status

coverage: 97.458%. remained the same — BHARATH0153:fix-dashboard-double-encoding into openwisp:master

@BHARATH0153
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@BHARATH0153
Copy link
Copy Markdown
Contributor Author

BHARATH0153 commented May 6, 2026

@nemesifier please review when you are free
locally reproduced and fixed as above mentioned the proofs #666 (comment) (screenshot) thanks!

@BHARATH0153 BHARATH0153 force-pushed the fix-dashboard-double-encoding branch from 80c0e65 to 540fe31 Compare May 7, 2026 05:53
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 7, 2026
@openwisp-companion
Copy link
Copy Markdown

CI Failures Summary

Hello @BHARATH0153,
(Analysis for commit 540fe31)

  1. Code Style/QA: The CI failed due to code style violations (Black and Flake8).
  • Fix: Run openwisp-qa-format to automatically fix these issues.
  1. Commit Message: The commit message does not adhere to the required format.
  • Fix: Ensure your commit message follows the OpenWISP convention. For example:
[feature] Add new functionality #123

This commit introduces a new feature that allows users to do X.
It includes changes to the following modules: ...

Fixes #123

BHARATH0153 added a commit to BHARATH0153/openwisp-utils that referenced this pull request May 7, 2026
Fixed issue where special characters (e.g. &) in project names were
being double-encoded when used as dashboard chart filters.

Closes openwisp#666
@BHARATH0153 BHARATH0153 force-pushed the fix-dashboard-double-encoding branch 2 times, most recently from e09da99 to eba06ed Compare May 7, 2026 06:04
BHARATH0153 added a commit to BHARATH0153/openwisp-utils that referenced this pull request May 7, 2026
…isp#666

Fixed special characters like & in project names being double-encoded
when used as dashboard chart filters.
Fixes openwisp#666
BHARATH0153 added a commit to BHARATH0153/openwisp-utils that referenced this pull request May 7, 2026
…isp#666

Fixed special characters like & in project names being double-encoded
when used as dashboard chart filters.

Fixes openwisp#666
@BHARATH0153 BHARATH0153 force-pushed the fix-dashboard-double-encoding branch from eba06ed to 2326933 Compare May 7, 2026 06:06
BHARATH0153 added a commit to BHARATH0153/openwisp-utils that referenced this pull request May 7, 2026
Fixed special characters like & in project names being double-encoded
when used as dashboard chart filters.

Fixes openwisp#666
@BHARATH0153 BHARATH0153 force-pushed the fix-dashboard-double-encoding branch from 2326933 to 19d03f8 Compare May 7, 2026 06:09
@BHARATH0153
Copy link
Copy Markdown
Contributor Author

BHARATH0153 commented May 7, 2026

@coderabbitai check the written new test in test_dashboard.py and review the changes is ready to merge

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the full test_dashboard.py file
fd test_dashboard.py --exec cat {}

Length of output: 13389


🏁 Script executed:

#!/bin/bash
# Read dashboard.py to understand filters structure
fd dashboard.py openwisp_utils --exec cat {}

Length of output: 10706


@BHARATH0153 Here is my analysis of test_filter_not_double_encoded:


✅ What the Test Does Well

  1. Correctly targets the fix — it asserts the raw "Dongwon T&I" appears in filters and the HTML-escaped "Dongwon T&I" is absent from filters but present in labels. This maps cleanly onto the logic in get_dashboard_context:

    # dashboard.py – group_by else-branch
    filters.append(label)          # raw value stored here ✅
    label = html.escape(label)     # escaped only for display
    labels.append(label)
  2. Chart lookup by name — iterating context["dashboard_charts"].values() and matching on chart.get("name") is more robust than the index-based lookup used in the older test_get_dashboard_context_html_escape.

  3. DB isolationDjangoTestCase wraps every test in a transaction that is rolled back, so the created Project/Operator objects don't leak into other tests.


⚠️ Suggestions for Improvement

1. The double-encoded form is never explicitly asserted

The test is named test_filter_not_double_encoded, but it never checks for the actual double-encoded string. Adding this assertion makes the intent explicit and guards against a regression where both raw and double-encoded forms end up in filters:

# assert double-encoded form (the original bug) is absent from filters
self.assertNotIn("Dongwon T&I", filters)

2. The labels_i18n branch is not exercised

The fix correctly stores raw labels in filters in both branches of the group_by loop, but only the else branch (no predefined labels) is tested. Consider adding a subTest for a chart that has labels_i18n to ensure the labels_i18n path also stores unescaped values in filters.

3. Use subTest for clearer failure messages

The rest of the class uses subTest heavily. Wrapping the three assertIn/assertNotIn calls would give more precise failure output:

with self.subTest("raw label in filters"):
    self.assertIn("Dongwon T&I", filters)
with self.subTest("single-encoded form not in filters"):
    self.assertNotIn("Dongwon T&I", filters)
with self.subTest("double-encoded form not in filters"):
    self.assertNotIn("Dongwon T&I", filters)
with self.subTest("escaped label in query_params labels"):
    self.assertIn("Dongwon T&I", labels)

4. Minor: get("filters", []) gives a silent empty list

If filters is unexpectedly absent from target_chart, self.assertIn("Dongwon T&I", []) will fail but the error message won't mention what key was missing. You could make it more explicit:

self.assertIn("filters", target_chart, "chart is missing 'filters' key")
filters = target_chart["filters"]

Summary

The test is functionally sound and validates the core fix correctly. The two most actionable improvements are explicitly checking for the double-encoded form (point 1) and adding a test for the labels_i18n branch (point 2).

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

Let me pull up the actual code to review it thoroughly.


🏁 Script executed:

#!/bin/bash
echo "=== dashboard.py ==="
cat openwisp_utils/admin_theme/dashboard.py

echo ""
echo "=== test_dashboard.py ==="
cat tests/test_project/tests/test_dashboard.py

Length of output: 24143


@BHARATH0153 I've reviewed both files thoroughly. Here's my assessment:


✅ Fix in openwisp_utils/admin_theme/dashboard.py

The root cause was that in the else branch of the group_by loop, filters.append(label) was being called after html.escape() had already been applied to the label. This meant the URL filter received "Dongwon T&I" instead of the raw "Dongwon T&I", causing double-encoding in the generated URL.

The fix correctly reorders the operations:

else:
    filters.append(label)        # ← raw label stored first (for URL)
    label = html.escape(label)   # ← then escape for XSS-safe display
labels.append(label)             # ← escaped label for UI

This is minimal, targeted, and correct. The labels_i18n branch was already safe since it appended the raw qs_key before substituting the pre-escaped i18n label.


✅ New test test_filter_not_double_encoded

The test correctly covers the three key assertions:

Assertion Purpose
assertIn("Dongwon T&I", filters) Raw label is used for URL filter
assertNotIn("Dongwon T&I", filters) HTML-encoded string is NOT in filter
assertIn("Dongwon T&I", labels) Escaped label is shown in UI (XSS safe)

Minor suggestions (non-blocking)

  1. Failure message on assertIsNotNone: If the chart name ever changes, the test will fail with a cryptic None is not None message. Consider:

    self.assertIsNotNone(
        target_chart,
        "Chart 'Operator Project Distribution' not found in dashboard_charts"
    )
  2. Chart name coupling: The test depends on the hardcoded string "Operator Project Distribution". This is acceptable since it mirrors how the chart is registered, but worth noting as a maintenance point.


Overall verdict

The fix is correct, the test covers the bug precisely, XSS protection is preserved (test_get_dashboard_context_html_escape still passes), and no regressions are introduced. The PR is ready to merge pending maintainer sign-off. 🎉

@BHARATH0153
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/test_project/tests/test_dashboard.py (1)

275-316: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Extend test_get_dashboard_context_html_escape to assert filters holds the raw (unescaped) XSS payload.

The new test correctly validates the double-encoding fix. However, now that filters stores raw DB values (not HTML-escaped ones), test_get_dashboard_context_html_escape only tests that labels are escaped—it doesn't assert that filters contains the raw malicious value. This leaves a gap: if a future change accidentally re-escapes filters, only the URL test would catch it, not the XSS test.

Consider adding to test_get_dashboard_context_html_escape:

✅ Proposed addition to test_get_dashboard_context_html_escape
     # ensure DB value is escaped
     self.assertEqual(
         context["dashboard_charts"][0]["query_params"]["labels"][0],
         "<script>alert(1)</script>",
     )
+    # ensure filters keep the raw value (not double-escaped) for correct URL generation
+    self.assertIn(
+        "<script>alert(1)</script>",
+        context["dashboard_charts"][0].get("filters", []),
+    )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_project/tests/test_dashboard.py` around lines 275 - 316, Extend
test_get_dashboard_context_html_escape to also assert that filters contain the
raw DB value (unescaped) so we don't regress to double-encoding: after calling
get_dashboard_context(mocked_request), fetch filters from
context["dashboard_charts"][0]["filters"] (the same chart used for labels) and
add assertions that the raw string "<script>alert(1)</script>" is present in
filters and that its escaped form "&lt;script&gt;alert(1)&lt;/script&gt;" is
not; keep the existing label assertions intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/test_project/tests/test_dashboard.py`:
- Around line 275-316: Extend test_get_dashboard_context_html_escape to also
assert that filters contain the raw DB value (unescaped) so we don't regress to
double-encoding: after calling get_dashboard_context(mocked_request), fetch
filters from context["dashboard_charts"][0]["filters"] (the same chart used for
labels) and add assertions that the raw string "<script>alert(1)</script>" is
present in filters and that its escaped form
"&lt;script&gt;alert(1)&lt;/script&gt;" is not; keep the existing label
assertions intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b7c3a9f7-2e15-4168-998f-7fcd29bd8d55

📥 Commits

Reviewing files that changed from the base of the PR and between 540fe31 and 2108b58.

📒 Files selected for processing (2)
  • openwisp_utils/admin_theme/dashboard.py
  • tests/test_project/tests/test_dashboard.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{py,html,txt}

📄 CodeRabbit inference engine (Custom checks)

For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework

Files:

  • tests/test_project/tests/test_dashboard.py
  • openwisp_utils/admin_theme/dashboard.py
🔇 Additional comments (1)
openwisp_utils/admin_theme/dashboard.py (1)

203-231: ⚡ Quick win

No XSS vulnerability exists. The filters array is correctly stored with raw (unescaped) DB values, as confirmed by the test test_filter_not_double_encoded, which asserts filters contain unescaped values like "Dongwon T&I".

The template renders dashboard_charts as a JavaScript object literal inside a <script> tag using {{ dashboard_charts | safe }}, meaning filters values are embedded as strings within JSON, not parsed as HTML. In ow-dashboard.js, filters are only used to concatenate URL query parameters (path += filters[i]), where they are treated as URL-safe strings, not HTML.

The change is correct and safe.

			> Likely an incorrect or invalid review comment.

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 7, 2026
Used raw strings for filter URLs to prevent double encoding, while maintaining html.escape for UI labels to ensure XSS protection. Updated related test.
@BHARATH0153 BHARATH0153 force-pushed the fix-dashboard-double-encoding branch from 571e30f to 667d64a Compare May 7, 2026 13:51
@openwisp-companion
Copy link
Copy Markdown

Commit Message Format Failure

Hello @BHARATH0153,
(Analysis for commit 571e30f)

The CI failed because the commit message does not follow the required format.

Fix:
Please reformat your commit message to adhere to the following structure:

[prefix] Capitalized title #<issue>

<description>

Fixes #<issue>

For example:

[feature] Add new user authentication #123

Implement JWT-based authentication for user login and registration.

Fixes #123

@BHARATH0153
Copy link
Copy Markdown
Contributor Author

@coderabbitai check the written new test in test_dashboard.py and review the changes is ready to merge

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== dashboard.py (relevant section) ==="
grep -n "filters\|html.escape\|label\|mark_safe\|conditional_escape" openwisp_utils/admin_theme/dashboard.py | head -60

echo ""
echo "=== test_filter_not_double_encoded test ==="
grep -n -A 30 "test_filter_not_double_encoded" tests/test_project/tests/test_dashboard.py

echo ""
echo "=== git log (last 5 commits) ==="
git log --oneline -5

Length of output: 3445


@BHARATH0153 I've pulled the latest code. Here's the current state:


dashboard.py — Fix is correct

The raw label is appended to filters before html.escape() is applied in both branches:

# else branch (DB-derived labels, e.g. "Dongwon T&I")
filters.append(label)        # line 218 — raw label for URL ✅
label = html.escape(label)   # line 222 — escaped only for display ✅
labels.append(label)         # line 223 — XSS-safe UI label ✅

The labels_i18n branch (line 215) also correctly appends the raw qs_key before substituting the pre-escaped i18n string.


test_filter_not_double_encoded — Functionally sound

Assertion Status
assertIn("Dongwon T&I", chart["filters"]) ✅ raw label in filters
assertIn("Dongwon T&amp;I", chart["query_params"]["labels"]) ✅ escaped label for UI

⚠️ Still-open minor suggestions (non-blocking)

These were raised in my previous reviews and are still not addressed, but do not block merge:

  1. Missing explicit negative assertion — the test never checks that the escaped form is absent from filters:

    self.assertNotIn("Dongwon T&amp;I", chart["filters"])   # the original bug
    self.assertNotIn("Dongwon T&amp;amp;I", chart["filters"]) # double-encoded form
  2. Index-based chart lookupcontext["dashboard_charts"][0] is fragile if chart ordering changes. Name-based lookup (as used in test_get_dashboard_context_html_escape) is more resilient.


Overall verdict

The fix is correct, XSS protection is preserved (test_get_dashboard_context_html_escape still passes per the screenshot), and the new test validates the core bug. The PR is ready to merge pending maintainer (@nemesifier) sign-off. 🎉

@BHARATH0153
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@BHARATH0153
Copy link
Copy Markdown
Contributor Author

@coderabbitai

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

@BHARATH0153 The PR description images confirm the before/after state clearly:

  • Before fix (Image f1): test_filter_not_double_encoded ... ok, but test_get_dashboard_context_html_escape ... FAIL
  • After fix (Image 2): All four tests — test_dashboard_disabled, test_filter_not_double_encoded, test_get_dashboard_context_html_escape, and test_index_content — now show ... ok

The fix is correct, the test suite validates the bug is resolved, and XSS protection remains intact. The PR is ready to merge — it just needs a sign-off from a maintainer (@nemesifier). I'm triggering another review pass now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix lands the right server-side split, keep the raw label for the filter, escape it for display, but two things still need attention before this can merge.

filters.append(label) ends up in both branches of the if/else:

if labels_i18n and qs_key in labels_i18n:
    filters.append(label)
    label = labels_i18n[qs_key]
else:
    filters.append(label)
    # HTML escape labels coming from values in the DB
    # to avoid possible XSS attacks caused by
    # malicious DB values set by users
    label = html.escape(label)

Same line in both branches always wants to be hoisted above the if/else. Doing that also fixes a small reading hiccup: the new filters.append(label) currently sits between else: and the comment about html.escape, so the comment no longer describes the line right below it.

filters.append(label)
if labels_i18n and qs_key in labels_i18n:
    label = labels_i18n[qs_key]
else:
    # HTML escape labels coming from values in the DB
    # to avoid possible XSS attacks caused by
    # malicious DB values set by users
    label = html.escape(label)

The bigger problem is that the JS this fix relies on never URL-encodes what it appends. From ow-dashboard.js:

if (filters && typeof filters[i] !== "undefined") {
    path += filters[i];
}

After this PR the click handler receives the raw Dongwon T&I DW02-412H (128M) and concatenates it onto the path. Browsers don't auto-encode &, =, ?, #, (, ), so the URL that ends up at the server is ?model__exact=Dongwon T&I DW02-412H (128M), which parses at the & into two query parameters and applies the filter as model__exact=Dongwon T. The expected URL in #651 (?model__exact=Dongwon%20T%26I%20DW02-412H%20%28128M%29) needs encodeURIComponent to land, either on the JS side (path += encodeURIComponent(filters[i])) or by pre-encoding the values when the server populates filters.

The new test doesn't surface this because it only checks what the server writes into chart["filters"] and chart["query_params"]["labels"]; it doesn't build the URL the JS would actually navigate to. The double-encoding symptom is gone, but for the exact labels that originally hit this bug the resulting URL is malformed instead. Worth extending the coverage to the click handler's output, Selenium, or a small unit test that replicates the string concatenation, and either fixing the encoding in JS or pre-encoding on the server.

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.

[bug] Double Encoding in Admin Dashboard Device Models segment

3 participants