Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -33,6 +33,7 @@
import hudson.model.ComputerSet;
import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.model.Failure;
import hudson.model.Item;
import hudson.model.ManageJenkinsAction;
import hudson.model.ModelObject;
Expand Down Expand Up @@ -656,6 +657,13 @@
try {
Credentials credentials = Descriptor.bindJSON(req, Credentials.class,
data.getJSONObject("credentials"));
try {
CredentialsStoreAction.checkCredentialsId(credentials);
} catch (Failure f) {
return new JSONObject()
.element("message", f.getMessage())
.element("notificationType", "ERROR");
}

Check warning on line 666 in src/main/java/com/cloudbees/plugins/credentials/CredentialsSelectHelper.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 661-666 are not covered by tests
credentialsWereAdded = store.addCredentials(wrapper.getDomain(), credentials);
} catch (LinkageError e) {
/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,17 @@
return getACL().hasPermission(permission);
}

/**
* Checks that the id of the supplied credentials does not contain illegal characters.
*
* @param credentials the credentials to check.
*/
static void checkCredentialsId(Credentials credentials) {
if (credentials instanceof IdCredentials) {

Check warning on line 538 in src/main/java/com/cloudbees/plugins/credentials/CredentialsStoreAction.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 538 is only partially covered, one branch is missing
Jenkins.checkGoodName(((IdCredentials) credentials).getId());
}
}

/**
* A wrapper object to bind and expose {@link Domain} instances into the web UI.
*/
Expand Down Expand Up @@ -768,6 +779,16 @@
} else {
JSONObject data = req.getSubmittedForm();
Credentials credentials = Descriptor.bindJSON(req, Credentials.class, data.getJSONObject("credentials"));
try {
checkCredentialsId(credentials);
} catch (Failure f) {
if (jsonResponse) {
return HttpResponses.okJSON(new JSONObject()
.element("message", f.getMessage())
.element("notificationType", "ERROR"));
}
throw f;
}
boolean credentialsWereAdded = getStore().addCredentials(domain, credentials);

if (jsonResponse) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,41 @@ void doAddCredentialsFromPopupWorksAsExpected() throws Exception {
}
}

@Test
@Issue("JENKINS-75943")
void doAddCredentialsFromPopupRejectsIllegalId() throws Exception {
try (JenkinsRule.WebClient wc = j.createWebClient()) {
HtmlPage htmlPage = wc.goTo("credentials-selection");

HtmlButton addCredentialsButton = htmlPage.querySelector(".credentials-add-menu");
addCredentialsButton.click();

HtmlButton jenkinsCredentialsOption = htmlPage.querySelector(".jenkins-dropdown__item");
HtmlElementUtil.click(jenkinsCredentialsOption);

HtmlRadioButtonInput item = htmlPage.querySelector(".jenkins-choice-list__item input");
HtmlElementUtil.click(item);

HtmlButton formSubmitButton = htmlPage.querySelector("#cr-dialog-next");
HtmlElementUtil.click(formSubmitButton);

HtmlForm form = htmlPage.querySelector("#credentials-dialog-form");

HtmlInput username = form.querySelector("input[name='_.username']");
username.setValue("bob");
HtmlInput password = form.querySelector("input[name='_.password']");
password.setValue("secret");
HtmlInput id = form.querySelector("input[name='_.id']");
id.setValue("illegal?id");

formSubmitButton = htmlPage.querySelector("#cr-dialog-submit");
HtmlElementUtil.click(formSubmitButton);

List<UsernamePasswordCredentials> creds = CredentialsProvider.lookupCredentialsInItem(UsernamePasswordCredentials.class, null, ACL.SYSTEM2);
assertThat(creds, Matchers.hasSize(0));
}
}

@Test
@Issue("JENKINS-74964")
void doAddCredentialsFromPopupForPEMCertificateKeystore() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -19,6 +20,7 @@
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
import org.xmlunit.matchers.CompareMatcher;
Expand Down Expand Up @@ -292,6 +294,42 @@ void restCRUDNonHappy() throws Exception {
assertThat(con.getResponseCode(), is(HttpServletResponse.SC_CONFLICT));
}

@Test
@Issue("JENKINS-75943")
void createCredentialsFromFormRejectsIllegalId() throws Exception {
j.getInstance().setCrumbIssuer(null);
HttpURLConnection con = postCreateByForm(systemStore, null,
"{\"credentials\": {\"$class\": \"com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl\", "
+ "\"scope\": \"GLOBAL\", \"id\": \"valid.id\", \"description\": \"first\", "
+ "\"username\": \"bob\", \"password\": \"secret\"}}");
assertThat(con.getResponseCode(), is(HttpServletResponse.SC_FOUND));
assertThat(CredentialsMatchers.firstOrNull(systemStore.getCredentials(Domain.global()),
CredentialsMatchers.withId("valid.id")), notNullValue());

con = postCreateByForm(systemStore, null,
"{\"credentials\": {\"$class\": \"com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl\", "
+ "\"scope\": \"GLOBAL\", \"id\": \"illegal?id\", \"description\": \"second\", "
+ "\"username\": \"bob\", \"password\": \"secret\"}}");
assertThat(con.getResponseCode(), is(HttpServletResponse.SC_BAD_REQUEST));
assertThat(CredentialsMatchers.firstOrNull(systemStore.getCredentials(Domain.global()),
CredentialsMatchers.withId("illegal?id")), nullValue());
}

private HttpURLConnection postCreateByForm(CredentialsStore store, String domainName, String json)
throws IOException {
HttpURLConnection con = (HttpURLConnection) new URL(j.getURL(),
"credentials/store/" + store.getStoreAction().getUrlName() + "/domain/" + Util
.rawEncode(StringUtils.defaultIfBlank(domainName, "_")) + "/createCredentials")
.openConnection();
con.setRequestMethod("POST");
con.setInstanceFollowRedirects(false);
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
con.setDoOutput(true);
con.getOutputStream()
.write(("json=" + URLEncoder.encode(json, StandardCharsets.UTF_8)).getBytes(StandardCharsets.UTF_8));
return con;
}

private HttpURLConnection postCreateByXml(CredentialsStore store, String xml)
throws IOException {
HttpURLConnection con = (HttpURLConnection) new URL(j.getURL(),
Expand Down
Loading