Skip to content

Commit 89b83e4

Browse files
committed
Add extra escaping to handle newlines and other characters.
1 parent ea9fd47 commit 89b83e4

File tree

2 files changed

+14
-8
lines changed

2 files changed

+14
-8
lines changed

core/src/main/scala/org/apache/spark/ui/jobs/AllJobsPage.scala

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import javax.servlet.http.HttpServletRequest
2323
import scala.collection.mutable.{HashMap, ListBuffer}
2424
import scala.xml._
2525

26+
import org.apache.commons.lang3.StringEscapeUtils
27+
2628
import org.apache.spark.JobExecutionStatus
2729
import org.apache.spark.ui.{ToolTips, UIUtils, WebUIPage}
2830
import org.apache.spark.ui.jobs.UIData.{ExecutorUIData, JobUIData}
@@ -87,9 +89,10 @@ private[ui] class AllJobsPage(parent: JobsTab) extends WebUIPage("") {
8789
case JobExecutionStatus.UNKNOWN => "unknown"
8890
}
8991

90-
// The timeline library treats contents as HTML, so we have to escape them; for the
91-
// data-title attribute string we have to escape them twice since that's in a string.
92+
// The timeline library treats contents as HTML, so we have to escape them. We need to add
93+
// extra layers of escaping in order to embed this in a Javascript string literal.
9294
val escapedDesc = Utility.escape(displayJobDescription)
95+
val jsEscapedDesc = StringEscapeUtils.escapeEcmaScript(escapedDesc)
9396
val jobEventJsonAsStr =
9497
s"""
9598
|{
@@ -99,7 +102,7 @@ private[ui] class AllJobsPage(parent: JobsTab) extends WebUIPage("") {
99102
| 'end': new Date(${completionTime}),
100103
| 'content': '<div class="application-timeline-content"' +
101104
| 'data-html="true" data-placement="top" data-toggle="tooltip"' +
102-
| "data-title=\\"${Utility.escape(escapedDesc)} (Job ${jobId})<br>" +
105+
| 'data-title="${jsEscapedDesc} (Job ${jobId})<br>' +
103106
| 'Status: ${status}<br>' +
104107
| 'Submitted: ${UIUtils.formatDate(new Date(submissionTime))}' +
105108
| '${
@@ -109,7 +112,7 @@ private[ui] class AllJobsPage(parent: JobsTab) extends WebUIPage("") {
109112
""
110113
}
111114
}">' +
112-
| "${escapedDesc} (Job ${jobId})</div>"
115+
| '${jsEscapedDesc} (Job ${jobId})</div>'
113116
|}
114117
""".stripMargin
115118
jobEventJsonAsStr

core/src/main/scala/org/apache/spark/ui/jobs/JobPage.scala

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import javax.servlet.http.HttpServletRequest
2323
import scala.collection.mutable.{Buffer, HashMap, ListBuffer}
2424
import scala.xml.{Node, NodeSeq, Unparsed, Utility}
2525

26+
import org.apache.commons.lang3.StringEscapeUtils
27+
2628
import org.apache.spark.JobExecutionStatus
2729
import org.apache.spark.scheduler.StageInfo
2830
import org.apache.spark.ui.{ToolTips, UIUtils, WebUIPage}
@@ -63,9 +65,10 @@ private[ui] class JobPage(parent: JobsTab) extends WebUIPage("job") {
6365
val submissionTime = stage.submissionTime.get
6466
val completionTime = stage.completionTime.getOrElse(System.currentTimeMillis())
6567

66-
// The timeline library treats contents as HTML, so we have to escape them; for the
67-
// data-title attribute string we have to escape them twice since that's in a string.
68+
// The timeline library treats contents as HTML, so we have to escape them. We need to add
69+
// extra layers of escaping in order to embed this in a Javascript string literal.
6870
val escapedName = Utility.escape(name)
71+
val jsEscapedName = StringEscapeUtils.escapeEcmaScript(escapedName)
6972
s"""
7073
|{
7174
| 'className': 'stage job-timeline-object ${status}',
@@ -74,7 +77,7 @@ private[ui] class JobPage(parent: JobsTab) extends WebUIPage("job") {
7477
| 'end': new Date(${completionTime}),
7578
| 'content': '<div class="job-timeline-content" data-toggle="tooltip"' +
7679
| 'data-placement="top" data-html="true"' +
77-
| "data-title=\\"${Utility.escape(escapedName)} (Stage ${stageId}.${attemptId})<br>" +
80+
| 'data-title="${jsEscapedName} (Stage ${stageId}.${attemptId})<br>' +
7881
| 'Status: ${status.toUpperCase}<br>' +
7982
| 'Submitted: ${UIUtils.formatDate(new Date(submissionTime))}' +
8083
| '${
@@ -84,7 +87,7 @@ private[ui] class JobPage(parent: JobsTab) extends WebUIPage("job") {
8487
""
8588
}
8689
}">' +
87-
| "${escapedName} (Stage ${stageId}.${attemptId})</div>",
90+
| '${jsEscapedName} (Stage ${stageId}.${attemptId})</div>',
8891
|}
8992
""".stripMargin
9093
}

0 commit comments

Comments
 (0)