diff --git a/core/src/main/resources/hudson/model/TextParameterValue/value.jelly b/core/src/main/resources/hudson/model/TextParameterValue/value.jelly
index 5c089e0ac0eae43e60744e094384eeed5b663108..8ae92827ade16c6d764956730c079ab372f6c397 100644
--- a/core/src/main/resources/hudson/model/TextParameterValue/value.jelly
+++ b/core/src/main/resources/hudson/model/TextParameterValue/value.jelly
@@ -26,7 +26,8 @@ THE SOFTWARE.
-
+
+
\ No newline at end of file
diff --git a/core/src/main/resources/hudson/model/User/sidepanel_eu.properties b/core/src/main/resources/hudson/model/User/sidepanel_eu.properties
index 2f485ef3a4de9229f9b534013e93544807183798..a8e0793ff1e7e1d6da39659984271782a041de5a 100644
--- a/core/src/main/resources/hudson/model/User/sidepanel_eu.properties
+++ b/core/src/main/resources/hudson/model/User/sidepanel_eu.properties
@@ -4,4 +4,4 @@ Builds=Kompilazioak
Configure=Itxuratu
My\ Views=Nire Begiak
People=Jendea
-Status=Estatus
+Status=Egoera
diff --git a/core/src/main/resources/hudson/model/View/People/index_eu.properties b/core/src/main/resources/hudson/model/View/People/index_eu.properties
index e366a8bd48ed730938b96a451f94c7a66bbfd95b..01b5e6988e7ace3d71758800be8802547ceef62e 100644
--- a/core/src/main/resources/hudson/model/View/People/index_eu.properties
+++ b/core/src/main/resources/hudson/model/View/People/index_eu.properties
@@ -1,3 +1,3 @@
# This file is under the MIT License by authors
-People=Gendea
+People=Jendea
diff --git a/core/src/main/resources/jenkins/security/s2m/filepath-filter.conf b/core/src/main/resources/jenkins/security/s2m/filepath-filter.conf
index affea08f6959cfb8267cf7cf33446aa62bf98dfc..605668acd7a5071cab747484995c1380f948c773 100644
--- a/core/src/main/resources/jenkins/security/s2m/filepath-filter.conf
+++ b/core/src/main/resources/jenkins/security/s2m/filepath-filter.conf
@@ -23,6 +23,9 @@ allow read,stat
/userContent($|/.*)
# In the next rule we grant general access under build directories, so first we protect
# the actual build record that Jenkins core reads, which nothing should be touching.
deny all /build.xml
+# Similarly for Pipeline build (WorkflowRun) metadata:
+deny all /program.dat
+deny all /workflow($|/.*)
# Various plugins read/write files under build directories, so allow them all.
# - git 1.x writes changelog.xml from the slave (2.x writes from the master so need not be listed)
diff --git a/core/src/main/resources/lib/form/entry.jelly b/core/src/main/resources/lib/form/entry.jelly
index 78e80aaf0cf553da4ef0f4edf2273b9204924db7..0b1c06d1f1a9ff54ffd3f54b077231d4dcda3259 100644
--- a/core/src/main/resources/lib/form/entry.jelly
+++ b/core/src/main/resources/lib/form/entry.jelly
@@ -32,6 +32,8 @@ THE SOFTWARE.
Name of the entry. Think of this like a label for the control.
+
+ This content is HTML (unless the boolean variable escapeEntryTitleAndDescription is set). Use h.escape if necessary.
Used for the databinding. TBD. When this attribute
@@ -46,6 +48,8 @@ THE SOFTWARE.
This text shouldn't get too long, and in recent Hudson, this feature
is somewhat de-emphasized, in favor of the inline foldable help page
specified via @help.
+
+ This content is HTML (unless the boolean variable escapeEntryTitleAndDescription is set). Use h.escape if necessary.
URL to the HTML page. When this attribute is specified, the entry gets
@@ -67,7 +71,7 @@ THE SOFTWARE.
|
-
+
|
@@ -78,7 +82,7 @@ THE SOFTWARE.
|
| | |
-
+
diff --git a/core/src/test/groovy/hudson/util/SecretRewriterTest.groovy b/core/src/test/groovy/hudson/util/SecretRewriterTest.groovy
index 9285f0bcdd6b4bd8e212f16b4fb60066b270ada5..b748703ac172ad6427d86b63768969319dcd4950 100644
--- a/core/src/test/groovy/hudson/util/SecretRewriterTest.groovy
+++ b/core/src/test/groovy/hudson/util/SecretRewriterTest.groovy
@@ -23,42 +23,49 @@ class SecretRewriterTest {
@Rule public TemporaryFolder tmp = new TemporaryFolder()
+ def FOO_PATTERN = /\{[A-Za-z0-9+\/]+={0,2}}<\/foo>/
+ def MSG_PATTERN = /\{[A-Za-z0-9+\/]+={0,2}}<\/msg>/
+ def FOO_PATTERN2 = /(\{[A-Za-z0-9+\/]+={0,2}}<\/foo>){2}/
+ def ABC_FOO_PATTERN = /\s\{[A-Za-z0-9+\/]+={0,2}}<\/foo>\s<\/abc>/
+
@Test
void singleFileRewrite() {
def o = encryptOld('foobar') // old
def n = encryptNew('foobar') // new
roundtrip "${o}",
- "${n}"
+ {assert it ==~ FOO_PATTERN}
+
roundtrip "${o}${o}",
- "${n}${n}"
+ {assert it ==~ FOO_PATTERN2}
roundtrip "${n}",
- "${n}"
+ {assert it == "${n}"}
roundtrip " thisIsLegalBase64AndLongEnoughThatItCouldLookLikeSecret ",
- " thisIsLegalBase64AndLongEnoughThatItCouldLookLikeSecret "
+ {assert it == "thisIsLegalBase64AndLongEnoughThatItCouldLookLikeSecret"}
// to be rewritten, it needs to be between a tag
- roundtrip "$o", "$o"
- roundtrip "$o", "$o"
+ roundtrip "$o", {assert it == "$o"}
+ roundtrip "$o", {assert it == "$o"}
//
- roundtrip "\n$o\n", "\n$n\n"
+ roundtrip "\n$o\n", {assert it ==~ ABC_FOO_PATTERN}
}
- void roundtrip(String before, String after) {
+ void roundtrip(String before, Closure check) {
def sr = new SecretRewriter(null);
def f = File.createTempFile("test", "xml", tmp.root)
f.text = before
sr.rewrite(f,null)
- assert after.replaceAll(System.getProperty("line.separator"), "\n").trim()==f.text.replaceAll(System.getProperty("line.separator"), "\n").trim()
+ check(f.text.replaceAll(System.getProperty("line.separator"), "\n").trim())
+ //assert after.replaceAll(System.getProperty("line.separator"), "\n").trim()==f.text.replaceAll(System.getProperty("line.separator"), "\n").trim()
}
String encryptOld(str) {
def cipher = Secret.getCipher("AES");
- cipher.init(Cipher.ENCRYPT_MODE, Secret.legacyKey);
- return new String(Base64.encode(cipher.doFinal((str + Secret.MAGIC).getBytes("UTF-8"))))
+ cipher.init(Cipher.ENCRYPT_MODE, HistoricalSecrets.legacyKey);
+ return new String(Base64.encode(cipher.doFinal((str + HistoricalSecrets.MAGIC).getBytes("UTF-8"))))
}
String encryptNew(str) {
@@ -70,8 +77,7 @@ class SecretRewriterTest {
*/
@Test
void recursionDetection() {
- def backup = tmp.newFolder("backup")
- def sw = new SecretRewriter(backup);
+ def sw = new SecretRewriter();
def st = StreamTaskListener.fromStdout()
def o = encryptOld("Hello world")
@@ -100,12 +106,11 @@ class SecretRewriterTest {
assert 6==sw.rewriteRecursive(t, st)
dirs.each { p->
- assert new File(t,"$p/foo.xml").text.trim()==answer
- assert new File(backup,"$p/foo.xml").text.trim()==payload
+ assert new File(t,"$p/foo.xml").text.trim() ==~ MSG_PATTERN
}
// t2 is only reachable by following a symlink. this should be covered, too
- assert new File(t2,"foo.xml").text.trim()==answer.trim();
+ assert new File(t2,"foo.xml").text.trim() ==~ MSG_PATTERN
}
}
diff --git a/core/src/test/groovy/hudson/util/SecretTest.groovy b/core/src/test/groovy/hudson/util/SecretTest.groovy
index f7a0ba3ad00a194c86fd1e7b7ca12b10e5512b08..8f39ae7ec026fa093dade93129a4ec31f204e28d 100644
--- a/core/src/test/groovy/hudson/util/SecretTest.groovy
+++ b/core/src/test/groovy/hudson/util/SecretTest.groovy
@@ -31,7 +31,8 @@ import org.junit.Rule
import org.junit.Test
import java.util.Random;
-import javax.crypto.Cipher;
+import javax.crypto.Cipher
+import java.util.regex.Pattern;
/**
* @author Kohsuke Kawaguchi
@@ -43,6 +44,8 @@ public class SecretTest {
@Rule
public MockSecretRule mockSecretRule = new MockSecretRule()
+ static final Pattern ENCRYPTED_VALUE_PATTERN = Pattern.compile("\\{?[A-Za-z0-9+/]+={0,2}}?");
+
@Test
void testEncrypt() {
def secret = Secret.fromString("abc");
@@ -54,6 +57,11 @@ public class SecretTest {
// can we round trip?
assert secret==Secret.fromString(secret.encryptedValue);
+
+ //Two consecutive encryption requests of the same object should result in the same encrypted value - SECURITY-304
+ assert secret.encryptedValue == secret.encryptedValue
+ //Two consecutive encryption requests of different objects with the same value should not result in the same encrypted value - SECURITY-304
+ assert secret.encryptedValue != Secret.fromString(secret.plainText).encryptedValue
}
@Test
@@ -62,9 +70,16 @@ public class SecretTest {
String plaintext = RandomStringUtils.random(new Random().nextInt(i));
String ciphertext = Secret.fromString(plaintext).getEncryptedValue();
//println "${plaintext} → ${ciphertext}"
- assert Secret.ENCRYPTED_VALUE_PATTERN.matcher(ciphertext).matches();
+ assert ENCRYPTED_VALUE_PATTERN.matcher(ciphertext).matches();
}
- assert !Secret.ENCRYPTED_VALUE_PATTERN.matcher("hello world").matches();
+ //Not "plain" text
+ assert !ENCRYPTED_VALUE_PATTERN.matcher("hello world").matches();
+ //Not "plain" text
+ assert !ENCRYPTED_VALUE_PATTERN.matcher("helloworld!").matches();
+ //legacy key
+ assert ENCRYPTED_VALUE_PATTERN.matcher("abcdefghijklmnopqr0123456789").matches();
+ //legacy key
+ assert ENCRYPTED_VALUE_PATTERN.matcher("abcdefghijklmnopqr012345678==").matches();
}
@Test
@@ -77,7 +92,7 @@ public class SecretTest {
def s = Secret.fromString("Mr.Jenkins");
def xml = Jenkins.XSTREAM.toXML(s);
assert !xml.contains(s.plainText)
- assert xml.contains(s.encryptedValue)
+ assert xml ==~ /\{[A-Za-z0-9+\/]+={0,2}}<\/hudson\.util\.Secret>/
def o = Jenkins.XSTREAM.fromXML(xml);
assert o==s : xml;
@@ -104,11 +119,11 @@ public class SecretTest {
*/
@Test
void migrationFromLegacyKeyToConfidentialStore() {
- def legacy = Secret.legacyKey
+ def legacy = HistoricalSecrets.legacyKey
["Hello world","","\u0000unprintable"].each { str ->
def cipher = Secret.getCipher("AES");
cipher.init(Cipher.ENCRYPT_MODE, legacy);
- def old = new String(Base64.encode(cipher.doFinal((str + Secret.MAGIC).getBytes("UTF-8"))))
+ def old = new String(Base64.encode(cipher.doFinal((str + HistoricalSecrets.MAGIC).getBytes("UTF-8"))))
def s = Secret.fromString(old)
assert s.plainText==str : "secret by the old key should decrypt"
assert s.encryptedValue!=old : "but when encrypting, ConfidentialKey should be in use"
diff --git a/pom.xml b/pom.xml
index b230d350e41de81c0d4398779fc0df4a9c78b8da..5efa721d02239978951ad2f2fc2883e432afd63c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -33,7 +33,7 @@ THE SOFTWARE.
org.jenkins-ci.main
pom
- 2.44-SNAPSHOT
+ 2.45-SNAPSHOT
pom
Jenkins main module
@@ -181,7 +181,7 @@ THE SOFTWARE.
org.jenkins-ci.main
remoting
- 3.4
+ 3.4.1
diff --git a/test/pom.xml b/test/pom.xml
index 65f2cc6a7dcd580adf78074ff9366058792d8c7e..633969b424b226cebd722627c17abe699d7ba03e 100644
--- a/test/pom.xml
+++ b/test/pom.xml
@@ -28,7 +28,7 @@ THE SOFTWARE.
org.jenkins-ci.main
pom
- 2.44-SNAPSHOT
+ 2.45-SNAPSHOT
test
diff --git a/test/src/test/groovy/hudson/model/AbstractProjectTest.groovy b/test/src/test/groovy/hudson/model/AbstractProjectTest.groovy
index ed2c156d08d1825bf2a29aaaae7fd535460bcb50..8c50f37f9247ae55e8bf160ad2388c1d40eeeac2 100644
--- a/test/src/test/groovy/hudson/model/AbstractProjectTest.groovy
+++ b/test/src/test/groovy/hudson/model/AbstractProjectTest.groovy
@@ -519,39 +519,6 @@ public class AbstractProjectTest extends HudsonTestCase {
done.signal()
}
- public void testRenameToPrivileged() {
- def secret = jenkins.createProject(FreeStyleProject.class,"secret");
- def regular = jenkins.createProject(FreeStyleProject.class,"regular")
-
- jenkins.securityRealm = createDummySecurityRealm();
- def auth = new ProjectMatrixAuthorizationStrategy();
- jenkins.authorizationStrategy = auth;
-
- auth.add(Jenkins.ADMINISTER, "alice");
- auth.add(Jenkins.READ, "bob");
-
- // bob the regular user can only see regular jobs
- regular.addProperty(new AuthorizationMatrixProperty([(Job.READ) : ["bob"] as Set]));
-
- def wc = createWebClient()
- wc.login("bob")
- wc.executeOnServer {
- assert jenkins.getItem("secret")==null;
- try {
- regular.renameTo("secret")
- fail("rename as an overwrite should have failed");
- } catch (Exception e) {
- // expected rename to fail in some non-descriptive generic way
- e.printStackTrace()
- }
- }
-
- // those two jobs should still be there
- assert jenkins.getItem("regular")!=null;
- assert jenkins.getItem("secret")!=null;
- }
-
-
/**
* Trying to POST to config.xml by a different job type should fail.
*/
diff --git a/test/src/test/java/hudson/console/AnnotatedLargeTextTest.java b/test/src/test/java/hudson/console/AnnotatedLargeTextTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..43ca1aee547a1fa888df332f0d60acd577779677
--- /dev/null
+++ b/test/src/test/java/hudson/console/AnnotatedLargeTextTest.java
@@ -0,0 +1,126 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2016 CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package hudson.console;
+
+import hudson.MarkupText;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.StringWriter;
+import java.util.logging.Level;
+import org.apache.commons.io.Charsets;
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.jvnet.hudson.test.For;
+import org.jvnet.hudson.test.Issue;
+import org.jvnet.hudson.test.JenkinsRule;
+import org.jvnet.hudson.test.LoggerRule;
+import org.kohsuke.stapler.framework.io.ByteBuffer;
+
+@For({AnnotatedLargeText.class, ConsoleNote.class, ConsoleAnnotationOutputStream.class, PlainTextConsoleOutputStream.class})
+public class AnnotatedLargeTextTest {
+
+ @ClassRule
+ public static JenkinsRule r = new JenkinsRule();
+
+ @Rule
+ public LoggerRule logging = new LoggerRule().record(ConsoleAnnotationOutputStream.class, Level.FINE).capture(100);
+
+ @Test
+ public void smokes() throws Exception {
+ ByteBuffer buf = new ByteBuffer();
+ PrintStream ps = new PrintStream(buf, true);
+ ps.print("Some text.\n");
+ ps.print("Go back to " + TestNote.encodeTo("/root", "your home") + ".\n");
+ ps.print("More text.\n");
+ AnnotatedLargeText text = new AnnotatedLargeText<>(buf, Charsets.UTF_8, true, null);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ text.writeLogTo(0, baos);
+ assertEquals("Some text.\nGo back to your home.\nMore text.\n", baos.toString());
+ StringWriter w = new StringWriter();
+ text.writeHtmlTo(0, w);
+ assertEquals("Some text.\nGo back to your home.\nMore text.\n", w.toString());
+ }
+
+ @Issue("SECURITY-382")
+ @Test
+ public void oldDeserialization() throws Exception {
+ ByteBuffer buf = new ByteBuffer();
+ buf.write(("hello" + ConsoleNote.PREAMBLE_STR + "AAAAwR+LCAAAAAAAAP9dzLEOwVAUxvHThtiNprYxsGiMQhiwNSIhMR/tSZXr3Lr3oJPwPt7FM5hM3gFh8i3/5Bt+1yeUrYH6ap9Yza1Ys9WKWuMiR05wqWhEgpmyEy306Jxvwb19ccGNoBJjLplmgWq0xgOGCjkNZ2IyTrsRlFayVTs4gVMYqP3pw28/JnznuABF/rYWyIyeJfLQe1vxZiDQ7NnYZLn0UZGRRjA9MiV+0OyFv3+utadQyH8B+aJxVM4AAAA=" + ConsoleNote.POSTAMBLE_STR + "there\n").getBytes());
+ AnnotatedLargeText text = new AnnotatedLargeText<>(buf, Charsets.UTF_8, true, null);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ text.writeLogTo(0, baos);
+ assertEquals("hellothere\n", baos.toString());
+ StringWriter w = new StringWriter();
+ text.writeHtmlTo(0, w);
+ assertEquals("hellothere\n", w.toString());
+ assertThat(logging.getMessages(), hasItem("Failed to resurrect annotation")); // TODO assert that this is IOException: Refusing to deserialize unsigned note from an old log.
+ ConsoleNote.INSECURE = true;
+ try {
+ w = new StringWriter();
+ text.writeHtmlTo(0, w);
+ assertThat(w.toString(), containsString("