Skip to content

Commit

Permalink
[JENKINS-74015][JENKINS-74746] Improve CSP compatibility (#312)
Browse files Browse the repository at this point in the history
* [JENKINS-74015][JENKINS-74746] Improve CSP compatibility
* Address review feedback
* Do not use shaded `commons-lang`
  • Loading branch information
yaroslavafenkin authored Nov 13, 2024
1 parent bf37023 commit c599ece
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 36 deletions.
27 changes: 15 additions & 12 deletions src/main/java/htmlpublisher/HtmlPublisher.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.util.DirScanner;
import jenkins.util.SystemProperties;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.accmod.restrictions.suppressions.SuppressRestrictedWarnings;
Expand All @@ -60,6 +61,7 @@
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.Functions;
import hudson.matrix.MatrixConfiguration;
import hudson.matrix.MatrixProject;
import hudson.model.AbstractBuild;
Expand Down Expand Up @@ -237,6 +239,7 @@ public static boolean publishReports(Run<?, ?> build, FilePath workspace, TaskLi
for (int i=0; i < reportTargets.size(); i++) {
// Create an array of lines we will eventually write out, initially the header.
List<String> reportLines = new ArrayList<>(headerLines);
reportLines.add("<script type=\"text/javascript\" src=\"" + getStaticResourcesUrl() + "/plugin/htmlpublisher/js/htmlpublisher.js\"></script>");
HtmlPublisherTarget reportTarget = reportTargets.get(i);
boolean keepAll = reportTarget.getKeepAll();
boolean allowMissing = reportTarget.getAllowMissing();
Expand Down Expand Up @@ -338,23 +341,14 @@ public static boolean publishReports(Run<?, ?> build, FilePath workspace, TaskLi
} else {
reportFile = report;
}
String tabItem = "<li id=\"" + tabNo + "\" class=\"unselected\" onclick=\"updateBody('" + tabNo + "');\" value=\"" + htmlAttributeEscape(report) + "\">" + htmlAttributeEscape(getTitle(reportFile, titles, j)) + "</li>";
String tabItem = "<li id=\"" + tabNo + "\" class=\"unselected\" value=\"" + htmlAttributeEscape(report) + "\">" + htmlAttributeEscape(getTitle(reportFile, titles, j)) + "</li>";
reportLines.add(tabItem);
}
// Add the JS to change the link as appropriate.
String hudsonUrl = Jenkins.get().getRootUrl();
Job job = build.getParent();
reportLines.add("<script type=\"text/javascript\">document.getElementById(\"hudson_link\").innerHTML=\"Back to " + htmlAttributeEscape(job.getName()) + "\";</script>");
// If the URL isn't configured in Hudson, the best we can do is attempt to go Back.
if (hudsonUrl == null) {
reportLines.add("<script type=\"text/javascript\">document.getElementById(\"hudson_link\").onclick = function() { history.go(-1); return false; };</script>");
} else {
String jobUrl = hudsonUrl + job.getUrl();
reportLines.add("<script type=\"text/javascript\">document.getElementById(\"hudson_link\").href=\"" + jobUrl + "\";</script>");
}

reportLines.add("<script type=\"text/javascript\">document.getElementById(\"zip_link\").href=\"*zip*/" + reportTarget.getSanitizedName() + ".zip\";</script>");

reportLines.add("<span class='links-data-holder' data-back-to-name='" + job.getName() + "' data-root-url='" +
hudsonUrl + "' data-job-url='" + job.getUrl() + "' data-zip-link='" + reportTarget.getSanitizedName() + "'/>");
// Now add the footer.
reportLines.addAll(footerLines);
// And write this as the index
Expand Down Expand Up @@ -398,6 +392,15 @@ public Collection<? extends Action> getProjectActions(AbstractProject<?, ?> proj
}
}

private static String getStaticResourcesUrl() {
String rootUrl = Jenkins.get().getRootUrl();
if (rootUrl == null) {

Check warning on line 397 in src/main/java/htmlpublisher/HtmlPublisher.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 397 is only partially covered, one branch is missing
rootUrl = "";

Check warning on line 398 in src/main/java/htmlpublisher/HtmlPublisher.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 398 is not covered by tests
}

return StringUtils.stripEnd(rootUrl, "/") + Functions.getResourcePath();
}

private static void adjustMatrixProject(AbstractProject<?, ?> project) {
MatrixProject mp = (MatrixProject) project;
if (mp.getActiveConfigurations() != null) {
Expand Down
19 changes: 1 addition & 18 deletions src/main/resources/htmlpublisher/HtmlPublisher/header.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,26 +86,9 @@
}

</style>

<script type="text/javascript">
function updateBody(tabId, page) {
document.getElementById(selectedTab).setAttribute("class", "unselected");
tab = document.getElementById(tabId)
tab.setAttribute("class", "selected");
selectedTab = tabId;
iframe = document.getElementById("myframe");
iframe.src = encodeURIComponent(tab.getAttribute("value")).replace(/%2F/g, '/');
}
function init(tabId){
updateBody(tabId);
}

var selectedTab = "tab1"
</script>

</head>

<body onload="init('tab1');">
<body>
<nav>
<h1><a id="hudson_link" href="#"></a></h1>
<h2><a id="zip_link" href="#">Zip</a></h2>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package htmlpublisher.HtmlPublisherTarget.BaseHTMLAction

import htmlpublisher.HtmlPublisher
import htmlpublisher.HtmlPublisherTarget
import hudson.Functions
import hudson.Util
import hudson.model.Descriptor

Expand All @@ -10,6 +11,8 @@ import java.security.MessageDigest
l = namespace(lib.LayoutTagLib)
st = namespace("jelly:stapler")

Functions.initPageVariables(context)

def text = new File(my.dir(), my.getHTMLTarget().getWrapperName()).text

def actual = Util.toHexString(MessageDigest.getInstance("SHA-1").digest(text.getBytes("UTF-8")))
Expand All @@ -26,9 +29,10 @@ def serveWrapper() {
def footer = HtmlPublisher.class.getResourceAsStream("/htmlpublisher/HtmlPublisher/footer.html").text

raw(header)
script(src: "${resURL}/plugin/htmlpublisher/js/htmlpublisher.js", type: "text/javascript")

def legacyFile = new File(my.dir(), "htmlpublisher-wrapper.html")
def matcher = legacyFile.text =~ /<li id="tab\d+" class="unselected" onclick="updateBody\('tab\d+'\);" value="([^"]+)">([^<]+)<\/li>/
def matcher = legacyFile.text =~ /<li id="tab\d+" class="unselected"(?: onclick="updateBody\('tab\d+'\);")? value="([^"]+)">([^<]+)<\/li>/

