Skip to content

Commit

Permalink
Enforce that there are no CSP violations when csp.rule is defined (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
basil authored Oct 25, 2024
1 parent 6b05a06 commit ebdf63f
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 5 deletions.
31 changes: 26 additions & 5 deletions src/main/java/org/jenkinsci/test/acceptance/junit/CspRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.google.inject.Inject;
import com.google.inject.Injector;
import java.util.List;
import org.jenkinsci.test.acceptance.plugins.csp.ContentSecurityPolicyReport;
import org.jenkinsci.test.acceptance.po.GlobalSecurityConfig;
import org.jenkinsci.test.acceptance.po.Jenkins;
import org.jenkinsci.test.acceptance.update_center.PluginSpec;
Expand All @@ -20,11 +22,9 @@ public Statement apply(final Statement base, final Description d) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
if (isEnabled()
&& d.getAnnotation(WithInstallWizard.class) == null
&& d.getTestClass().getAnnotation(WithInstallWizard.class) == null) {
Jenkins jenkins = injector.getInstance(Jenkins.class);
Jenkins jenkins = injector.getInstance(Jenkins.class);

if (isEnabled() && !isSkipped()) {
PluginSpec plugin = new PluginSpec("csp");
jenkins.getPluginManager().installPlugins(plugin);

Expand All @@ -33,7 +33,28 @@ public void evaluate() throws Throwable {
security.disableCspReportOnly();
security.save();
}
base.evaluate();
try {
base.evaluate();
} finally {
// TODO enable for ArtifactoryPluginTest when JENKINS-74047 is resolved
// TODO enable for SubversionPluginTest when JENKINS-73900 is resolved
if (isEnabled()
&& !isSkipped()
&& !d.getTestClass().getName().equals("plugins.ArtifactoryPluginTest")
&& !d.getTestClass().getName().equals("plugins.SubversionPluginTest")) {
ContentSecurityPolicyReport csp = new ContentSecurityPolicyReport(jenkins);
csp.open();
List<String> lines = csp.getReport();
if (lines.size() > 2) {
throw new AssertionError(String.join("\n", lines));
}
}
}
}

private boolean isSkipped() {
return d.getAnnotation(WithInstallWizard.class) != null
|| d.getTestClass().getAnnotation(WithInstallWizard.class) != null;
}

private static boolean isEnabled() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.jenkinsci.test.acceptance.plugins.csp;

import java.util.ArrayList;
import java.util.List;
import org.jenkinsci.test.acceptance.po.Jenkins;
import org.jenkinsci.test.acceptance.po.PageObject;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;

public class ContentSecurityPolicyReport extends PageObject {
public ContentSecurityPolicyReport(Jenkins context) {
super(context, context.url("content-security-policy-reports/"));
}

public List<String> getReport() {
List<String> lines = new ArrayList<>();
WebElement table = find(By.className("bigtable"));
List<WebElement> headers = table.findElements(By.tagName("th"));
StringBuilder sb = new StringBuilder();
for (WebElement header : headers) {
sb.append(header.getText()).append("\t");
}
lines.add(sb.toString());
sb = new StringBuilder();
List<WebElement> rows = table.findElements(By.tagName("tr"));
for (WebElement row : rows) {
List<WebElement> cells = row.findElements(By.tagName("td"));
for (WebElement cell : cells) {
sb.append(cell.getText()).append("\t");
}
lines.add(sb.toString());
sb = new StringBuilder();
}
return lines;
}
}
9 changes: 9 additions & 0 deletions src/test/java/core/PublisherOrderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.jenkinsci.test.acceptance.po.FreeStyleJob;
import org.jenkinsci.test.acceptance.po.JUnitPublisher;
import org.junit.Test;
import org.openqa.selenium.By;

@WithPlugins("junit")
public class PublisherOrderTest extends AbstractJUnitTest {
Expand Down Expand Up @@ -59,5 +60,13 @@ public void testUnordered() {
archiver.includes("another.txt");
JUnitPublisher junit = upstream.addPublisher(JUnitPublisher.class);
fingerprint.targets.set("yetanother");

/*
* Navigate back to the dashboard first to dismiss the alert so that CspRule can check for violations (see
* FormValidationTest).
*/
jenkins.runThenConfirmAlert(() -> driver.findElement(By.xpath("//ol[@id=\"breadcrumbs\"]/li[1]/a"))
.click());
sleep(1000);
}
}
9 changes: 9 additions & 0 deletions src/test/java/plugins/ArtifactoryPluginTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.jvnet.hudson.test.Issue;
import org.openqa.selenium.By;

/**
* Checks the successful integration of Artifactory plugin.
Expand Down Expand Up @@ -65,6 +66,14 @@ public void check_config_is_persisted() {
hasContent(
Pattern.compile(
"Error occurred while requesting version information: Connection( to http://localhost:4898)* refused")));

/*
* Navigate back to the dashboard first to dismiss the alert so that CspRule can check for violations (see
* FormValidationTest).
*/
jenkins.runThenConfirmAlert(() -> driver.findElement(By.xpath("//ol[@id=\"breadcrumbs\"]/li[1]/a"))
.click());
sleep(1000);
}

@Test
Expand Down
20 changes: 20 additions & 0 deletions src/test/java/plugins/JobDslPluginTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,10 @@ public void should_use_script_security() {

// Build should succeed because script is approved now
seedJob.scheduleBuild().shouldSucceed();

// Switch back to an admin user so that CspRule can check for violations.
jenkins.logout();
jenkins.login().doLogin(ADMIN);
}

/**
Expand Down Expand Up @@ -622,6 +626,10 @@ public void should_use_script_approval() {

// Build should succeed because script is approved now
seedJob.scheduleBuild().shouldSucceed();

// Switch back to an admin user so that CspRule can check for violations.
jenkins.logout();
jenkins.login().doLogin(ADMIN);
}

/**
Expand Down Expand Up @@ -651,6 +659,10 @@ public void should_use_grooy_sandbox_whitelisted_content() {
// Build should succeed because the script runs in Groovy sandbox
// and only Job DSL methods are used.
seedJob.scheduleBuild().shouldSucceed();

// Switch back to an admin user so that CspRule can check for violations.
jenkins.logout();
jenkins.login().doLogin(ADMIN);
}

/**
Expand Down Expand Up @@ -694,6 +706,10 @@ public void should_use_grooy_sandbox_no_whitelisted_content() {

// Build should succeed because the not whitelisted content was approved.
seedJob.scheduleBuild().shouldSucceed();

// Switch back to an admin user so that CspRule can check for violations.
jenkins.logout();
jenkins.login().doLogin(ADMIN);
}

/**
Expand Down Expand Up @@ -725,6 +741,10 @@ public void should_run_grooy_sandbox_as_particular_user() {
jenkins.login().doLogin(USER);
// Build should succeed because now a particular user is specified
seedJob.scheduleBuild().shouldSucceed();

// Switch back to an admin user so that CspRule can check for violations.
jenkins.logout();
jenkins.login().doLogin(ADMIN);
}

/**
Expand Down
9 changes: 9 additions & 0 deletions src/test/java/plugins/MailerPluginTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.openqa.selenium.By;

@WithPlugins("mailer")
@Category(DockerTest.class)
Expand All @@ -41,6 +42,14 @@ public void send_test_mail() throws IOException {
Pattern.compile("Test email #1"),
"admin@example.com",
Pattern.compile("This is test email #1 sent from Jenkins"));

/*
* Navigate back to the dashboard first to dismiss the alert so that CspRule can check for violations (see
* FormValidationTest).
*/
jenkins.runThenConfirmAlert(() -> driver.findElement(By.xpath("//ol[@id=\"breadcrumbs\"]/li[1]/a"))
.click());
sleep(1000);
}

