Skip to content

Commit b86d481

Browse files
committed
HTML/URI encode path/content where possible
1 parent 3ed9ab1 commit b86d481

File tree

6 files changed

+60
-57
lines changed

6 files changed

+60
-57
lines changed

opengrok-indexer/src/main/java/org/opengrok/indexer/web/Util.java

+17-17
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ private static boolean needsHtmlize(CharSequence q, boolean pre) {
350350
* Convenience method for {@code breadcrumbPath(urlPrefix, path, PATH_SEPARATOR)}.
351351
*
352352
* @param urlPrefix prefix to add to each url
353-
* @param path path to crack
353+
* @param path the full path from which the breadcrumb path is built
354354
* @return HTML markup for the breadcrumb or the path itself.
355355
*
356356
* @see #breadcrumbPath(String, String, char)
@@ -364,7 +364,7 @@ public static String breadcrumbPath(String urlPrefix, String path) {
364364
* {@code breadcrumbPath(urlPrefix, path, sep, "", false)}.
365365
*
366366
* @param urlPrefix prefix to add to each url
367-
* @param path path to crack
367+
* @param path the full path from which the breadcrumb path is built
368368
* @param sep separator to use to crack the given path
369369
*
370370
* @return HTML markup fro the breadcrumb or the path itself.
@@ -379,13 +379,13 @@ public static String breadcrumbPath(String urlPrefix, String path, char sep) {
379379
* {@code breadcrumbPath(urlPrefix, path, sep, "", false, path.endsWith(sep)}.
380380
*
381381
* @param urlPrefix prefix to add to each url
382-
* @param path path to crack
382+
* @param path the full path from which the breadcrumb path is built
383383
* @param sep separator to use to crack the given path
384384
* @param urlPostfix suffix to add to each url
385385
* @param compact if {@code true} the given path gets transformed into its
386-
* canonical form (.i.e. all '.' and '..' and double separators removed, but
386+
* canonical form (.i.e. all <code>'.'</code> and <code>'..'</code> and double separators removed, but
387387
* not always resolves to an absolute path) before processing starts.
388-
* @return HTML markup fro the breadcrumb or the path itself.
388+
* @return HTML markup for the breadcrumb or the path itself
389389
* @see #breadcrumbPath(String, String, char, String, boolean, boolean)
390390
* @see #getCanonicalPath(String, char)
391391
*/
@@ -408,12 +408,10 @@ public static String breadcrumbPath(String urlPrefix, String path,
408408
* neither whether the path [component] exists nor which type it is).
409409
*
410410
* @param urlPrefix what should be prepended to the constructed URL
411-
* @param path the full path from which the breadcrumb path is built.
412-
* @param sep the character that separates the path components in
413-
* <var>path</var>
411+
* @param path the full path from which the breadcrumb path is built
412+
* @param sep the character that separates the path components in <var>path</var>
414413
* @param urlPostfix what should be appended to the constructed URL
415-
* @param compact if {@code true}, a canonical path gets constructed before
416-
* processing.
414+
* @param compact if {@code true}, a canonical path gets constructed before processing.
417415
* @param isDir if {@code true} a "/" gets append to the last path
418416
* component's link and <var>sep</var> to its name
419417
* @return <var>path</var> if it resolves to an empty or "/" or {@code null}
@@ -431,8 +429,7 @@ public static String breadcrumbPath(String urlPrefix, String path,
431429
String prefix = urlPrefix == null ? "" : urlPrefix;
432430
String postfix = urlPostfix == null ? "" : urlPostfix;
433431
StringBuilder pwd = new StringBuilder(path.length() + pnames.length);
434-
StringBuilder markup
435-
= new StringBuilder((pnames.length + 3 >> 1) * path.length()
432+
StringBuilder markup = new StringBuilder((pnames.length + 3 >> 1) * path.length()
436433
+ pnames.length
437434
* (17 + prefix.length() + postfix.length()));
438435
int k = path.indexOf(pnames[0]);
@@ -445,9 +442,13 @@ public static String breadcrumbPath(String urlPrefix, String path,
445442
if (isDir || i < pnames.length - 1) {
446443
pwd.append(PATH_SEPARATOR);
447444
}
448-
markup.append(ANCHOR_LINK_START).append(prefix).append(pwd)
449-
.append(postfix).append(CLOSE_QUOTED_TAG).append(pnames[i])
450-
.append(ANCHOR_END);
445+
markup.append(ANCHOR_LINK_START).
446+
append(prefix).
447+
append(pwd).
448+
append(postfix).
449+
append(CLOSE_QUOTED_TAG).
450+
append(Util.htmlize(pnames[i])).
451+
append(ANCHOR_END);
451452
if (isDir || i < pnames.length - 1) {
452453
markup.append(sep);
453454
}
@@ -520,8 +521,7 @@ public static String getEmail(String author) {
520521

521522
/**
522523
* Remove all empty and {@code null} string elements from the given
523-
* <var>names</var> and optionally all redundant information like "." and
524-
* "..".
524+
* <var>names</var> and optionally all redundant information like <code>"."</code> and <code>".."</code>.
525525
*
526526
* @param names names to check
527527
* @param canonical if {@code true}, remove redundant elements as well.

opengrok-indexer/src/test/java/org/opengrok/indexer/web/UtilTest.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919

2020
/*
21-
* Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
21+
* Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
2222
* Portions Copyright (c) 2017, 2019, Chris Fraire <[email protected]>.
2323
*/
2424
package org.opengrok.indexer.web;
@@ -112,8 +112,7 @@ void breadcrumbPath() {
112112
// parent directories have a trailing slash in href
113113
assertEquals("<a href=\"/r/a/\">a</a>/<a href=\"/r/a/b\">b</a>",
114114
Util.breadcrumbPath("/r/", "a/b"));
115-
// if basename is a dir (ends with file seperator), href link also
116-
// ends with a '/'
115+
// if basename is a dir (ends with file separator), href link also ends with a '/'
117116
assertEquals("<a href=\"/r/a/\">a</a>/<a href=\"/r/a/b/\">b</a>/",
118117
Util.breadcrumbPath("/r/", "a/b/"));
119118
// should work the same way with a '.' as file separator
@@ -129,11 +128,15 @@ void breadcrumbPath() {
129128
// Prefix gets just prefixed as is and not mangled wrt. path -> "//"
130129
assertEquals("/<a href=\"/root//xx&project=y\">xx</a>",
131130
Util.breadcrumbPath("/root/", "../xx", '/', "&project=y", true));
132-
// relative pathes are resolved wrt. / , so path resolves to /a/c/d
131+
// relative paths are resolved wrt. / , so path resolves to /a/c/d
133132
assertEquals("/<a href=\"/r//a/\">a</a>/"
134133
+ "<a href=\"/r//a/c/\">c</a>/"
135134
+ "<a href=\"/r//a/c/d\">d</a>",
136135
Util.breadcrumbPath("/r/", "../a/b/../c//d", '/', "", true));
136+
// path components should be URI encoded and htmlized
137+
assertEquals("<a href=\"/root/foo/&project=y\">foo</a>/"
138+
+ "<a href=\"/root/foo/bar%3E&project=y\">bar&gt;</a>",
139+
Util.breadcrumbPath("/root/", "foo/bar>", '/', "&project=y", true));
137140
}
138141

139142
@Test

opengrok-web/src/main/webapp/history.jsp

+26-25
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ information: Portions Copyright [yyyy] [name of copyright owner]
1818
1919
CDDL HEADER END
2020
21-
Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
21+
Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
2222
Portions Copyright 2011 Jens Elkner.
2323
Portions Copyright (c) 2018-2020, Chris Fraire <[email protected]>.
2424
--%>
@@ -47,6 +47,7 @@ org.opengrok.indexer.web.Util"
4747
%>
4848
<%@ page import="jakarta.servlet.http.HttpServletResponse" %>
4949
<%@ page import="org.opengrok.indexer.web.SortOrder" %>
50+
<%@ page import="java.util.Optional" %>
5051
<%/* ---------------------- history.jsp start --------------------- */
5152
{
5253
final Logger LOGGER = LoggerFactory.getLogger(getClass());
@@ -59,7 +60,7 @@ org.opengrok.indexer.web.Util"
5960
6061
String path = cfg.getPath();
6162
62-
if (path.length() > 0) {
63+
if (!path.isEmpty()) {
6364
String primePath = path;
6465
Project project = cfg.getProject();
6566
if (project != null) {
@@ -75,8 +76,7 @@ org.opengrok.indexer.web.Util"
7576
try {
7677
primePath = searchHelper.getPrimeRelativePath(project.getName(), path);
7778
} catch (IOException | ForbiddenSymlinkException ex) {
78-
LOGGER.log(Level.WARNING, String.format(
79-
"Error getting prime relative for %s", path), ex);
79+
LOGGER.log(Level.WARNING, String.format("Error getting prime relative for '%s'", path), ex);
8080
}
8181
}
8282
@@ -148,7 +148,7 @@ include file="/httpheader.jspf"
148148
request.setAttribute("history.jsp-slider", Util.createSlider(startIndex, max, totalHits, request));
149149
%>
150150
<div id="Masthead">History log of
151-
<%= Util.breadcrumbPath(context + Prefix.XREF_P, path,'/',"",true,cfg.isDir()) %>
151+
<%= Util.breadcrumbPath(context + Prefix.XREF_P, path, '/', "", true, cfg.isDir()) %>
152152
(Results <span class="bold"> <%= totalHits != 0 ? startIndex + 1 : 0 %><%= startIndex + thisPageIndex
153153
%></span> of <span class="bold"><%= totalHits %></span>)
154154
</div>
@@ -258,16 +258,17 @@ document.domReady.push(function() {domReadyHistory();});
258258
<%
259259
int count=0;
260260
for (HistoryEntry entry : hist.getHistoryEntries(maxItems, startIndex)) {
261-
String dispRev = entry.getDisplayRevision();
262-
if (dispRev == null || dispRev.length() == 0) {
263-
dispRev = "";
261+
if (Objects.isNull(entry)) {
262+
continue;
264263
}
265-
String rev = entry.getRevision();
266-
if (rev == null || rev.length() == 0) {
267-
rev = "";
268-
}
269-
String tags = hist.getTags().get(rev);
270264
265+
final String htmlEncodedDisplayRevision = Optional.ofNullable(entry.getDisplayRevision()).
266+
map(Util::htmlize).
267+
orElse("");
268+
final String rev = Optional.ofNullable(entry.getRevision()).
269+
orElse("");
270+
271+
String tags = hist.getTags().get(rev);
271272
if (tags != null) {
272273
int colspan;
273274
if (cfg.isDir())
@@ -285,7 +286,7 @@ document.domReady.push(function() {domReadyHistory();});
285286
<tr><%
286287
if (cfg.isDir()) {
287288
%>
288-
<td><%= dispRev %></td><%
289+
<td><%= htmlEncodedDisplayRevision %></td><%
289290
} else {
290291
if (entry.isActive()) {
291292
StringBuffer urlBuffer = request.getRequestURL();
@@ -297,7 +298,7 @@ document.domReady.push(function() {domReadyHistory();});
297298
<td><a href="<%= urlBuffer %>"
298299
title="link to revision line">#</a>
299300
<a href="<%= context + Prefix.XREF_P + uriEncodedName + "?" +
300-
QueryParameters.REVISION_PARAM_EQ + Util.uriEncode(rev) %>"><%= dispRev %>
301+
QueryParameters.REVISION_PARAM_EQ + Util.uriEncode(rev) %>"><%= htmlEncodedDisplayRevision %>
301302
</a></td>
302303
<td><%
303304
%><input type="radio"
@@ -339,7 +340,7 @@ document.domReady.push(function() {domReadyHistory();});
339340
} else {
340341
striked = true;
341342
%>
342-
<td><del><%= dispRev %></del></td>
343+
<td><del><%= htmlEncodedDisplayRevision %></del></td>
343344
<td></td><%
344345
}
345346
}
@@ -354,23 +355,23 @@ document.domReady.push(function() {domReadyHistory();});
354355
String author = entry.getAuthor();
355356
if (author == null) {
356357
%>(no author)<%
357-
} else if (userPage != null && userPage.length() > 0) {
358+
} else if (userPage != null && !userPage.isEmpty()) {
358359
String alink = Util.getEmail(author);
359360
%><a href="<%= userPage + Util.htmlize(alink) + userPageSuffix
360361
%>"><%= Util.htmlize(author)%></a><%
361362
} else {
362363
%><%= Util.htmlize(author) %><%
363364
}
364365
%></td>
365-
<td><a id="<%= dispRev %>"></a><%
366+
<td><a id="<%= htmlEncodedDisplayRevision %>"></a><%
366367
// revision message collapse threshold minimum of 10
367368
int summaryLength = Math.max(10, cfg.getRevisionMessageCollapseThreshold());
368369
String cout = Util.htmlize(entry.getMessage());
369370
370-
if (bugPage != null && bugPage.length() > 0 && bugPattern != null) {
371+
if (bugPage != null && !bugPage.isEmpty() && bugPattern != null) {
371372
cout = Util.linkifyPattern(cout, bugPattern, "$1", Util.completeUrl(bugPage + "$1", request));
372373
}
373-
if (reviewPage != null && reviewPage.length() > 0 && reviewPattern != null) {
374+
if (reviewPage != null && !reviewPage.isEmpty() && reviewPattern != null) {
374375
cout = Util.linkifyPattern(cout, reviewPattern, "$1", Util.completeUrl(reviewPage + "$1", request));
375376
}
376377
@@ -380,10 +381,10 @@ document.domReady.push(function() {domReadyHistory();});
380381
showSummary = true;
381382
coutSummary = coutSummary.substring(0, summaryLength - 1);
382383
coutSummary = Util.htmlize(coutSummary);
383-
if (bugPage != null && bugPage.length() > 0 && bugPattern != null) {
384+
if (bugPage != null && !bugPage.isEmpty() && bugPattern != null) {
384385
coutSummary = Util.linkifyPattern(coutSummary, bugPattern, "$1", Util.completeUrl(bugPage + "$1", request));
385386
}
386-
if (reviewPage != null && reviewPage.length() > 0 && reviewPattern != null) {
387+
if (reviewPage != null && !reviewPage.isEmpty() && reviewPattern != null) {
387388
coutSummary = Util.linkifyPattern(coutSummary, reviewPattern, "$1", Util.completeUrl(reviewPage + "$1", request));
388389
}
389390
}
@@ -406,11 +407,11 @@ document.domReady.push(function() {domReadyHistory();});
406407
String jfile = Util.stripPathPrefix(path, ifile);
407408
if (Objects.equals(rev, "")) {
408409
%>
409-
<a class="h" href="<%= context + Prefix.XREF_P + ifile %>"><%= jfile %></a><br/><%
410+
<a class="h" href="<%= context + Prefix.XREF_P + Util.uriEncodePath(ifile) %>"><%= Util.htmlize(jfile) %></a><br/><%
410411
} else {
411412
%>
412-
<a class="h" href="<%= context + Prefix.XREF_P + ifile %>?<%= QueryParameters.REVISION_PARAM_EQ %>
413-
<%= rev %>"><%= jfile %></a><br/><%
413+
<a class="h" href="<%= context + Prefix.XREF_P + Util.uriEncodePath(ifile) %>?<%= QueryParameters.REVISION_PARAM_EQ %>
414+
<%= Util.uriEncode(rev) %>"><%= Util.htmlize(jfile) %></a><br/><%
414415
}
415416
}
416417
%></div><%

opengrok-web/src/main/webapp/httpheader.jspf

+4-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ information: Portions Copyright [yyyy] [name of copyright owner]
1818

1919
CDDL HEADER END
2020

21-
Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
21+
Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
2222
Portions Copyright 2011 Jens Elkner.
2323
Portions Copyright (c) 2017-2018, 2020, Chris Fraire <[email protected]>.
2424
Portions Copyright (c) 2020, Aleksandr Kirillov <[email protected]>.
@@ -38,6 +38,7 @@ to set the title of the document before the include directive for this fragment:
3838
org.opengrok.indexer.Info,
3939
org.opengrok.web.PageConfig,
4040
org.opengrok.indexer.web.Prefix,
41+
org.opengrok.indexer.web.Util,
4142
org.opengrok.web.Scripts"
4243
%><%
4344
/* ---------------------- httpheader.jsp start --------------------- */
@@ -92,8 +93,8 @@ org.opengrok.web.Scripts"
9293

9394
if (cfg.getPrefix().equals(Prefix.HIST_L)) {
9495
out.write("<link rel=\"alternate\" type=\"application/rss+xml\" " +
95-
"title=\"RSS feed for " + cfg.getPath() + "\" " +
96-
"href=\"" + ctxPath + Prefix.RSS_P + cfg.getPath() + "\" />");
96+
"title=\"RSS feed for " + Util.htmlize(cfg.getPath()) + "\" " +
97+
"href=\"" + ctxPath + Prefix.RSS_P + Util.uriEncodePath(cfg.getPath()) + "\" />");
9798
}
9899
%>
99100
<link rel="search" href="<%=ctxPath%>/opensearch"

opengrok-web/src/main/webapp/mast.jsp

+4-6
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ org.opengrok.indexer.web.Util"%>
6666
}
6767
6868
// set the default page title
69-
String path = cfg.getPath();
7069
cfg.setTitle(cfg.getPathTitle());
7170
}
7271
%>
@@ -94,19 +93,18 @@ include file="/httpheader.jspf"
9493
9594
String messages = "";
9695
if (cfg.getProject() != null) {
97-
messages = MessagesUtils.messagesToJson(cfg.getProject(),
98-
MessagesContainer.MESSAGES_MAIN_PAGE_TAG);
96+
messages = MessagesUtils.messagesToJson(cfg.getProject(), MessagesContainer.MESSAGES_MAIN_PAGE_TAG);
9997
}
10098
%>
10199
<a href="<%= context + Prefix.XREF_P %>/">xref</a>:
102-
<%= Util.breadcrumbPath(context + Prefix.XREF_P, path,'/',"",true,cfg.isDir()) %>
103-
<% if (rev.length() != 0) { %>
100+
<%= Util.breadcrumbPath(context + Prefix.XREF_P, path, '/', "", true, cfg.isDir()) %>
101+
<% if (!rev.isEmpty()) { %>
104102
(revision <%= Util.htmlize(rev) %>)
105103
<% } %>
106104
<span id="dtag">
107105
<%
108106
String dtag = cfg.getDefineTagsIndex();
109-
if (dtag.length() > 0) {
107+
if (!dtag.isEmpty()) {
110108
%> (<%= dtag %>)<%
111109
}
112110
%></span>

opengrok-web/src/main/webapp/more.jsp

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ information: Portions Copyright [yyyy] [name of copyright owner]
1818
1919
CDDL HEADER END
2020
21-
Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved.
21+
Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
2222
Portions Copyright 2011 Jens Elkner.
2323
Portions Copyright (c) 2018, 2020, Chris Fraire <[email protected]>.
2424
@@ -99,7 +99,7 @@ org.opengrok.indexer.web.SearchHelper"
9999
*/
100100
Context sourceContext = new Context(tquery, qbuilder);
101101
sourceContext.toggleAlt();
102-
// SRCROOT is read with UTF-8 as a default.
102+
// Files under source root are read with UTF-8 as a default.
103103
try (Reader r = IOUtils.createBOMStrippedReader(
104104
new FileInputStream(resourceFile),
105105
StandardCharsets.UTF_8.name())) {

0 commit comments

Comments
 (0)