Skip to content
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

[JENKINS-62448] Enhance information displayed in approval page #300

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
cbfedb6
[JENKINS-62448] Enhance information displayed in approval page
Wadeck May 25, 2020
b7a3312
Update src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/Scr…
Wadeck May 29, 2020
4c583a4
Update src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/Scr…
Wadeck May 29, 2020
349ac04
Update src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/met…
Wadeck May 29, 2020
c559fe8
Update src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/met…
Wadeck May 29, 2020
1d39eff
Update src/main/resources/org/jenkinsci/plugins/scriptsecurity/script…
Wadeck May 29, 2020
de369d6
Update src/main/resources/org/jenkinsci/plugins/scriptsecurity/script…
Wadeck May 29, 2020
e4708f7
Update src/main/resources/org/jenkinsci/plugins/scriptsecurity/script…
Wadeck May 29, 2020
380deda
Update src/main/resources/org/jenkinsci/plugins/scriptsecurity/script…
Wadeck May 29, 2020
79b1995
Update src/main/resources/org/jenkinsci/plugins/scriptsecurity/script…
Wadeck May 29, 2020
0ea0c93
Update src/main/resources/org/jenkinsci/plugins/scriptsecurity/script…
Wadeck May 29, 2020
e978559
Update src/main/resources/org/jenkinsci/plugins/scriptsecurity/script…
Wadeck May 29, 2020
eadf75f
Apply suggestions from code review
Wadeck May 29, 2020
9bcceb4
Various updates after reviews
Wadeck Aug 18, 2020
1c5493f
Merge branch 'master' into UX_revamp
Wadeck Aug 18, 2020
3ee25d8
Adjust wording with Josh's proposal
Wadeck Aug 18, 2020
d934b69
Merge remote-tracking branch 'origin/UX_revamp' into UX_revamp
Wadeck Aug 18, 2020
0b0b624
Adding migration tests
Wadeck Aug 19, 2020
fa17df8
Adjusting existing tests
Wadeck Aug 19, 2020
8f55c06
Adjusting existing tests
Wadeck Aug 20, 2020
65926ce
Adjust grammar
Wadeck Aug 20, 2020
9dc032c
Adjust another wording
Wadeck Aug 20, 2020
18bcb49
Merge remote-tracking branch 'origin/UX_revamp' into UX_revamp
Wadeck Aug 20, 2020
fcb41b7
Adjustment after review
Wadeck Sep 2, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ private PendingClasspathEntry getPendingClasspathEntry(@Nonnull String hash) {
}

@GuardedBy("this")
private transient MetadataStorage metadataStorage;
@VisibleForTesting transient MetadataStorage metadataStorage;

@DataBoundConstructor
public ScriptApproval() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,48 @@
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlTextArea;
import hudson.model.FreeStyleProject;
import hudson.model.Job;
import hudson.model.Result;
import hudson.security.Permission;
import hudson.util.VersionNumber;
import jenkins.model.Jenkins;
import org.apache.tools.ant.taskdefs.optional.ejb.WeblogicDeploymentTool;
import org.hamcrest.Matcher;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.jenkinsci.plugins.scriptsecurity.sandbox.Whitelist;
import org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript;
import org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.TestGroovyRecorder;
import org.jenkinsci.plugins.scriptsecurity.scripts.languages.GroovyLanguage;
import org.jenkinsci.plugins.scriptsecurity.scripts.metadata.HashAndFullScriptMetadata;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.LoggerRule;
import org.jvnet.hudson.test.MockAuthorizationStrategy;
import org.jvnet.hudson.test.recipes.LocalData;
import org.xml.sax.SAXException;

import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
import static org.hamcrest.Matchers.arrayWithSize;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