@Test
Expand Down
4 changes: 4 additions & 0 deletions src/test/java/plugins/MatrixAuthPluginTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,5 +132,9 @@ public void projectMatrixAuth() throws Exception {
jenkins.login().doLogin("bob");

assertTrue(j.open().getTitle().contains(j.name));

// Switch back to an admin user so that CspRule can check for violations.
jenkins.logout();
jenkins.login().doLogin("alice");
}
}
17 changes: 17 additions & 0 deletions src/test/java/plugins/PlainCredentialsBindingTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.jenkinsci.test.acceptance.po.FreeStyleJob;
import org.jenkinsci.test.acceptance.po.ShellBuildStep;
import org.junit.Test;
import org.openqa.selenium.By;

/**
* Tests the plain-credentials and credentials-binding plugins together.
Expand All @@ -69,11 +70,27 @@ public void globalSecretTextCredentialTest() throws URISyntaxException {
@Test
public void systemSecretFileCredentialTest() throws URISyntaxException {
createAndUseCredential(SYSTEM_SCOPE, FileCredentials.class);

/*
* Navigate back to the dashboard first to dismiss the alert so that CspRule can check for violations (see
* FormValidationTest).
*/
jenkins.runThenConfirmAlert(() -> driver.findElement(By.xpath("//ol[@id=\"breadcrumbs\"]/li[1]/a"))
.click());
sleep(1000);
}

