Skip to content

Commit 224ac39

Browse files
gitar-botchirag-madlani
authored andcommitted
fix: resolve checkstyle and CodeQL security issues
- Fix import ordering by moving static imports to the end - Add path traversal validation to prevent security vulnerability - Normalize paths and validate against resource directory to prevent directory traversal attacks - Handle null returns from getPathToCheck for invalid paths Co-authored-by: chirag-madlani <[email protected]>
1 parent a1d69ca commit 224ac39

File tree

2 files changed

+81
-56
lines changed

2 files changed

+81
-56
lines changed

openmetadata-service/src/main/java/org/openmetadata/service/socket/OpenMetadataAssetServlet.java

Lines changed: 78 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,19 @@
1313

1414
package org.openmetadata.service.socket;
1515

16-
import java.net.URL;
17-
import jakarta.servlet.ServletRequest;
18-
import jakarta.servlet.ServletResponse;
19-
import jakarta.servlet.http.HttpServletRequestWrapper;
20-
import jakarta.servlet.http.HttpServletResponseWrapper;
21-
2216
import static org.openmetadata.service.exception.OMErrorPageHandler.setSecurityHeader;
2317

2418
import io.dropwizard.servlets.assets.AssetServlet;
2519
import jakarta.servlet.ServletException;
2620
import jakarta.servlet.http.HttpServletRequest;
21+
import jakarta.servlet.http.HttpServletRequestWrapper;
2722
import jakarta.servlet.http.HttpServletResponse;
23+
import jakarta.servlet.http.HttpServletResponseWrapper;
2824
import java.io.IOException;
25+
import java.net.URL;
2926
import java.nio.charset.StandardCharsets;
27+
import java.nio.file.Path;
28+
import java.nio.file.Paths;
3029
import org.jetbrains.annotations.Nullable;
3130
import org.openmetadata.service.config.OMWebConfiguration;
3231
import org.openmetadata.service.resources.system.IndexResource;
@@ -66,27 +65,31 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
6665

6766
// 1. Check for Brotli (br)
6867
if (acceptEncoding != null && acceptEncoding.contains("br")) {
69-
try {
70-
String fullResourcePath = getPathToCheck(req, requestUri, ".br");
71-
URL url = this.getClass().getResource(fullResourcePath);
72-
if (url != null) {
73-
serveCompressed(req, resp, requestUri, "br", "br");
74-
return;
75-
}
76-
} catch (Exception e) {
77-
// Ignore and try next
78-
}
68+
try {
69+
String fullResourcePath = getPathToCheck(req, requestUri, ".br");
70+
if (fullResourcePath != null) {
71+
URL url = this.getClass().getResource(fullResourcePath);
72+
if (url != null) {
73+
serveCompressed(req, resp, requestUri, "br", "br");
74+
return;
75+
}
76+
}
77+
} catch (Exception e) {
78+
// Ignore and try next
79+
}
7980
}
8081

