Skip to content

Commit 2e35f6d

Browse files
committed
MLE-24375 Fixing Polaris warnings, including buildUri change
This is a significant change to buildUri, but should be a necessary change to use a modern API for building a URI - UriComponentsBuilder - that guards against the warnings raised by Polaris.
1 parent 4176f7e commit 2e35f6d

File tree

4 files changed

+65
-19
lines changed

4 files changed

+65
-19
lines changed

docker-compose.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ services:
55
marklogic:
66
image: "${MARKLOGIC_IMAGE}"
77
platform: linux/amd64
8+
# The NET_RAW capability allows a process to create raw sockets. Polaris does not like that.
9+
# This setting removes the NET_RAW capability from the container.
10+
cap_drop:
11+
- NET_RAW
812
environment:
913
- MARKLOGIC_INIT=true
1014
- MARKLOGIC_ADMIN_USERNAME=admin

ml-app-deployer/src/main/java/com/marklogic/appdeployer/command/AbstractCommand.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@ public abstract class AbstractCommand extends LoggingObject implements Command {
4242
private int executeSortOrder = Integer.MAX_VALUE;
4343
private boolean storeResourceIdsAsCustomTokens = false;
4444

45+
// In 6.1.0, changing this from FilenameFilter to ResourceFilenameFilter based on Polaris warnings on
46+
// FilenameFilter. And all uses of this in the codebase involve ResourceFilenameFilter.
47+
private ResourceFilenameFilter resourceFilenameFilter = new ResourceFilenameFilter();
48+
4549
protected PayloadTokenReplacer payloadTokenReplacer = new DefaultPayloadTokenReplacer();
46-
private FilenameFilter resourceFilenameFilter = new ResourceFilenameFilter();
4750
private PayloadParser payloadParser = new PayloadParser();
4851

4952
private Class<? extends Resource> resourceClassType;
@@ -564,7 +567,11 @@ public void setStoreResourceIdsAsCustomTokens(boolean storeResourceIdsAsCustomTo
564567
}
565568

566569
public void setResourceFilenameFilter(FilenameFilter resourceFilenameFilter) {
567-
this.resourceFilenameFilter = resourceFilenameFilter;
570+
if (!(resourceFilenameFilter instanceof ResourceFilenameFilter)) {
571+
throw new IllegalArgumentException("resourceFilenameFilter must be an instanceof " +
572+
ResourceFilenameFilter.class.getName());
573+
}
574+
this.resourceFilenameFilter = (ResourceFilenameFilter) resourceFilenameFilter;
568575
}
569576

570577
public FilenameFilter getResourceFilenameFilter() {

ml-app-deployer/src/main/java/com/marklogic/appdeployer/command/viewschemas/DeployViewSchemasCommand.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,11 @@ protected void afterResourceSaved(ResourceManager mgr, CommandContext context, R
7878
File resourceFile = reference.getLastFile();
7979
if (resourceFile != null) {
8080
PayloadParser parser = new PayloadParser();
81-
String viewSchemaName = parser.getPayloadFieldValue(receipt.getPayload(), "view-schema-name");
81+
final String viewSchemaName = parser.getPayloadFieldValue(receipt.getPayload(), "view-schema-name");
8282
File parentFile = resourceFile.getParentFile();
8383
if (parentFile != null) {
84+
// Polaris flags this as a warning because viewSchemaName is user-provided - but we know it's been
85+
// validated by MarkLogic already.
8486
File viewDir = new File(parentFile, viewSchemaName + "-views");
8587
if (viewDir.exists()) {
8688
ViewManager viewMgr = new ViewManager(context.getManageClient(), currentDatabaseIdOrName, viewSchemaName);

ml-app-deployer/src/main/java/com/marklogic/rest/util/RestConfig.java

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
import com.marklogic.client.ext.ssl.SslUtil;
1212
import okhttp3.OkHttpClient;
1313
import org.springframework.util.StringUtils;
14+
import org.springframework.web.util.UriComponentsBuilder;
1415

1516
import javax.net.ssl.SSLContext;
1617
import java.net.URI;
17-
import java.net.URISyntaxException;
1818

1919
public class RestConfig {
2020

@@ -158,28 +158,61 @@ public String toString() {
158158
}
159159

160160
/**
161-
* Using the java.net.URI constructor that takes a string. Using any other constructor runs into encoding problems,
162-
* e.g. when a mimetype has a plus in it, that plus needs to be encoded, but doing as %2B will result in the % being
163-
* double encoded. Unfortunately, it seems some encoding is still needed - e.g. for a pipeline like "Flexible Replication"
164-
* with a space in its name, the space must be encoded properly as a "+".
161+
* Builds a URI using Spring's UriComponentsBuilder to prevent URL manipulation attacks.
162+
* This replaces the previous vulnerable implementation that allowed user-controllable URL construction.
163+
* Handles query parameters that may be included in the path for backwards compatibility.
165164
*
166165
* @param path
167166
* @return
168167
*/
169168
public URI buildUri(String path) {
170-
String basePathToAppend = "";
171-
if (basePath != null) {
172-
if (!basePath.startsWith("/")) {
173-
basePathToAppend = "/";
169+
try {
170+
UriComponentsBuilder builder = UriComponentsBuilder.newInstance()
171+
.scheme(getScheme())
172+
.host(getHost())
173+
.port(getPort());
174+
175+
// Handle base path
176+
String fullPath = path;
177+
if (basePath != null) {
178+
String normalizedBasePath = basePath.startsWith("/") ? basePath : "/" + basePath;
179+
if (path.startsWith("/") && normalizedBasePath.endsWith("/")) {
180+
normalizedBasePath = normalizedBasePath.substring(0, normalizedBasePath.length() - 1);
181+
}
182+
fullPath = normalizedBasePath + path;
174183
}
175-
basePathToAppend += basePath;
176-
if (path.startsWith("/") && basePathToAppend.endsWith("/")) {
177-
basePathToAppend = basePathToAppend.substring(0, basePathToAppend.length() - 1);
184+
185+
// Check if the path contains query parameters
186+
int queryIndex = fullPath.indexOf('?');
187+
if (queryIndex != -1) {
188+
// Split path and query parameters
189+
String pathPart = fullPath.substring(0, queryIndex);
190+
String queryPart = fullPath.substring(queryIndex + 1);
191+
192+
builder.path(pathPart);
193+
194+
// Parse and add query parameters
195+
if (!queryPart.isEmpty()) {
196+
String[] params = queryPart.split("&");
197+
for (String param : params) {
198+
int equalIndex = param.indexOf('=');
199+
if (equalIndex != -1) {
200+
String key = param.substring(0, equalIndex);
201+
String value = param.substring(equalIndex + 1);
202+
builder.queryParam(key, value);
203+
} else {
204+
// Handle parameters without values
205+
builder.queryParam(param, "");
206+
}
207+
}
208+
}
209+
} else {
210+
// No query parameters, just set the path
211+
builder.path(fullPath);
178212
}
179-
}
180-
try {
181-
return new URI(String.format("%s://%s:%d%s%s", getScheme(), getHost(), getPort(), basePathToAppend, path.replace(" ", "+")));
182-
} catch (URISyntaxException ex) {
213+
214+
return builder.build().toUri();
215+
} catch (Exception ex) {
183216
throw new RuntimeException("Unable to build URI for path: " + path + "; cause: " + ex.getMessage(), ex);
184217
}
185218
}

0 commit comments

Comments
 (0)