Skip to content

Commit 329c609

Browse files
PierreBtzdaniel-beck
authored andcommitted
[SECURITY-1815]
1 parent 4343314 commit 329c609

3 files changed

Lines changed: 74 additions & 9 deletions

File tree

src/main/java/hudson/plugins/audit_trail/AuditTrailFilter.java

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
import javax.servlet.*;
3535
import javax.servlet.http.HttpServletRequest;
3636
import java.io.IOException;
37+
import java.util.ArrayList;
38+
import java.util.Arrays;
39+
import java.util.List;
3740
import java.util.logging.Level;
3841
import java.util.logging.Logger;
3942
import java.util.regex.Pattern;
@@ -79,14 +82,7 @@ static void setPattern(String pattern) throws PatternSyntaxException {
7982
public void doFilter(ServletRequest request, ServletResponse res, FilterChain chain)
8083
throws IOException, ServletException {
8184
HttpServletRequest req = (HttpServletRequest) request;
82-
String uri;
83-
if (req.getPathInfo() == null) {
84-
// workaround: on some containers such as CloudBees DEV@cloud, req.getPathInfo() is unexpectedly null,
85-
// construct pathInfo based on contextPath and requestUri
86-
uri = req.getRequestURI().substring(req.getContextPath().length());
87-
} else {
88-
uri = req.getPathInfo();
89-
}
85+
String uri = getPathInfo(req);
9086
if (uriPattern != null && uriPattern.matcher(uri).matches()) {
9187
User user = User.current();
9288
String username = user != null ? user.getId() : req.getRemoteAddr();
@@ -151,4 +147,45 @@ private void onRequest(String uri, String extra, String username) {
151147
}
152148
}
153149
}
150+
151+
// See SECURITY-1815
152+
private static String getPathInfo(HttpServletRequest request) {
153+
return canonicalPath(request.getRequestURI().substring(request.getContextPath().length()));
154+
}
155+
156+
// Copied from Stapler#canonicalPath
157+
private static String canonicalPath(String path) {
158+
List<String> r = new ArrayList<>(Arrays.asList(path.split("/+")));
159+
for (int i = 0; i < r.size(); ) {
160+
if (r.get(i).length() == 0 || r.get(i).equals(".")) {
161+
// empty token occurs for example, "".split("/+") is [""]
162+
r.remove(i);
163+
} else if (r.get(i).equals("..")) {
164+
// i==0 means this is a broken URI.
165+
r.remove(i);
166+
if (i > 0) {
167+
r.remove(i - 1);
168+
i--;
169+
}
170+
} else {
171+
i++;
172+
}
173+
}
174+
175+
StringBuilder buf = new StringBuilder();
176+
if (path.startsWith("/")) {
177+
buf.append('/');
178+
}
179+
boolean first = true;
180+
for (String token : r) {
181+
if (!first) buf.append('/');
182+
else first = false;
183+
buf.append(token);
184+
}
185+
// translation: if (path.endsWith("/") && !buf.endsWith("/"))
186+
if (path.endsWith("/") && (buf.length() == 0 || buf.charAt(buf.length() - 1) != '/')) {
187+
buf.append('/');
188+
}
189+
return buf.toString();
190+
}
154191
}

src/test/java/hudson/plugins/audit_trail/AuditTrailFilterTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
import hudson.Util;
77
import hudson.model.Cause;
88
import hudson.model.FreeStyleProject;
9+
import org.apache.http.HttpStatus;
910
import org.junit.Rule;
1011
import org.junit.Test;
1112
import org.junit.rules.TemporaryFolder;
13+
import org.jvnet.hudson.test.Issue;
1214
import org.jvnet.hudson.test.JenkinsRule;
1315

1416
import java.io.File;
@@ -49,4 +51,30 @@ public void cancelItemLogsTheQueryStringAndTheUser() throws Exception {
4951
String log = Util.loadFile(new File(tmpDir.getRoot(), "test.log.0"), StandardCharsets.UTF_8);
5052
assertTrue("logged actions: " + log, Pattern.compile(".*id=1.*job/test-job.*by \\Q127.0.0.1\\E.*", Pattern.DOTALL).matcher(log).matches());
5153
}
54+
55+
@Test
56+
@Issue("SECURITY-1815")
57+
public void requestWithSemiColumnIsProperlyLogged() throws Exception {
58+
String logFileName = "security-1815.log";
59+
File logFile = new File(tmpDir.getRoot(), logFileName);
60+
JenkinsRule.WebClient wc = j.createWebClient();
61+
new SimpleAuditTrailPluginConfiguratorHelper(logFile).sendConfiguration(j, wc);
62+
63+
WebRequest request = new WebRequest(new URL(wc.getContextPath()+ "quietDown/..;/") , HttpMethod.POST);
64+
wc.addCrumb(request);
65+
66+
try {
67+
wc.getPage(request);
68+
} catch (FailingHttpStatusCodeException e) {
69+
if(e.getStatusCode() != HttpStatus.SC_METHOD_NOT_ALLOWED) {
70+
// when the plugin is moved to a Core implementing SECURITY-1815 this request will start returning with
71+
// a 400 error code. Voluntarily rethrowing to have this fail,
72+
// because this failing test will be the time to reconsider removing this specific dev
73+
throw e;
74+
}
75+
// otherwise silently ignore, the endpoint returns a 405
76+
}
77+
String log = Util.loadFile(new File(tmpDir.getRoot(), logFileName + ".0"), StandardCharsets.UTF_8);
78+
assertTrue("logged actions: " + log, log.contains("quietDown"));
79+
}
5280
}

src/test/java/hudson/plugins/audit_trail/SimpleAuditTrailPluginConfiguratorHelper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class SimpleAuditTrailPluginConfiguratorHelper {
2525
private final File logFile;
2626

2727
private boolean logBuildCause =true;
28-
private String pattern = ".*/(?:enable|cancelItem)";
28+
private String pattern = ".*/(?:enable|cancelItem|quietDown)/?.*";
2929

3030
public SimpleAuditTrailPluginConfiguratorHelper(File logFile) {
3131
this.logFile = logFile;

0 commit comments

Comments
 (0)