Merge pull request from GHSA-ggrq-h43f-3w7m
This fixes a CVE (currently https://github.com/nim-lang/Nim/security/advisories/GHSA-ggrq-h43f-3w7m)
This commit is contained in:
parent
520881af9a
commit
cb894c7094
3 changed files with 58 additions and 11 deletions
|
|
@ -282,7 +282,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
|
||||||
result.cache = cache
|
result.cache = cache
|
||||||
result.outDir = conf.outDir.string
|
result.outDir = conf.outDir.string
|
||||||
result.isPureRst = isPureRst
|
result.isPureRst = isPureRst
|
||||||
var options= {roSupportRawDirective, roSupportMarkdown, roPreferMarkdown}
|
var options= {roSupportRawDirective, roSupportMarkdown, roPreferMarkdown, roSandboxDisabled}
|
||||||
if not isPureRst: options.incl roNimFile
|
if not isPureRst: options.incl roNimFile
|
||||||
result.sharedState = newRstSharedState(
|
result.sharedState = newRstSharedState(
|
||||||
options, filename.string,
|
options, filename.string,
|
||||||
|
|
|
||||||
|
|
@ -235,6 +235,12 @@ type
|
||||||
## to Markdown) -- implies `roSupportMarkdown`
|
## to Markdown) -- implies `roSupportMarkdown`
|
||||||
roNimFile ## set for Nim files where default interpreted
|
roNimFile ## set for Nim files where default interpreted
|
||||||
## text role should be :nim:
|
## text role should be :nim:
|
||||||
|
roSandboxDisabled ## this option enables certain options
|
||||||
|
## (e.g. raw, include)
|
||||||
|
## which are disabled by default as they can
|
||||||
|
## enable users to read arbitrary data and
|
||||||
|
## perform XSS if the parser is used in a web
|
||||||
|
## app.
|
||||||
|
|
||||||
RstParseOptions* = set[RstParseOption]
|
RstParseOptions* = set[RstParseOption]
|
||||||
|
|
||||||
|
|
@ -260,7 +266,8 @@ type
|
||||||
mwBrokenLink = "broken link '$1'",
|
mwBrokenLink = "broken link '$1'",
|
||||||
mwUnsupportedLanguage = "language '$1' not supported",
|
mwUnsupportedLanguage = "language '$1' not supported",
|
||||||
mwUnsupportedField = "field '$1' not supported",
|
mwUnsupportedField = "field '$1' not supported",
|
||||||
mwRstStyle = "RST style: $1"
|
mwRstStyle = "RST style: $1",
|
||||||
|
meSandboxedDirective = "disabled directive: '$1'",
|
||||||
|
|
||||||
MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
|
MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
|
||||||
arg: string) {.closure, gcsafe.} ## what to do in case of an error
|
arg: string) {.closure, gcsafe.} ## what to do in case of an error
|
||||||
|
|
@ -315,6 +322,7 @@ const
|
||||||
":geek:": "icon_e_geek",
|
":geek:": "icon_e_geek",
|
||||||
":ugeek:": "icon_e_ugeek"
|
":ugeek:": "icon_e_ugeek"
|
||||||
}
|
}
|
||||||
|
SandboxDirAllowlist = ["image", "code", "code-block"]
|
||||||
|
|
||||||
type
|
type
|
||||||
TokType = enum
|
TokType = enum
|
||||||
|
|
@ -2987,10 +2995,14 @@ proc dirCodeBlock(p: var RstParser, nimExtension = false): PRstNode =
|
||||||
##
|
##
|
||||||
## As an extension this proc will process the ``file`` extension field and if
|
## As an extension this proc will process the ``file`` extension field and if
|
||||||
## present will replace the code block with the contents of the referenced
|
## present will replace the code block with the contents of the referenced
|
||||||
## file.
|
## file. This behaviour is disabled in sandboxed mode and can be re-enabled
|
||||||
|
## with the `roSandboxDisabled` flag.
|
||||||
result = parseDirective(p, rnCodeBlock, {hasArg, hasOptions}, parseLiteralBlock)
|
result = parseDirective(p, rnCodeBlock, {hasArg, hasOptions}, parseLiteralBlock)
|
||||||
var filename = strip(getFieldValue(result, "file"))
|
var filename = strip(getFieldValue(result, "file"))
|
||||||
if filename != "":
|
if filename != "":
|
||||||
|
if roSandboxDisabled notin p.s.options:
|
||||||
|
let tok = p.tok[p.idx-2]
|
||||||
|
rstMessage(p, meSandboxedDirective, "file", tok.line, tok.col)
|
||||||
var path = p.findRelativeFile(filename)
|
var path = p.findRelativeFile(filename)
|
||||||
if path == "": rstMessage(p, meCannotOpenFile, filename)
|
if path == "": rstMessage(p, meCannotOpenFile, filename)
|
||||||
var n = newRstNode(rnLiteralBlock)
|
var n = newRstNode(rnLiteralBlock)
|
||||||
|
|
@ -3086,6 +3098,11 @@ proc dirRaw(p: var RstParser): PRstNode =
|
||||||
|
|
||||||
proc selectDir(p: var RstParser, d: string): PRstNode =
|
proc selectDir(p: var RstParser, d: string): PRstNode =
|
||||||
result = nil
|
result = nil
|
||||||
|
let tok = p.tok[p.idx-2] # report on directive in ".. directive::"
|
||||||
|
if roSandboxDisabled notin p.s.options:
|
||||||
|
if d notin SandboxDirAllowlist:
|
||||||
|
rstMessage(p, meSandboxedDirective, d, tok.line, tok.col)
|
||||||
|
|
||||||
case d
|
case d
|
||||||
of "admonition", "attention", "caution": result = dirAdmonition(p, d)
|
of "admonition", "attention", "caution": result = dirAdmonition(p, d)
|
||||||
of "code": result = dirCodeBlock(p)
|
of "code": result = dirCodeBlock(p)
|
||||||
|
|
@ -3112,7 +3129,6 @@ proc selectDir(p: var RstParser, d: string): PRstNode =
|
||||||
of "title": result = dirTitle(p)
|
of "title": result = dirTitle(p)
|
||||||
of "warning": result = dirAdmonition(p, d)
|
of "warning": result = dirAdmonition(p, d)
|
||||||
else:
|
else:
|
||||||
let tok = p.tok[p.idx-2] # report on directive in ".. directive::"
|
|
||||||
rstMessage(p, meInvalidDirective, d, tok.line, tok.col)
|
rstMessage(p, meInvalidDirective, d, tok.line, tok.col)
|
||||||
|
|
||||||
proc prefix(ftnType: FootnoteType): string =
|
proc prefix(ftnType: FootnoteType): string =
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,10 @@ proc optionListLabel(opt: string): string =
|
||||||
opt &
|
opt &
|
||||||
"</span></tt></div>"
|
"</span></tt></div>"
|
||||||
|
|
||||||
|
const
|
||||||
|
NoSandboxOpts = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled}
|
||||||
|
|
||||||
|
|
||||||
suite "YAML syntax highlighting":
|
suite "YAML syntax highlighting":
|
||||||
test "Basics":
|
test "Basics":
|
||||||
let input = """.. code-block:: yaml
|
let input = """.. code-block:: yaml
|
||||||
|
|
@ -631,7 +635,9 @@ let x = 1
|
||||||
let p3 = """<p>Par3 <tt class="docutils literal"><span class="pre">""" & id"value3" & "</span></tt>.</p>"
|
let p3 = """<p>Par3 <tt class="docutils literal"><span class="pre">""" & id"value3" & "</span></tt>.</p>"
|
||||||
let p4 = """<p>Par4 <tt class="docutils literal"><span class="pre">value4</span></tt>.</p>"""
|
let p4 = """<p>Par4 <tt class="docutils literal"><span class="pre">value4</span></tt>.</p>"""
|
||||||
let expected = p1 & p2 & "\n" & p3 & "\n" & p4
|
let expected = p1 & p2 & "\n" & p3 & "\n" & p4
|
||||||
check(input.toHtml == expected)
|
check(
|
||||||
|
input.toHtml(NoSandboxOpts) == expected
|
||||||
|
)
|
||||||
|
|
||||||
test "role directive":
|
test "role directive":
|
||||||
let input = dedent"""
|
let input = dedent"""
|
||||||
|
|
@ -642,7 +648,10 @@ let x = 1
|
||||||
:language: brainhelp
|
:language: brainhelp
|
||||||
"""
|
"""
|
||||||
var warnings = new seq[string]
|
var warnings = new seq[string]
|
||||||
let output = input.toHtml(warnings=warnings)
|
let output = input.toHtml(
|
||||||
|
NoSandboxOpts,
|
||||||
|
warnings=warnings
|
||||||
|
)
|
||||||
check(warnings[].len == 1 and "language 'brainhelp' not supported" in warnings[0])
|
check(warnings[].len == 1 and "language 'brainhelp' not supported" in warnings[0])
|
||||||
|
|
||||||
test "RST comments":
|
test "RST comments":
|
||||||
|
|
@ -1187,7 +1196,9 @@ Test1
|
||||||
.. tip:: endOf tip
|
.. tip:: endOf tip
|
||||||
.. warning:: endOf warning
|
.. warning:: endOf warning
|
||||||
"""
|
"""
|
||||||
let output0 = input0.toHtml
|
let output0 = input0.toHtml(
|
||||||
|
NoSandboxOpts
|
||||||
|
)
|
||||||
for a in ["admonition", "attention", "caution", "danger", "error", "hint",
|
for a in ["admonition", "attention", "caution", "danger", "error", "hint",
|
||||||
"important", "note", "tip", "warning" ]:
|
"important", "note", "tip", "warning" ]:
|
||||||
doAssert "endOf " & a & "</div>" in output0
|
doAssert "endOf " & a & "</div>" in output0
|
||||||
|
|
@ -1198,7 +1209,9 @@ Test1
|
||||||
|
|
||||||
Test paragraph.
|
Test paragraph.
|
||||||
"""
|
"""
|
||||||
let output1 = input1.toHtml
|
let output1 = input1.toHtml(
|
||||||
|
NoSandboxOpts
|
||||||
|
)
|
||||||
doAssert "endOfError</div>" in output1
|
doAssert "endOfError</div>" in output1
|
||||||
doAssert "<p>Test paragraph. </p>" in output1
|
doAssert "<p>Test paragraph. </p>" in output1
|
||||||
doAssert "class=\"admonition admonition-error\"" in output1
|
doAssert "class=\"admonition admonition-error\"" in output1
|
||||||
|
|
@ -1210,7 +1223,9 @@ Test1
|
||||||
|
|
||||||
Test paragraph.
|
Test paragraph.
|
||||||
"""
|
"""
|
||||||
let output2 = input2.toHtml
|
let output2 = input2.toHtml(
|
||||||
|
NoSandboxOpts
|
||||||
|
)
|
||||||
doAssert "endOfError Test2p.</div>" in output2
|
doAssert "endOfError Test2p.</div>" in output2
|
||||||
doAssert "<p>Test paragraph. </p>" in output2
|
doAssert "<p>Test paragraph. </p>" in output2
|
||||||
doAssert "class=\"admonition admonition-error\"" in output2
|
doAssert "class=\"admonition admonition-error\"" in output2
|
||||||
|
|
@ -1218,7 +1233,9 @@ Test1
|
||||||
let input3 = dedent """
|
let input3 = dedent """
|
||||||
.. note:: endOfNote
|
.. note:: endOfNote
|
||||||
"""
|
"""
|
||||||
let output3 = input3.toHtml
|
let output3 = input3.toHtml(
|
||||||
|
NoSandboxOpts
|
||||||
|
)
|
||||||
doAssert "endOfNote</div>" in output3
|
doAssert "endOfNote</div>" in output3
|
||||||
doAssert "class=\"admonition admonition-info\"" in output3
|
doAssert "class=\"admonition admonition-info\"" in output3
|
||||||
|
|
||||||
|
|
@ -1303,7 +1320,9 @@ Test1
|
||||||
|
|
||||||
That was a transition.
|
That was a transition.
|
||||||
"""
|
"""
|
||||||
let output1 = input1.toHtml
|
let output1 = input1.toHtml(
|
||||||
|
NoSandboxOpts
|
||||||
|
)
|
||||||
doAssert "<p id=\"target000\"" in output1
|
doAssert "<p id=\"target000\"" in output1
|
||||||
doAssert "<ul id=\"target001\"" in output1
|
doAssert "<ul id=\"target001\"" in output1
|
||||||
doAssert "<ol id=\"target002\"" in output1
|
doAssert "<ol id=\"target002\"" in output1
|
||||||
|
|
@ -1576,3 +1595,15 @@ suite "invalid targets":
|
||||||
"""((<a class="reference external" href="https://nim-lang.org/">Nim</a>))""")
|
"""((<a class="reference external" href="https://nim-lang.org/">Nim</a>))""")
|
||||||
check("(([Nim](javascript://nim-lang.org/)))".toHtml ==
|
check("(([Nim](javascript://nim-lang.org/)))".toHtml ==
|
||||||
"""((<a class="reference external" href="">Nim</a>))""")
|
"""((<a class="reference external" href="">Nim</a>))""")
|
||||||
|
|
||||||
|
suite "local file inclusion":
|
||||||
|
test "cannot include files in sandboxed mode":
|
||||||
|
var error = new string
|
||||||
|
discard ".. include:: ./readme.md".toHtml(error=error)
|
||||||
|
check(error[] == "input(1, 11) Error: disabled directive: 'include'")
|
||||||
|
|
||||||
|
test "code-block file directive is disabled":
|
||||||
|
var error = new string
|
||||||
|
discard ".. code-block:: nim\n :file: ./readme.md".toHtml(error=error)
|
||||||
|
check(error[] == "input(2, 20) Error: disabled directive: 'file'")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue