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 @@ -4,12 +4,15 @@
import org.apache.commons.lang3.BooleanUtils;
import org.labkey.api.module.ModuleLoader;
import org.labkey.api.settings.AdminConsole.OptionalFeatureFlag;
import org.labkey.api.util.DOM;
import org.labkey.api.util.StartupListener;

import java.util.Comparator;
import java.util.Map;

import static org.labkey.api.settings.AppProps.SCOPE_OPTIONAL_FEATURE;
import static org.labkey.api.util.DOM.SPAN;
import static org.labkey.api.util.DOM.STRONG;

public class OptionalFeatureStartupListener implements StartupListener
{
Expand Down Expand Up @@ -38,6 +41,12 @@ public OptionalFeatureStartupPropertyHandler()
);
}

@Override
public DOM.Renderable getScopeDescription()
{
return SPAN(STRONG(getScope()), " - set these properties to true/false to enable/disable the corresponding feature flag");
}

@Override
public void handle(Map<OptionalFeatureFlag, StartupPropertyEntry> properties)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
public class ContentSecurityPolicyFilter implements Filter
{
public static final String FEATURE_FLAG_DISABLE_ENFORCE_CSP = "disableEnforceCsp";
public static final String FEATURE_FLAG_FORWARD_CSP_REPORTS = "forwardCspReports";

private static final String NONCE_SUBST = "REQUEST.SCRIPT.NONCE";
private static final String REPORT_PARAMETER_SUBSTITUTION = "CSP.REPORT.PARAMS";
Expand Down
76 changes: 66 additions & 10 deletions core/src/org/labkey/core/admin/AdminController.java
Original file line number Diff line number Diff line change
Expand Up @@ -361,11 +361,15 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand Down Expand Up @@ -11909,6 +11913,13 @@ public void addNavTrail(NavTree root)
}
}

private static final URI LABKEY_ORG_REPORT_ACTION;

static
{
LABKEY_ORG_REPORT_ACTION = URI.create("https://www.labkey.org/admin-contentSecurityPolicyReport.api");
}

@RequiresNoPermission
@CSRF(CSRF.Method.NONE)
public static class ContentSecurityPolicyReportAction extends ReadOnlyApiAction<SimpleApiJsonForm>
Expand Down Expand Up @@ -11955,19 +11966,64 @@ public Object execute(SimpleApiJsonForm form, BindException errors) throws Excep
String urlString = cspReport.optString("document-uri", null);
if (urlString != null)
{
String path = new URLHelper(urlString).deleteParameters().getPath();
String path = new URLHelper(urlString).deleteParameters().getURIString();
if (null == reports.put(path, Boolean.TRUE) || _log.isDebugEnabled())
{
if (isNotBlank(userAgent))
jsonObj.put("user-agent", userAgent);
String labkeyVersion = request.getParameter("labkeyVersion");
if (null != labkeyVersion)
jsonObj.put("labkeyVersion", labkeyVersion);
String cspVersion = request.getParameter("cspVersion");
if (null != cspVersion)
jsonObj.put("cspVersion", cspVersion);
// Don't modify forwarded reports; they already have user, ip, user-agent, etc. from the forwarding server.
boolean forwarded = jsonObj.optBoolean("forwarded", false);
if (!forwarded)
{
jsonObj.put("user", getUser().getEmail());
String ipAddress = request.getHeader("X-FORWARDED-FOR");
if (ipAddress == null)
ipAddress = request.getRemoteAddr();
jsonObj.put("ip", ipAddress);
if (isNotBlank(userAgent))
jsonObj.put("user-agent", userAgent);
String labkeyVersion = request.getParameter("labkeyVersion");
if (null != labkeyVersion)
jsonObj.put("labkeyVersion", labkeyVersion);
String cspVersion = request.getParameter("cspVersion");
if (null != cspVersion)
jsonObj.put("cspVersion", cspVersion);
}

var jsonStr = jsonObj.toString(2);
_log.warn("ContentSecurityPolicy warning on page: " + urlString + "\n" + jsonStr);
_log.warn("ContentSecurityPolicy warning on page: {}\n{}", urlString, jsonStr);

if (!forwarded && OptionalFeatureService.get().isFeatureEnabled(ContentSecurityPolicyFilter.FEATURE_FLAG_FORWARD_CSP_REPORTS))
{
jsonObj.put("forwarded", true);

// Create an HttpClient
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();

// Create the POST request
HttpRequest remoteRequest = HttpRequest.newBuilder()
.uri(LABKEY_ORG_REPORT_ACTION)
.header("Content-Type", request.getContentType()) // Use whatever the browser set
.POST(HttpRequest.BodyPublishers.ofString(jsonObj.toString(2)))
.build();

// Send the request and get the response
HttpResponse<String> response = client.send(remoteRequest, HttpResponse.BodyHandlers.ofString());

if (response.statusCode() != 200)
{
_log.error("ContentSecurityPolicy report forwarding to https://www.labkey.org failed: {}\n{}", response.statusCode(), response.body());
}
else
{
JSONObject jsonResponse = new JSONObject(response.body());
boolean success = jsonResponse.optBoolean("success", false);
if (!success)
{
_log.error("ContentSecurityPolicy report forwarding to https://www.labkey.org failed: {}", jsonResponse);
}
}
}
}
}
}
Expand Down