commit 75981fe7d03091c78f901e1589288be4a27ce56c Author: Karsten Loesing karsten.loesing@gmx.net Date: Thu Oct 19 14:46:28 2017 +0200
Support quoted qualified search terms.
With this patch, the "search" parameter does not only accept unquoted qualified search terms like `search=contact:John Doe` where "John" would be looked up in the contact line and "Doe" in the nickname or base64-encoded fingerprint. It also accepts quoted qualified search terms like `search=contact:"John Doe"` where "John Doe" would be looked up in the contact line. It further accepts escaped double quotes (") within quoted qualified search terms.
Implements #21366. --- CHANGELOG.md | 8 ++++- .../torproject/onionoo/server/ResourceServlet.java | 38 ++++++++++++++++++++-- .../onionoo/server/ResourceServletTest.java | 36 +++++++++++++++++++- 3 files changed, 77 insertions(+), 5 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md index ca17fa7..74f29b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ -# Changes in version 4.2-1.6.1 - 2017-10-2? +# Changes in version 4.3-1.7.0 - 2017-1?-?? + + * Medium changes + - Support quoted qualified search terms. + + +# Changes in version 4.2-1.6.1 - 2017-10-26
* Medium changes - Fix two NullPointerExceptions caused by accessing optional parts diff --git a/src/main/java/org/torproject/onionoo/server/ResourceServlet.java b/src/main/java/org/torproject/onionoo/server/ResourceServlet.java index 31244c2..2fff913 100644 --- a/src/main/java/org/torproject/onionoo/server/ResourceServlet.java +++ b/src/main/java/org/torproject/onionoo/server/ResourceServlet.java @@ -3,6 +3,8 @@
package org.torproject.onionoo.server;
+import org.apache.commons.lang3.StringUtils; + import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; @@ -169,6 +171,12 @@ public class ResourceServlet extends HttpServlet { } if (!parameterMap.containsKey(parameterKey)) { String parameterValue = parts[1]; + if (parameterValue.startsWith(""") + && parameterValue.endsWith(""")) { + parameterValue = parameterValue + .substring(1, parameterValue.length() - 1) + .replaceAll("\\"", """); + } parameterMap.put(parameterKey, parameterValue); } } else { @@ -388,7 +396,7 @@ public class ResourceServlet extends HttpServlet { + "^[0-9a-zA-Z+/]{1,27}$|" /* Base64 fingerprint. */ + "^[0-9a-zA-Z\.]{1,19}$|" /* Nickname or IPv4 address. */ + ipv6AddressPatternString + "|" /* IPv6 address. */ - + "^[a-zA-Z_]+:\p{Graph}+$" /* Qualified search term. */); + + "^[a-zA-Z_]+:"?[\p{Graph} ]+"?$"); /* Qualified search term. */
protected static String[] parseSearchParameters(String queryString) { Matcher searchQueryStringMatcher = searchQueryStringPattern.matcher( @@ -398,15 +406,39 @@ public class ResourceServlet extends HttpServlet { return null; } String parameter = searchQueryStringMatcher.group(1); - String[] searchParameters = + String[] spaceSeparatedParts = parameter.replaceAll("%20", " ").split(" "); + List<String> searchParameters = new ArrayList<>(); + StringBuilder doubleQuotedSearchTerm = null; + for (String spaceSeparatedPart : spaceSeparatedParts) { + if ((StringUtils.countMatches(spaceSeparatedPart, '"') + - StringUtils.countMatches(spaceSeparatedPart, "\"")) % 2 == 0) { + if (null == doubleQuotedSearchTerm) { + searchParameters.add(spaceSeparatedPart); + } else { + doubleQuotedSearchTerm.append(' ').append(spaceSeparatedPart); + } + } else { + if (null == doubleQuotedSearchTerm) { + doubleQuotedSearchTerm = new StringBuilder(spaceSeparatedPart); + } else { + doubleQuotedSearchTerm.append(' ').append(spaceSeparatedPart); + searchParameters.add(doubleQuotedSearchTerm.toString()); + doubleQuotedSearchTerm = null; + } + } + } + if (null != doubleQuotedSearchTerm) { + /* Opening double quote is not followed by closing double quote. */ + return null; + } for (String searchParameter : searchParameters) { if (!searchParameterPattern.matcher(searchParameter).matches()) { /* Illegal search term. */ return null; } } - return searchParameters; + return searchParameters.toArray(new String[0]); }
private static Pattern fingerprintParameterPattern = diff --git a/src/test/java/org/torproject/onionoo/server/ResourceServletTest.java b/src/test/java/org/torproject/onionoo/server/ResourceServletTest.java index 303cf21..1588f8a 100644 --- a/src/test/java/org/torproject/onionoo/server/ResourceServletTest.java +++ b/src/test/java/org/torproject/onionoo/server/ResourceServletTest.java @@ -169,7 +169,7 @@ public class ResourceServletTest { new TreeSet<>(Arrays.asList(new String[] { "Fast", "Running", "Unnamed", "V2Dir", "Valid" })), 63L, "a1", DateTimeHelper.parse("2013-04-16 18:00:00"), "AS6830", - "1024D/51E2A1C7 steven j. murdoch " + "1024d/51e2a1c7 "steven j. murdoch" " + "tor+steven.murdoch@cl.cam.ac.uk fb-token:5sr_k_zs2wm=", new TreeSet<String>(), new TreeSet<String>(), "0.2.3.25"); this.relays.put("0025C136C1F3A9EEFE2AE3F918F03BFA21B5070B", @@ -933,6 +933,40 @@ public class ResourceServletTest { }
@Test(timeout = 100) + public void testSearchDoubleQuotedEmailAddress() { + this.assertSummaryDocument( + "/summary?search=contact:"klaus dot zufall at gmx dot de"", 1, + new String[] { "TorkaZ" }, 0, null); + } + + @Test(timeout = 100) + public void testSearchDoubleQuotedContactAndNickname() { + this.assertSummaryDocument( + "/summary?search=contact:"dot de" TorkaZ", 1, + new String[] { "TorkaZ" }, 0, null); + } + + @Test(timeout = 100) + public void testSearchMissingEndingDoubleQuote() { + this.assertErrorStatusCode( + "/summary?search=contact:"klaus dot zufall at gmx dot de", 400); + } + + @Test(timeout = 100) + public void testSearchEvenNumberOfDoubleQuotes() { + this.assertSummaryDocument( + "/summary?search=contact:""" """", 0, + null, 0, null); + } + + @Test(timeout = 100) + public void testSearchContactEscapedDoubleQuotes() { + this.assertSummaryDocument( + "/summary?search=contact:"1024D/51E2A1C7 \"Steven J. Murdoch\""", + 1, new String[] { "TimMayTribute" }, 0, null); + } + + @Test(timeout = 100) public void testLookupFingerprint() { this.assertSummaryDocument( "/summary?lookup=000C5F55BD4814B917CC474BD537F1A3B33CCE2A", 1,
tor-commits@lists.torproject.org