public class ScriptApprovalTest extends AbstractApprovalTest<ScriptApprovalTest.Script> {
Expand Down Expand Up @@ -79,7 +97,7 @@ public void malformedScriptApproval() throws Exception {
} catch (Exception e) {
// ignore - we want to make sure we're logging this properly.
}
assertThat(logging.getRecords(), Matchers.hasSize(Matchers.equalTo(1)));
assertThat(logging.getRecords(), hasSize(equalTo(1)));
assertEquals("Malformed signature entry in scriptApproval.xml: ' new java.lang.Exception java.lang.String'",
logging.getRecords().get(0).getMessage());
}
Expand All @@ -101,14 +119,14 @@ public void malformedScriptApproval() throws Exception {
assertEquals(expectedLinkCount, scriptApprovalLinks.size()); // the icon link and the textual link

String managePageBodyText = managePage.getBody().getTextContent();
assertThat(managePageBodyText, Matchers.containsString("1 dangerous signatures previously approved which ought not have been."));
assertThat(managePageBodyText, containsString("1 dangerous signatures previously approved which ought not have been."));

HtmlPage scriptApprovalPage = managePage.getAnchorByHref("scriptApproval").click();
HtmlTextArea approvedTextArea = scriptApprovalPage.getHtmlElementById("approvedSignatures");
HtmlTextArea dangerousTextArea = scriptApprovalPage.getHtmlElementById("dangerousApprovedSignatures");

assertThat(approvedTextArea.getTextContent(), Matchers.containsString(DANGEROUS_SIGNATURE));
assertThat(dangerousTextArea.getTextContent(), Matchers.containsString(DANGEROUS_SIGNATURE));
assertThat(approvedTextArea.getTextContent(), containsString(DANGEROUS_SIGNATURE));
assertThat(dangerousTextArea.getTextContent(), containsString(DANGEROUS_SIGNATURE));
}

@Test public void nothingHappening() throws Exception {
Expand Down Expand Up @@ -193,6 +211,75 @@ String getClearAllApprovedId() {
return CLEAR_ALL_ID;
}

@Test
@Issue("JENKINS-62448")
@LocalData("legacyApproval")
public void legacyAreStillRecognized() {
List<HashAndFullScriptMetadata> approvedFullScriptMetadata = ScriptApproval.get().getApprovedFullScriptMetadata();
assertThat(approvedFullScriptMetadata, hasSize(6));
Optional<HashAndFullScriptMetadata> helloScript = approvedFullScriptMetadata.stream().filter(m -> m.hash.equals("ca57380cdd93d5cbff29daf6951e425d05908ea1")).findFirst();
assertTrue("No hash present for the echo Hello", helloScript.isPresent());

assertThat(ScriptApproval.get().getApprovedSignatures(), arrayWithSize(2));
assertThat(ScriptApproval.get().getApprovedSignatures(), arrayContainingInAnyOrder(
"staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods println java.lang.Object java.lang.Object",
"new java.io.File java.lang.String java.lang.String"
));

assertThat(ScriptApproval.get().getDangerousApprovedSignatures(), arrayWithSize(1));
assertThat(ScriptApproval.get().getDangerousApprovedSignatures()[0], equalTo("new java.io.File java.lang.String java.lang.String"));

assertThat(ScriptApproval.get().getAclApprovedSignatures(), arrayWithSize(1));
assertThat(ScriptApproval.get().getAclApprovedSignatures()[0], equalTo("new java.io.File java.lang.String"));

assertThat(ScriptApproval.get().getPendingSignatures(), hasSize(1));
assertThat(ScriptApproval.get().getPendingSignatures().iterator().next().signature, equalTo("method java.io.File length"));

assertThat(ScriptApproval.get().getApprovedClasspathEntries(), hasSize(1));
assertThat(ScriptApproval.get().getApprovedClasspathEntries().get(0).getHash(), equalTo("c8a65bd626dd7b34fc329434c9c1e728a4abe828"));
assertThat(ScriptApproval.get().getApprovedClasspathEntries().get(0).getURL().toString(), equalTo("https://repo.jenkins-ci.org/javanet2-cache/org/jvnet/hudson/main/maven-plugin/1.301/maven-plugin-1.301.hpi"));

assertThat(ScriptApproval.get().getPendingClasspathEntries(), hasSize(1));
assertThat(ScriptApproval.get().getPendingClasspathEntries().get(0).getHash(), equalTo("7f014e0dab147d3ae431efa1b8b1305112711b18"));
assertThat(ScriptApproval.get().getPendingClasspathEntries().get(0).getURL().toString(), equalTo("https://repo.jenkins-ci.org/javanet2-cache/org/jvnet/hudson/main/maven-plugin/1.302/maven-plugin-1.302.hpi"));
assertThat(ScriptApproval.get().getPendingClasspathEntries().get(0).getContext().getUser(), equalTo("config"));

assertThat(ScriptApproval.get().getPendingScriptsSorted(), hasSize(2));
}

@Test
@Issue("JENKINS-62448")
@LocalData("legacyApproval")
public void legacyIsEnhancedWithMetadataAfterUse() throws Exception {
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy()
.grant(Jenkins.ADMINISTER).everywhere().to("admin")
.grant(Permission.READ, Job.CREATE).everywhere().to("config"));

JenkinsRule.WebClient wc = r.createWebClient();
wc.login("config", "config");

Optional<HashAndFullScriptMetadata> executeScriptBefore = ScriptApproval.get().getApprovedFullScriptMetadata().stream()
.filter(m -> m.hash.equals("d224435330553e6054e66fbe050cfafafeadc732"))
.findFirst();
assertTrue(executeScriptBefore.isPresent());
assertTrue(executeScriptBefore.get().metadata.isEmpty());
assertThat(ScriptApproval.get().metadataStorage.readScript("d224435330553e6054e66fbe050cfafafeadc732"), nullValue());

FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(
new SecureGroovyScript("jenkins.model.Jenkins.instance", false, null)));

p.scheduleBuild2(0).get();

Optional<HashAndFullScriptMetadata> executeScriptAfter = ScriptApproval.get().getApprovedFullScriptMetadata().stream()
.filter(m -> m.hash.equals("d224435330553e6054e66fbe050cfafafeadc732"))
.findFirst();
assertTrue(executeScriptAfter.isPresent());
assertFalse(executeScriptAfter.get().metadata.isEmpty());
assertThat(ScriptApproval.get().metadataStorage.readScript("d224435330553e6054e66fbe050cfafafeadc732"), equalTo("jenkins.model.Jenkins.instance"));
}

static final class Script extends Approvable<Script> {
private final String groovy;
private final String hash;
Expand Down Expand Up @@ -283,5 +370,4 @@ public String toString() {
return String.format("Script[%s]", groovy);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?xml version='1.1' encoding='UTF-8'?>
<scriptApproval plugin="[email protected]">
<approvedScriptHashes>
<!--###############################
pipeline {
agent { label 'agent' }

stages {
stage('Hello') {
steps {
sh 'echo Hello'
}
}
}
}
###############################-->
<string>ca57380cdd93d5cbff29daf6951e425d05908ea1</string>
<!--###############################
"pwd".execute()
###############################-->
<string>146947747e2b865e621389bbb8a45fdb379e5f8d</string>
<!--###############################
jenkins.model.Jenkins.instance
###############################-->
<string>d224435330553e6054e66fbe050cfafafeadc732</string>
<string>ade57cf30c3bd49dba1ed236844b7a8cb532988e</string>
<string>b4aedcd3a5c832ea3ebfc0a1c104e48eb2d74477</string>
<string>b841429a248cfccb551582bbfbcf2e72ba4faf0f</string>
</approvedScriptHashes>
<approvedSignatures>
<string>staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods println java.lang.Object java.lang.Object</string>
<!-- new File("parent", "child") -->
<string>new java.io.File java.lang.String java.lang.String</string>
</approvedSignatures>
<aclApprovedSignatures>
<!-- new File(".") -->
<string>new java.io.File java.lang.String</string>
</aclApprovedSignatures>
<approvedClasspathEntries>
<approvedClasspathEntry>
<hash>c8a65bd626dd7b34fc329434c9c1e728a4abe828</hash>
<url>https://repo.jenkins-ci.org/javanet2-cache/org/jvnet/hudson/main/maven-plugin/1.301/maven-plugin-1.301.hpi</url>
</approvedClasspathEntry>
</approvedClasspathEntries>
<pendingScripts>
<pendingScript>
<script>
node {
echo &apos;Hello&apos;
}
</script>
<language>groovy</language>
</pendingScript>
<pendingScript>
<script>
node {
sh &apos;&apos;&apos;
rm -rf newFolder
mkdir newFolder
cd newFolder
}
}
</script>
<language>groovy</language>
</pendingScript>
</pendingScripts>
<pendingSignatures>
<pendingSignature>
<context/>
<signature>method java.io.File length</signature>
<dangerous>false</dangerous>
</pendingSignature>
</pendingSignatures>
<pendingClasspathEntries>
<pendingClasspathEntry>
<context>
<user>config</user>
<item>free-for-classpath</item>
</context>
<hash>7f014e0dab147d3ae431efa1b8b1305112711b18</hash>
<url>https://repo.jenkins-ci.org/javanet2-cache/org/jvnet/hudson/main/maven-plugin/1.302/maven-plugin-1.302.hpi</url>
</pendingClasspathEntry>
</pendingClasspathEntries>
</scriptApproval>
Wadeck marked this conversation as resolved.
Show resolved Hide resolved