未验证 提交 9ba18f5d 编写于 作者: T Tim Jacomb 提交者: GitHub

Merge branch 'master' into jep-223

......@@ -23,6 +23,7 @@
*/
package hudson.cli;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.cli.client.Messages;
import java.io.DataInputStream;
import javax.net.ssl.HostnameVerifier;
......@@ -175,6 +176,7 @@ public class CLI {
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
// bypass host name check, too.
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@SuppressFBWarnings(value = "WEAK_HOSTNAME_VERIFIER", justification = "User set parameter to skip verifier.")
public boolean verify(String s, SSLSession sslSession) {
return true;
}
......@@ -188,7 +190,7 @@ public class CLI {
continue;
}
if(head.equals("-i") && args.size()>=2) {
File f = new File(args.get(1));
File f = getFileFromArguments(args);
if (!f.exists()) {
printUsage(Messages.CLI_NoSuchFileExists(f));
return -1;
......@@ -290,7 +292,7 @@ public class CLI {
if (userInfo != null) {
factory = factory.basicAuth(userInfo);
} else if (auth != null) {
factory = factory.basicAuth(auth.startsWith("@") ? FileUtils.readFileToString(new File(auth.substring(1)), Charset.defaultCharset()).trim() : auth);
factory = factory.basicAuth(auth.startsWith("@") ? readAuthFromFile(auth).trim() : auth);
}
if (mode == Mode.HTTP) {
......@@ -304,6 +306,16 @@ public class CLI {
throw new AssertionError();
}
@SuppressFBWarnings(value = {"PATH_TRAVERSAL_IN", "URLCONNECTION_SSRF_FD"}, justification = "User provided values for running the program.")
private static String readAuthFromFile(String auth) throws IOException {
return FileUtils.readFileToString(new File(auth.substring(1)), Charset.defaultCharset());
}
@SuppressFBWarnings(value = {"PATH_TRAVERSAL_IN", "URLCONNECTION_SSRF_FD"}, justification = "User provided values for running the program.")
private static File getFileFromArguments(List<String> args) {
return new File(args.get(1));
}
private static int webSocketConnection(String url, List<String> args, CLIConnectionFactory factory) throws Exception {
LOGGER.fine(() -> "Trying to connect to " + url + " via plain protocol over WebSocket");
class CLIEndpoint extends Endpoint {
......
package hudson.cli;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
......@@ -60,7 +62,7 @@ public class FullDuplexHttpStream {
// server->client
LOGGER.fine("establishing download side");
HttpURLConnection con = (HttpURLConnection) target.openConnection();
HttpURLConnection con = openHttpConnection(target);
con.setDoOutput(true); // request POST to avoid caching
con.setRequestMethod("POST");
con.addRequestProperty("Session", uuid.toString());
......@@ -78,7 +80,7 @@ public class FullDuplexHttpStream {
// client->server uses chunked encoded POST for unlimited capacity.
LOGGER.fine("establishing upload side");
con = (HttpURLConnection) target.openConnection();
con = openHttpConnection(target);
con.setDoOutput(true); // request POST
con.setRequestMethod("POST");
con.setChunkedStreamingMode(0);
......@@ -92,10 +94,15 @@ public class FullDuplexHttpStream {
LOGGER.fine("established upload side");
}
// As this transport mode is using POST, it is necessary to resolve possible redirections using GET first.
@SuppressFBWarnings(value = "URLCONNECTION_SSRF_FD", justification = "Client-side code doesn't involve SSRF.")
private HttpURLConnection openHttpConnection(URL target) throws IOException {
return (HttpURLConnection) target.openConnection();
}
// As this transport mode is using POST, it is necessary to resolve possible redirections using GET first. @SuppressFBWarnings(value = "URLCONNECTION_SSRF_FD", justification = "Client-side code doesn't involve SSRF.")
private URL tryToResolveRedirects(URL base, String authorization) {
try {
HttpURLConnection con = (HttpURLConnection) base.openConnection();
HttpURLConnection con = openHttpConnection(base);
if (authorization != null) {
con.addRequestProperty("Authorization", authorization);
}
......
package hudson.cli;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
......@@ -8,12 +10,15 @@ import java.security.cert.X509Certificate;
* @author Kohsuke Kawaguchi
*/
public class NoCheckTrustManager implements X509TrustManager {
@SuppressFBWarnings(value = "WEAK_TRUST_MANAGER", justification = "User set parameter to skip verifier.")
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@SuppressFBWarnings(value = "WEAK_TRUST_MANAGER", justification = "User set parameter to skip verifier.")
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@SuppressFBWarnings(value = "WEAK_TRUST_MANAGER", justification = "User set parameter to skip verifier.")
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
......
......@@ -61,11 +61,10 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
*/
class SSHCLI {
@SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE", justification = "Due to whatever reason FindBugs reports it fot try-with-resources")
static int sshConnection(String jenkinsUrl, String user, List<String> args, PrivateKeyProvider provider, final boolean strictHostKey) throws IOException {
Logger.getLogger(SecurityUtils.class.getName()).setLevel(Level.WARNING); // suppress: BouncyCastle not registered, using the default JCE provider
URL url = new URL(jenkinsUrl + "login");
URLConnection conn = url.openConnection();
URLConnection conn = openConnection(url);
CLI.verifyJenkinsConnection(conn);
String endpointDescription = conn.getHeaderField("X-SSH-Endpoint");
......@@ -131,6 +130,11 @@ class SSHCLI {
}
}
@SuppressFBWarnings(value = "URLCONNECTION_SSRF_FD", justification = "Client-side code doesn't involve SSRF.")
private static URLConnection openConnection(URL url) throws IOException {
return url.openConnection();
}
private SSHCLI() {}
}
......@@ -431,6 +431,7 @@ public class ClassicPluginStrategy implements PluginStrategy {
return null;
}
@SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "Administrator action installing a plugin, which could do far worse.")
private static File resolve(File base, String relative) {
File rel = new File(relative);
if(rel.isAbsolute())
......
......@@ -23,6 +23,7 @@
*/
package hudson;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Launcher.ProcStarter;
import hudson.model.TaskListener;
import hudson.remoting.Channel;
......@@ -215,6 +216,7 @@ public abstract class Proc {
* @param err
* null to redirect stderr to stdout.
*/
@SuppressFBWarnings(value = "COMMAND_INJECTION", justification = "Command injection is the point of this old, barely used class.")
public LocalProc(String[] cmd,String[] env,InputStream in,OutputStream out,OutputStream err,File workDir) throws IOException {
this( calcName(cmd),
stderr(environment(new ProcessBuilder(cmd),env).directory(workDir), err==null || err== SELFPUMP_OUTPUT),
......
......@@ -589,6 +589,8 @@ public class Util {
/**
* Computes MD5 digest of the given input stream.
*
* This method should only be used for non-security applications where the MD5 weakness is not a problem.
*
* @param source
* The stream will be closed by this method at the end of this method.
* @return
......@@ -598,7 +600,7 @@ public class Util {
@Nonnull
public static String getDigestOf(@Nonnull InputStream source) throws IOException {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
MessageDigest md5 = getMd5();
DigestInputStream in = new DigestInputStream(source, md5);
// Note: IOUtils.copy() buffers the input internally, so there is no
// need to use a BufferedInputStream.
......@@ -618,6 +620,14 @@ public class Util {
*/
}
// TODO JENKINS-60563 remove MD5 from all usages in Jenkins
@SuppressFBWarnings(value = "WEAK_MESSAGE_DIGEST_MD5", justification =
"This method should only be used for non-security applications where the MD5 weakness is not a problem.")
@Deprecated
private static MessageDigest getMd5() throws NoSuchAlgorithmException {
return MessageDigest.getInstance("MD5");
}
@Nonnull
public static String getDigestOf(@Nonnull String text) {
try {
......
......@@ -23,6 +23,7 @@
*/
package hudson.cli;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.remoting.ClassFilter;
import hudson.remoting.ObjectInputStreamEx;
import hudson.remoting.SocketChannelStream;
......@@ -113,6 +114,8 @@ public class Connection {
/**
* Receives an object sent by {@link #writeObject(Object)}
*/
// TODO JENKINS-60562 remove this class
@SuppressFBWarnings(value = "OBJECT_DESERIALIZATION", justification = "Not used. We should just remove it. Class is deprecated.")
public <T> T readObject() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStreamEx(in,
getClass().getClassLoader(), ClassFilter.DEFAULT);
......@@ -194,16 +197,20 @@ public class Connection {
*/
public Connection encryptConnection(SecretKey sessionKey, String algorithm) throws IOException, GeneralSecurityException {
Cipher cout = Cipher.getInstance(algorithm);
cout.init(Cipher.ENCRYPT_MODE, sessionKey, new IvParameterSpec(sessionKey.getEncoded()));
cout.init(Cipher.ENCRYPT_MODE, sessionKey, createIv(sessionKey));
CipherOutputStream o = new CipherOutputStream(out, cout);
Cipher cin = Cipher.getInstance(algorithm);
cin.init(Cipher.DECRYPT_MODE, sessionKey, new IvParameterSpec(sessionKey.getEncoded()));
cin.init(Cipher.DECRYPT_MODE, sessionKey, createIv(sessionKey));
CipherInputStream i = new CipherInputStream(in, cin);
return new Connection(i,o);
}
private IvParameterSpec createIv(SecretKey sessionKey) {
return new IvParameterSpec(sessionKey.getEncoded());
}
/**
* Given a byte array that contains arbitrary number of bytes, digests or expands those bits into the specified
* number of bytes without loss of entropy.
......
......@@ -25,6 +25,7 @@
*/
package hudson.console;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jenkins.model.Jenkins;
import hudson.remoting.ObjectInputStreamEx;
import java.util.concurrent.TimeUnit;
......@@ -128,7 +129,7 @@ public class AnnotatedLargeText<T> extends LargeText {
long timestamp = ois.readLong();
if (TimeUnit.HOURS.toMillis(1) > abs(System.currentTimeMillis()-timestamp))
// don't deserialize something too old to prevent a replay attack
return (ConsoleAnnotator) ois.readObject();
return getConsoleAnnotator(ois);
} catch (RuntimeException ex) {
throw new IOException("Could not decode input", ex);
}
......@@ -140,6 +141,11 @@ public class AnnotatedLargeText<T> extends LargeText {
return ConsoleAnnotator.initial(context);
}
@SuppressFBWarnings(value = "OBJECT_DESERIALIZATION", justification = "Deserialization is protected by logic.")
private ConsoleAnnotator getConsoleAnnotator(ObjectInputStream ois) throws IOException, ClassNotFoundException {
return (ConsoleAnnotator) ois.readObject();
}
@CheckReturnValue
@Override
public long writeLogTo(long start, Writer w) throws IOException {
......
......@@ -23,6 +23,7 @@
*/
package hudson.console;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.ExtensionPoint;
import hudson.Functions;
import hudson.MarkupText;
......@@ -275,7 +276,7 @@ public abstract class ConsoleNote<T> implements Serializable, Describable<Consol
try (ObjectInputStream ois = new ObjectInputStreamEx(new GZIPInputStream(new ByteArrayInputStream(buf)),
jenkins != null ? jenkins.pluginManager.uberClassLoader : ConsoleNote.class.getClassLoader(),
ClassFilter.DEFAULT)) {
return (ConsoleNote) ois.readObject();
return getConsoleNote(ois);
}
} catch (Error e) {
// for example, bogus 'sz' can result in OutOfMemoryError.
......@@ -284,6 +285,11 @@ public abstract class ConsoleNote<T> implements Serializable, Describable<Consol
}
}
@SuppressFBWarnings(value = "OBJECT_DESERIALIZATION", justification = "Deserialization is protected by logic.")
private static ConsoleNote getConsoleNote(ObjectInputStream ois) throws IOException, ClassNotFoundException {
return (ConsoleNote) ois.readObject();
}
/**
* Skips the encoded console note.
*/
......
......@@ -23,6 +23,8 @@
*/
package hudson.scheduler;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
......@@ -51,10 +53,10 @@ public abstract class Hash {
* Produces an integer in [0,n)
*/
public abstract int next(int n);
public static Hash from(String seed) {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
MessageDigest md5 = getMd5();
md5.update(seed.getBytes(StandardCharsets.UTF_8));
byte[] digest = md5.digest();
......@@ -77,6 +79,12 @@ public abstract class Hash {
}
}
// TODO JENKINS-60563 remove MD5 from all usages in Jenkins
@SuppressFBWarnings(value = "WEAK_MESSAGE_DIGEST_MD5", justification = "Should not be used for security.")
private static MessageDigest getMd5() throws NoSuchAlgorithmException {
return MessageDigest.getInstance("MD5");
}
/**
* Creates a hash that always return 0.
*/
......
......@@ -23,6 +23,7 @@
*/
package hudson.security;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.model.User;
import jenkins.model.Jenkins;
import hudson.util.Scrambler;
......@@ -162,8 +163,7 @@ public class BasicAuthenticationFilter implements Filter {
path += '?'+q;
// prepare a redirect
rsp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
rsp.setHeader("Location",path);
prepareRedirect(rsp, path);
// ... but first let the container authenticate this request
RequestDispatcher d = servletContext.getRequestDispatcher("/j_security_check?j_username="+
......@@ -171,6 +171,12 @@ public class BasicAuthenticationFilter implements Filter {
d.include(req,rsp);
}
@SuppressFBWarnings(value = "UNVALIDATED_REDIRECT", justification = "Redirect is validated as processed.")
private void prepareRedirect(HttpServletResponse rsp, String path) {
rsp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
rsp.setHeader("Location",path);
}
//public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// HttpServletRequest req = (HttpServletRequest) request;
// String authorization = req.getHeader("Authorization");
......
......@@ -23,6 +23,7 @@
*/
package hudson.security;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Functions;
import com.google.common.base.Strings;
......@@ -100,15 +101,7 @@ public class HudsonAuthenticationEntryPoint extends AuthenticationProcessingFilt
} catch (IllegalStateException e) {
out = rsp.getWriter();
}
out.printf(
"<html><head>" +
"<meta http-equiv='refresh' content='1;url=%1$s'/>" +
"<script>window.location.replace('%1$s');</script>" +
"</head>" +
"<body style='background-color:white; color:white;'>%n" +
"%n%n"+
"Authentication required%n"+
"<!--%n",loginForm);
printResponse(loginForm, out);
if (cause!=null)
cause.report(out);
......@@ -123,4 +116,17 @@ public class HudsonAuthenticationEntryPoint extends AuthenticationProcessingFilt
out.close();
}
}
@SuppressFBWarnings(value = "XSS_SERVLET", justification = "Intermediate step for redirecting users to login page.")
private void printResponse(String loginForm, PrintWriter out) {
out.printf(
"<html><head>" +
"<meta http-equiv='refresh' content='1;url=%1$s'/>" +
"<script>window.location.replace('%1$s');</script>" +
"</head>" +
"<body style='background-color:white; color:white;'>%n" +
"%n%n"+
"Authentication required%n"+
"<!--%n",loginForm);
}
}
......@@ -490,7 +490,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
}
}
@Restricted(NoExternalUse.class)
@Restricted(NoExternalUse.class) // _entryForm.jelly and signup.jelly
public boolean isMailerPluginPresent() {
try {
// mail support has moved to a separate plugin
......
......@@ -26,6 +26,7 @@ package hudson.util;
import java.lang.RuntimeException;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
......@@ -33,6 +34,7 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.util.Iterators.DuplicateFilterIterator;
/**
......@@ -290,7 +292,7 @@ public class ConsistentHash<T> {
*/
private int md5(String s) {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
MessageDigest md5 = getMd5();
md5.update(s.getBytes());
byte[] digest = md5.digest();
......@@ -303,6 +305,12 @@ public class ConsistentHash<T> {
}
}
// TODO JENKINS-60563 remove MD5 from all usages in Jenkins
@SuppressFBWarnings(value = "WEAK_MESSAGE_DIGEST_MD5", justification = "Not used for security.")
private MessageDigest getMd5() throws NoSuchAlgorithmException {
return MessageDigest.getInstance("MD5");
}
/**
* unsigned byte->int.
*/
......
package hudson.util;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.EnvVars;
import hudson.Util;
import org.kohsuke.accmod.Restricted;
......@@ -17,19 +18,23 @@ class DOSToUnixPathHelper {
void validate(File fexe);
}
static private boolean checkPrefix(String prefix, Helper helper) {
File f = new File(prefix);
File f = constructFile(prefix);
if(f.exists()) {
helper.checkExecutable(f);
return true;
}
File fexe = new File(prefix+".exe");
File fexe = constructFile(prefix+".exe");
if(fexe.exists()) {
helper.checkExecutable(fexe);
return true;
}
return false;
}
@SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "Limited use for locating shell executable by administrator.")
private static File constructFile(String prefix) {
return new File(prefix);
}
static void iteratePath(String exe, Helper helper) {
exe = fixEmpty(exe);
if(exe==null) {
......
......@@ -23,6 +23,7 @@
*/
package hudson.util;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.FilePath;
import hudson.ProxyConfiguration;
import hudson.Util;
......@@ -145,6 +146,7 @@ public abstract class FormFieldValidator {
/**
* Gets the parameter as a file.
*/
@SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "Not used.")
protected final File getFileParameter(String paramName) {
return new File(Util.fixNull(request.getParameter(paramName)));
}
......@@ -329,7 +331,7 @@ public abstract class FormFieldValidator {
try {
URL url = new URL(value);
HttpURLConnection con = (HttpURLConnection)url.openConnection();
HttpURLConnection con = openConnection(url);
con.connect();
if(con.getResponseCode()!=200
|| con.getHeaderField("X-Hudson")==null) {
......@@ -342,6 +344,11 @@ public abstract class FormFieldValidator {
handleIOException(value,e);
}
}
@SuppressFBWarnings(value = "URLCONNECTION_SSRF_FD", justification = "Not used.")
private HttpURLConnection openConnection(URL url) throws IOException {
return (HttpURLConnection)url.openConnection();
}
}
/**
......
......@@ -4,10 +4,12 @@ import com.google.common.collect.Lists;
import jenkins.util.SystemProperties;
import hudson.init.InitMilestone;
import hudson.init.InitReactorListener;
import hudson.security.ACL;
import hudson.util.DaemonThreadFactory;
import hudson.util.NamingThreadFactory;
import jenkins.model.Configuration;
import jenkins.model.Jenkins;
import jenkins.security.ImpersonatingExecutorService;
import org.jvnet.hudson.reactor.Milestone;
import org.jvnet.hudson.reactor.Reactor;
import org.jvnet.hudson.reactor.ReactorException;
......@@ -45,7 +47,7 @@ public class InitReactorRunner {
else
es = Executors.newSingleThreadExecutor(new NamingThreadFactory(new DaemonThreadFactory(), "InitReactorRunner"));
try {
reactor.execute(es,buildReactorListener());
reactor.execute(new ImpersonatingExecutorService(es, ACL.SYSTEM), buildReactorListener());
} finally {
es.shutdownNow(); // upon a successful return the executor queue should be empty. Upon an exception, we want to cancel all pending tasks
}
......
......@@ -24,6 +24,7 @@
package jenkins.model;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Extension;
import hudson.Util;
import hudson.model.Job;
......@@ -313,13 +314,19 @@ public final class RunIdMigrator {
if (args.length != 1) {
throw new Exception("pass one parameter, $JENKINS_HOME");
}
File root = new File(args[0]);
File root = constructFile(args[0]);
File jobs = new File(root, "jobs");
if (!jobs.isDirectory()) {
throw new FileNotFoundException("no such $JENKINS_HOME " + root);
}
new RunIdMigrator().unmigrateJobsDir(jobs);
}
@SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "Only invoked from the command line as a standalone utility")
private static File constructFile(String arg) {
return new File(arg);
}
private void unmigrateJobsDir(File jobs) throws Exception {
File[] jobDirs = jobs.listFiles();
if (jobDirs == null) {
......
package jenkins.model.identity;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Extension;
import hudson.model.UnprotectedRootAction;
import java.security.MessageDigest;
......@@ -61,6 +62,7 @@ public class IdentityRootAction implements UnprotectedRootAction {
*
* @return the fingerprint of the public key.
*/
@SuppressFBWarnings(value = "WEAK_MESSAGE_DIGEST_MD5", justification = "Not used for security. ")
public String getFingerprint() {
RSAPublicKey key = InstanceIdentityProvider.RSA.getPublicKey();
if (key == null) {
......@@ -68,7 +70,7 @@ public class IdentityRootAction implements UnprotectedRootAction {
}
// TODO replace with org.jenkinsci.remoting.util.KeyUtils once JENKINS-36871 changes are merged
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
MessageDigest digest = getMd5();
digest.reset();
byte[] bytes = digest.digest(key.getEncoded());
StringBuilder result = new StringBuilder(Math.max(0, bytes.length * 3 - 1));
......@@ -84,4 +86,10 @@ public class IdentityRootAction implements UnprotectedRootAction {
throw new IllegalStateException("JLS mandates MD5 support");
}
}
// TODO JENKINS-60563 remove MD5 from all usages in Jenkins
@SuppressFBWarnings(value = "WEAK_MESSAGE_DIGEST_MD5", justification = "Not used for security. ")
private MessageDigest getMd5() throws NoSuchAlgorithmException {
return MessageDigest.getInstance("MD5");
}
}
......@@ -111,7 +111,7 @@ public class JnlpSlaveAgentProtocol4 extends AgentProtocol {
// prepare our keyStore so we can provide our authentication
keyStore = KeyStore.getInstance("JKS");
char[] password = "password".toCharArray();
char[] password = constructPassword();
try {
keyStore.load(null, password);
} catch (IOException e) {
......@@ -146,6 +146,10 @@ public class JnlpSlaveAgentProtocol4 extends AgentProtocol {
sslContext.init(kmf.getKeyManagers(), trustManagers, null);
}
private char[] constructPassword() {
return "password".toCharArray();
}
/**
* Inject the {@link IOHubProvider}
*
......@@ -182,7 +186,7 @@ public class JnlpSlaveAgentProtocol4 extends AgentProtocol {
LOGGER.log(Level.INFO, "Updating {0} TLS certificate to retain validity", getName());
X509Certificate identityCertificate = InstanceIdentityProvider.RSA.getCertificate();
RSAPrivateKey privateKey = InstanceIdentityProvider.RSA.getPrivateKey();
char[] password = "password".toCharArray();
char[] password = constructPassword();
keyStore.setKeyEntry("jenkins", privateKey, password, new X509Certificate[]{identityCertificate});
}
} catch (KeyStoreException e) {
......
......@@ -100,7 +100,7 @@ THE SOFTWARE.
<l:progressiveRendering handler="${it}" callback="display"/>
<table class="sortable pane bigtable" id="people">
<tr>
<th />
<th class="minimum-width" />
<th>${%User ID}</th>
<th>${%Name}</th>
<th initialSortDir="up">${%Last Commit Activity}</th>
......
......@@ -24,23 +24,41 @@ THE SOFTWARE.
<!-- tag file used by both signup.jelly and addUser.jelly -->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler">
<st:documentation>
<st:attribute name="it" use="required" type="hudson.security.HudsonPrivateSecurityRealm">
Context where the page is loaded.
</st:attribute>
<st:attribute name="title" use="required">
Title of the HTML page. Rendered in the page content inside a &lt;h1>.
</st:attribute>
<st:attribute name="action" use="required">
The method to call from within the HudsonPrivateSecurityRealm.
</st:attribute>
<st:attribute name="captcha" use="optional">
Determines if the tag will include the captcha.
</st:attribute>
<st:attribute name="data" use="optional" type="hudson.security.HudsonPrivateSecurityRealm.SignupInfo">
The wrapper for the data provided by the user and the error message(s) if any. Is null on first form display.
</st:attribute>
</st:documentation>
<h1>${title}</h1>
<div style="margin: 2em;">
<div class="form-content">
<j:if test="${data.errorMessage!=null}">
<div class="error" style="margin-bottom:1em">
<div class="error">
${data.errorMessage}
</div>
</j:if>
<j:forEach var="error" items="${data.errors}">
<div class="error" style="margin-bottom:1em">
<div class="error">
${error.value}
</div>
</j:forEach>
<table>
<tr>
<td>${%Username}:</td>
<td><input type="text" name="username" id="username" value="${data.username}" /></td>
<td><input type="text" name="username" id="username" value="${data.username}" autofocus="autofocus" /></td>
</tr>
<tr>
<td>${%Password}:</td>
......@@ -55,17 +73,17 @@ THE SOFTWARE.
<td><input type="text" name="fullname" value="${data.fullname}" /></td>
</tr>
<j:if test="${it.mailerPluginPresent}">
<tr>
<td>${%E-mail address}:</td>
<td><input type="text" name="email" value="${data.email}" /></td>
</tr>
<tr>
<td>${%E-mail address}:</td>
<td><input type="text" name="email" value="${data.email}" /></td>
</tr>
</j:if>
<j:if test="${captcha}">
<tr>
<td>${%Enter text as shown}:</td>
<td>
<input type="text" name="captcha" autocomplete="off" /><br />
<img src="${rootURL}/securityRealm/captcha" alt="[${%captcha}]"/>
<img src="${rootURL}/securityRealm/captcha" alt="[${%captcha}]" />
</td>
</tr>
</j:if>
......
......@@ -22,28 +22,36 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<!-- tag file used by both signup.jelly and addUser.jelly -->
<!-- tag file used by signupWithFederatedIdentity.jelly, addUser.jelly and firstUser.jelly -->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout" xmlns:f="/lib/form" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm">
<st:documentation>
<st:attribute name="host" use="required" type="hudson.security.HudsonPrivateSecurityRealm">
Corresponds to the "it" of Jelly, meaning the context where the page is loaded.
</st:attribute>
<st:attribute name="title" use="required">
Title of the HTML page.
Rendered into &lt;title> tag, in the page content inside a &lt;h1> and as the label of the submit button.
</st:attribute>
<st:attribute name="action" use="required">
The method to call from within the HudsonPrivateSecurityRealm.
</st:attribute>
<st:attribute name="captcha" use="optional" type="boolean">
Determines if the tag will include the captcha.
</st:attribute>
<st:attribute name="data" use="optional" type="hudson.security.HudsonPrivateSecurityRealm.SignupInfo">
The wrapper for the data provided by the user and the error message(s) if any. Is null on first form display.
</st:attribute>
</st:documentation>
<l:layout norefresh="true" title="${title}">
<l:header>
<style>
<!-- match width with captcha image -->
INPUT {
width:200px;
}
</style>
</l:header>
<st:adjunct includes="hudson.security.HudsonPrivateSecurityRealm._entryFormPage.resources" />
<l:hasPermission permission="${app.READ}" it="${host}">
<st:include page="sidepanel.jelly" it="${host}" />
</l:hasPermission>
<l:main-panel>
<form action="${rootURL}/securityRealm/${action}" method="post" style="text-size:smaller">
<local:_entryForm title="${title}" action="${action}" captcha="${captcha}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
<form action="${rootURL}/securityRealm/${action}" method="post">
<local:_entryForm title="${title}" action="${action}" captcha="${captcha}" it="${host}" data="${data}" />
<f:submit value="${title}" />
<script>
$('username').focus();
</script>
</form>
</l:main-panel>
</l:layout>
......
input {
/* match width with captcha image */
width:200px;
}
.form-content {
margin: 2em;
}
.form-content .error {
margin-bottom: 1em;
}
......@@ -26,6 +26,6 @@ THE SOFTWARE.
Page for admin to create a new user
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<local:_entryFormPage host="${it}" title="${%Create User}" action="createAccountByAdmin" captcha="${false}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
</j:jelly>
\ No newline at end of file
<j:jelly xmlns:j="jelly:core" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm">
<local:_entryFormPage host="${it}" title="${%Create User}" action="createAccountByAdmin" captcha="${false}" data="${data}" />
</j:jelly>
......@@ -23,8 +23,7 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson"
xmlns:f="/lib/form">
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:f="/lib/form">
<f:entry field="allowsSignup">
<f:checkbox default="false" title="${%Allow users to sign up}" />
</f:entry>
......
......@@ -26,6 +26,6 @@ THE SOFTWARE.
This is used to create the first user.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<local:_entryFormPage host="${it}" title="${%Create First Admin User}" action="createFirstAccount" captcha="${false}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
</j:jelly>
\ No newline at end of file
<j:jelly xmlns:j="jelly:core" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm">
<local:_entryFormPage host="${it}" title="${%Create First Admin User}" action="createFirstAccount" captcha="${false}" data="${data}"/>
</j:jelly>
......@@ -23,7 +23,7 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout">
<l:layout permission="${app.ADMINISTER}" title="${%Users}">
<st:include page="sidepanel.jelly" />
<l:main-panel>
......@@ -32,20 +32,20 @@ THE SOFTWARE.
<table class="sortable pane bigtable" id="people">
<tr>
<th style="width:32px"/>
<th class="minimum-width" />
<th>${%User ID}</th>
<th>${%Name}</th>
<th style="width:32px"/>
<th class="minimum-width" />
</tr>
<j:forEach var="user" items="${it.allUsers}">
<tr>
<td><a href="${user.url}/" class="model-link inside"><img src="${h.getUserAvatar(user,'32x32')}" alt="" height="32" width="32"/></a></td>
<td><a href="${user.url}/" class="model-link inside"><img src="${h.getUserAvatar(user,'32x32')}" alt="" height="32" width="32" /></a></td>
<td><a href="${user.url}/">${user.id}</a></td>
<td><a href="${user.url}/">${user}</a></td>
<td>
<a href="${user.url}/configure"><l:icon class="icon-gear2 icon-lg"/></a>
<a href="${user.url}/configure"><l:icon class="icon-gear2 icon-lg" /></a>
<j:if test="${user.canDelete()}">
<a href="${user.url}/delete"><l:icon class="icon-edit-delete icon-md"/></a>
<a href="${user.url}/delete"><l:icon class="icon-edit-delete icon-md" /></a>
</j:if>
</td>
</tr>
......
......@@ -23,10 +23,10 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout">
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler">
<st:include page="/hudson/security/SecurityRealm/loginLink.jelly" />
<j:if test="${it.allowsSignup()}">
|
<a href="${rootURL}/signup"><b>${%sign up}</b></a>
</j:if>
</j:jelly>
\ No newline at end of file
</j:jelly>
......@@ -23,13 +23,13 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:s="/lib/form">
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout">
<l:header />
<l:side-panel>
<l:tasks>
<l:task href="${rootURL}/" icon="icon-up icon-md" title="${%Back to Dashboard}"/>
<l:task href="${rootURL}/manage" icon="icon-gear2 icon-md" permission="${app.MANAGE}" title="${%Manage Jenkins}"/>
<l:task href="addUser" icon="icon-new-user icon-md" permission="${app.ADMINISTER}" title="${%Create User}"/>
<l:task href="${rootURL}/" icon="icon-up icon-md" title="${%Back to Dashboard}" />
<l:task href="${rootURL}/manage" icon="icon-gear2 icon-md" permission="${app.MANAGE}" title="${%Manage Jenkins}" />
<l:task href="addUser" icon="icon-new-user icon-md" permission="${app.ADMINISTER}" title="${%Create User}" />
</l:tasks>
</l:side-panel>
</j:jelly>
\ No newline at end of file
</j:jelly>
......@@ -26,6 +26,6 @@ THE SOFTWARE.
User self sign up page.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<local:_entryFormPage host="${app}" title="${%Sign up}" action="createAccountWithFederatedIdentity" captcha="${true}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
</j:jelly>
\ No newline at end of file
<j:jelly xmlns:j="jelly:core" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm">
<local:_entryFormPage host="${app}" title="${%Sign up}" action="createAccountWithFederatedIdentity" captcha="${true}" data="${data}" />
</j:jelly>
......@@ -23,7 +23,7 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout">
<l:layout norefresh="true">
<l:hasPermission permission="${app.READ}" it="${app}">
<st:include page="sidepanel.jelly" it="${app}" />
......
......@@ -22,7 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt">
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout">
<st:statusCode value="404" />
<st:include page="sidepanel.jelly" from="${app}" it="${app}" />
<l:layout title="${%Signup not supported}">
......
......@@ -26,7 +26,13 @@ THE SOFTWARE.
<j:jelly xmlns:j="jelly:core" xmlns:d="jelly:define" xmlns:st="jelly:stapler">
<st:documentation>
Renders the body only if the current user has the specified permission
<st:attribute name="permission" use="required" type="Permission">
<st:attribute name="it" use="optional">
By default it will reuse the current context.
If the provided value does not inherit from hudson.security.AccessControlled,
the tag will look for the first ancestor satisfying the condition.
The hasPermission will be performed against that value.
</st:attribute>
<st:attribute name="permission" use="required" type="hudson.security.Permission">
permission object to check. If this is null, the body will be also rendered.
</st:attribute>
</st:documentation>
......@@ -34,4 +40,4 @@ THE SOFTWARE.
<j:if test="${h.hasPermission(it, permission)}">
<d:invokeBody/>
</j:if>
</j:jelly>
\ No newline at end of file
</j:jelly>
......@@ -27,8 +27,11 @@ import hudson.util.XStream2;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Test;
import org.xmlunit.diff.DefaultNodeMatcher;
import org.xmlunit.diff.ElementSelectors;
import static org.junit.Assert.*;
import static org.xmlunit.matchers.CompareMatcher.isSimilarTo;
import java.io.IOException;
import java.io.InputStream;
......@@ -62,7 +65,8 @@ public class XStreamDOMTest {
Foo foo = createSomeFoo();
String xml = xs.toXML(foo);
System.out.println(xml);
assertEquals(getTestData1().trim(), xml.trim());
assertThat(getTestData1().trim(), isSimilarTo(xml.trim()).ignoreWhitespace()
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText)));
}
private String getTestData1() throws IOException {
......@@ -100,7 +104,8 @@ public class XStreamDOMTest {
String xml = xs.toXML(foo);
System.out.println(xml);
assertEquals(getTestData1().trim(), xml.trim());
assertThat(getTestData1().trim(), isSimilarTo(xml.trim()).ignoreWhitespace()
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText)));
}
@Test
......
......@@ -407,6 +407,20 @@ THE SOFTWARE.
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<configuration>
<plugins>
<plugin>
<groupId>com.h3xstream.findsecbugs</groupId>
<artifactId>findsecbugs-plugin</artifactId>
<version>1.10.1</version>
</plugin>
</plugins>
<maxHeap>768</maxHeap>
</configuration>
</plugin>
</plugins>
</pluginManagement>
......
......@@ -13,4 +13,12 @@
<Bug pattern="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE"/>
</Or>
</Match>
<Match>
<!--We don't care about this behavior.-->
<Bug pattern="CRLF_INJECTION_LOGS"/>
</Match>
<Match>
<!--Jenkins handles this issue differently or doesn't care about it.-->
<Bug pattern="INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE"/>
</Match>
</FindBugsFilter>
......@@ -503,6 +503,18 @@ public class PluginManagerTest {
}
}
@Issue("JENKINS-61071")
@Test
public void requireSystemInInitializer() throws Exception {
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy());
String pluginShortName = "require-system-in-initializer";
dynamicLoad(pluginShortName + ".jpi");
try (ACLContext context = ACL.as(User.getById("underprivileged", true).impersonate())) {
r.jenkins.pluginManager.start(Collections.singletonList(r.jenkins.pluginManager.getPlugin(pluginShortName)));
}
}
private void dynamicLoad(String plugin) throws IOException, InterruptedException, RestartRequiredException {
PluginManagerUtil.dynamicLoad(plugin, r.jenkins);
}
......
......@@ -360,7 +360,7 @@ THE SOFTWARE.
<artifactItem>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>script-security</artifactId>
<version>1.68</version>
<version>1.70</version>
<type>hpi</type>
</artifactItem>
<artifactItem>
......
......@@ -522,6 +522,10 @@ table.bigtable.pane > tbody > tr > td:last-child {
white-space: nowrap;
}
.bigtable th.minimum-width {
width: 1px;
}
.bigtable td {
vertical-align: middle;
padding: 3px 4px 3px 4px;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册