Skip to content

Instantly share code, notes, and snippets.

Created April 29, 2020 19:33
Show Gist options
  • Save sdorra/b9f4374583c1b15b79f15354a78a4e05 to your computer and use it in GitHub Desktop.
Save sdorra/b9f4374583c1b15b79f15354a78a4e05 to your computer and use it in GitHub Desktop.
svnkit-dav checksums
@Grab(group='org.eclipse.jetty', module='jetty-server', version='9.1.6.v20160112')
@Grab(group='org.eclipse.jetty', module='jetty-servlet', version='9.1.6.v20160112')
@Grab(group='org.eclipse.jetty', module='jetty-proxy', version='9.1.6.v20160112')
@Grab(group='', module='guava', version='29.0-jre')
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.proxy.ProxyServlet;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
public class SvnProtocolProxy extends ProxyServlet.Transparent {
protected void customizeProxyRequest(Request proxyRequest, HttpServletRequest request) {
if (isSvnPutRequest(request)) {
try (ServletInputStream inputStream = request.getInputStream()) {
byte[] original = ByteStreams.toByteArray(inputStream);
byte[] corrupted = new byte[original.length];
for (int i = 0; i < original.length; i++) {
char c = (char) original[i];
if (c == 'a') {
corrupted[i] = 'z';
} else {
corrupted[i] = original[i];
byte[][] chunks = new byte[][]{
proxyRequest.content(new BytesContentProvider(chunks));
} catch (IOException ex) {
private boolean isSvnPutRequest(HttpServletRequest request) {
return "PUT".equalsIgnoreCase(request.getMethod())
&& request.getHeader("X-SVN-Result-Fulltext-MD5") != null;
Server server = new Server(8000);
ServletHolder proxyServlet = new ServletHolder(SvnProtocolProxy.class);
proxyServlet.setInitParameter("proxyTo", "http://localhost:8080/");
proxyServlet.setInitParameter("prefix", "/");
ServletContextHandler context = new ServletContextHandler();
context.addServlet(proxyServlet, "/*");
# first we have to create an svn repository via scm-manager
# in the following example we assume the repository scmadmin/hitchhiker was created
$ svn checkout http://localhost:8000/scm/repo/scmadmin/hitchhiker
$ cd hitchhiker
$ echo a > a.txt
$ svn add a.txt
# the corrupting proxy will replace the content of 'a' with 'z'
$ svn commit -m 'added a.txt'
Adding a.txt
Transmitting file data .done
Committing transaction...
Committed revision 1.
# the local copy contains 'a' but the server has 'z'
$ svn commit -m 'update a.txt'
Sending a.txt
Transmitting file data .svn: E200014: Commit failed (details follow):
svn: E200014: svn: E200014: Base checksum mismatch on '/a.txt':
expected: 60b725f10c9c85c70d97880dfe8191b3
actual: a8a78d0ff555c931f045b6f448129846
$ cd ..
$ rm -rf hitchhiker
$ svn checkout http://localhost:8000/scm/repo/scmadmin/hitchhiker
A hitchhiker/a.txt
Checked out revision 1.
$ cat hitchhiker/a.txt
# but the repository on the server side does not know about the corruption
$ svnadmin verify ${SCM_HOME}/repositories/idOfRepository/data
* Verifying repository metadata ...
* Verified revision 0.
* Verified revision 1.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment