Submitted By:            Douglas R. Reno <renodr at linuxfromscratch dot org>
Date:                    2025-04-14
Initial Package Version: 42.1
Origin:                  Patch submitted to upstream by reporter
Upstream Status:         Pending (Issue #221 in the yelp repository)
Description:             Fixes a security vulnerability that allows for yelp to
                         arbitrary exfiltrate files from a system when loading
                         a help document. This is caused by arbitrary JavaScript
                         execution, and a proof of concept exploit is now public
                         that exfiltrate's a user's OpenSSH private key. The
                         CVE number for this vulnerability is CVE-2025-3155.
                         Upstream has been aware of this for a while and the
                         reporter of the vulnerability provided patches, though
                         the Yelp/yelp-xsl maintainers have not fixed it yet.
                         A GNOME Security Team representative has recommended
                         using the reporter's patches though as it is *not*
                         advisable to skip patching this vulnerability.
                         The patches in yelp-xsl and yelp add a Content Security
                         Policy (CSP) preventing JavaScript execution through 
                         help documents.


diff --git a/data/xslt/mal2html.xsl.in b/data/xslt/mal2html.xsl.in
index 9e44b734..0a74da55 100644
--- a/data/xslt/mal2html.xsl.in
+++ b/data/xslt/mal2html.xsl.in
@@ -19,6 +19,11 @@
 <xsl:param name="mal.link.prefix" select="'xref:'"/>
 <xsl:param name="mal.link.extension" select="''"/>
 
+<xsl:template name="html.head.top.custom">
+  <xsl:param name="node" select="."/>
+  <meta http-equiv="Content-Security-Policy" content="default-src bogus-ghelp: bogus-gnome-help: bogus-help: bogus-help-list: bogus-info: bogus-man: ; script-src 'nonce-{$html.csp.nonce}'; style-src 'nonce-{$html.csp.nonce}'; "/>
+</xsl:template>
+
 <xsl:template name="mal.link.target.custom">
   <xsl:param name="node" select="."/>
   <xsl:param name="action" select="$node/@action"/>
diff --git a/data/xslt/man2html.xsl.in b/data/xslt/man2html.xsl.in
index 676ce3eb..56bc1f5c 100644
--- a/data/xslt/man2html.xsl.in
+++ b/data/xslt/man2html.xsl.in
@@ -131,7 +131,7 @@
   the correct styling and a single character which we measure the
   width of and update each sheet as required.
 -->
-<script type="text/javascript" language="javascript">
+<script type="text/javascript" language="javascript" nonce="{$html.csp.nonce}">
 <xsl:text>
 $(document).ready (function () {
   var div = document.getElementById("invisible-char");
diff --git a/data/xslt/yelp-common.xsl.in b/data/xslt/yelp-common.xsl.in
index 0c1ec9bb..421fc02d 100644
--- a/data/xslt/yelp-common.xsl.in
+++ b/data/xslt/yelp-common.xsl.in
@@ -15,6 +15,13 @@
 <xsl:param name="html.syntax.highlight" select="true()"/>
 <xsl:param name="html.js.root" select="'file://@XSL_JSDIR@/'"/>
 
+<xsl:param name="html.csp.nonce" select="yelp:generate_nonce()"/>
+
+<xsl:template name="html.head.top.custom">
+  <xsl:param name="node" select="."/>
+  <meta http-equiv="Content-Security-Policy" content="default-src bogus-ghelp: bogus-gnome-help: bogus-help: bogus-help-list: bogus-info: bogus-man: ; script-src 'nonce-{$html.csp.nonce}'; style-src 'unsafe-inline'; "/>
+</xsl:template>
+
 <xsl:template name="html.js.mathjax">
   <xsl:param name="node" select="."/>
   <xsl:if test="$node//mml:*[1]">
diff --git a/libyelp/yelp-transform.c b/libyelp/yelp-transform.c
index e74eb463..2ce1d05b 100644
--- a/libyelp/yelp-transform.c
+++ b/libyelp/yelp-transform.c
@@ -71,6 +71,8 @@ static void      xslt_yelp_cache            (xsltTransformContextPtr  ctxt,
                                              xsltStylePreCompPtr      comp);
 static void      xslt_yelp_aux              (xmlXPathParserContextPtr ctxt,
                                              int                      nargs);
+static void      xslt_yelp_generate_nonce   (xmlXPathParserContextPtr ctxt,
+                                             int                      nargs);
 
 enum {
     PROP_0,
@@ -412,6 +414,10 @@ transform_run (YelpTransform *transform)
                              BAD_CAST "input",
                              BAD_CAST YELP_NAMESPACE,
                              (xmlXPathFunction) xslt_yelp_aux);
+    xsltRegisterExtFunction (priv->context,
+                         BAD_CAST "generate_nonce",
+                         BAD_CAST YELP_NAMESPACE,
+                         (xmlXPathFunction) xslt_yelp_generate_nonce);
 
     priv->output = xsltApplyStylesheetUser (priv->stylesheet,
                                             priv->input,
@@ -607,3 +613,16 @@ xslt_yelp_aux (xmlXPathParserContextPtr ctxt, int nargs)
     xsltExtensionInstructionResultRegister (tctxt, ret);
     valuePush (ctxt, ret);
 }
+
+static void
+xslt_yelp_generate_nonce (xmlXPathParserContextPtr ctxt, int nargs)
+{
+    GRand* rand;
+    gchar* nonce_str;
+
+    rand = g_rand_new ();
+    nonce_str = g_strdup_printf("%08x%08x", g_rand_int (rand), g_rand_int (rand));
+    xmlXPathReturnString (ctxt, xmlStrdup ((xmlChar *) nonce_str));
+    g_free(nonce_str);
+    g_rand_free(rand);
+}
diff --git a/libyelp/yelp-view.c b/libyelp/yelp-view.c
index 32ae131e..d544c5df 100644
--- a/libyelp/yelp-view.c
+++ b/libyelp/yelp-view.c
@@ -971,7 +971,7 @@ view_external_uri (YelpView *view,
 
     if (app_info)
       {
-        if (!strstr (g_app_info_get_executable (app_info), "yelp"))
+        if (!strstr (g_app_info_get_executable (app_info), "yelp") && !strstr (struri, "%3C") && !strstr (struri, "%3E"))
           {
             GList l;
 
