fix: escape special_anchor in HtmlOutput JS string literal#1337
Merged
karlkleinpaste merged 1 commit intoJun 30, 2026
Merged
Conversation
HtmlOutput() interpolates settings.special_anchor into a JavaScript
string literal via g_strdup_printf("%s") inside a <script> tag.
The value originates from the '#' or '!' fragment of sword:// URLs,
which are constructed from untrusted BibleSync navigation packets
(biblesync_glue.cc:179) and module content hyperlinks
(wk-html.c:96). A crafted ref containing #";alert(1);}// breaks
out of the JS string and executes arbitrary script in the
JS-enabled WebKit2 view with file:// origin.
Escape the anchor with g_strescape() before interpolation to
neutralize backslash, double-quote, and other JS string literal
metacharacters.
hyder365
added a commit
to hyder365/xiphos
that referenced
this pull request
Jun 30, 2026
g_strescape() escapes backslash, double-quote, and C0 control bytes but leaves '<', '>', and '/' untouched, so a sword:// fragment of "</script><script>PAYLOAD</script>" passes through unchanged when interpolated into the inline <script> block in HtmlOutput(). WebKit's HTML parser ends the <script> block at the literal '</script>' and starts a new <script>PAYLOAD</script> block, which executes arbitrary JavaScript in the file:// origin that wk_html_set_base_uri() hardcodes. The fragment can originate from any BibleSync UDP multicast peer on the LAN (biblesync_glue.cc:179 constructs the sword:// URL from peer-supplied bible and ref), from argv[1], or from a sword:// href click inside a malicious SWORD module. Substitute '</' with '<\/' in the already-escaped anchor (OWASP recommendation) so the HTML parser can no longer see the end of the script block. g_strescape is kept in place to also handle the backslash-and-quote break-out case from the prior fix (crosswire#1337).
hyder365
added a commit
to hyder365/xiphos
that referenced
this pull request
Jul 1, 2026
g_strescape() escapes backslash, double-quote, and C0 control bytes but leaves '<', '>', and '/' untouched, so a sword:// fragment of "</script><script>PAYLOAD</script>" passes through unchanged when interpolated into the inline <script> block in HtmlOutput(). WebKit's HTML parser ends the <script> block at the literal '</script>' and starts a new <script>PAYLOAD</script> block, which executes arbitrary JavaScript in the file:// origin that wk_html_set_base_uri() hardcodes. The fragment can originate from any BibleSync UDP multicast peer on the LAN (biblesync_glue.cc:179 constructs the sword:// URL from peer-supplied bible and ref), from argv[1], or from a sword:// href click inside a malicious SWORD module. Substitute '</' with '<\/' in the already-escaped anchor (OWASP recommendation) so the HTML parser can no longer see the end of the script block. g_strescape is kept in place to also handle the backslash-and-quote break-out case from the prior fix (crosswire#1337).
karlkleinpaste
pushed a commit
that referenced
this pull request
Jul 1, 2026
g_strescape() escapes backslash, double-quote, and C0 control bytes but leaves '<', '>', and '/' untouched, so a sword:// fragment of "</script><script>PAYLOAD</script>" passes through unchanged when interpolated into the inline <script> block in HtmlOutput(). WebKit's HTML parser ends the <script> block at the literal '</script>' and starts a new <script>PAYLOAD</script> block, which executes arbitrary JavaScript in the file:// origin that wk_html_set_base_uri() hardcodes. The fragment can originate from any BibleSync UDP multicast peer on the LAN (biblesync_glue.cc:179 constructs the sword:// URL from peer-supplied bible and ref), from argv[1], or from a sword:// href click inside a malicious SWORD module. Substitute '</' with '<\/' in the already-escaped anchor (OWASP recommendation) so the HTML parser can no longer see the end of the script block. g_strescape is kept in place to also handle the backslash-and-quote break-out case from the prior fix (#1337). Co-authored-by: Devon Kirk <hyder365@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
HtmlOutput() in src/gtk/utilities.c interpolates settings.special_anchor into a JavaScript string literal inside a <script> tag via g_strdup_printf("window.location.hash = "%s"") with no escaping.
special_anchor is set in sword_uri() (src/main/url.cc:759) from the '#' or '!' fragment of sword:// URLs. These URLs are constructed from untrusted BibleSync navigation packets at biblesync_glue.cc:179, where a LAN peer controls the bible and ref fields directly from the UDP packet.
A crafted ref containing #"alert(1);}// breaks out of the JS string literal and executes arbitrary script in the JS-enabled WebKit2 view with file:// origin, when a user has BibleSync enabled.
The fix escapes the anchor value with g_strescape() before interpolation into the script tag, neutralizing backslash, double-quote, and other JS string literal metacharacters. The unescaped value is still used for XIPHOS_HTML_JUMP_TO_ANCHOR which handles it as an HTML anchor name, not executable code.