From b29e88c18eefae19778e6a761e8b1af6480c8c84 Mon Sep 17 00:00:00 2001 From: patbos Date: Mon, 10 Mar 2014 10:23:13 +0100 Subject: [PATCH] JENKINS-22100 Added escape of messages in changelog --- src/main/webapp/pipe.js | 59 +++++++++++++++++--------------- src/test/javascript/pipe-test.js | 26 ++++++++++++++ 2 files changed, 58 insertions(+), 27 deletions(-) diff --git a/src/main/webapp/pipe.js b/src/main/webapp/pipe.js index 27215f0b3..f4d727af0 100644 --- a/src/main/webapp/pipe.js +++ b/src/main/webapp/pipe.js @@ -12,7 +12,7 @@ function updatePipelines(divNames, errorDiv, view, showAvatars, showChanges, tim }, timeout); }, error: function (xhr, status, error) { - Q("#" + errorDiv).html('Error communicating to server! ' + error); + Q("#" + errorDiv).html('Error communicating to server! ' + htmlEncode(error)); Q("#" + errorDiv).show(); plumb.repaintEverything(); setTimeout(function () { @@ -44,7 +44,7 @@ function refreshPipelines(data, divNames, errorDiv, view, showAvatars, showChang for (var c = 0; c < data.pipelines.length; c++) { var component = data.pipelines[c]; var html = "
"; - html = html + "

" + component.name + "

"; + html = html + "

" + htmlEncode(component.name) + "

"; if (component.pipelines.length == 0) { html = html + "No builds done yet."; } @@ -56,7 +56,7 @@ function refreshPipelines(data, divNames, errorDiv, view, showAvatars, showChang if (pipeline.triggeredBy && pipeline.triggeredBy.length > 0) { for (var y = 0; y < pipeline.triggeredBy.length; y++) { var trigger = pipeline.triggeredBy[y]; - triggered = triggered + ' ' + trigger.description + ''; + triggered = triggered + ' ' + htmlEncode(trigger.description) + ''; } if (y < pipeline.triggeredBy.length - 1) { triggered = triggered + ", "; @@ -66,35 +66,17 @@ function refreshPipelines(data, divNames, errorDiv, view, showAvatars, showChang if (pipeline.aggregated) { html = html + '

Aggregated view

' } else { - html = html + '

' + pipeline.version; + html = html + '

' + htmlEncode(pipeline.version); if (triggered != "") { html = html + " triggered by " + triggered; } html = html + ' started ' + formatDate(pipeline.timestamp, lastUpdate) + '

'; if (showChanges && pipeline.changes && pipeline.changes.length > 0) { - html = html + '
'; - html = html + '

Changes:

'; - for (var o = 0; o < pipeline.changes.length; o++) { - html = html + '
'; - var change = pipeline.changes[o]; - html = html + '
' + change.author.name + '
'; - if (change.changeLink) { - html = html + ''; - } else { - html = html + '
' + change.message + '
'; - } - html = html + '
'; - } - html = html + '
'; - - + html = html + generateChangeLog(pipeline.changes); } - - } - var row = 0; var column = 0; @@ -116,7 +98,7 @@ function refreshPipelines(data, divNames, errorDiv, view, showAvatars, showChang } html = html + '
'; - html = html + '
' + stage.name + ''; + html = html + '
' + htmlEncode(stage.name) + ''; if (!pipeline.aggregated) { html = html + '
' } else { @@ -124,7 +106,7 @@ function refreshPipelines(data, divNames, errorDiv, view, showAvatars, showChang if (!stageversion) { stageversion = "N/A" } - html = html + ' ' + stageversion + '
' + html = html + ' ' + htmlEncode(stageversion) + '' } for (var k = 0; k < stage.tasks.length; k++) { var task = stage.tasks[k]; @@ -136,7 +118,7 @@ function refreshPipelines(data, divNames, errorDiv, view, showAvatars, showChang tasks.push({id: id, taskId: task.id, buildId: task.buildId}); html = html + "
"; + "\">"; if (timestamp != "") { html = html + "" + timestamp + "" @@ -186,7 +168,7 @@ function refreshPipelines(data, divNames, errorDiv, view, showAvatars, showChang [ "Arrow", { location: 1}] ], cssClass: "relation", - connector: ["Flowchart", { stub: 25, gap: 2, midpoint: 1, alwaysRespectStubs:true } ], + connector: ["Flowchart", { stub: 25, gap: 2, midpoint: 1, alwaysRespectStubs: true } ], paintStyle: { lineWidth: 2, strokeStyle: "rgba(0,0,0,0.5)" }, drawEndpoints: false }); @@ -226,6 +208,24 @@ function refreshPipelines(data, divNames, errorDiv, view, showAvatars, showChang plumb.repaintEverything(); } +function generateChangeLog(changes) { + var html = '
'; + html = html + '

Changes:

'; + for (var i = 0; i < changes.length; i++) { + html = html + '
'; + var change = changes[i]; + html = html + '
' + htmlEncode(change.author.name) + '
'; + if (change.changeLink) { + html = html + ''; + } else { + html = html + '
' + htmlEncode(change.message) + '
'; + } + html = html + '
'; + } + html = html + '
'; + return html; +} + function getStageClassName(stagename) { return "stage_" + replace(stagename, " ", "_"); } @@ -268,6 +268,11 @@ function formatDuration(millis) { return "0 sec"; } +function htmlEncode(html) { + html = document.createElement('a').appendChild( + document.createTextNode(html)).parentNode.innerHTML; + return html.replace(/\n/g, '
'); +} function getStageId(name, count) { var re = new RegExp(' ', 'g'); return name.replace(re, '_') + "_" + count; diff --git a/src/test/javascript/pipe-test.js b/src/test/javascript/pipe-test.js index d72f722bc..d44def8b6 100644 --- a/src/test/javascript/pipe-test.js +++ b/src/test/javascript/pipe-test.js @@ -23,3 +23,29 @@ describe("getStageClassName", function() { expect(getStageClassName("QA 1")).toEqual("stage_QA_1"); }); }); + +describe("htmlEncode", function() { + it("correctly returns html safe strings", function() { + expect(htmlEncode("Line1\nLine2")).toEqual("Line1
Line2"); + expect(htmlEncode("UTF-8")).toEqual("<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>"); + }); +}); + +describe("generateChangeLog", function() { + it("Linebreak in changelog comment", function() { + var data = JSON.parse('{"changes":[{"author":{"name":"Firstname Lastname","avatarUrl":null,"url":"user/user"},"changeLink":null,"commitId":"2718","message":"Firstline\\nSecondLine"}]}'); + expect(generateChangeLog(data.changes)).toEqual('

Changes:

Firstname Lastname
Firstline
SecondLine
'); + }); + it("XML in changelog comment", function() { + var data = JSON.parse('{"changes":[{"author":{"name":"Firstname Lastname","avatarUrl":null,"url":"user/user"},"changeLink":null,"commitId":"2718","message":"data"}]}'); + expect(generateChangeLog(data.changes)).toEqual('

Changes:

Firstname Lastname
<xml>data</xml>
'); + }); + it("Swedish characters in changelog comment", function() { + var data = JSON.parse('{"changes":[{"author":{"name":"Firstname Lastname","avatarUrl":null,"url":"user/user"},"changeLink":null,"commitId":"2718","message":"Räksmörgås"}]}'); + expect(generateChangeLog(data.changes)).toEqual('

Changes:

Firstname Lastname
Räksmörgås
'); + }); + it("Multiple changelogs", function() { + var data = JSON.parse('{"changes":[{"author":{"name":"Firstname Lastname","avatarUrl":null,"url":"user/user"},"changeLink":null,"commitId":"2718","message":"First change"}, {"author":{"name":"Firstname Lastname","avatarUrl":null,"url":"user/user"},"changeLink":null,"commitId":"2718","message":"Second change"}]}'); + expect(generateChangeLog(data.changes)).toEqual('

Changes:

Firstname Lastname
First change
Firstname Lastname
Second change
'); + }); +});