aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorMatteo Mara <matteo.mara@sonarsource.com>2022-09-26 14:01:18 +0200
committersonartech <sonartech@sonarsource.com>2022-09-26 20:03:17 +0000
commitba965061cb213d8897a8575c95d2ad944671d2eb (patch)
tree3bb7ffc8ceadb742d1f81209559950a87599930e /server
parent45a14f04afcba9b22944a5e4432dae7463c46485 (diff)
downloadsonarqube-ba965061cb213d8897a8575c95d2ad944671d2eb.tar.gz
sonarqube-ba965061cb213d8897a8575c95d2ad944671d2eb.zip
SONAR-17296 sanitize SAML response fields
Diffstat (limited to 'server')
-rw-r--r--server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlAuthStatusPageGenerator.java27
-rw-r--r--server/sonar-auth-saml/src/main/resources/samlAuthResult.html20
-rw-r--r--server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlAuthStatusPageGeneratorTest.java23
-rw-r--r--server/sonar-auth-saml/src/test/resources/samlAuthResultComplete.html209
-rw-r--r--server/sonar-auth-saml/src/test/resources/samlAuthResultEmpty.html20
5 files changed, 29 insertions, 270 deletions
diff --git a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlAuthStatusPageGenerator.java b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlAuthStatusPageGenerator.java
index 82a3a8ae035..c6093ebd0a5 100644
--- a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlAuthStatusPageGenerator.java
+++ b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlAuthStatusPageGenerator.java
@@ -23,20 +23,14 @@ import com.google.common.io.Resources;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
-import java.util.List;
+import java.util.Base64;
import java.util.Map;
-import java.util.stream.Collectors;
-import javax.annotation.Nullable;
import org.json.JSONObject;
public final class SamlAuthStatusPageGenerator {
private static final String WEB_CONTEXT = "%WEB_CONTEXT%";
- private static final String STATUS = "%STATUS%";
- private static final String ERRORS = "%ERRORS%";
- private static final String WARNINGS = "%WARNINGS%";
- private static final String AVAILABLE_ATTRIBUTES = "%AVAILABLE_ATTRIBUTES%";
- private static final String ATTRIBUTE_MAPPINGS = "%ATTRIBUTE_MAPPINGS%";
+ private static final String SAML_AUTHENTICATION_STATUS = "%SAML_AUTHENTICATION_STATUS%";
private static final String HTML_TEMPLATE_NAME = "samlAuthResult.html";
@@ -57,21 +51,12 @@ public final class SamlAuthStatusPageGenerator {
private static Map<String, String> getSubstitutionsMap(SamlAuthenticationStatus samlAuthenticationStatus) {
return Map.of(
WEB_CONTEXT, "",
- STATUS, toJsString(samlAuthenticationStatus.getStatus()),
- ERRORS, toJsArrayFromList(samlAuthenticationStatus.getErrors()),
- WARNINGS, toJsArrayFromList(samlAuthenticationStatus.getWarnings()),
- AVAILABLE_ATTRIBUTES, new JSONObject(samlAuthenticationStatus.getAvailableAttributes()).toString(),
- ATTRIBUTE_MAPPINGS, new JSONObject(samlAuthenticationStatus.getMappedAttributes()).toString());
+ SAML_AUTHENTICATION_STATUS, getBase64EncodedStatus(samlAuthenticationStatus));
}
- private static String toJsString(@Nullable String inputString) {
- return String.format("'%s'", inputString != null ? inputString.replace("'", "\\'") : "");
- }
-
- private static String toJsArrayFromList(List<String> inputArray) {
- return "[" + inputArray.stream()
- .map(SamlAuthStatusPageGenerator::toJsString)
- .collect(Collectors.joining(",")) + "]";
+ private static String getBase64EncodedStatus(SamlAuthenticationStatus samlAuthenticationStatus) {
+ byte[] bytes = new JSONObject(samlAuthenticationStatus).toString().getBytes(StandardCharsets.UTF_8);
+ return String.format("'%s'", Base64.getEncoder().encodeToString(bytes));
}
private static String getPlainTemplate() {
diff --git a/server/sonar-auth-saml/src/main/resources/samlAuthResult.html b/server/sonar-auth-saml/src/main/resources/samlAuthResult.html
index 398266b2d29..85e987b33ca 100644
--- a/server/sonar-auth-saml/src/main/resources/samlAuthResult.html
+++ b/server/sonar-auth-saml/src/main/resources/samlAuthResult.html
@@ -124,7 +124,7 @@
function createSectionTitle(title) {
const node = document.createElement("h2");
- node.innerText = title;
+ node.textContent = title;
return node;
}
@@ -134,7 +134,7 @@
arr.forEach((item) => {
const message = document.createElement("li");
message.className = className;
- message.innerText = item;
+ message.textContent = item;
list.appendChild(message);
});
@@ -150,7 +150,7 @@
const row = document.createElement("tr");
const keyNode = document.createElement("td");
- keyNode.innerText = key;
+ keyNode.textContent = key;
row.appendChild(keyNode);
const valueNode = document.createElement("td");
@@ -173,16 +173,18 @@
container.appendChild(box);
}
- const status = %STATUS%;
- const attributes = %AVAILABLE_ATTRIBUTES%;
- const mappings = %ATTRIBUTE_MAPPINGS%;
- const errors = %ERRORS%;
- const warnings = %WARNINGS%;
+ const response = %SAML_AUTHENTICATION_STATUS%;
+ const decodedStatus = JSON.parse(atob(response));
+ const status = decodedStatus.status;
+ const attributes = decodedStatus.availableAttributes;
+ const mappings = decodedStatus.mappedAttributes;
+ const errors = decodedStatus.errors;
+ const warnings = decodedStatus.warnings;
// Switch status class
const statusNode = document.querySelector("#status");
statusNode.classList.add(status);
- statusNode.innerText = status;
+ statusNode.textContent = status;
// generate content
const container = document.querySelector("#content");
diff --git a/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlAuthStatusPageGeneratorTest.java b/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlAuthStatusPageGeneratorTest.java
index 966d61d440f..7e5e43953c2 100644
--- a/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlAuthStatusPageGeneratorTest.java
+++ b/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlAuthStatusPageGeneratorTest.java
@@ -25,8 +25,6 @@ import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
@@ -35,30 +33,11 @@ import static org.mockito.Mockito.when;
import static org.sonar.auth.saml.SamlAuthStatusPageGenerator.getSamlAuthStatusHtml;
public class SamlAuthStatusPageGeneratorTest {
-
- private final SamlAuthenticationStatus samlAuthenticationStatus = mock(SamlAuthenticationStatus.class);
-
- private static final String HTML_TEMPLATE_NAME = "samlAuthResultComplete.html";
private static final String EMPTY_HTML_TEMPLATE_NAME = "samlAuthResultEmpty.html";
@Test
- public void test_full_html_generation() {
-
- when(samlAuthenticationStatus.getStatus()).thenReturn("success");
- when(samlAuthenticationStatus.getErrors()).thenReturn(List.of("error1", "error2 'with message'"));
- when(samlAuthenticationStatus.getWarnings()).thenReturn(List.of("warning1", "warning2 'with message'"));
- when(samlAuthenticationStatus.getAvailableAttributes()).thenReturn(Map.of("key1", List.of("value1", "value2 with weird chars \n\t\"\\"), "key2", List.of("value3", "value4")));
- when(samlAuthenticationStatus.getMappedAttributes()).thenReturn(Map.of("key1", List.of("value1", "value2"), "key2", List.of("value3", "value4")));
-
- String completeHtmlTemplate = getSamlAuthStatusHtml(samlAuthenticationStatus);
- String expectedTemplate = loadTemplateFromResources(HTML_TEMPLATE_NAME);
-
- assertEquals(expectedTemplate, completeHtmlTemplate);
-
- }
-
- @Test
public void test_full_html_generation_with_empty_values() {
+ SamlAuthenticationStatus samlAuthenticationStatus = mock(SamlAuthenticationStatus.class);
when(samlAuthenticationStatus.getStatus()).thenReturn(null);
when(samlAuthenticationStatus.getErrors()).thenReturn(new ArrayList<>());
diff --git a/server/sonar-auth-saml/src/test/resources/samlAuthResultComplete.html b/server/sonar-auth-saml/src/test/resources/samlAuthResultComplete.html
deleted file mode 100644
index f12552ec361..00000000000
--- a/server/sonar-auth-saml/src/test/resources/samlAuthResultComplete.html
+++ /dev/null
@@ -1,209 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
- <head>
- <meta http-equiv="content-type" content="text/html; charset=UTF-8" charset="UTF-8" />
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
- <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
- <link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png" />
- <link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png" />
- <link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png" />
- <link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png" />
- <link
- rel="apple-touch-icon"
- sizes="114x114"
- href="/apple-touch-icon-114x114.png"
- />
- <link
- rel="apple-touch-icon"
- sizes="120x120"
- href="/apple-touch-icon-120x120.png"
- />
- <link
- rel="apple-touch-icon"
- sizes="144x144"
- href="/apple-touch-icon-144x144.png"
- />
- <link
- rel="apple-touch-icon"
- sizes="152x152"
- href="/apple-touch-icon-152x152.png"
- />
- <link
- rel="apple-touch-icon"
- sizes="180x180"
- href="/apple-touch-icon-180x180.png"
- />
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
- <meta name="application-name" content="SonarQube" />
- <meta name="msapplication-TileColor" content="#FFFFFF" />
- <meta name="msapplication-TileImage" content="/mstile-512x512.png" />
- <title>SAML Authentication Test</title>
-
- <style>
- body {
- background-color: #f3f3f3;
- }
-
- h1 {
- margin: 0 8px 8px;
- }
- h2 {
- margin: 0 0 8px;
- }
-
- ul {
- list-style: none;
- margin: 0 8px;
- padding: 0;
- }
-
- li + li {
- padding-top: 12px;
- margin-top: 12px;
- border-top: 1px solid rgba(150, 150, 150, 0.5);
- }
-
- table {
- border-collapse: collapse;
- }
-
- tr:nth-child(2n) {
- background-color: #e6e6e6;
- }
-
- td {
- border: 1px solid #a3a3a3;
- padding: 4px 24px 4px 8px;
- vertical-align: top;
- font-family: "Courier New", Courier, monospace;
- white-space: pre-line;
- }
-
- #content {
- padding: 16px;
- }
-
- .box {
- padding: 8px;
- margin: 8px;
- border: 1px solid #e6e6e6;
- background-color: white;
- box-shadow: 2px 2px 3px 0px rgba(0, 0, 0, 0.5);
- }
-
- #status {
- padding: 16px 8px;
- font-size: large;
- color: white;
- }
-
- .error {
- background-color: #d02f3a;
- }
-
- .success {
- background-color: #008a25;
- }
- </style>
- </head>
-
- <body>
- <div id="content">
- <h1>SAML Authentication Test</h1>
- <div class="box">
- <div id="status"></div>
- </div>
- </div>
-
- <script>
- function createBox() {
- const box = document.createElement("div");
- box.className = "box";
- return box;
- }
-
- function createSectionTitle(title) {
- const node = document.createElement("h2");
- node.innerText = title;
- return node;
- }
-
- function createList(arr, className = "") {
- const list = document.createElement("ul");
-
- arr.forEach((item) => {
- const message = document.createElement("li");
- message.className = className;
- message.innerText = item;
- list.appendChild(message);
- });
-
- return list;
- }
-
- function createTable(obj) {
- const table = document.createElement("table");
- const tbody = document.createElement("tbody");
- table.appendChild(tbody);
-
- Object.keys(obj).forEach((key) => {
- const row = document.createElement("tr");
-
- const keyNode = document.createElement("td");
- keyNode.innerText = key;
- row.appendChild(keyNode);
-
- const valueNode = document.createElement("td");
- // wrap in array, to handle single values as well
- valueNode.textContent = [].concat(obj[key]).join("\r\n");
- row.appendChild(valueNode);
-
- tbody.appendChild(row);
- });
-
- return table;
- }
-
- function addSection(container, title, contents) {
- const box = createBox();
-
- box.appendChild(createSectionTitle(title));
- box.appendChild(contents);
-
- container.appendChild(box);
- }
-
- const status = 'success';
- const attributes = {"key1":["value1","value2 with weird chars \n\t\"\\"],"key2":["value3","value4"]};
- const mappings = {"key1":["value1","value2"],"key2":["value3","value4"]};
- const errors = ['error1','error2 \'with message\''];
- const warnings = ['warning1','warning2 \'with message\''];
-
- // Switch status class
- const statusNode = document.querySelector("#status");
- statusNode.classList.add(status);
- statusNode.innerText = status;
-
- // generate content
- const container = document.querySelector("#content");
-
- if (warnings && warnings.length > 0) {
- addSection(container, "Warnings", createList(warnings));
- }
-
- if (status === "error" && errors && errors.length > 0) {
- addSection(container, "Errors", createList(errors));
- }
-
- if (status === "success") {
- if (attributes && Object.keys(attributes).length > 0) {
- addSection(container, "Available attributes", createTable(attributes));
- }
-
- if (mappings && Object.keys(mappings).length > 0) {
- addSection(container, "Attribute mappings", createTable(mappings));
- }
- }
- </script>
- </body>
-</html>
diff --git a/server/sonar-auth-saml/src/test/resources/samlAuthResultEmpty.html b/server/sonar-auth-saml/src/test/resources/samlAuthResultEmpty.html
index 6ae670910bd..de4879d8ce3 100644
--- a/server/sonar-auth-saml/src/test/resources/samlAuthResultEmpty.html
+++ b/server/sonar-auth-saml/src/test/resources/samlAuthResultEmpty.html
@@ -124,7 +124,7 @@
function createSectionTitle(title) {
const node = document.createElement("h2");
- node.innerText = title;
+ node.textContent = title;
return node;
}
@@ -134,7 +134,7 @@
arr.forEach((item) => {
const message = document.createElement("li");
message.className = className;
- message.innerText = item;
+ message.textContent = item;
list.appendChild(message);
});
@@ -150,7 +150,7 @@
const row = document.createElement("tr");
const keyNode = document.createElement("td");
- keyNode.innerText = key;
+ keyNode.textContent = key;
row.appendChild(keyNode);
const valueNode = document.createElement("td");
@@ -173,16 +173,18 @@
container.appendChild(box);
}
- const status = '';
- const attributes = {};
- const mappings = {};
- const errors = [];
- const warnings = [];
+ const response = 'eyJ3YXJuaW5ncyI6W10sImF2YWlsYWJsZUF0dHJpYnV0ZXMiOnt9LCJlcnJvcnMiOltdLCJtYXBwZWRBdHRyaWJ1dGVzIjp7fX0=';
+ const decodedStatus = JSON.parse(atob(response));
+ const status = decodedStatus.status;
+ const attributes = decodedStatus.availableAttributes;
+ const mappings = decodedStatus.mappedAttributes;
+ const errors = decodedStatus.errors;
+ const warnings = decodedStatus.warnings;
// Switch status class
const statusNode = document.querySelector("#status");
statusNode.classList.add(status);
- statusNode.innerText = status;
+ statusNode.textContent = status;
// generate content
const container = document.querySelector("#content");