From b13429c2f250f07a49df70d4d433517bc4546851 Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Mon, 29 Jun 2026 17:36:09 +0200 Subject: [PATCH 1/2] UNOMI-945: Apply sanitized parameter values in sanitizeCondition() sanitizeCondition() computed filtered parameter values but stored the original entries and never applied them back onto the Condition. Mixed safe/script:: list parameters kept malicious entries in the evaluated condition even after sanitizeValue() filtered the list. --- .../org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java index 099926c75..39eca1220 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java @@ -331,11 +331,12 @@ private Condition sanitizeCondition(Condition condition) { for (Map.Entry parameterEntry : condition.getParameterValues().entrySet()) { Object sanitizedValue = sanitizeValue(parameterEntry.getValue()); if (sanitizedValue != null) { - newParameterValues.put(parameterEntry.getKey(), parameterEntry.getValue()); + newParameterValues.put(parameterEntry.getKey(), sanitizedValue); } else { return null; } } + condition.setParameterValues(newParameterValues); return condition; } From d445ad6d79f1e5fdb75a27a59f3b2be3e5f649db Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Mon, 29 Jun 2026 17:36:10 +0200 Subject: [PATCH 2/2] UNOMI-945: Add IT for list-based context filter script sanitization Cover propertyValues list payloads that mix safe values with script:: entries; assert the exploit file is not created. --- .../apache/unomi/itests/ContextServletIT.java | 20 ++++++++++++++++ .../resources/security/mvel-payload-list.json | 23 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 itests/src/test/resources/security/mvel-payload-list.json diff --git a/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java b/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java index 7014ec66e..fa51b228b 100644 --- a/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java +++ b/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java @@ -491,6 +491,26 @@ public void testMVELVulnerability() throws Exception { exists -> exists == Boolean.FALSE, DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES); } + @Test + public void testMVELVulnerabilityWithListPropertyValues() throws Exception { + File vulnFile = new File("target/vuln-file-list.txt"); + if (vulnFile.exists()) { + vulnFile.delete(); + } + String vulnFileCanonicalPath = vulnFile.getCanonicalPath(); + vulnFileCanonicalPath = vulnFileCanonicalPath.replace("\\", "\\\\"); // this is required for Windows support + + Map parameters = new HashMap<>(); + parameters.put("VULN_FILE_PATH", vulnFileCanonicalPath); + HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL)); + request.setEntity( + new StringEntity(getValidatedBundleJSON("security/mvel-payload-list.json", parameters), ContentType.APPLICATION_JSON)); + TestUtils.executeContextJSONRequest(request); + + shouldBeTrueUntilEnd("Vulnerability successfully executed ! File created at " + vulnFileCanonicalPath, vulnFile::exists, + exists -> exists == Boolean.FALSE, DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES); + } + @Test public void testPersonalization() throws Exception { diff --git a/itests/src/test/resources/security/mvel-payload-list.json b/itests/src/test/resources/security/mvel-payload-list.json new file mode 100644 index 000000000..bfc6ff8f8 --- /dev/null +++ b/itests/src/test/resources/security/mvel-payload-list.json @@ -0,0 +1,23 @@ +{ + "filters": [ + { + "id": "filter1", + "filters": [ + { + "condition": { + "parameterValues": { + "propertyName": "firstName", + "comparisonOperator": "equals", + "propertyValues": [ + "safe-value", + "script::Runtime r = Runtime.getRuntime(); r.exec(\"touch ###VULN_FILE_PATH###\");" + ] + }, + "type": "profilePropertyCondition" + } + } + ] + } + ], + "sessionId": "demo-session-id" +}