You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spark.apache.org by ho...@apache.org on 2026/09/24 22:28:16 UTC

(spark) branch branch-4.2 updated: [SPARK-59406][UI] Fix UI display on weird custom log names

This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/branch-4.2 by this push:
     new e938550a4ac2 [SPARK-59406][UI] Fix UI display on weird custom log names
e938550a4ac2 is described below

commit e938550a4ac20cb6c4d8a29af9c763853de1c1e6
Author: Holden Karau <ho...@pigscanfly.ca>
AuthorDate: Thu Sep 24 15:26:38 2026 -0700

    [SPARK-59406][UI] Fix UI display on weird custom log names
    
    ### What changes were proposed in this pull request?
    
    Better handling for custom log names and URLs
    
    ### Why are the changes needed?
    Custom log names can be weird, so let's validate that they're displayable before we display them.
    
    ### Does this PR introduce _any_ user-facing change?
    
    Invalid log URLs will be replaced with plain text.
    
    ### How was this patch tested?
    
    New unit tests
    
    ### Was this patch authored or co-authored using generative AI tooling?
    Generated-by: Claude (mixture of models) and Cursor (Kimi K3)
    
    Closes #58699 from holdenk/f026-validate-custom-log-names.
    
    Lead-authored-by: Holden Karau <ho...@pigscanfly.ca>
    Co-authored-by: Holden Karau <ho...@snowflake.com>
    Signed-off-by: Holden Karau <ho...@snowflake.com>
    (cherry picked from commit b3149c6aaf64d73de6dd96b5b209106c55cd7e5a)
    Signed-off-by: Holden Karau <ho...@snowflake.com>
---
 .../resources/org/apache/spark/ui/static/utils.js  | 18 +++++++++++++++++-
 ui-test/tests/utils.test.js                        | 22 ++++++++++++++++++++++
 2 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/core/src/main/resources/org/apache/spark/ui/static/utils.js b/core/src/main/resources/org/apache/spark/ui/static/utils.js
index 3f0885b9c4f5..bc3143fa4efe 100644
--- a/core/src/main/resources/org/apache/spark/ui/static/utils.js
+++ b/core/src/main/resources/org/apache/spark/ui/static/utils.js
@@ -88,12 +88,28 @@ function getTimeZone() {
   }
 }
 
+/* Custom log URLs are rendered as links only when they are http(s) or relative
+ * URLs; anything else is shown as plain text. Browsers strip ASCII whitespace
+ * and control characters when parsing URLs, so remove them before checking
+ * the scheme. */
+function isHttpOrRelativeUrl(url) {
+  if (typeof url !== 'string') return false;
+  /* eslint-disable-next-line no-control-regex */
+  var normalized = url.replace(/[\u0000-\u0020]/g, '').toLowerCase();
+  return /^https?:\/\//.test(normalized) || !/^[a-z][a-z0-9+.-]*:/.test(normalized);
+}
+
 function formatLogsCells(execLogs, type) {
   if (type !== 'display') return Object.keys(execLogs);
   if (!execLogs) return;
   var result = '';
   $.each(execLogs, function (logName, logUrl) {
-    result += '<div><a href=' + logUrl + '>' + logName + '</a></div>'
+    // Custom log names and URLs can contain markup characters.
+    if (isHttpOrRelativeUrl(logUrl)) {
+      result += '<div><a href="' + escapeHtml(logUrl) + '">' + escapeHtml(logName) + '</a></div>'
+    } else {
+      result += '<div>' + escapeHtml(logName) + '</div>'
+    }
   });
   return result;
 }
diff --git a/ui-test/tests/utils.test.js b/ui-test/tests/utils.test.js
index b49f5034ff72..a2774475bf4f 100644
--- a/ui-test/tests/utils.test.js
+++ b/ui-test/tests/utils.test.js
@@ -16,6 +16,7 @@
  */
 
 
+import '../../core/src/main/resources/org/apache/spark/ui/static/jquery.min.js';
 import * as utils from '../../core/src/main/resources/org/apache/spark/ui/static/utils.js';
 
 test('ConvertDurationString', function () {
@@ -88,3 +89,24 @@ test('errorMessageCell escapes HTML in error messages', function () {
   expect(multiLine).not.toContain(payload);
   expect(multiLine).toContain('&lt;img src=x onerror=alert(document.domain)&gt;');
 });
+
+test('formatLogsCells escapes custom log names and URLs', function () {
+  // Names and URLs are escaped, and the href attribute is quoted.
+  const rendered = utils.formatLogsCells(
+    {'<b>stdout</b>': 'http://worker:8081/log?a=1&b="2"'}, 'display');
+  expect(rendered).toBe(
+    '<div><a href="http://worker:8081/log?a=1&amp;b=&quot;2&quot;">&lt;b&gt;stdout&lt;/b&gt;</a></div>');
+
+  // Only http(s) and relative URLs become links; other schemes render as text.
+  ['javascript:alert(1)', 'JAVAS\tCRIPT:alert(1)', 'data:text/html,x'].forEach(function (url) {
+    expect(utils.formatLogsCells({'stdout': url}, 'display')).toBe('<div>stdout</div>');
+  });
+
+  expect(utils.formatLogsCells({'stderr': 'logPage/?self&logType=stderr'}, 'display'))
+    .toBe('<div><a href="logPage/?self&amp;logType=stderr">stderr</a></div>');
+  expect(utils.formatLogsCells({'stdout': 'https://worker:8081/logPage'}, 'display'))
+    .toBe('<div><a href="https://worker:8081/logPage">stdout</a></div>');
+
+  // Non-display rendering returns the log names for sorting and filtering.
+  expect(utils.formatLogsCells({'stdout': 'javascript:alert(1)'}, 'sort')).toEqual(['stdout']);
+});


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@spark.apache.org
For additional commands, e-mail: commits-help@spark.apache.org