Skip to content

Conversation

@madewulf
Copy link
Member

@madewulf madewulf commented Dec 8, 2024

What problem is this PR solving? Explain here in one sentence.

Related JIRA tickets : IA-XXX, WC2-XXX, POLIO-XXX

Self proofreading checklist

  • Did I use eslint and black formatters
  • Is my code clear enough and well documented
  • Are my typescript files well typed
  • New translations have been added or updated if new strings have been introduced in the frontend
  • My migrations file are included
  • Are there enough tests
  • Documentation has been included (for new feature)

Doc

Tell us where the doc can be found (docs folder, wiki, in the code...).

Changes

Explain the changes that were made.

The idea is not to list exhaustively all the changes made (GitHub already provides a full diff), but to help the reviewers better understand:

  • which specific file changes go together, e.g: when creating a table in the front-end, there usually is a config file that goes with it
  • the reasoning behind some changes, e.g: deleted files because they are now redundant
  • the behaviour to expect, e.g: tooltip has purple background color because the client likes it so, changed a key in the API response to be consistent with other endpoints

How to test

Explain how to test your PR.

If a specific config is required explain it here: dataset, account, profile, etc.

Print screen / video

Upload here print screens or videos showing the changes.

Notes

Things that the reviewers should know:

  • known bugs that are out of the scope of the PR
  • other trade-offs that were made
  • does the PR depends on a PR in bluesquare-components?
  • should the PR be merged into another PR?

Follow the Conventional Commits specification

The merge message of a pull request must follow the Conventional Commits specification.

This convention helps to automatically generate release notes.

Use lowercase for consistency.

Example:

fix: empty instance pop up

Refs: IA-3665

Note that the Jira reference is preceded by a line break.

Both the line break and the Jira reference are entered in the Add an optional extended description… field.

madewulf and others added 30 commits December 7, 2024 23:57
Reusable across screens and handling users at different levels in the pyramid
@beygorghor beygorghor changed the base branch from main to develop September 9, 2025 14:14
console.log(result)

renderTable(result, dataUrl) // Implement this function based on your data structure
excelLink.href = excelUrl

Check failure

Code scanning / CodeQL

DOM text reinterpreted as HTML High

DOM text
is reinterpreted as HTML without escaping meta-characters.
DOM text
is reinterpreted as HTML without escaping meta-characters.

Copilot Autofix

AI 26 days ago

General Fix:
The best way to mitigate this vulnerability is to ensure that both selectedMonth and selectedMode values are validated and/or sanitized before they are interpolated into URLs such as excelUrl and subsequently set into the DOM.

Detailed Fix:

  • For both monthSelect and modeSelect, only allow values that are present in their corresponding options (i.e., by validating against the option values that exist in the select).
  • Before constructing the URL and assigning it to any DOM element, explicitly validate and sanitize the input values.
  • Alternatively, use a client-side escaping function to ensure that these values cannot inject unwanted characters, especially characters such as &, ?, #, /, and (critically) cannot introduce new protocol schemes or JavaScript snippets.
  • Update the code at the points where selectedMonth and selectedMode are read (lines 18 and 19) and before their use in URLs (lines 30/31/32/39/67).

What is needed:

  • A function that validates values against the corresponding <select>'s options (ideally generic to both elements).
  • Sanitize or otherwise refuse if the value is not present in the allowed list.
  • Use the validated value for building URLs.