def items = []
def itemsTitle = []
Expand All @@ -39,14 +43,15 @@ def serveWrapper() {

def idx = 1
items.each { file ->
li(itemsTitle[idx - 1], id: "tab${idx}", class: "unselected", onclick: "updateBody('tab${idx}')", value: file.trim())
li(itemsTitle[idx - 1], id: "tab${idx}", class: "unselected", value: file.trim())
idx++
}

// TODO replace unnecessary JS usage by properly integrating header.html/footer.html in this groovy view
raw("<script type=\"text/javascript\">document.getElementById(\"hudson_link\").innerHTML=\"Back to ${my.backToName}\";</script>")
raw("<script type=\"text/javascript\">document.getElementById(\"hudson_link\").href=\"${rootURL}/${my.backToUrl}\";</script>")
raw("<script type=\"text/javascript\">document.getElementById(\"zip_link\").href=\"*zip*/${my.getHTMLTarget().sanitizedName}.zip\";</script>")
span(class: "links-data-holder",
"data-back-to-name": "${my.backToName}",
"data-root-url": "${rootURL}",
"data-job-url": "${my.backToUrl}",
"data-zip-link": "${my.getHTMLTarget().sanitizedName}")

raw(footer)
}
Expand Down
31 changes: 31 additions & 0 deletions src/main/webapp/js/htmlpublisher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
function updateBody(tabId) {
document.getElementById(selectedTab).setAttribute("class", "unselected");
tab = document.getElementById(tabId)
tab.setAttribute("class", "selected");
selectedTab = tabId;
iframe = document.getElementById("myframe");
iframe.src = encodeURIComponent(tab.getAttribute("value")).replace(/%2F/g, '/');
}
function init(tabId){
updateBody(tabId);
}

var selectedTab = "tab1";

window.addEventListener("DOMContentLoaded", () => {
init("tab1");

const dataHolder = document.querySelector(".links-data-holder");
const { backToName, rootUrl, jobUrl, zipLink } = dataHolder.dataset;
const backButton = document.querySelector("#hudson_link");
backButton.innerText = `Back to ${backToName}`;
backButton.href = `${rootUrl}/${jobUrl}`;

document.querySelector("#zip_link").href = `*zip*/${zipLink}.zip`;

document.querySelectorAll("#tabnav li").forEach((item) => {
item.addEventListener("click", (event) => {
updateBody(event.target.id);
});
});
});

0 comments on commit c599ece

Please sign in to comment.