diff --git a/pom.xml b/pom.xml
index 17309b31ad..33b36731db 100644
--- a/pom.xml
+++ b/pom.xml
@@ -261,6 +261,17 @@
+
+ commons-validator
+ commons-validator
+ 1.6
+
+
+ commons-digester
+ commons-digester
+
+
+
diff --git a/src/main/java/hudson/plugins/git/browser/AssemblaWeb.java b/src/main/java/hudson/plugins/git/browser/AssemblaWeb.java
index a6627fae84..aa05edf63b 100644
--- a/src/main/java/hudson/plugins/git/browser/AssemblaWeb.java
+++ b/src/main/java/hudson/plugins/git/browser/AssemblaWeb.java
@@ -1,15 +1,19 @@
package hudson.plugins.git.browser;
import hudson.Extension;
+import hudson.Util;
import hudson.model.Descriptor;
+import hudson.model.Item;
import hudson.plugins.git.GitChangeSet;
import hudson.plugins.git.GitChangeSet.Path;
+import hudson.plugins.git.Messages;
import hudson.scm.EditType;
import hudson.scm.RepositoryBrowser;
import hudson.util.FormValidation;
import hudson.util.FormValidation.URLCheck;
-import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
+import org.apache.commons.validator.routines.UrlValidator;
+import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.interceptor.RequirePOST;
import org.kohsuke.stapler.QueryParameter;
@@ -18,6 +22,8 @@
import javax.annotation.Nonnull;
import javax.servlet.ServletException;
import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.net.URL;
/**
@@ -94,18 +100,21 @@ public AssemblaWeb newInstance(StaplerRequest req, @Nonnull JSONObject jsonObjec
}
@RequirePOST
- public FormValidation doCheckUrl(@QueryParameter(fixEmpty = true) final String url)
- throws IOException, ServletException {
- if (url == null) // nothing entered yet
+ public FormValidation doCheckRepoUrl(@AncestorInPath Item project, @QueryParameter(fixEmpty = true) final String repoUrl)
+ throws IOException, ServletException, URISyntaxException {
+
+ String cleanUrl = Util.fixEmptyAndTrim(repoUrl);
+ if (initialChecksAndReturnOk(project, cleanUrl))
{
return FormValidation.ok();
}
- // Connect to URL and check content only if we have admin permission
- if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER))
- return FormValidation.ok();
+ // Connect to URL and check content only if we have permission
+ if (!checkURIFormatAndHostName(cleanUrl, "assembla")) {
+ return FormValidation.error(Messages.invalidUrl());
+ }
return new URLCheck() {
protected FormValidation check() throws IOException, ServletException {
- String v = url;
+ String v = cleanUrl;
if (!v.endsWith("/")) {
v += '/';
}
@@ -114,7 +123,7 @@ protected FormValidation check() throws IOException, ServletException {
if (findText(open(new URL(v)), "Assembla")) {
return FormValidation.ok();
} else {
- return FormValidation.error("This is a valid URL but it doesn't look like Assembla");
+ return FormValidation.error("This is a valid URL but it does not look like Assembla");
}
} catch (IOException e) {
return handleIOException(v, e);
@@ -122,5 +131,13 @@ protected FormValidation check() throws IOException, ServletException {
}
}.check();
}
+
+ private boolean checkURIFormatAndHostName(String url, String hostNameFragment) throws URISyntaxException {
+ URI uri = new URI(url);
+ String[] schemes = {"http", "https"};
+ UrlValidator urlValidator = new UrlValidator(schemes);
+ hostNameFragment = hostNameFragment + ".";
+ return urlValidator.isValid(uri.toString()) && uri.getHost().contains(hostNameFragment);
+ }
}
}
diff --git a/src/main/java/hudson/plugins/git/browser/GitBlitRepositoryBrowser.java b/src/main/java/hudson/plugins/git/browser/GitBlitRepositoryBrowser.java
index bfd8c4331b..5a4706a32f 100644
--- a/src/main/java/hudson/plugins/git/browser/GitBlitRepositoryBrowser.java
+++ b/src/main/java/hudson/plugins/git/browser/GitBlitRepositoryBrowser.java
@@ -1,15 +1,18 @@
package hudson.plugins.git.browser;
import hudson.Extension;
+import hudson.Util;
import hudson.model.Descriptor;
+import hudson.model.Item;
import hudson.plugins.git.GitChangeSet;
import hudson.plugins.git.GitChangeSet.Path;
+import hudson.plugins.git.Messages;
import hudson.scm.EditType;
import hudson.scm.RepositoryBrowser;
import hudson.util.FormValidation;
import hudson.util.FormValidation.URLCheck;
-import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
+import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.interceptor.RequirePOST;
import org.kohsuke.stapler.QueryParameter;
@@ -19,6 +22,7 @@
import javax.servlet.ServletException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
+import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
@@ -66,6 +70,7 @@ public String getProjectName() {
private String encodeString(final String s) throws UnsupportedEncodingException {
return URLEncoder.encode(s, "UTF-8").replaceAll("\\+", "%20");
}
+
@Extension
public static class ViewGitWebDescriptor extends Descriptor> {
@Nonnull
@@ -80,18 +85,21 @@ public GitBlitRepositoryBrowser newInstance(StaplerRequest req, @Nonnull JSONObj
}
@RequirePOST
- public FormValidation doCheckUrl(@QueryParameter(fixEmpty = true) final String url)
- throws IOException, ServletException {
- if (url == null) // nothing entered yet
+ public FormValidation doCheckRepoUrl(@AncestorInPath Item project, @QueryParameter(fixEmpty = true) final String repoUrl)
+ throws IOException, ServletException, URISyntaxException {
+
+ String cleanUrl = Util.fixEmptyAndTrim(repoUrl);
+ if (initialChecksAndReturnOk(project, cleanUrl))
{
return FormValidation.ok();
}
- // Connect to URL and check content only if we have admin permission
- if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER))
- return FormValidation.ok();
+ if (!checkURIFormat(cleanUrl))
+ {
+ return FormValidation.error(Messages.invalidUrl());
+ }
return new URLCheck() {
protected FormValidation check() throws IOException, ServletException {
- String v = url;
+ String v = cleanUrl;
if (!v.endsWith("/")) {
v += '/';
}
diff --git a/src/main/java/hudson/plugins/git/browser/GitRepositoryBrowser.java b/src/main/java/hudson/plugins/git/browser/GitRepositoryBrowser.java
index e23661756c..b5b22d5a5d 100644
--- a/src/main/java/hudson/plugins/git/browser/GitRepositoryBrowser.java
+++ b/src/main/java/hudson/plugins/git/browser/GitRepositoryBrowser.java
@@ -1,12 +1,14 @@
package hudson.plugins.git.browser;
import hudson.EnvVars;
+import hudson.model.Item;
import hudson.model.Job;
import hudson.model.TaskListener;
import hudson.plugins.git.GitChangeSet;
import hudson.plugins.git.GitChangeSet.Path;
import hudson.scm.RepositoryBrowser;
+import org.apache.commons.validator.routines.UrlValidator;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
@@ -117,5 +119,25 @@ public static URL encodeURL(URL url) throws IOException {
}
}
+ protected static boolean initialChecksAndReturnOk(Item project, String cleanUrl){
+ if (cleanUrl == null) {
+ return true;
+ }
+ if (project == null || !project.hasPermission(Item.CONFIGURE)) {
+ return true;
+ }
+ if (cleanUrl.contains("$")) {
+ // set by variable, can't validate
+ return true;
+ }
+ return false;
+ }
+
+ protected static boolean checkURIFormat(String url) throws URISyntaxException {
+ String[] schemes = {"http", "https"};
+ UrlValidator urlValidator = new UrlValidator(schemes);
+ return urlValidator.isValid(url);
+ }
+
private static final long serialVersionUID = 1L;
}
diff --git a/src/main/java/hudson/plugins/git/browser/Gitiles.java b/src/main/java/hudson/plugins/git/browser/Gitiles.java
index 9768e4b1f2..acf32dbfa8 100644
--- a/src/main/java/hudson/plugins/git/browser/Gitiles.java
+++ b/src/main/java/hudson/plugins/git/browser/Gitiles.java
@@ -1,16 +1,18 @@
package hudson.plugins.git.browser;
import hudson.Extension;
+import hudson.Util;
import hudson.model.Descriptor;
+import hudson.model.Item;
import hudson.plugins.git.GitChangeSet;
import hudson.plugins.git.GitChangeSet.Path;
+import hudson.plugins.git.Messages;
import hudson.scm.RepositoryBrowser;
import hudson.util.FormValidation;
import hudson.util.FormValidation.URLCheck;
-import jenkins.model.Jenkins;
-
import java.io.IOException;
+import java.net.URISyntaxException;
import java.net.URL;
import javax.annotation.Nonnull;
@@ -18,6 +20,7 @@
import net.sf.json.JSONObject;
+import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.interceptor.RequirePOST;
import org.kohsuke.stapler.QueryParameter;
@@ -70,15 +73,19 @@ public Gitiles newInstance(StaplerRequest req, @Nonnull JSONObject jsonObject) t
}
@RequirePOST
- public FormValidation doCheckUrl(@QueryParameter(fixEmpty = true) final String url) throws IOException, ServletException {
- if (url == null) // nothing entered yet
- return FormValidation.ok();
- // Connect to URL and check content only if we have admin permission
- if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER))
+ public FormValidation doCheckRepoUrl(@AncestorInPath Item project, @QueryParameter(fixEmpty = true) final String repoUrl)
+ throws IOException, ServletException, URISyntaxException {
+
+ String cleanUrl = Util.fixEmptyAndTrim(repoUrl);
+ if(initialChecksAndReturnOk(project, cleanUrl)){
return FormValidation.ok();
+ }
+ if (!checkURIFormat(cleanUrl)) {
+ return FormValidation.error(Messages.invalidUrl());
+ }
return new URLCheck() {
protected FormValidation check() throws IOException, ServletException {
- String v = url;
+ String v = cleanUrl;
if (!v.endsWith("/"))
v += '/';
diff --git a/src/main/java/hudson/plugins/git/browser/ViewGitWeb.java b/src/main/java/hudson/plugins/git/browser/ViewGitWeb.java
index ad3c3cafa3..74234f8ea0 100644
--- a/src/main/java/hudson/plugins/git/browser/ViewGitWeb.java
+++ b/src/main/java/hudson/plugins/git/browser/ViewGitWeb.java
@@ -1,16 +1,19 @@
package hudson.plugins.git.browser;
import hudson.Extension;
+import hudson.Util;
import hudson.model.Descriptor;
+import hudson.model.Item;
import hudson.plugins.git.GitChangeSet;
import hudson.plugins.git.GitChangeSet.Path;
+import hudson.plugins.git.Messages;
import hudson.scm.EditType;
import hudson.scm.RepositoryBrowser;
import hudson.scm.browsers.QueryBuilder;
import hudson.util.FormValidation;
import hudson.util.FormValidation.URLCheck;
-import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
+import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.interceptor.RequirePOST;
import org.kohsuke.stapler.QueryParameter;
@@ -20,6 +23,7 @@
import javax.servlet.ServletException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
+import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
@@ -89,15 +93,19 @@ public ViewGitWeb newInstance(StaplerRequest req, @Nonnull JSONObject jsonObject
}
@RequirePOST
- public FormValidation doCheckUrl(@QueryParameter(fixEmpty = true) final String url) throws IOException, ServletException {
- if (url == null) // nothing entered yet
- return FormValidation.ok();
+ public FormValidation doCheckRepoUrl(@AncestorInPath Item project, @QueryParameter(fixEmpty = true) final String repoUrl)
+ throws IOException, ServletException, URISyntaxException {
+
+ String cleanUrl = Util.fixEmptyAndTrim(repoUrl);
// Connect to URL and check content only if we have admin permission
- if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER))
+ if (initialChecksAndReturnOk(project, cleanUrl))
return FormValidation.ok();
+ if (!checkURIFormat(cleanUrl)) {
+ return FormValidation.error(Messages.invalidUrl());
+ }
return new URLCheck() {
protected FormValidation check() throws IOException, ServletException {
- String v = url;
+ String v = cleanUrl;
if (!v.endsWith("/"))
v += '/';
diff --git a/src/main/resources/hudson/plugins/git/Messages.properties b/src/main/resources/hudson/plugins/git/Messages.properties
index 11401f4bd1..f64069d344 100644
--- a/src/main/resources/hudson/plugins/git/Messages.properties
+++ b/src/main/resources/hudson/plugins/git/Messages.properties
@@ -6,6 +6,7 @@ BuildChooser_BuildingLastRevision=No new revisions were found; the most-recently
UserRemoteConfig.FailedToConnect=Failed to connect to repository : {0}
UserRemoteConfig.CheckUrl.UrlIsNull=Please enter Git repository.
UserRemoteConfig.CheckRefSpec.InvalidRefSpec=Specification is invalid.
+invalidUrl=Invalid URL
GitPublisher.Check.TagName=Tag Name
GitPublisher.Check.BranchName=Branch Name
diff --git a/src/test/java/hudson/plugins/git/browser/AssemblaWebDoCheckURLTest.java b/src/test/java/hudson/plugins/git/browser/AssemblaWebDoCheckURLTest.java
new file mode 100644
index 0000000000..9f51080885
--- /dev/null
+++ b/src/test/java/hudson/plugins/git/browser/AssemblaWebDoCheckURLTest.java
@@ -0,0 +1,104 @@
+package hudson.plugins.git.browser;
+
+import hudson.model.FreeStyleProject;
+import hudson.util.FormValidation;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.jvnet.hudson.test.JenkinsRule;
+
+import static org.hamcrest.MatcherAssert.*;
+import static org.hamcrest.Matchers.*;
+
+public class AssemblaWebDoCheckURLTest {
+
+ @ClassRule
+ public static JenkinsRule r = new JenkinsRule();
+ private static int counter = 0;
+
+ private FreeStyleProject project;
+ private AssemblaWeb.AssemblaWebDescriptor assemblaWebDescriptor;
+
+ @Before
+ public void setProject() throws Exception {
+ project = r.createFreeStyleProject("assembla-project-" + counter++);
+ assemblaWebDescriptor = new AssemblaWeb.AssemblaWebDescriptor();
+ }
+
+ @Test
+ public void testInitialChecksOnRepoUrl() throws Exception {
+ String url = "https://app.assembla.com/spaces/git-plugin/git/source";
+ assertThat(assemblaWebDescriptor.doCheckRepoUrl(project, url), is(FormValidation.ok()));
+ }
+
+ @Test
+ public void testInitialChecksOnRepoUrlEmpty() throws Exception {
+ String url = "";
+ assertThat(assemblaWebDescriptor.doCheckRepoUrl(project, url), is(FormValidation.ok()));
+ }
+
+ @Test
+ public void testInitialChecksOnRepoUrlWithVariable() throws Exception {
+ String url = "https://www.assembla.com/spaces/$";
+ assertThat(assemblaWebDescriptor.doCheckRepoUrl(project, url), is(FormValidation.ok()));
+ }
+
+ @Test
+ public void testDomainLevelChecksOnRepoUrl() throws Exception {
+ // Invalid URL, missing '/' character - Earlier it would open connection for such mistakes but now check resolves it beforehand.
+ String url = "https:/assembla.com";
+ assertThat(assemblaWebDescriptor.doCheckRepoUrl(project, url).getLocalizedMessage(), is("Invalid URL"));
+ }
+
+ @Test
+ public void testDomainLevelChecksOnRepoUrlInvalidURL() throws Exception {
+ // Invalid URL, missing ':' character - Earlier it would open connection for such mistakes but now check resolves it beforehand.
+ String url = "http//assmebla";
+ assertThat(assemblaWebDescriptor.doCheckRepoUrl(project, url).getLocalizedMessage(), is("Invalid URL"));
+ }
+
+ @Test
+ public void testPathLevelChecksOnRepoUrlInvalidPathSyntax() throws Exception {
+ // Invalid hostname in URL
+ String url = "https://assembla.comspaces/git-plugin/git/source";
+ assertThat(assemblaWebDescriptor.doCheckRepoUrl(project, url).getLocalizedMessage(), is("Invalid URL"));
+ }
+
+ @Test
+ public void testPathLevelChecksOnRepoUrlValidURLNullProject() throws Exception {
+ String url = "https://app.assembla.com/space/git-plugin/git/source";
+ assertThat(assemblaWebDescriptor.doCheckRepoUrl(null, url), is(FormValidation.ok()));
+ }
+
+ @Test
+ public void testPathLevelChecksOnRepoUrlUnableToConnect() throws Exception {
+ // Syntax issue related specific to Assembla
+ String url = "https://app.assembla.com/space/git-plugin/git/source";
+ assertThat(assemblaWebDescriptor.doCheckRepoUrl(project, url).getLocalizedMessage(),
+ is("Unable to connect https://app.assembla.com/space/git-plugin/git/source/"));
+ }
+
+ @Test
+ public void testPathLevelChecksOnRepoUrl() throws Exception {
+ // Any path related errors will not be caught except syntax issues
+ String url = "https://app.assembla.com/spaces/";
+ assertThat(assemblaWebDescriptor.doCheckRepoUrl(project, url), is(FormValidation.ok()));
+ }
+
+ @Test
+ public void testPathLevelChecksOnRepoUrlSupersetOfAssembla() throws Exception {
+ java.util.Random random = new java.util.Random();
+ String [] urls = {
+ "http://assemblage.com/",
+ "http://assemblage.net/",
+ "http://assemblage.org/",
+ "http://assemblages.com/",
+ "http://assemblages.net/",
+ "http://assemblages.org/",
+ "http://assemblagist.com/",
+ };
+ String url = urls[random.nextInt(urls.length)]; // Don't abuse a single web site with tests
+ assertThat(assemblaWebDescriptor.doCheckRepoUrl(project, url).getLocalizedMessage(),
+ is("Invalid URL"));
+ }
+}