Suggested changeset 1
plugins/active_list/static/patient_list_dynamic.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/plugins/active_list/static/patient_list_dynamic.js b/plugins/active_list/static/patient_list_dynamic.js
--- a/plugins/active_list/static/patient_list_dynamic.js
+++ b/plugins/active_list/static/patient_list_dynamic.js
@@ -11,12 +11,23 @@
   async function fetchPatientData () {
     hideData() // Clear previous results and hide elements
 
+    // Helper to safely get select value from allowed options only
+    function getSafeSelectValue(selectEl) {
+      const value = selectEl.value;
+      for (let i = 0; i < selectEl.options.length; ++i) {
+        if (selectEl.options[i].value === value) {
+          return value;
+        }
+      }
+      return '';
+    }
+
     // Find the ID of the most specific selected org unit
     let finalOrgUnitId = null
 
     // Get other filter values
-    const selectedMonth = monthSelect.value
-    const selectedMode = modeSelect.value
+    const selectedMonth = getSafeSelectValue(monthSelect)
+    const selectedMode = getSafeSelectValue(modeSelect)
 
     // Only fetch data if a month and at least one org unit level (or the intended level) is selected
     if (!selectedMonth || !selectedOrgUnit) {
EOF
@@ -11,12 +11,23 @@
async function fetchPatientData () {
hideData() // Clear previous results and hide elements

// Helper to safely get select value from allowed options only
function getSafeSelectValue(selectEl) {
const value = selectEl.value;
for (let i = 0; i < selectEl.options.length; ++i) {
if (selectEl.options[i].value === value) {
return value;
}
}
return '';
}

// Find the ID of the most specific selected org unit
let finalOrgUnitId = null

// Get other filter values
const selectedMonth = monthSelect.value
const selectedMode = modeSelect.value
const selectedMonth = getSafeSelectValue(monthSelect)
const selectedMode = getSafeSelectValue(modeSelect)

// Only fetch data if a month and at least one org unit level (or the intended level) is selected
if (!selectedMonth || !selectedOrgUnit) {
Copilot is powered by AI and may make mistakes. Always verify output.

var excel_link = $('#excel_link')

excel_link.attr('href', url + '&format=xls')

Check failure

Code scanning / CodeQL

DOM text reinterpreted as HTML High

DOM text
is reinterpreted as HTML without escaping meta-characters.
DOM text
is reinterpreted as HTML without escaping meta-characters.

Copilot Autofix

AI 26 days ago

To fix the problem, we should ensure all user-controlled values incorporated into URLs that are assigned to HTML attributes (such as href) are properly escaped or validated to prevent malicious payloads, especially those that might lead to XSS (e.g., via javascript:). The best approach here is to:

  1. Use functions to strictly validate and encode the relevant, variable user input before placing it in URLs.
  2. Specifically, for URLs, use encodeURIComponent for all dynamically inserted path/query parts, so that no meta-characters can break out and create a dangerous attribute value.
  3. In this code, we should ensure that selectedMonth and selectedMode are both encoded via encodeURIComponent before constructing URLs (dataUrl, excelUrl, etc.). Likewise, any other user inputs (e.g., selectedOrgUnit) should be similarly handled if there's even a theoretical way for user input to appear.
  4. Make these changes at the point where the variables are used to assemble URLs (e.g., lines 30, 31, and 32) in fetchPatientData, and when constructing the Excel link in renderTable, if variables are re-used or new data is passed in.

Summary of steps:

  • On lines constructing data URLs, encode all user-controlled values with encodeURIComponent.
  • Ensure any values passed into renderTable and used for hrefs are also appropriately encoded.
  • No additional dependencies are required for this fix.
Suggested changeset 1
plugins/active_list/static/patient_list_dynamic.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/plugins/active_list/static/patient_list_dynamic.js b/plugins/active_list/static/patient_list_dynamic.js
--- a/plugins/active_list/static/patient_list_dynamic.js
+++ b/plugins/active_list/static/patient_list_dynamic.js
@@ -15,8 +15,8 @@
     let finalOrgUnitId = null
 
     // Get other filter values
-    const selectedMonth = monthSelect.value
-    const selectedMode = modeSelect.value
+    const selectedMonth = encodeURIComponent(monthSelect.value)
+    const selectedMode = encodeURIComponent(modeSelect.value)
 
     // Only fetch data if a month and at least one org unit level (or the intended level) is selected
     if (!selectedMonth || !selectedOrgUnit) {
@@ -27,8 +27,8 @@
       return
     }
 
-    const dataUrl = `/active_list/patient_list_api/${selectedOrgUnit}/${selectedMonth}/?mode=${selectedMode}`
-    const excelUrl = `/active_list/patient_list_upload_format/${selectedOrgUnit}/${selectedMonth}/?mode=${selectedMode}` + '&format=xls' // Assuming separate excel endpoint
+    const dataUrl = `/active_list/patient_list_api/${encodeURIComponent(selectedOrgUnit)}/${selectedMonth}/?mode=${selectedMode}`
+    const excelUrl = `/active_list/patient_list_upload_format/${encodeURIComponent(selectedOrgUnit)}/${selectedMonth}/?mode=${selectedMode}` + '&format=xls' // Assuming separate excel endpoint
     const htmlUrl = dataUrl + '&format=html'
     console.log(`fetching patient data from: ${dataUrl}`)
 
EOF
@@ -15,8 +15,8 @@
let finalOrgUnitId = null

// Get other filter values
const selectedMonth = monthSelect.value
const selectedMode = modeSelect.value
const selectedMonth = encodeURIComponent(monthSelect.value)
const selectedMode = encodeURIComponent(modeSelect.value)

// Only fetch data if a month and at least one org unit level (or the intended level) is selected
if (!selectedMonth || !selectedOrgUnit) {
@@ -27,8 +27,8 @@
return
}

const dataUrl = `/active_list/patient_list_api/${selectedOrgUnit}/${selectedMonth}/?mode=${selectedMode}`
const excelUrl = `/active_list/patient_list_upload_format/${selectedOrgUnit}/${selectedMonth}/?mode=${selectedMode}` + '&format=xls' // Assuming separate excel endpoint
const dataUrl = `/active_list/patient_list_api/${encodeURIComponent(selectedOrgUnit)}/${selectedMonth}/?mode=${selectedMode}`
const excelUrl = `/active_list/patient_list_upload_format/${encodeURIComponent(selectedOrgUnit)}/${selectedMonth}/?mode=${selectedMode}` + '&format=xls' // Assuming separate excel endpoint
const htmlUrl = dataUrl + '&format=html'
console.log(`fetching patient data from: ${dataUrl}`)

Copilot is powered by AI and may make mistakes. Always verify output.
<link rel="stylesheet" href="{% static 'theme.default.min.css' %}">
<script type="text/javascript" src="{% static 'jquery.tablesorter.min.js' %}"></script>
<!-- Tablesorter widgets including sticky headers -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.31.3/js/widgets/widget-stickyHeaders.min.js"></script>

Check warning

Code scanning / CodeQL

Inclusion of functionality from an untrusted source Medium

Script loaded from content delivery network with no integrity check.
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.

3 participants