Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ public int getMinIncubationPeriod(Disease disease) {
}

public String getCaseDefinitionText(Disease disease) {
return service.getDiseaseConfiguration(disease).getCaseDefinitionText();
return service.getDiseaseConfiguration(disease) == null ? null : service.getDiseaseConfiguration(disease).getCaseDefinitionText();
}

@Override
Expand Down
64 changes: 54 additions & 10 deletions sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseDataForm.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import java.util.Objects;
import java.util.stream.Collectors;

import de.symeda.sormas.api.person.PersonReferenceDto;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

Expand All @@ -54,6 +53,7 @@
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.Image;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.Window.CloseListener;
import com.vaadin.ui.themes.ValoTheme;
Expand All @@ -70,6 +70,7 @@
import com.vaadin.v7.ui.TextArea;
import com.vaadin.v7.ui.TextField;

import de.symeda.sormas.api.CaseClassificationCalculationMode;
import de.symeda.sormas.api.CountryHelper;
import de.symeda.sormas.api.Disease;
import de.symeda.sormas.api.DiseaseHelper;
Expand Down Expand Up @@ -115,6 +116,7 @@
import de.symeda.sormas.api.infrastructure.facility.FacilityTypeGroup;
import de.symeda.sormas.api.infrastructure.region.RegionReferenceDto;
import de.symeda.sormas.api.person.PersonDto;
import de.symeda.sormas.api.person.PersonReferenceDto;
import de.symeda.sormas.api.person.Sex;
import de.symeda.sormas.api.sample.PathogenTestDto;
import de.symeda.sormas.api.sample.SampleDto;
Expand Down Expand Up @@ -1361,15 +1363,26 @@ && isVisibleAllowed(CaseDataDto.CASE_CLASSIFICATION)) {

// Automatic case classification rules button - invisible for other diseases
DiseaseClassificationCriteriaDto diseaseClassificationCriteria = FacadeProvider.getCaseClassificationFacade().getByDisease(disease);
if (diseaseClassificationExists()) {
Button classificationRulesButton = ButtonHelper.createIconButton(
Captions.info,
VaadinIcons.INFO_CIRCLE,
e -> ControllerProvider.getCaseController().openClassificationRulesPopup(diseaseClassificationCriteria),
ValoTheme.BUTTON_PRIMARY,
FORCE_CAPTION);

getContent().addComponent(classificationRulesButton, CLASSIFICATION_RULES_LOC);

CaseClassificationCalculationMode caseClassificationCalculationMode =
FacadeProvider.getConfigFacade().getCaseClassificationCalculationMode(disease);
// If case classification is not disabled for the disease.
if (CaseClassificationCalculationMode.DISABLED != caseClassificationCalculationMode) {
// If automatic classification is enabled for the disease and it has the classification criteria.
if (FacadeProvider.getConfigFacade().getCaseClassificationCalculationMode(disease).isAutomaticEnabled()
&& diseaseClassificationExists()) {
Button classificationRulesButton = ButtonHelper.createIconButton(
Captions.info,
VaadinIcons.INFO_CIRCLE,
e -> ControllerProvider.getCaseController().openClassificationRulesPopup(diseaseClassificationCriteria),
ValoTheme.BUTTON_PRIMARY,
FORCE_CAPTION);

getContent().addComponent(classificationRulesButton, CLASSIFICATION_RULES_LOC);
} else {
// If Manual classification is enabled for the disease.
getManualCaseDefinition();
}
}

addField(CaseDataDto.DELETION_REASON);
Expand Down Expand Up @@ -1525,6 +1538,37 @@ public String getFormattedHtmlMessage() {
CaseDataDto.ADDITIONAL_DETAILS);
}

/**
* If a manual case definition is configured in the properties files and has the case definition in the disease configuration,
* then display the button with case definition.
*/
private void getManualCaseDefinition() {
// If a disease has caseDefinitionText, it should display; otherwise criteria will display as it is.
String caseDefinitionText = FacadeProvider.getDiseaseConfigurationFacade().getCaseDefinitionText(disease);
if (caseDefinitionText == null) {
return;
}

Button caseDefinitionButton = ButtonHelper.createIconButton(Captions.info, VaadinIcons.INFO_CIRCLE, e -> {
VerticalLayout classificationRulesLayout = new VerticalLayout();
classificationRulesLayout.setMargin(true);
Label suspectContent = new Label();
suspectContent.setContentMode(ContentMode.HTML);
suspectContent.setWidth(100, Unit.PERCENTAGE);
suspectContent.setValue(caseDefinitionText);
classificationRulesLayout.addComponent(suspectContent);
Comment on lines +1555 to +1559
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, check if the file exists and examine the lines in question
head -1559 "sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseDataForm.java" | tail -20 | cat -n

Repository: SORMAS-Foundation/SORMAS-Project

Length of output: 1181


🏁 Script executed:

# Get more context around the flagged lines
sed -n '1540,1570p' "sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseDataForm.java" | cat -n

Repository: SORMAS-Foundation/SORMAS-Project

Length of output: 1730


🏁 Script executed:

# Search for where caseDefinitionText is defined/assigned
rg "caseDefinitionText" "sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseDataForm.java" -B 5 -A 2

Repository: SORMAS-Foundation/SORMAS-Project

Length of output: 1014


🏁 Script executed:

# Check for any sanitization or escaping functions in the file
rg "sanitize|escape|HTML\.escape" "sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseDataForm.java"

Repository: SORMAS-Foundation/SORMAS-Project

Length of output: 58


🏁 Script executed:

# Search for other ContentMode.HTML usages in this file to understand patterns
rg "ContentMode\.HTML" "sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseDataForm.java" -B 3 -A 3

Repository: SORMAS-Foundation/SORMAS-Project

Length of output: 2311


🏁 Script executed:

# Find the DiseaseConfigurationFacade implementation
find . -name "*DiseaseConfigurationFacade*" -type f | head -20

Repository: SORMAS-Foundation/SORMAS-Project

Length of output: 459


🏁 Script executed:

# Search for getCaseDefinitionText method definition
rg "getCaseDefinitionText" --type java -B 2 -A 10

Repository: SORMAS-Foundation/SORMAS-Project

Length of output: 14621


🏁 Script executed:

# Check if caseDefinitionText is stored in database and could be user-controlled
rg "caseDefinitionText" --type java -B 3 -A 3 | head -100

Repository: SORMAS-Foundation/SORMAS-Project

Length of output: 11482


🏁 Script executed:

# Look for any HTML sanitization utilities in the codebase
rg "sanitize|escapeHtml|HtmlUtils" --type java | head -30

Repository: SORMAS-Foundation/SORMAS-Project

Length of output: 2852


Change ContentMode.HTML to ContentMode.TEXT to prevent stored XSS.

The caseDefinitionText is editable by administrators through a RichTextArea in the disease configuration form and stored in the database without sanitization. Rendering it with ContentMode.HTML allows arbitrary HTML/JavaScript injection, creating a stored XSS vulnerability. Using ContentMode.TEXT will safely display the content as plain text.

Proposed fix
-			Label suspectContent = new Label();
-			suspectContent.setContentMode(ContentMode.HTML);
+			Label suspectContent = new Label();
+			suspectContent.setContentMode(ContentMode.TEXT);
 			suspectContent.setWidth(100, Unit.PERCENTAGE);
 			suspectContent.setValue(caseDefinitionText);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Label suspectContent = new Label();
suspectContent.setContentMode(ContentMode.HTML);
suspectContent.setWidth(100, Unit.PERCENTAGE);
suspectContent.setValue(caseDefinitionText);
classificationRulesLayout.addComponent(suspectContent);
Label suspectContent = new Label();
suspectContent.setContentMode(ContentMode.TEXT);
suspectContent.setWidth(100, Unit.PERCENTAGE);
suspectContent.setValue(caseDefinitionText);
classificationRulesLayout.addComponent(suspectContent);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseDataForm.java` around
lines 1555 - 1559, Replace the HTML rendering mode for the label showing
caseDefinitionText to avoid rendering raw HTML: locate the Label instance
suspectContent in CaseDataForm and change the call
suspectContent.setContentMode(ContentMode.HTML) to
suspectContent.setContentMode(ContentMode.TEXT) so the value from
caseDefinitionText is rendered as plain text before adding it to
classificationRulesLayout.

Window popupWindow = VaadinUiUtil.showPopupWindow(classificationRulesLayout);
popupWindow.addCloseListener(e1 -> {
popupWindow.close();
});
popupWindow.setWidth(860, Unit.PIXELS);
popupWindow.setHeight(80, Unit.PERCENTAGE);
popupWindow.setCaption(I18nProperties.getString(Strings.classificationRulesFor) + " " + disease);
}, ValoTheme.BUTTON_PRIMARY, FORCE_CAPTION);

getContent().addComponent(caseDefinitionButton, CLASSIFICATION_RULES_LOC);
}

private void hideJurisdictionFields() {
getField(CaseDataDto.CASE_ORIGIN).setVisible(false);
getContent().getComponent(RESPONSIBLE_JURISDICTION_HEADING_LOC).setVisible(false);
Expand Down
Loading