[JENKINS-61808] Always transmit f:password values as Secret (#4630)

Co-authored-by: Daniel Beck <daniel-beck@users.noreply.github.com>
This commit is contained in:
Daniel BeckandDaniel Beck authored and GitHub committed 2020-05-10 11:17:51 +02:00
1 parent 1537c6e57f
commit 8a9a1b2f7b
12 files changed
+608 -14

No files matched your search

+52 -6
View File
@@ -166,6 +166,7 @@ import hudson.util.RunList;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import org.apache.commons.io.IOUtils;
@@ -2013,21 +2014,66 @@ public class Functions {
* Used by {@code <f:password/>} so that we send an encrypted value to the client.
*/
public String getPasswordValue(Object o) {
if (o==null) return null;
if (o instanceof Secret) {
StaplerRequest req = Stapler.getCurrentRequest();
if (o == null) {
return null;
}
/*
Return plain value if it's the default value for PasswordParameterDefinition.
This needs to work even when the user doesn't have CONFIGURE permission
*/
if (o.equals(PasswordParameterDefinition.DEFAULT_VALUE)) {
return o.toString();
}
/* Mask from Extended Read */
StaplerRequest req = Stapler.getCurrentRequest();
if (o instanceof Secret || Secret.BLANK_NONSECRET_PASSWORD_FIELDS_WITHOUT_ITEM_CONFIGURE) {
if (req != null) {
Item item = req.findAncestorObject(Item.class);
if (item != null && !item.hasPermission(Item.CONFIGURE)) {
return "********";
}
}
}
/* Return encrypted value if it's a Secret */
if (o instanceof Secret) {
return ((Secret) o).getEncryptedValue();
}
if (getIsUnitTest() && !o.equals(PasswordParameterDefinition.DEFAULT_VALUE)) {
throw new SecurityException("attempted to render plaintext ‘" + o + "’ in password field; use a getter of type Secret instead");
/* Log a warning if we're in development mode (core or plugin): There's an f:password backed by a non-Secret */
if (req != null && (Boolean.getBoolean("hudson.hpi.run") || Boolean.getBoolean("hudson.Main.development"))) {
LOGGER.log(Level.WARNING, () -> "<f:password/> form control in " + getJellyViewsInformationForCurrentRequest() +
" is not backed by hudson.util.Secret. Learn more: https://jenkins.io/redirect/hudson.util.Secret");
}
return o.toString();
/* Return plain value if it's not a Secret and the escape hatch is set */
if (!Secret.AUTO_ENCRYPT_PASSWORD_CONTROL) {
return o.toString();
}
/* Make it a Secret and return its encrypted value */
return Secret.fromString(o.toString()).getEncryptedValue();
}
private String getJellyViewsInformationForCurrentRequest() {
final Thread thread = Thread.currentThread();
String threadName = thread.getName();
// try to simplify based on org.kohsuke.stapler.jelly.JellyViewScript
// Views are expected to contain a slash and a period, neither as the first char, and the last slash before the first period: Class/view.jelly
// Nested classes use slashes, so we do not expect period before: Class/Nested/view.jelly
String views = Arrays.stream(threadName.split(" ")).filter(part -> {
int slash = part.lastIndexOf("/");
int firstPeriod = part.indexOf(".");
return slash > 0 && firstPeriod > 0 && slash < firstPeriod;
}).collect(Collectors.joining(" "));
if (StringUtils.isBlank(views)) {
// fallback to full thread name if there are no apparent views
return threadName;
}
return views;
}
public List filterDescriptors(Object context, Iterable descriptors) {