@Test
public void systemSecretTextCredentialTest() throws URISyntaxException {
createAndUseCredential(SYSTEM_SCOPE, StringCredentials.class);

/*
* Navigate back to the dashboard first to dismiss the alert so that CspRule can check for violations (see
* FormValidationTest).
*/
jenkins.runThenConfirmAlert(() -> driver.findElement(By.xpath("//ol[@id=\"breadcrumbs\"]/li[1]/a"))
.click());
sleep(1000);
}

@Test
Expand Down
16 changes: 16 additions & 0 deletions src/test/java/plugins/ScriptSecurityPluginTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ public void scriptNeedsApproval() throws Exception {
sa.find(job.name).approve();
}
shouldSucceed(job); // Script approved

// Switch back to an admin user so that CspRule can check for violations.
jenkins.logout();
login(ADMIN);
}

@Test
Expand All @@ -127,6 +131,10 @@ public void signatureNeedsApproval() throws Exception {
sa.findSignature("getProperties").approve();
}
shouldSucceed(job); // Script approved

// Switch back to an admin user so that CspRule can check for violations.
jenkins.logout();
login(ADMIN);
}

@Test
Expand All @@ -140,6 +148,10 @@ public void pipelineNeedsApproval() throws Exception {
sa.find(job.name).approve();
}
shouldSucceed(job); // Script approved

// Switch back to an admin user so that CspRule can check for violations.
jenkins.logout();
login(ADMIN);
}

@Test
Expand All @@ -153,5 +165,9 @@ public void pipelineSignatureNeedsApproval() throws Exception {
sa.findSignature("getProperty").approve();
}
shouldSucceed(job); // Script approved

// Switch back to an admin user so that CspRule can check for violations.
jenkins.logout();
login(ADMIN);
}
}
9 changes: 9 additions & 0 deletions src/test/java/plugins/SshSlavesPluginTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.jvnet.hudson.test.Issue;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;

@WithPlugins({"ssh-slaves", "credentials", "ssh-credentials"})
Expand Down Expand Up @@ -146,6 +147,14 @@ public void newSlaveWithExistingCredential() throws Exception {
l.host.set("127.0.0.1");

l.credentialsId.select(String.format("%s (%s)", username, description));

/*
* Navigate back to the dashboard first to dismiss the alert so that CspRule can check for violations (see
* FormValidationTest).
*/
jenkins.runThenConfirmAlert(() -> driver.findElement(By.xpath("//ol[@id=\"breadcrumbs\"]/li[1]/a"))
.click());
sleep(1000);
}

private void verifyValueForCredential(CredentialsPage cp, Control element, String expected) {
Expand Down

0 comments on commit ebdf63f

Please sign in to comment.