Skip to content

Commit 99333c0

Browse files
authored
Merge pull request #545 from Vlatombe/fix-load
Fix reloading configuration from disk
2 parents 48317e9 + ed5b0bf commit 99333c0

File tree

4 files changed

+86
-18
lines changed

4 files changed

+86
-18
lines changed

src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package org.jenkinsci.plugins.scriptsecurity.scripts;
2626

2727
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
28+
import hudson.init.InitMilestone;
2829
import hudson.model.BallColor;
2930
import hudson.model.PageDecorator;
3031
import hudson.security.ACLContext;
@@ -268,7 +269,7 @@ String hashClasspathEntry(URL entry) throws IOException {
268269
/** All external classpath entries allowed used for scripts. */
269270
private /*final*/ TreeSet<ApprovedClasspathEntry> approvedClasspathEntries;
270271

271-
/* for test */ void addApprovedClasspathEntry(ApprovedClasspathEntry acp) {
272+
/* for test */ synchronized void addApprovedClasspathEntry(ApprovedClasspathEntry acp) {
272273
approvedClasspathEntries.add(acp);
273274
}
274275

@@ -503,16 +504,12 @@ private PendingClasspathEntry getPendingClasspathEntry(@NonNull String hash) {
503504
@DataBoundConstructor
504505
public ScriptApproval() {
505506
load();
506-
/* can be null when upgraded from old versions.*/
507-
if (aclApprovedSignatures == null) {
508-
aclApprovedSignatures = new TreeSet<>();
509-
}
510-
if (approvedClasspathEntries == null) {
511-
approvedClasspathEntries = new TreeSet<>();
512-
}
513-
if (pendingClasspathEntries == null) {
514-
pendingClasspathEntries = new TreeSet<>();
515-
}
507+
}
508+
509+
@Override
510+
public synchronized void load() {
511+
clear();
512+
super.load();
516513
// Check for loaded class directories
517514
boolean changed = false;
518515
int dcp = 0;
@@ -536,6 +533,40 @@ public ScriptApproval() {
536533
if (changed) {
537534
save();
538535
}
536+
// only call on subsequent load to avoid cycle
537+
if (Jenkins.get().getInitLevel() == InitMilestone.COMPLETED) {
538+
try {
539+
LOG.log(Level.FINE, "Reconfiguring ScriptApproval after loading configuration from disk");
540+
reconfigure();
541+
} catch (IOException e) {
542+
LOG.log(Level.WARNING, e, () -> "Failed to reconfigure ScriptApproval");
543+
}
544+
} else {
545+
LOG.log(Level.FINE, "Skipping reconfiguration of ScriptApproval during Jenkins startup sequence");
546+
}
547+
}
548+
549+
private void clear() {
550+
approvedScriptHashes.clear();
551+
approvedSignatures.clear();
552+
pendingScripts.clear();
553+
pendingSignatures.clear();
554+
/* can be null when upgraded from old versions.*/
555+
if (aclApprovedSignatures == null) {
556+
aclApprovedSignatures = new TreeSet<>();
557+
} else {
558+
aclApprovedSignatures.clear();
559+
}
560+
if (approvedClasspathEntries == null) {
561+
approvedClasspathEntries = new TreeSet<>();
562+
} else {
563+
approvedClasspathEntries.clear();
564+
}
565+
if (pendingClasspathEntries == null) {
566+
pendingClasspathEntries = new TreeSet<>();
567+
} else {
568+
pendingClasspathEntries.clear();
569+
}
539570
}
540571

541572
@Restricted(NoExternalUse.class)
@@ -571,7 +602,7 @@ public synchronized boolean hasDeprecatedApprovedClasspathHashes() {
571602
}
572603

573604
/** Nothing has ever been approved or is pending. */
574-
boolean isEmpty() {
605+
synchronized boolean isEmpty() {
575606
return approvedScriptHashes.isEmpty() &&
576607
approvedSignatures.isEmpty() &&
577608
aclApprovedSignatures.isEmpty() &&
@@ -1201,7 +1232,7 @@ public JSON getClasspathRenderInfo() {
12011232

12021233
@Restricted(NoExternalUse.class) // for use from AJAX
12031234
@JavaScriptMethod
1204-
public JSON approveClasspathEntry(String hash) throws IOException {
1235+
public synchronized JSON approveClasspathEntry(String hash) throws IOException {
12051236
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
12061237
URL url = null;
12071238
synchronized (this) {
@@ -1225,7 +1256,7 @@ public JSON approveClasspathEntry(String hash) throws IOException {
12251256

12261257
@Restricted(NoExternalUse.class) // for use from AJAX
12271258
@JavaScriptMethod
1228-
public JSON denyClasspathEntry(String hash) throws IOException {
1259+
public synchronized JSON denyClasspathEntry(String hash) throws IOException {
12291260
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
12301261
PendingClasspathEntry cp = getPendingClasspathEntry(hash);
12311262
if (cp != null) {
@@ -1237,7 +1268,7 @@ public JSON denyClasspathEntry(String hash) throws IOException {
12371268

12381269
@Restricted(NoExternalUse.class) // for use from AJAX
12391270
@JavaScriptMethod
1240-
public JSON denyApprovedClasspathEntry(String hash) throws IOException {
1271+
public synchronized JSON denyApprovedClasspathEntry(String hash) throws IOException {
12411272
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
12421273
if (approvedClasspathEntries.remove(new ApprovedClasspathEntry(hash, null))) {
12431274
save();

src/test/java/org/jenkinsci/plugins/scriptsecurity/scripts/HasherScriptApprovalTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
import java.util.logging.Level;
1616

1717
import static org.hamcrest.MatcherAssert.assertThat;
18-
import static org.hamcrest.Matchers.containsInAnyOrder;
1918
import static org.hamcrest.Matchers.containsInRelativeOrder;
2019
import static org.hamcrest.Matchers.containsString;
20+
import static org.hamcrest.Matchers.hasItem;
2121
import static org.hamcrest.Matchers.not;
2222
import static org.junit.Assert.assertEquals;
2323
import static org.junit.Assert.assertTrue;
@@ -51,7 +51,7 @@ public void warnsAndClearsDeprecatedScriptHashes() throws Throwable {
5151
session.then(r -> {
5252
final ScriptApproval approval = ScriptApproval.get();
5353
assertEquals(2, approval.countDeprecatedApprovedScriptHashes());
54-
assertThat(log.getMessages(), containsInAnyOrder(
54+
assertThat(log.getMessages(), hasItem(
5555
containsString("There are 2 deprecated approved script hashes " +
5656
"and 0 deprecated approved classpath hashes.")));
5757
approval.clearDeprecatedApprovedScripts();
@@ -102,7 +102,7 @@ public void testConvertApprovedClasspathEntries() throws Throwable {
102102
final ScriptApproval approval = ScriptApproval.get();
103103
assertEquals(2, approval.countDeprecatedApprovedClasspathHashes());
104104

105-
assertThat(log.getMessages(), containsInAnyOrder(
105+
assertThat(log.getMessages(), hasItem(
106106
containsString("There are 0 deprecated approved script hashes " +
107107
"and 2 deprecated approved classpath hashes.")));
108108

src/test/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApprovalTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,29 @@ public void upgradeSmokes() throws Exception {
176176
r.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()));
177177
}
178178

179+
@LocalData // Some scriptApproval.xml with existing signatures approved
180+
@Test
181+
public void reload() throws Exception {
182+
configureSecurity();
183+
ScriptApproval sa = ScriptApproval.get();
184+
185+
FreeStyleProject p = r.createFreeStyleProject();
186+
p.getPublishersList().add(new TestGroovyRecorder(
187+
new SecureGroovyScript("jenkins.model.Jenkins.instance", true, null)));
188+
p.getPublishersList().add(new TestGroovyRecorder(
189+
new SecureGroovyScript("println(jenkins.model.Jenkins.instance.getLabels())", false, null)));
190+
r.assertLogNotContains("org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: "
191+
+ "Scripts not permitted to use staticMethod jenkins.model.Jenkins getInstance",
192+
r.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()));
193+
r.assertLogNotContains("org.jenkinsci.plugins.scriptsecurity.scripts.UnapprovedUsageException: script not yet approved for use",
194+
r.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()));
195+
196+
ScriptApproval.get().getConfigFile().delete();
197+
sa.load();
198+
r.assertLogContains("org.jenkinsci.plugins.scriptsecurity.scripts.UnapprovedUsageException: script not yet approved for use",
199+
r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()));
200+
}
201+
179202
private Script script(String groovy) {
180203
return new Script(groovy);
181204
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<scriptApproval plugin="script-security@1.24">
3+
<approvedScriptHashes>
4+
<string>fccae58c5762bdd15daca97318e9d74333203106</string>
5+
</approvedScriptHashes>
6+
<approvedSignatures>
7+
<string>staticMethod jenkins.model.Jenkins getInstance</string>
8+
</approvedSignatures>
9+
<aclApprovedSignatures/>
10+
<approvedClasspathEntries/>
11+
<pendingScripts/>
12+
<pendingSignatures/>
13+
<pendingClasspathEntries/>
14+
</scriptApproval>

0 commit comments

Comments
 (0)