8182
// 2. Check for Gzip
8283
if (acceptEncoding != null && acceptEncoding.contains("gzip")) {
8384
try {
8485
String fullResourcePath = getPathToCheck(req, requestUri, ".gz");
85-
URL url = this.getClass().getResource(fullResourcePath);
86-
87-
if (url != null) {
88-
serveCompressed(req, resp, requestUri, "gzip", "gz");
89-
return;
86+
if (fullResourcePath != null) {
87+
URL url = this.getClass().getResource(fullResourcePath);
88+
89+
if (url != null) {
90+
serveCompressed(req, resp, requestUri, "gzip", "gz");
91+
return;
92+
}
9093
}
9194
} catch (Exception e) {
9295
// Fallback to default behavior
@@ -109,43 +112,67 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
109112
}
110113

111114
private String getPathToCheck(HttpServletRequest req, String requestUri, String extension) {
112-
String pathToCheck = requestUri;
113-
String contextPath = req.getContextPath();
114-
if (contextPath != null && requestUri.startsWith(contextPath)) {
115-
pathToCheck = requestUri.substring(contextPath.length());
116-
}
117-
return this.resourcePath + (pathToCheck.startsWith("/") ? "" : "/") + pathToCheck + extension;
118-
}
115+
String pathToCheck = requestUri;
116+
String contextPath = req.getContextPath();
117+
if (contextPath != null && requestUri.startsWith(contextPath)) {
118+
pathToCheck = requestUri.substring(contextPath.length());
119+
}
119120

120-
private void serveCompressed(HttpServletRequest req, HttpServletResponse resp, String requestUri, String contentEncoding, String extension) throws ServletException, IOException {
121-
resp.setHeader("Content-Encoding", contentEncoding);
122-
String mimeType = req.getServletContext().getMimeType(requestUri);
121+
String fullPath =
122+
this.resourcePath + (pathToCheck.startsWith("/") ? "" : "/") + pathToCheck + extension;
123123

124-
HttpServletRequestWrapper compressedReq = new HttpServletRequestWrapper(req) {
125-
@Override
126-
public String getPathInfo() {
127-
String pathInfo = super.getPathInfo();
128-
return pathInfo != null ? pathInfo + "." + extension : null;
129-
}
124+
// Validate against path traversal attacks
125+
try {
126+
Path normalizedPath = Paths.get(fullPath).normalize();
127+
Path baseResourcePath = Paths.get(this.resourcePath).normalize();
130128

131-
@Override
132-
public String getRequestURI() {
133-
return super.getRequestURI() + "." + extension;
134-
}
135-
};
129+
if (!normalizedPath.startsWith(baseResourcePath)) {
130+
return null;
131+
}
132+
} catch (Exception e) {
133+
return null;
134+
}
135+
136+
return fullPath;
137+
}
136138

137-
HttpServletResponseWrapper compressedResp = new HttpServletResponseWrapper(resp) {
138-
@Override
139-
public void setContentType(String type) {
140-
if (mimeType != null) {
139+
private void serveCompressed(
140+
HttpServletRequest req,
141+
HttpServletResponse resp,
142+
String requestUri,
143+
String contentEncoding,
144+
String extension)
145+
throws ServletException, IOException {
146+
resp.setHeader("Content-Encoding", contentEncoding);
147+
String mimeType = req.getServletContext().getMimeType(requestUri);
148+
149+
HttpServletRequestWrapper compressedReq =
150+
new HttpServletRequestWrapper(req) {
151+
@Override
152+
public String getPathInfo() {
153+
String pathInfo = super.getPathInfo();
154+
return pathInfo != null ? pathInfo + "." + extension : null;
155+
}
156+
157+
@Override
158+
public String getRequestURI() {
159+
return super.getRequestURI() + "." + extension;
160+
}
161+
};
162+
163+
HttpServletResponseWrapper compressedResp =
164+
new HttpServletResponseWrapper(resp) {
165+
@Override
166+
public void setContentType(String type) {
167+
if (mimeType != null) {
141168
super.setContentType(mimeType);
142-
} else {
169+
} else {
143170
super.setContentType(type);
144-
}
145-
}
146-
};
171+
}
172+
}
173+
};
147174

148-
super.doGet(compressedReq, compressedResp);
175+
super.doGet(compressedReq, compressedResp);
149176
}
150177

151178
/**

openmetadata-service/src/test/java/org/openmetadata/service/socket/OpenMetadataAssetServletTest.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
import jakarta.servlet.ServletOutputStream;
99
import jakarta.servlet.http.HttpServletRequest;
1010
import jakarta.servlet.http.HttpServletResponse;
11-
import java.net.URL;
12-
import java.util.Collections;
1311
import org.junit.jupiter.api.BeforeEach;
1412
import org.junit.jupiter.api.Test;
1513
import org.mockito.Mock;
@@ -31,7 +29,7 @@ public void setup() throws Exception {
3129
MockitoAnnotations.openMocks(this);
3230
when(request.getServletContext()).thenReturn(servletContext);
3331
when(response.getOutputStream()).thenReturn(outputStream);
34-
32+
3533
// Initialize servlet with /assets as resource path
3634
servlet = new OpenMetadataAssetServlet("/", "/assets", "/", "index.html", webConfiguration);
3735
}
@@ -122,8 +120,8 @@ public void testServeBrotliAsset() throws Exception {
122120
try {
123121
servlet.doGet(request, response);
124122
} catch (Exception e) {
125-
e.printStackTrace();
126-
throw e;
123+
e.printStackTrace();
124+
throw e;
127125
}
128126

129127
// Verify Content-Encoding is br

0 commit comments

Comments
 (0)