-
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
[JENKINS-59849] Don't fail to serve resource files with nontrivial names #4302
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ | |
| */ | ||
| package jenkins.security; | ||
|
|
||
| import com.google.common.annotations.VisibleForTesting; | ||
| import hudson.Extension; | ||
| import hudson.ExtensionList; | ||
| import hudson.Util; | ||
|
|
@@ -51,6 +52,7 @@ | |
| import java.util.Base64; | ||
| import java.util.logging.Level; | ||
| import java.util.logging.Logger; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| import static java.time.Instant.*; | ||
| import static java.time.temporal.ChronoUnit.MINUTES; | ||
|
|
@@ -147,7 +149,7 @@ public String getRedirectUrl(@Nonnull Token token, @Nonnull String restOfPath) { | |
| // Unsure whether this can happen -- just be safe here | ||
| restOfPath = "/" + restOfPath; | ||
| } | ||
| return resourceRootUrl + getUrlName() + "/" + token.encode() + restOfPath; | ||
| return resourceRootUrl + getUrlName() + "/" + token.encode() + Arrays.stream(restOfPath.split("[/]")).map(Util::rawEncode).collect(Collectors.joining("/")); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is going to drop a trailing
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a test that hopefully demonstrates that this does not matter. Redirect URLs are only ever obtained for files, and directory browsing on the resource domain continues to work. |
||
| } | ||
|
|
||
| private static String getResourceRootUrl() { | ||
|
|
@@ -165,7 +167,7 @@ private static String getResourceRootUrl() { | |
| @CheckForNull | ||
| public Token getToken(@Nonnull DirectoryBrowserSupport dbs, @Nonnull StaplerRequest req) { | ||
| // This is the "restOfPath" of the DirectoryBrowserSupport, i.e. the directory/file/pattern "inside" the DBS. | ||
| final String dbsFile = req.getRestOfPath(); | ||
| final String dbsFile = req.getOriginalRestOfPath(); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the most important part of the fix, otherwise the math below will be off: We subtracted the length of |
||
|
|
||
| // Now get the 'restOfUrl' after the top-level ancestor (which is the Jenkins singleton). | ||
| // In other words, this is the complete URL after Jenkins handled the top-level request. | ||
|
|
@@ -222,7 +224,8 @@ public void doDynamic(StaplerRequest req, StaplerResponse rsp) throws IOExceptio | |
|
|
||
| try (ACLContext ignored = ACL.as(auth)) { | ||
| try { | ||
| Stapler.getCurrent().invoke(req, rsp, Jenkins.get(), requestUrlSuffix + restOfPath); | ||
| String path = requestUrlSuffix + Arrays.stream(restOfPath.split("[/]")).map(Util::rawEncode).collect(Collectors.joining("/")); | ||
|
daniel-beck marked this conversation as resolved.
|
||
| Stapler.getCurrent().invoke(req, rsp, Jenkins.get(), path); | ||
| } catch (Exception ex) { | ||
| // cf. UnwrapSecurityExceptionFilter | ||
| Throwable cause = ex.getCause(); | ||
|
|
@@ -263,7 +266,9 @@ public static class Token { | |
| private String path; | ||
| private String username; | ||
| private Instant timestamp; | ||
| private Token (String path, @Nullable String username, Instant timestamp) { | ||
|
|
||
| @VisibleForTesting | ||
| Token (String path, @Nullable String username, Instant timestamp) { | ||
| this.path = path; | ||
| this.username = Util.fixNull(username); | ||
| this.timestamp = timestamp; | ||
|
|
@@ -277,8 +282,8 @@ private String encode() { | |
| } | ||
|
|
||
| private static Token decode(String value) { | ||
| byte[] byteValue = Base64.getUrlDecoder().decode(value); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not strictly related, but @jsoref could make this throw an exception, so just move it down. |
||
| try { | ||
| byte[] byteValue = Base64.getUrlDecoder().decode(value); | ||
| byte[] mac = Arrays.copyOf(byteValue, 32); | ||
| byte[] restBytes = Arrays.copyOfRange(byteValue, 32, byteValue.length); | ||
| String rest = new String(restBytes, StandardCharsets.UTF_8); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.