Support format=TEXT for /+refs List refs in the same format in an /info/refs request for the git protocol, plus some minor sanitization to deal with XSS. Support scoping of refs by adding additional path components (e.g. /heads) after /+refs. This means allowing refs views with paths, but note that this only applies to the TEXT (and soon JSON) format, not HTML. Change-Id: I7c7074544af366f38791d5a90d0a024d2555c92e
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/RefServletTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/RefServletTest.java new file mode 100644 index 0000000..9e8bac4 --- /dev/null +++ b/gitiles-servlet/src/test/java/com/google/gitiles/RefServletTest.java
@@ -0,0 +1,177 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gitiles; + +import junit.framework.TestCase; + +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevTag; +import org.eclipse.jgit.storage.dfs.DfsRepository; +import org.eclipse.jgit.storage.dfs.DfsRepositoryDescription; +import org.eclipse.jgit.storage.dfs.InMemoryRepository; + +import java.io.IOException; + +/** Tests for {@link Linkifier}. */ +public class RefServletTest extends TestCase { + private TestRepository<DfsRepository> repo;; + private GitilesServlet servlet; + + @Override + protected void setUp() throws Exception { + DfsRepository r = new InMemoryRepository(new DfsRepositoryDescription("test")); + repo = new TestRepository<DfsRepository>(r); + + RevCommit commit = repo.branch("refs/heads/master").commit().create(); + repo.update("refs/heads/branch", commit); + repo.update("refs/tags/ctag", commit); + RevTag tag = repo.tag("atag", commit); + repo.update("refs/tags/atag", tag); + r.updateRef("HEAD").link("refs/heads/master"); + + servlet = TestGitilesServlet.create(repo); + } + + private String id(String refName) throws IOException { + return ObjectId.toString(repo.getRepository().getRef(refName).getObjectId()); + } + + private String peeled(String refName) throws IOException { + return ObjectId.toString(repo.getRepository().peel( + repo.getRepository().getRef(refName)).getPeeledObjectId()); + } + + public void testEvilRefName() throws Exception { + String evilRefName = "refs/evil/<script>window.close();</script>/&foo"; + assertTrue(Repository.isValidRefName(evilRefName)); + repo.branch(evilRefName).commit().create(); + + FakeHttpServletRequest req = FakeHttpServletRequest.newRequest(); + req.setPathInfo("/test/+refs/evil"); + req.setQueryString("format=TEXT"); + FakeHttpServletResponse res = new FakeHttpServletResponse(); + servlet.service(req, res); + + assertEquals( + id(evilRefName) + " refs/evil/<script>window.close();</script>/&foo\n", + res.getActualBodyString()); + } + + public void testGetRefsTextAll() throws Exception { + FakeHttpServletRequest req = FakeHttpServletRequest.newRequest(); + req.setPathInfo("/test/+refs"); + req.setQueryString("format=TEXT"); + FakeHttpServletResponse res = new FakeHttpServletResponse(); + servlet.service(req, res); + + assertEquals(200, res.getStatus()); + assertEquals( + id("HEAD") + " HEAD\n" + + id("refs/heads/branch") + " refs/heads/branch\n" + + id("refs/heads/master") + " refs/heads/master\n" + + id("refs/tags/atag") + " refs/tags/atag\n" + + peeled("refs/tags/atag") + " refs/tags/atag^{}\n" + + id("refs/tags/ctag") + " refs/tags/ctag\n", + res.getActualBodyString()); + } + + public void testGetRefsTextAllTrailingSlash() throws Exception { + FakeHttpServletRequest req = FakeHttpServletRequest.newRequest(); + req.setPathInfo("/test/+refs"); + req.setQueryString("format=TEXT"); + FakeHttpServletResponse res = new FakeHttpServletResponse(); + servlet.service(req, res); + + assertEquals(200, res.getStatus()); + assertEquals( + id("HEAD") + " HEAD\n" + + id("refs/heads/branch") + " refs/heads/branch\n" + + id("refs/heads/master") + " refs/heads/master\n" + + id("refs/tags/atag") + " refs/tags/atag\n" + + peeled("refs/tags/atag") + " refs/tags/atag^{}\n" + + id("refs/tags/ctag") + " refs/tags/ctag\n", + res.getActualBodyString()); + } + + public void testGetRefsHeadsText() throws Exception { + FakeHttpServletRequest req = FakeHttpServletRequest.newRequest(); + req.setPathInfo("/test/+refs/heads"); + req.setQueryString("format=TEXT"); + FakeHttpServletResponse res = new FakeHttpServletResponse(); + servlet.service(req, res); + + assertEquals(200, res.getStatus()); + assertEquals( + id("refs/heads/branch") + " refs/heads/branch\n" + + id("refs/heads/master") + " refs/heads/master\n", + res.getActualBodyString()); + } + + public void testGetRefsHeadsTextTrailingSlash() throws Exception { + FakeHttpServletRequest req = FakeHttpServletRequest.newRequest(); + req.setPathInfo("/test/+refs/heads/"); + req.setQueryString("format=TEXT"); + FakeHttpServletResponse res = new FakeHttpServletResponse(); + servlet.service(req, res); + + assertEquals(200, res.getStatus()); + assertEquals( + id("refs/heads/branch") + " refs/heads/branch\n" + + id("refs/heads/master") + " refs/heads/master\n", + res.getActualBodyString()); + } + + public void testNoHeadText() throws Exception { + FakeHttpServletRequest req = FakeHttpServletRequest.newRequest(); + req.setPathInfo("/test/+refs/HEAD"); + req.setQueryString("format=TEXT"); + FakeHttpServletResponse res = new FakeHttpServletResponse(); + servlet.service(req, res); + + assertEquals(200, res.getStatus()); + // /+refs/foo means refs/foo(/*), so this is empty. + assertEquals("", res.getActualBodyString()); + } + + public void testSingleHeadText() throws Exception { + FakeHttpServletRequest req = FakeHttpServletRequest.newRequest(); + req.setPathInfo("/test/+refs/heads/master"); + req.setQueryString("format=TEXT"); + FakeHttpServletResponse res = new FakeHttpServletResponse(); + servlet.service(req, res); + + assertEquals(200, res.getStatus()); + assertEquals( + id("refs/heads/master") + " refs/heads/master\n", + res.getActualBodyString()); + } + + public void testSinglePeeledTagText() throws Exception { + FakeHttpServletRequest req = FakeHttpServletRequest.newRequest(); + req.setPathInfo("/test/+refs/tags/atag"); + req.setQueryString("format=TEXT"); + FakeHttpServletResponse res = new FakeHttpServletResponse(); + servlet.service(req, res); + + assertEquals(200, res.getStatus()); + assertEquals( + id("refs/tags/atag") + " refs/tags/atag\n" + + peeled("refs/tags/atag") + " refs/tags/atag^{}\n", + res.getActualBodyString()); + } +}
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/ViewFilterTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/ViewFilterTest.java index 2c3ae2a..1088692 100644 --- a/gitiles-servlet/src/test/java/com/google/gitiles/ViewFilterTest.java +++ b/gitiles-servlet/src/test/java/com/google/gitiles/ViewFilterTest.java
@@ -97,12 +97,42 @@ } public void testRefs() throws Exception { - GitilesView view = getView("/repo/+refs"); + GitilesView view; + + view = getView("/repo/+refs"); assertEquals(Type.REFS, view.getType()); assertEquals("repo", view.getRepositoryName()); assertEquals(Revision.NULL, view.getRevision()); assertEquals(Revision.NULL, view.getOldRevision()); - assertNull(view.getTreePath()); + assertEquals("", view.getTreePath()); + + view = getView("/repo/+refs/"); + assertEquals(Type.REFS, view.getType()); + assertEquals("repo", view.getRepositoryName()); + assertEquals(Revision.NULL, view.getRevision()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertEquals("", view.getTreePath()); + + view = getView("/repo/+refs/heads"); + assertEquals(Type.REFS, view.getType()); + assertEquals("repo", view.getRepositoryName()); + assertEquals(Revision.NULL, view.getRevision()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertEquals("heads", view.getTreePath()); + + view = getView("/repo/+refs/heads/"); + assertEquals(Type.REFS, view.getType()); + assertEquals("repo", view.getRepositoryName()); + assertEquals(Revision.NULL, view.getRevision()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertEquals("heads", view.getTreePath()); + + view = getView("/repo/+refs/heads/master"); + assertEquals(Type.REFS, view.getType()); + assertEquals("repo", view.getRepositoryName()); + assertEquals(Revision.NULL, view.getRevision()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertEquals("heads/master", view.getTreePath()); } public void testBranches() throws Exception {