mirror of
https://github.com/danog/blackfriday.git
synced 2024-11-27 04:24:41 +01:00
Merge pull request #22 from rtfb/master
Add some protection against script injection
This commit is contained in:
commit
2336fd3109
78
html.go
78
html.go
@ -28,6 +28,7 @@ const (
|
||||
HTML_SKIP_STYLE // skip embedded <style> elements
|
||||
HTML_SKIP_IMAGES // skip embedded images
|
||||
HTML_SKIP_LINKS // skip all links
|
||||
HTML_SKIP_SCRIPT // skip embedded <script> elements
|
||||
HTML_SAFELINK // only link to trusted protocols
|
||||
HTML_TOC // generate a table of contents
|
||||
HTML_OMIT_CONTENTS // skip the main contents (for a standalone table of contents)
|
||||
@ -167,10 +168,32 @@ func (options *Html) BlockHtml(out *bytes.Buffer, text []byte) {
|
||||
}
|
||||
|
||||
doubleSpace(out)
|
||||
if options.flags&HTML_SKIP_SCRIPT != 0 {
|
||||
out.Write(stripTag(string(text), "script", "p"))
|
||||
} else {
|
||||
out.Write(text)
|
||||
}
|
||||
out.WriteByte('\n')
|
||||
}
|
||||
|
||||
func stripTag(text, tag, newTag string) []byte {
|
||||
closeNewTag := fmt.Sprintf("</%s>", newTag)
|
||||
i := 0
|
||||
for i < len(text) && text[i] != '<' {
|
||||
i++
|
||||
}
|
||||
if i == len(text) {
|
||||
return []byte(text)
|
||||
}
|
||||
found, end := findHtmlTagPos([]byte(text[i:]), tag)
|
||||
closeTag := fmt.Sprintf("</%s>", tag)
|
||||
noOpen := text
|
||||
if found {
|
||||
noOpen = text[0:i+1] + newTag + text[end:]
|
||||
}
|
||||
return []byte(strings.Replace(noOpen, closeTag, closeNewTag, -1))
|
||||
}
|
||||
|
||||
func (options *Html) HRule(out *bytes.Buffer) {
|
||||
doubleSpace(out)
|
||||
out.WriteString("<hr")
|
||||
@ -460,6 +483,9 @@ func (options *Html) RawHtmlTag(out *bytes.Buffer, text []byte) {
|
||||
if options.flags&HTML_SKIP_IMAGES != 0 && isHtmlTag(text, "img") {
|
||||
return
|
||||
}
|
||||
if options.flags&HTML_SKIP_SCRIPT != 0 && isHtmlTag(text, "script") {
|
||||
return
|
||||
}
|
||||
out.Write(text)
|
||||
}
|
||||
|
||||
@ -646,39 +672,65 @@ func (options *Html) TocFinalize() {
|
||||
}
|
||||
|
||||
func isHtmlTag(tag []byte, tagname string) bool {
|
||||
found, _ := findHtmlTagPos(tag, tagname)
|
||||
return found
|
||||
}
|
||||
|
||||
func findHtmlTagPos(tag []byte, tagname string) (bool, int) {
|
||||
i := 0
|
||||
if i < len(tag) && tag[0] != '<' {
|
||||
return false
|
||||
return false, -1
|
||||
}
|
||||
i++
|
||||
for i < len(tag) && isspace(tag[i]) {
|
||||
i++
|
||||
}
|
||||
i = skipSpace(tag, i)
|
||||
|
||||
if i < len(tag) && tag[i] == '/' {
|
||||
i++
|
||||
}
|
||||
|
||||
for i < len(tag) && isspace(tag[i]) {
|
||||
i++
|
||||
}
|
||||
|
||||
j := i
|
||||
i = skipSpace(tag, i)
|
||||
j := 0
|
||||
for ; i < len(tag); i, j = i+1, j+1 {
|
||||
if j >= len(tagname) {
|
||||
break
|
||||
}
|
||||
|
||||
if tag[i] != tagname[j] {
|
||||
return false
|
||||
if strings.ToLower(string(tag[i]))[0] != tagname[j] {
|
||||
return false, -1
|
||||
}
|
||||
}
|
||||
|
||||
if i == len(tag) {
|
||||
return false
|
||||
return false, -1
|
||||
}
|
||||
|
||||
return isspace(tag[i]) || tag[i] == '>'
|
||||
// Now look for closing '>', but ignore it when it's in any kind of quotes,
|
||||
// it might be JavaScript
|
||||
inSingleQuote := false
|
||||
inDoubleQuote := false
|
||||
inGraveQuote := false
|
||||
for i < len(tag) {
|
||||
switch {
|
||||
case tag[i] == '>' && !inSingleQuote && !inDoubleQuote && !inGraveQuote:
|
||||
return true, i
|
||||
case tag[i] == '\'':
|
||||
inSingleQuote = !inSingleQuote
|
||||
case tag[i] == '"':
|
||||
inDoubleQuote = !inDoubleQuote
|
||||
case tag[i] == '`':
|
||||
inGraveQuote = !inGraveQuote
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
return false, -1
|
||||
}
|
||||
|
||||
func skipSpace(tag []byte, i int) int {
|
||||
for i < len(tag) && isspace(tag[i]) {
|
||||
i++
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func doubleSpace(out *bytes.Buffer) {
|
||||
|
@ -17,12 +17,10 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func runMarkdownInline(input string) string {
|
||||
extensions := 0
|
||||
func runMarkdownInline(input string, extensions, htmlFlags int) string {
|
||||
extensions |= EXTENSION_AUTOLINK
|
||||
extensions |= EXTENSION_STRIKETHROUGH
|
||||
|
||||
htmlFlags := 0
|
||||
htmlFlags |= HTML_USE_XHTML
|
||||
|
||||
renderer := HtmlRenderer(htmlFlags, "", "")
|
||||
@ -31,6 +29,10 @@ func runMarkdownInline(input string) string {
|
||||
}
|
||||
|
||||
func doTestsInline(t *testing.T, tests []string) {
|
||||
doTestsInlineParam(t, tests, 0, 0)
|
||||
}
|
||||
|
||||
func doTestsInlineParam(t *testing.T, tests []string, extensions, htmlFlags int) {
|
||||
// catch and report panics
|
||||
var candidate string
|
||||
defer func() {
|
||||
@ -43,7 +45,7 @@ func doTestsInline(t *testing.T, tests []string) {
|
||||
input := tests[i]
|
||||
candidate = input
|
||||
expected := tests[i+1]
|
||||
actual := runMarkdownInline(candidate)
|
||||
actual := runMarkdownInline(candidate, extensions, htmlFlags)
|
||||
if actual != expected {
|
||||
t.Errorf("\nInput [%#v]\nExpected[%#v]\nActual [%#v]",
|
||||
candidate, expected, actual)
|
||||
@ -54,13 +56,48 @@ func doTestsInline(t *testing.T, tests []string) {
|
||||
for start := 0; start < len(input); start++ {
|
||||
for end := start + 1; end <= len(input); end++ {
|
||||
candidate = input[start:end]
|
||||
_ = runMarkdownInline(candidate)
|
||||
_ = runMarkdownInline(candidate, extensions, htmlFlags)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawHtmlTag(t *testing.T) {
|
||||
tests := []string{
|
||||
"zz <style>p {}</style>\n",
|
||||
"<p>zz p {}</p>\n",
|
||||
|
||||
"zz <STYLE>p {}</STYLE>\n",
|
||||
"<p>zz p {}</p>\n",
|
||||
|
||||
"<SCRIPT>alert()</SCRIPT>\n",
|
||||
"<p>alert()</p>\n",
|
||||
|
||||
"zz <SCRIPT>alert()</SCRIPT>\n",
|
||||
"<p>zz alert()</p>\n",
|
||||
|
||||
"zz <script>alert()</script>\n",
|
||||
"<p>zz alert()</p>\n",
|
||||
|
||||
" <script>alert()</script>\n",
|
||||
"<p>alert()</p>\n",
|
||||
|
||||
"<script>alert()</script>\n",
|
||||
"<p>alert()</p>\n",
|
||||
|
||||
"<script src='foo'></script>\n",
|
||||
"<p></p>\n",
|
||||
|
||||
"zz <script src='foo'></script>\n",
|
||||
"<p>zz </p>\n",
|
||||
|
||||
"zz <script src=foo></script>\n",
|
||||
"<p>zz </p>\n",
|
||||
}
|
||||
doTestsInlineParam(t, tests, 0, HTML_SKIP_STYLE|HTML_SKIP_SCRIPT)
|
||||
}
|
||||
|
||||
func TestEmphasis(t *testing.T) {
|
||||
var tests = []string{
|
||||
"nothing inline\n",
|
||||
|
@ -202,7 +202,7 @@ func MarkdownBasic(input []byte) []byte {
|
||||
//
|
||||
// * Smartypants processing with smart fractions and LaTeX dashes
|
||||
//
|
||||
// * Intra-word emphasis supression
|
||||
// * Intra-word emphasis suppression
|
||||
//
|
||||
// * Tables
|
||||
//
|
||||
@ -220,6 +220,7 @@ func MarkdownCommon(input []byte) []byte {
|
||||
htmlFlags |= HTML_USE_SMARTYPANTS
|
||||
htmlFlags |= HTML_SMARTYPANTS_FRACTIONS
|
||||
htmlFlags |= HTML_SMARTYPANTS_LATEX_DASHES
|
||||
htmlFlags |= HTML_SKIP_SCRIPT
|
||||
renderer := HtmlRenderer(htmlFlags, "", "")
|
||||
|
||||
// set up the parser
|
||||
|
Loading…
Reference in New Issue
Block a user