This is an automated email from the ASF dual-hosted git repository.
jsinovassin pushed a commit to branch unomi-3.0.x
in repository https://gitbox.apache.org/repos/asf/unomi.git
The following commit(s) were added to refs/heads/unomi-3.0.x by this push:
new 12a7603bc Write filtered personalization contents back onto the
request (unomi-3.0.x) (#890)
12a7603bc is described below
commit 12a7603bc9a22216303a047971add8c4782ee50f
Author: Jonathan SINOVASSIN-NAIK <[email protected]>
AuthorDate: Wed Oct 14 17:32:28 2026 +0200
Write filtered personalization contents back onto the request (unomi-3.0.x)
(#890)
Co-authored-by: Serge Huber <[email protected]>
---
.../org/apache/unomi/itests/ContextServletIT.java | 26 ++++++++++++++++++
.../mvel-payload-personalization-mixed.json | 32 ++++++++++++++++++++++
.../unomi/rest/endpoints/ContextJsonEndpoint.java | 1 +
3 files changed, 59 insertions(+)
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 fa51b228b..606598056 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
@@ -511,6 +511,32 @@ public class ContextServletIT extends BaseIT {
exists -> exists == Boolean.FALSE, DEFAULT_TRYING_TIMEOUT,
DEFAULT_TRYING_TRIES);
}
+ @Test
+ public void testPersonalizationDropsScriptContentAndKeepsFallback() throws
Exception {
+ File vulnFile = new File("target/vuln-file-personalization-mixed.txt");
+ if (vulnFile.exists()) {
+ vulnFile.delete();
+ }
+ String vulnFileCanonicalPath =
vulnFile.getCanonicalPath().replace("\\", "\\\\");
+
+ Map<String, String> parameters = new HashMap<>();
+ parameters.put("VULN_FILE_PATH", vulnFileCanonicalPath);
+ HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
+ request.setEntity(new StringEntity(
+
getValidatedBundleJSON("security/mvel-payload-personalization-mixed.json",
parameters),
+ ContentType.APPLICATION_JSON));
+ // The payload declares its own sessionId, so the request is sent
without asserting one:
+ // the two-argument helper checks that the context echoes the id it
was given.
+ TestUtils.RequestResponse response =
TestUtils.executeContextJSONRequest(request);
+
+ assertEquals("Invalid response code", 200, response.getStatusCode());
+ List<String> variants =
response.getContextResponse().getPersonalizations().get("mixed-sanitize");
+ assertNotNull(variants);
+ assertEquals(Collections.singletonList("safe-fallback"), variants);
+ 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-personalization-mixed.json
b/itests/src/test/resources/security/mvel-payload-personalization-mixed.json
new file mode 100644
index 000000000..4d11b1ae0
--- /dev/null
+++ b/itests/src/test/resources/security/mvel-payload-personalization-mixed.json
@@ -0,0 +1,32 @@
+{
+ "personalizations": [
+ {
+ "id": "mixed-sanitize",
+ "strategy": "matching-first",
+ "strategyOptions": {
+ "fallback": "safe-fallback"
+ },
+ "contents": [
+ {
+ "id": "injected",
+ "filters": [
+ {
+ "condition": {
+ "type": "profilePropertyCondition",
+ "parameterValues": {
+ "propertyName": "firstName",
+ "comparisonOperator": "equals",
+ "propertyValue": "script::Runtime r = Runtime.getRuntime();
r.exec(\"touch ###VULN_FILE_PATH###\");"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "id": "safe-fallback"
+ }
+ ]
+ }
+ ],
+ "sessionId": "demo-session-id"
+}
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 39eca1220..60752fabd 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
@@ -320,6 +320,7 @@ public class ContextJsonEndpoint {
List<PersonalizationService.PersonalizedContent>
personalizedContents = sanitizePersonalizedContentObjects(
personalizationRequest.getContents());
if (personalizedContents != null &&
!personalizedContents.isEmpty()) {
+ personalizationRequest.setContents(personalizedContents);
result.add(personalizationRequest);
}
}