From 0cbbe6297e2f2079fcb33b7b1da23b94c8f83f4f Mon Sep 17 00:00:00 2001
From: zzz <zzz@i2pmail.org>
Date: Wed, 18 Jan 2023 12:21:29 -0500
Subject: [PATCH] i2psnark: Rename search param to bypass the XSS filter

Fix encode/decode search param
Copy CSS to non-default themes, not tweaked yet
Add support for shorter nf_ prefix to XSS filter
Remove unneeded float_right, reported by drzed
---
 .../org/klomp/snark/web/I2PSnarkServlet.java  | 34 +++++++++++--------
 apps/i2psnark/resources/js/initajax.js        |  4 +--
 apps/i2psnark/resources/themes/dark/snark.css | 21 ++++++++++++
 .../i2psnark/resources/themes/light/snark.css | 21 ++++++++++++
 .../resources/themes/ubergine/snark.css       |  1 -
 .../resources/themes/vanilla/snark.css        | 21 ++++++++++++
 .../servlet/filters/XSSRequestWrapper.java    |  9 +++--
 7 files changed, 91 insertions(+), 20 deletions(-)

diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
index 2bc68376aa..564399957e 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
@@ -387,13 +387,12 @@ public class I2PSnarkServlet extends BasicServlet {
         out.write("</div>\n");
 
         if (!isConfigure) {
-            String search = req.getParameter("s");
+            String search = req.getParameter("nf_s");
             if (_manager.getTorrents().size() > 1 || (search != null && search.length() > 0)) {
                 out.write("<form class=\"search\" id = \"search\" action=\"" + _contextPath + "\" method=\"GET\">" +
-                          "<input type=\"text\" name=\"s\" size=\"20\" class=\"search\" id=\"searchbox\"");
-                String s = req.getParameter("s");
-                if (s != null)
-                    out.write(" value=\"" + DataHelper.escapeHTML(s) + '"');
+                          "<input type=\"text\" name=\"nf_s\" size=\"20\" class=\"search\" id=\"searchbox\"");
+                if (search != null)
+                    out.write(" value=\"" + DataHelper.escapeHTML(search) + '"');
                 out.write(">" +
                           "<input type=\"reset\" class=\"cancel\" id=\"searchcancel\" value=\"\">" +
                           "</form>\n");
@@ -507,7 +506,7 @@ public class I2PSnarkServlet extends BasicServlet {
 
         // search
         boolean isSearch = false;
-        String search = req.getParameter("s");
+        String search = req.getParameter("nf_s");
         if (search != null && search.length() > 0) {
             List<Snark> matches = search(search, snarks);
             if (matches != null) {
@@ -851,12 +850,17 @@ public class I2PSnarkServlet extends BasicServlet {
     /**
      *  search torrents for matching terms
      *
-     *  @param search non-null
-     *  @param snarks unmodified
-     *  @return null if no valid search, or matching torrents in same order
+     *  @param search non-null and %-encoded, will be decoded here
+     *  @param snarks unmodified, order will be honored
+     *  @return null if not a valid search, or matching torrents in same order, possibly empty
      *  @since 0.9.58
      */
     private static List<Snark> search(String search, Collection<Snark> snarks) {
+        try {
+            search = decodePath(search);
+        } catch (IOException ioe) {
+            return null;
+        }
         List<String> searchList = null;
         String[] terms = DataHelper.split(search, " ");
         for (int i = 0; i < terms.length; i++) {
@@ -927,7 +931,7 @@ public class I2PSnarkServlet extends BasicServlet {
                .append(action).append("\" >\n");
         } else {
             // for buttons, keep the search term
-            String sParam = req.getParameter("s");
+            String sParam = req.getParameter("nf_s");
             if (sParam != null) {
                 buf.append("<input type=\"hidden\" name=\"s\" value=\"")
                    .append(DataHelper.escapeHTML(sParam)).append("\" >\n");
@@ -987,15 +991,15 @@ public class I2PSnarkServlet extends BasicServlet {
             buf.append(st);
         }
         if (s == null) {
-            s = req.getParameter("s");
+            s = req.getParameter("nf_s");
             if (s != null)
                 s = DataHelper.escapeHTML(s);
         }
         if (s != null && !s.equals("")) {
             if (buf.length() <= 0)
-                buf.append("?s=");
+                buf.append("?nf_s=");
             else
-                buf.append("&amp;s=");
+                buf.append("&amp;nf_s=");
             buf.append(s);
         }
         return buf.toString();
@@ -1555,7 +1559,7 @@ public class I2PSnarkServlet extends BasicServlet {
                 _manager.addMessage(_t("Error creating torrent - you must enter a file or directory"));
             }
         } else if ("StopAll".equals(action)) {
-            String search = req.getParameter("s");
+            String search = req.getParameter("nf_s");
             if (search != null && search.length() > 0) {
                 List<Snark> matches = search(search, _manager.getTorrents());
                 if (matches != null) {
@@ -1567,7 +1571,7 @@ public class I2PSnarkServlet extends BasicServlet {
             }
             _manager.stopAllTorrents(false);
         } else if ("StartAll".equals(action)) {
-            String search = req.getParameter("s");
+            String search = req.getParameter("nf_s");
             if (search != null && search.length() > 0) {
                 List<Snark> matches = search(search, _manager.getTorrents());
                 if (matches != null) {
diff --git a/apps/i2psnark/resources/js/initajax.js b/apps/i2psnark/resources/js/initajax.js
index 87de15cd36..7697b0086e 100644
--- a/apps/i2psnark/resources/js/initajax.js
+++ b/apps/i2psnark/resources/js/initajax.js
@@ -29,12 +29,12 @@ function requestAjax2(refreshtime) {
                 query = "";
             }
             var q = new URLSearchParams(query);
-            q.set("s", search);
+            q.set("nf_s", encodeURIComponent(search));
             query = "?" + q.toString();
         } else {
             if (query != null) {
                 var q = new URLSearchParams(query);
-                q.delete("s");
+                q.delete("nf_s");
                 var newq = q.toString();
                 if (newq != null && newq.length > 0) {
                     query = "?" + newq;
diff --git a/apps/i2psnark/resources/themes/dark/snark.css b/apps/i2psnark/resources/themes/dark/snark.css
index ff10571f7f..f392273d97 100644
--- a/apps/i2psnark/resources/themes/dark/snark.css
+++ b/apps/i2psnark/resources/themes/dark/snark.css
@@ -224,6 +224,27 @@ _:-ms-lang(x), .snarknavbar {
      margin-top: 0 !important;
 }
 
+#search {
+     display: inline-block;
+     position: absolute;
+     top: 6px;
+     right: 3px;
+}
+
+#searchbox {
+     background: #f60 url(/themes/console/images/buttons/search.png) 7px center no-repeat !important;
+     margin: 2px 4px 2px 24px !important;
+     padding: 4px 32px 4px 32px !important;
+     color: black;
+}
+
+#searchcancel {
+     background: url(images/cancel.png);
+     margin: 2px 4px 2px -28px;
+     color: transparent;
+     border: none;
+}
+
 /* end topnav */
 
 /* screenlog */
diff --git a/apps/i2psnark/resources/themes/light/snark.css b/apps/i2psnark/resources/themes/light/snark.css
index c3e3391f06..18134c4146 100644
--- a/apps/i2psnark/resources/themes/light/snark.css
+++ b/apps/i2psnark/resources/themes/light/snark.css
@@ -229,6 +229,27 @@ button::-moz-focus-inner, input::-moz-focus-inner {
      background: #f60 url(images/button_tracker_active.png) 8px center no-repeat;
 }
 
+#search {
+     display: inline-block;
+     position: absolute;
+     top: 6px;
+     right: 3px;
+}
+
+#searchbox {
+     background: #f60 url(/themes/console/images/buttons/search.png) 7px center no-repeat !important;
+     margin: 2px 4px 2px 24px !important;
+     padding: 4px 32px 4px 32px !important;
+     color: black;
+}
+
+#searchcancel {
+     background: url(images/cancel.png);
+     margin: 2px 4px 2px -28px;
+     color: transparent;
+     border: none;
+}
+
 /* end top nav */
 
 /* screenlog */
diff --git a/apps/i2psnark/resources/themes/ubergine/snark.css b/apps/i2psnark/resources/themes/ubergine/snark.css
index 6394cecea2..e3afeb55d5 100644
--- a/apps/i2psnark/resources/themes/ubergine/snark.css
+++ b/apps/i2psnark/resources/themes/ubergine/snark.css
@@ -231,7 +231,6 @@ _:-ms-lang(x), .snarkNav:last-child[href="/i2psnark/"] {
 
 #search {
      display: inline-block;
-     float: right;
      position: absolute;
      top: 6px;
      right: 3px;
diff --git a/apps/i2psnark/resources/themes/vanilla/snark.css b/apps/i2psnark/resources/themes/vanilla/snark.css
index a119bc1001..1a70c5cac7 100644
--- a/apps/i2psnark/resources/themes/vanilla/snark.css
+++ b/apps/i2psnark/resources/themes/vanilla/snark.css
@@ -272,6 +272,27 @@ _:-ms-lang(x), .snarkNav:link, .snarkNav:visited {
      transition: ease box-shadow 0.1s;
 }
 
+#search {
+     display: inline-block;
+     position: absolute;
+     top: 6px;
+     right: 3px;
+}
+
+#searchbox {
+     background: #f60 url(/themes/console/images/buttons/search.png) 7px center no-repeat !important;
+     margin: 2px 4px 2px 24px !important;
+     padding: 4px 32px 4px 32px !important;
+     color: black;
+}
+
+#searchcancel {
+     background: url(images/cancel.png);
+     margin: 2px 4px 2px -28px;
+     color: transparent;
+     border: none;
+}
+
 /* end topnav */
 
 /* screenlog */
diff --git a/apps/jetty/java/src/net/i2p/servlet/filters/XSSRequestWrapper.java b/apps/jetty/java/src/net/i2p/servlet/filters/XSSRequestWrapper.java
index b24c3d324f..163505906c 100644
--- a/apps/jetty/java/src/net/i2p/servlet/filters/XSSRequestWrapper.java
+++ b/apps/jetty/java/src/net/i2p/servlet/filters/XSSRequestWrapper.java
@@ -26,6 +26,8 @@ public class XSSRequestWrapper extends HttpServletRequestWrapper {
     private static final Pattern parameterValuePattern = Pattern.compile(SystemVersion.isWindows() ? WIN_PATTERN : NON_WIN_PATTERN);
     private static final Pattern headerValuePattern = Pattern.compile("^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ \"]*$");
     private static final String NOFILTER = "nofilter_";
+    // shorter flavor
+    private static final String NOFILTER2 = "nf_";
 
     public XSSRequestWrapper(HttpServletRequest servletRequest) {
         super(servletRequest);
@@ -33,11 +35,12 @@ public class XSSRequestWrapper extends HttpServletRequestWrapper {
 
     /**
      *  Parameter names starting with "nofilter_" will not be filtered.
+     *  As of 0.9.58, names starting with "nf_" will not be filtered.
      */
     @Override
     public String[] getParameterValues(String parameter) {
         String[] values = super.getParameterValues(parameter);
-        if (parameter.startsWith(NOFILTER))
+        if (parameter.startsWith(NOFILTER) || parameter.startsWith(NOFILTER2))
             return values;
 
         if (values == null) {
@@ -70,11 +73,12 @@ public class XSSRequestWrapper extends HttpServletRequestWrapper {
 
     /**
      *  Parameter names starting with "nofilter_" will not be filtered.
+     *  As of 0.9.58, names starting with "nf_" will not be filtered.
      */
     @Override
     public String getParameter(String parameter) {
         String value = super.getParameter(parameter);
-        if (parameter.startsWith(NOFILTER))
+        if (parameter.startsWith(NOFILTER) || parameter.startsWith(NOFILTER2))
             return value;
         String rv = stripXSS(value, parameterValuePattern);
         if (value != null && rv == null) {
@@ -86,6 +90,7 @@ public class XSSRequestWrapper extends HttpServletRequestWrapper {
 
     /**
      *  Parameter names starting with "nofilter_" will not be filtered.
+     *  As of 0.9.58, names starting with "nf_" will not be filtered.
      */
     @Override
     public Map<String, String[]> getParameterMap() {
-- 
GitLab