From 83a64ad483602a770b50276609093fa6460d8d85 Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Thu, 15 Sep 2022 12:44:24 +0200 Subject: [PATCH 01/14] Adds XSS Benchmark --- sqli_test.go | 14 +++++++------- xss_test.go | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/sqli_test.go b/sqli_test.go index 89ae860..12bdf5c 100644 --- a/sqli_test.go +++ b/sqli_test.go @@ -136,7 +136,7 @@ func runSQLiTest(t testing.TB, data map[string]string, filename string, flag str } func TestSQLiDriver(t *testing.T) { - baseDir := "tests" + baseDir := "./tests/" dir, err := os.ReadDir(baseDir) if err != nil { t.Fatal(err) @@ -158,7 +158,7 @@ func TestSQLiDriver(t *testing.T) { } } -type testCase struct { +type testCaseSQLI struct { name string data map[string]string } @@ -171,16 +171,16 @@ func BenchmarkSQLiDriver(b *testing.B) { } cases := struct { - sqli []testCase - folding []testCase - tokensMySQL []testCase - tokens []testCase + sqli []testCaseSQLI + folding []testCaseSQLI + tokensMySQL []testCaseSQLI + tokens []testCaseSQLI }{} for _, fi := range dir { p := filepath.Join(baseDir, fi.Name()) data := readTestData(p) - tc := testCase{ + tc := testCaseSQLI{ name: fi.Name(), data: data, } diff --git a/xss_test.go b/xss_test.go index 332fd77..05dbef3 100644 --- a/xss_test.go +++ b/xss_test.go @@ -3,6 +3,7 @@ package libinjection import ( "fmt" "os" + "path/filepath" "strings" "testing" ) @@ -78,10 +79,10 @@ func printHTML5Token(h *h5State) string { h.tokenStart[:h.tokenLen]) } -func runXSSTest(filename, flag string) { +func runXSSTest(t testing.TB, data map[string]string, filename, flag string) { + t.Helper() var ( actual = "" - data = readTestData(filename) ) switch flag { @@ -114,14 +115,56 @@ func TestXSSDriver(t *testing.T) { } for _, fi := range dir { + p := filepath.Join(baseDir, fi.Name()) + data := readTestData(p) if strings.Contains(fi.Name(), "-html5-") { - runXSSTest(baseDir+fi.Name(), html5) + runXSSTest(t, data, p, html5) } } t.Log("False testing count: ", xssCount) } +type testCaseXSS struct { + name string + data map[string]string +} + +func BenchmarkXSSDriver(b *testing.B) { + baseDir := "./tests/" + dir, err := os.ReadDir(baseDir) + if err != nil { + b.Fatal(err) + } + + cases := struct { + html5 []testCaseXSS + }{} + + for _, fi := range dir { + p := filepath.Join(baseDir, fi.Name()) + data := readTestData(p) + tc := testCaseXSS{ + name: fi.Name(), + data: data, + } + switch { + case strings.Contains(fi.Name(), "-html5-"): + cases.html5 = append(cases.html5, tc) + } + } + + b.Run("html5", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + for _, tc := range cases.html5 { + tt := tc + runXSSTest(b, tt.data, tt.name, html5) + } + } + }) +} + func TestXSS(t *testing.T) { tests := []struct { input string From a35e8dea6343441b2b987e0c2486789c9815d471 Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Thu, 15 Sep 2022 17:59:54 +0200 Subject: [PATCH 02/14] small perf improvements to xss_helpers --- xss_helpers.go | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/xss_helpers.go b/xss_helpers.go index bbf000e..25545e8 100644 --- a/xss_helpers.go +++ b/xss_helpers.go @@ -13,22 +13,17 @@ func isBlackTag(s string) bool { return false } + sUpper := strings.ToUpper(strings.ReplaceAll(s, "\x00", "")) for i := 0; i < len(blackTags); i++ { - if strings.ToUpper(strings.ReplaceAll(s, "\x00", "")) == blackTags[i] { + if sUpper == blackTags[i] { return true } } - // anything SVG related - if strings.ToUpper(s) == "SVG" { + // anything SVG related or XSL(t) related + if sUpper == "SVG" || sUpper == "XSL" { return true } - - // anything XSL(t) related - if strings.ToUpper(s) == "XSL" { - return true - } - return false } @@ -38,6 +33,7 @@ func isBlackAttr(s string) int { return attributeTypeNone } + sUpper := strings.ToUpper(strings.ReplaceAll(s, "\x00", "")) if length >= 5 { // javascript on.* if strings.ToUpper(s[:2]) == "ON" { @@ -45,15 +41,14 @@ func isBlackAttr(s string) int { return attributeTypeBlack } - if strings.ToUpper(strings.ReplaceAll(s, "\x00", "")) == "XMLNS" || - strings.ToUpper(strings.ReplaceAll(s, "\x00", "")) == "XLINK" { + if sUpper == "XMLNS" || sUpper == "XLINK" { // got xmlns or xlink tags return attributeTypeBlack } } for _, black := range blacks { - if strings.ToUpper(strings.ReplaceAll(s, "\x00", "")) == black.name { + if sUpper == black.name { // got banner attribute name return black.attributeType } From 7ff33181d990d9a78e8a2a8660fc0d0fa46572bf Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Thu, 15 Sep 2022 18:06:43 +0200 Subject: [PATCH 03/14] small perf improvements to xss.go --- xss.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/xss.go b/xss.go index d3099b8..e16ebc0 100644 --- a/xss.go +++ b/xss.go @@ -69,13 +69,14 @@ func isXSS(input string, flags int) bool { } if h5.tokenLen > 5 { + tokenStartUpper := strings.ToUpper(strings.ReplaceAll(h5.tokenStart[:6], "\x00", "")) // IE Date: Thu, 15 Sep 2022 18:07:01 +0200 Subject: [PATCH 04/14] small perf improvements to html5.go --- html5.go | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/html5.go b/html5.go index 0ad71a4..6ef0b05 100644 --- a/html5.go +++ b/html5.go @@ -73,11 +73,12 @@ func (h *h5State) stateBogusComment2() bool { // 12.2.4.49 // 12.2.4.50 // 12.2.4.51 -// state machine spec is confusing since it can only look -// at one character at a time but simply it's comments end by: -// 1) EOF -// 2) ending in --> -// 3) ending in -!> +// +// state machine spec is confusing since it can only look +// at one character at a time but simply it's comments end by: +// 1) EOF +// 2) ending in --> +// 3) ending in -!> func (h *h5State) stateComment() bool { pos := h.pos @@ -92,14 +93,14 @@ func (h *h5State) stateComment() bool { h.tokenType = html5TypeTagComment return true } - offset := 1 - + offset := 1 // offset is set at the beginning of each iteration + posIndexOffset := pos + index + offset // skip all nulls - for pos+index+offset < h.len && h.s[pos+index+offset] == 0x00 { - offset++ + for posIndexOffset < h.len && h.s[posIndexOffset] == 0x00 { + posIndexOffset++ //offset++ } - if pos+index+offset == h.len { + if posIndexOffset == h.len { h.state = h.stateEOF h.tokenStart = h.s[h.pos:] h.tokenLen = h.len - h.pos @@ -107,14 +108,15 @@ func (h *h5State) stateComment() bool { return true } - ch := h.s[pos+index+offset] + ch := h.s[posIndexOffset] if ch != byteDash && ch != byteBang { pos = pos + index + 1 + // pos updated, but the next iteration starts and posIndexOffset is calculated again continue } - offset++ + posIndexOffset++ //offset++ - if pos+index+offset == h.len { + if posIndexOffset == h.len { h.state = h.stateEOF h.tokenStart = h.s[h.pos:] h.tokenLen = h.len - h.pos @@ -122,16 +124,17 @@ func (h *h5State) stateComment() bool { return true } - if h.s[pos+index+offset] != byteGT { + if h.s[posIndexOffset] != byteGT { pos = pos + index + 1 + // pos updated, but the next iteration starts and posIndexOffset is calculated again continue } - offset++ + posIndexOffset++ //offset++ // ends in --> or -!> h.tokenStart = h.s[h.pos:] h.tokenLen = index + pos - h.pos - h.pos = pos + index + offset + h.pos = posIndexOffset h.state = h.stateData h.tokenType = html5TypeTagComment return true From 91635c266a01c46aec6df9fb70147bbe18c09708 Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Thu, 15 Sep 2022 20:00:33 +0200 Subject: [PATCH 05/14] reducing pointers --- xss_helpers.go | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/xss_helpers.go b/xss_helpers.go index 25545e8..138c358 100644 --- a/xss_helpers.go +++ b/xss_helpers.go @@ -56,24 +56,24 @@ func isBlackAttr(s string) int { return attributeTypeNone } -func htmlDecodeByteAt(s string, consumed *int) int { +func htmlDecodeByteAt(s string, consumed int) (int, int) { length := len(s) val := 0 if length == 0 { - *consumed = 0 - return byteEOF + consumed = 0 + return byteEOF, consumed } - *consumed = 1 + consumed = 1 if s[0] != '&' || length < 2 { - return int(s[0]) + return int(s[0]), consumed } if s[1] != '#' || len(s) < 3 { // normally this would be for named entities // but for this case we don't actually care - return '&' + return '&', consumed } if s[2] == 'x' || s[2] == 'X' { @@ -84,7 +84,7 @@ func htmlDecodeByteAt(s string, consumed *int) int { ch = gsHexDecodeMap[ch] if ch == 256 { // degenerate case '&#[?]' - return '&' + return '&', consumed } val = ch i := 4 @@ -92,48 +92,48 @@ func htmlDecodeByteAt(s string, consumed *int) int { for i < length { ch = int(s[i]) if ch == ';' { - *consumed = i + 1 - return val + consumed = i + 1 + return val, consumed } ch = gsHexDecodeMap[ch] if ch == 256 { - *consumed = i - return val + consumed = i + return val, consumed } val = val*16 + ch if val > 0x1000FF { - return '&' + return '&', consumed } i++ } - *consumed = i + consumed = i } else { i := 2 ch := int(s[i]) if ch < '0' || ch > '9' { - return '&' + return '&', consumed } val = ch - '0' i++ for i < length { ch = int(s[i]) if ch == ';' { - *consumed = i + 1 - return val + consumed = i + 1 + return val, consumed } if ch < '0' || ch > '9' { - *consumed = i - return val + consumed = i + return val, consumed } val = val*10 + (ch - '0') if val > 0x1000FF { - return '&' + return '&', consumed } i++ } - *consumed = i + consumed = i } - return val + return val, consumed } // Does an HTML encoded binary string (const char*, length) start with @@ -150,7 +150,8 @@ func htmlEncodeStartsWith(a, b string) bool { ) for length > 0 { - cb := htmlDecodeByteAt(b[pos:], &consumed) + cb, consumedUpdated := htmlDecodeByteAt(b[pos:], consumed) + consumed = consumedUpdated pos += consumed length -= consumed From 88fd950b2f5493ecf71549b054b75ede508ec205 Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Fri, 16 Sep 2022 12:14:32 +0200 Subject: [PATCH 06/14] better var names --- xss.go | 6 +++--- xss_helpers.go | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/xss.go b/xss.go index e16ebc0..3359c1f 100644 --- a/xss.go +++ b/xss.go @@ -69,14 +69,14 @@ func isXSS(input string, flags int) bool { } if h5.tokenLen > 5 { - tokenStartUpper := strings.ToUpper(strings.ReplaceAll(h5.tokenStart[:6], "\x00", "")) + upperTokenStart := strings.ToUpper(strings.ReplaceAll(h5.tokenStart[:6], "\x00", "")) // IE = 5 { // javascript on.* if strings.ToUpper(s[:2]) == "ON" { @@ -41,14 +41,14 @@ func isBlackAttr(s string) int { return attributeTypeBlack } - if sUpper == "XMLNS" || sUpper == "XLINK" { + if upperS == "XMLNS" || upperS == "XLINK" { // got xmlns or xlink tags return attributeTypeBlack } } for _, black := range blacks { - if sUpper == black.name { + if upperS == black.name { // got banner attribute name return black.attributeType } From 1acc74ddce7b9ba2ae52a3c47d7dfb781dd75ff5 Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Fri, 16 Sep 2022 12:14:51 +0200 Subject: [PATCH 07/14] Revert "small perf improvements to html5.go" This reverts commit 9a8bc1bdaeebb667fdd0449eb6a96e2bfb63f0f3. --- html5.go | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/html5.go b/html5.go index 6ef0b05..0ad71a4 100644 --- a/html5.go +++ b/html5.go @@ -73,12 +73,11 @@ func (h *h5State) stateBogusComment2() bool { // 12.2.4.49 // 12.2.4.50 // 12.2.4.51 -// -// state machine spec is confusing since it can only look -// at one character at a time but simply it's comments end by: -// 1) EOF -// 2) ending in --> -// 3) ending in -!> +// state machine spec is confusing since it can only look +// at one character at a time but simply it's comments end by: +// 1) EOF +// 2) ending in --> +// 3) ending in -!> func (h *h5State) stateComment() bool { pos := h.pos @@ -93,14 +92,14 @@ func (h *h5State) stateComment() bool { h.tokenType = html5TypeTagComment return true } - offset := 1 // offset is set at the beginning of each iteration - posIndexOffset := pos + index + offset + offset := 1 + // skip all nulls - for posIndexOffset < h.len && h.s[posIndexOffset] == 0x00 { - posIndexOffset++ //offset++ + for pos+index+offset < h.len && h.s[pos+index+offset] == 0x00 { + offset++ } - if posIndexOffset == h.len { + if pos+index+offset == h.len { h.state = h.stateEOF h.tokenStart = h.s[h.pos:] h.tokenLen = h.len - h.pos @@ -108,15 +107,14 @@ func (h *h5State) stateComment() bool { return true } - ch := h.s[posIndexOffset] + ch := h.s[pos+index+offset] if ch != byteDash && ch != byteBang { pos = pos + index + 1 - // pos updated, but the next iteration starts and posIndexOffset is calculated again continue } - posIndexOffset++ //offset++ + offset++ - if posIndexOffset == h.len { + if pos+index+offset == h.len { h.state = h.stateEOF h.tokenStart = h.s[h.pos:] h.tokenLen = h.len - h.pos @@ -124,17 +122,16 @@ func (h *h5State) stateComment() bool { return true } - if h.s[posIndexOffset] != byteGT { + if h.s[pos+index+offset] != byteGT { pos = pos + index + 1 - // pos updated, but the next iteration starts and posIndexOffset is calculated again continue } - posIndexOffset++ //offset++ + offset++ // ends in --> or -!> h.tokenStart = h.s[h.pos:] h.tokenLen = index + pos - h.pos - h.pos = posIndexOffset + h.pos = pos + index + offset h.state = h.stateData h.tokenType = html5TypeTagComment return true From 12d503ae2b21116a42d5cd5eb9cf00d87024a861 Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Sun, 18 Sep 2022 23:25:49 +0200 Subject: [PATCH 08/14] minor refactor --- html5.go | 11 ++++++----- sqli_test.go | 2 +- xss.go | 9 +++------ 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/html5.go b/html5.go index 0ad71a4..72c38c4 100644 --- a/html5.go +++ b/html5.go @@ -73,11 +73,12 @@ func (h *h5State) stateBogusComment2() bool { // 12.2.4.49 // 12.2.4.50 // 12.2.4.51 -// state machine spec is confusing since it can only look -// at one character at a time but simply it's comments end by: -// 1) EOF -// 2) ending in --> -// 3) ending in -!> +// +// state machine spec is confusing since it can only look +// at one character at a time but simply it's comments end by: +// 1) EOF +// 2) ending in --> +// 3) ending in -!> func (h *h5State) stateComment() bool { pos := h.pos diff --git a/sqli_test.go b/sqli_test.go index 12bdf5c..7ce3ae9 100644 --- a/sqli_test.go +++ b/sqli_test.go @@ -136,7 +136,7 @@ func runSQLiTest(t testing.TB, data map[string]string, filename string, flag str } func TestSQLiDriver(t *testing.T) { - baseDir := "./tests/" + baseDir := "tests" dir, err := os.ReadDir(baseDir) if err != nil { t.Fatal(err) diff --git a/xss.go b/xss.go index 3359c1f..c6feada 100644 --- a/xss.go +++ b/xss.go @@ -70,13 +70,10 @@ func isXSS(input string, flags int) bool { if h5.tokenLen > 5 { upperTokenStart := strings.ToUpper(strings.ReplaceAll(h5.tokenStart[:6], "\x00", "")) - // IE Date: Mon, 26 Sep 2022 14:50:39 +0200 Subject: [PATCH 09/14] Removes consumed variable, minor refactor --- xss_helpers.go | 55 ++++++++++++++++++++++---------------------------- xss_test.go | 11 ++-------- 2 files changed, 26 insertions(+), 40 deletions(-) diff --git a/xss_helpers.go b/xss_helpers.go index e1e7c3b..9bbfe13 100644 --- a/xss_helpers.go +++ b/xss_helpers.go @@ -20,11 +20,13 @@ func isBlackTag(s string) bool { } } - // anything SVG related or XSL(t) related - if upperS == "SVG" || upperS == "XSL" { + switch upperS { + // anything SVG or XSL(t) related + case "SVT", "XSL": return true + default: + return false } - return false } func isBlackAttr(s string) int { @@ -56,24 +58,22 @@ func isBlackAttr(s string) int { return attributeTypeNone } -func htmlDecodeByteAt(s string, consumed int) (int, int) { +func htmlDecodeByteAt(s string) (int, int) { length := len(s) val := 0 if length == 0 { - consumed = 0 - return byteEOF, consumed + return byteEOF, 0 } - consumed = 1 if s[0] != '&' || length < 2 { - return int(s[0]), consumed + return int(s[0]), 1 } if s[1] != '#' || len(s) < 3 { // normally this would be for named entities // but for this case we don't actually care - return '&', consumed + return '&', 1 } if s[2] == 'x' || s[2] == 'X' { @@ -84,7 +84,7 @@ func htmlDecodeByteAt(s string, consumed int) (int, int) { ch = gsHexDecodeMap[ch] if ch == 256 { // degenerate case '&#[?]' - return '&', consumed + return '&', 1 } val = ch i := 4 @@ -92,48 +92,43 @@ func htmlDecodeByteAt(s string, consumed int) (int, int) { for i < length { ch = int(s[i]) if ch == ';' { - consumed = i + 1 - return val, consumed + return val, i + 1 } ch = gsHexDecodeMap[ch] if ch == 256 { - consumed = i - return val, consumed + return val, i } val = val*16 + ch if val > 0x1000FF { - return '&', consumed + return '&', 1 } i++ } - consumed = i + return val, i } else { i := 2 ch := int(s[i]) if ch < '0' || ch > '9' { - return '&', consumed + return '&', 1 } val = ch - '0' i++ for i < length { ch = int(s[i]) if ch == ';' { - consumed = i + 1 - return val, consumed + return val, i + 1 } if ch < '0' || ch > '9' { - consumed = i - return val, consumed + return val, i } val = val*10 + (ch - '0') if val > 0x1000FF { - return '&', consumed + return '&', 1 } i++ } - consumed = i + return val, i } - return val, consumed } // Does an HTML encoded binary string (const char*, length) start with @@ -142,16 +137,14 @@ func htmlDecodeByteAt(s string, consumed int) (int, int) { // also ignore any embedded nulls in the HTML string! func htmlEncodeStartsWith(a, b string) bool { var ( - consumed = 0 - first = true - bs []byte - pos = 0 - length = len(b) + first = true + bs []byte + pos = 0 + length = len(b) ) for length > 0 { - cb, consumedUpdated := htmlDecodeByteAt(b[pos:], consumed) - consumed = consumedUpdated + cb, consumed := htmlDecodeByteAt(b[pos:]) pos += consumed length -= consumed diff --git a/xss_test.go b/xss_test.go index 05dbef3..7c7fcff 100644 --- a/xss_test.go +++ b/xss_test.go @@ -43,8 +43,6 @@ const ( xss = "xss" ) -var xssCount = 0 - func h5TypeToString(h5Type int) string { switch h5Type { case html5TypeDataText: @@ -99,11 +97,8 @@ func runXSSTest(t testing.TB, data map[string]string, filename, flag string) { actual = strings.TrimSpace(actual) if actual != data["--EXPECTED--"] { - xssCount++ - fmt.Println("FILE: (" + filename + ")") - fmt.Println("INPUT: (" + data["--INPUT--"] + ")") - fmt.Println("EXPECTED: (" + data["--EXPECTED--"] + ")") - fmt.Println("GOT: (" + actual + ")") + t.Errorf("FILE: (%s)\nINPUT: (%s)\nEXPECTED: (%s)\nGOT: (%s)\n", + filename, data["--INPUT--"], data["--EXPECTED--"], actual) } } @@ -121,8 +116,6 @@ func TestXSSDriver(t *testing.T) { runXSSTest(t, data, p, html5) } } - - t.Log("False testing count: ", xssCount) } type testCaseXSS struct { From e3c55352b3c6faf0a75b576076cc6a1d95c5d064 Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Mon, 26 Sep 2022 21:55:16 +0200 Subject: [PATCH 10/14] t.Run usage, lint --- sqli_test.go | 16 ++++++++++++---- xss_helpers.go | 37 ++++++++++++++++++------------------- xss_test.go | 5 ++++- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/sqli_test.go b/sqli_test.go index 7ce3ae9..72451ab 100644 --- a/sqli_test.go +++ b/sqli_test.go @@ -147,13 +147,21 @@ func TestSQLiDriver(t *testing.T) { data := readTestData(p) switch { case strings.Contains(fi.Name(), "-sqli-"): - runSQLiTest(t, data, p, fingerprints, 0) + t.Run("SQLi Driver - sqli", func(t *testing.T) { + runSQLiTest(t, data, p, fingerprints, 0) + }) case strings.Contains(fi.Name(), "-folding-"): - runSQLiTest(t, data, p, folding, sqliFlagQuoteNone|sqliFlagSQLAnsi) + t.Run("SQLi Driver - folding", func(t *testing.T) { + runSQLiTest(t, data, p, folding, sqliFlagQuoteNone|sqliFlagSQLAnsi) + }) case strings.Contains(fi.Name(), "-tokens_mysql-"): - runSQLiTest(t, data, p, tokens, sqliFlagQuoteNone|sqliFlagSQLMysql) + t.Run("SQLi Driver - tokens_mysql", func(t *testing.T) { + runSQLiTest(t, data, p, tokens, sqliFlagQuoteNone|sqliFlagSQLMysql) + }) case strings.Contains(fi.Name(), "-tokens-"): - runSQLiTest(t, data, p, tokens, sqliFlagQuoteNone|sqliFlagSQLAnsi) + t.Run("SQLi Driver - tokens", func(t *testing.T) { + runSQLiTest(t, data, p, tokens, sqliFlagQuoteNone|sqliFlagSQLAnsi) + }) } } } diff --git a/xss_helpers.go b/xss_helpers.go index 9bbfe13..4eaa479 100644 --- a/xss_helpers.go +++ b/xss_helpers.go @@ -105,30 +105,29 @@ func htmlDecodeByteAt(s string) (int, int) { i++ } return val, i - } else { - i := 2 - ch := int(s[i]) + } + i := 2 + ch := int(s[i]) + if ch < '0' || ch > '9' { + return '&', 1 + } + val = ch - '0' + i++ + for i < length { + ch = int(s[i]) + if ch == ';' { + return val, i + 1 + } if ch < '0' || ch > '9' { + return val, i + } + val = val*10 + (ch - '0') + if val > 0x1000FF { return '&', 1 } - val = ch - '0' i++ - for i < length { - ch = int(s[i]) - if ch == ';' { - return val, i + 1 - } - if ch < '0' || ch > '9' { - return val, i - } - val = val*10 + (ch - '0') - if val > 0x1000FF { - return '&', 1 - } - i++ - } - return val, i } + return val, i } // Does an HTML encoded binary string (const char*, length) start with diff --git a/xss_test.go b/xss_test.go index 7c7fcff..db73493 100644 --- a/xss_test.go +++ b/xss_test.go @@ -113,7 +113,9 @@ func TestXSSDriver(t *testing.T) { p := filepath.Join(baseDir, fi.Name()) data := readTestData(p) if strings.Contains(fi.Name(), "-html5-") { - runXSSTest(t, data, p, html5) + t.Run("XSS Driver - html5", func(t *testing.T) { + runXSSTest(t, data, p, html5) + }) } } } @@ -144,6 +146,7 @@ func BenchmarkXSSDriver(b *testing.B) { switch { case strings.Contains(fi.Name(), "-html5-"): cases.html5 = append(cases.html5, tc) + default: } } From dfe965f26b08ad863c49fe6fa12e899e542c2ffc Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Tue, 27 Sep 2022 00:17:19 +0200 Subject: [PATCH 11/14] fixing t.Run test name --- sqli_test.go | 8 ++++---- xss_test.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sqli_test.go b/sqli_test.go index 72451ab..d5e5124 100644 --- a/sqli_test.go +++ b/sqli_test.go @@ -147,19 +147,19 @@ func TestSQLiDriver(t *testing.T) { data := readTestData(p) switch { case strings.Contains(fi.Name(), "-sqli-"): - t.Run("SQLi Driver - sqli", func(t *testing.T) { + t.Run(fi.Name(), func(t *testing.T) { runSQLiTest(t, data, p, fingerprints, 0) }) case strings.Contains(fi.Name(), "-folding-"): - t.Run("SQLi Driver - folding", func(t *testing.T) { + t.Run(fi.Name(), func(t *testing.T) { runSQLiTest(t, data, p, folding, sqliFlagQuoteNone|sqliFlagSQLAnsi) }) case strings.Contains(fi.Name(), "-tokens_mysql-"): - t.Run("SQLi Driver - tokens_mysql", func(t *testing.T) { + t.Run(fi.Name(), func(t *testing.T) { runSQLiTest(t, data, p, tokens, sqliFlagQuoteNone|sqliFlagSQLMysql) }) case strings.Contains(fi.Name(), "-tokens-"): - t.Run("SQLi Driver - tokens", func(t *testing.T) { + t.Run(fi.Name(), func(t *testing.T) { runSQLiTest(t, data, p, tokens, sqliFlagQuoteNone|sqliFlagSQLAnsi) }) } diff --git a/xss_test.go b/xss_test.go index db73493..9e585c6 100644 --- a/xss_test.go +++ b/xss_test.go @@ -113,7 +113,7 @@ func TestXSSDriver(t *testing.T) { p := filepath.Join(baseDir, fi.Name()) data := readTestData(p) if strings.Contains(fi.Name(), "-html5-") { - t.Run("XSS Driver - html5", func(t *testing.T) { + t.Run(fi.Name(), func(t *testing.T) { runXSSTest(t, data, p, html5) }) } From 050015b4b2836abe602f27aa9d03733b52411af6 Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Sat, 27 Jan 2024 23:44:56 +0100 Subject: [PATCH 12/14] adds longer tests --- tests/test-html5-068.txt | 12 ++++++++++++ tests/test-html5-069.txt | 9 +++++++++ tests/test-html5-070.txt | 10 ++++++++++ tests/test-html5-071.txt | 42 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 tests/test-html5-068.txt create mode 100644 tests/test-html5-069.txt create mode 100644 tests/test-html5-070.txt create mode 100644 tests/test-html5-071.txt diff --git a/tests/test-html5-068.txt b/tests/test-html5-068.txt new file mode 100644 index 0000000..b1243e7 --- /dev/null +++ b/tests/test-html5-068.txt @@ -0,0 +1,12 @@ +--TEST-- +tags +--INPUT-- +quitesomerandomcharacters +--EXPECTED-- +DATA_TEXT,25,quitesomerandomcharacters +TAG_NAME_OPEN,5,TABLE +ATTR_NAME,10,BACKGROUND +ATTR_VALUE,34,javascript:javascript:alert(99111) +TAG_NAME_CLOSE,1,> + + diff --git a/tests/test-html5-069.txt b/tests/test-html5-069.txt new file mode 100644 index 0000000..171a924 --- /dev/null +++ b/tests/test-html5-069.txt @@ -0,0 +1,9 @@ +--TEST-- +cdata, degenerate. Longer text +--INPUT-- + +--EXPECTED-- +TAG_COMMENT,19, looooooonger foo=" +TAG_NAME_OPEN,6,script +TAG_NAME_CLOSE,1,> +DATA_TEXT,84,ReferenceError.prototype.__defineGetter__('name', function(){javascript:alert(1)}),x +TAG_CLOSE,6,script diff --git a/tests/test-html5-071.txt b/tests/test-html5-071.txt new file mode 100644 index 0000000..a01046c --- /dev/null +++ b/tests/test-html5-071.txt @@ -0,0 +1,42 @@ +--TEST-- +Extended Body Test +--INPUT-- +

Welcome to my awesome Website!

This is a paragraph of text on my website. It contains more characters to make the test longer.

Here's another paragraph of text. This one is inside a div with an id of "content".

  • List item 1
  • List item 2
  • List item 3
+--EXPECTED-- +TAG_NAME_OPEN,4,BODY +ATTR_NAME,10,BACKGROUND +ATTR_VALUE,23,javascript:alert('XSS') +TAG_NAME_CLOSE,1,> +TAG_NAME_OPEN,2,h1 +TAG_NAME_CLOSE,1,> +DATA_TEXT,30,Welcome to my awesome Website! +TAG_CLOSE,2,h1 +TAG_NAME_OPEN,1,p +TAG_NAME_CLOSE,1,> +DATA_TEXT,95,This is a paragraph of text on my website. It contains more characters to make the test longer. +TAG_CLOSE,1,p +TAG_NAME_OPEN,3,div +ATTR_NAME,2,id +ATTR_VALUE,7,content +TAG_NAME_CLOSE,1,> +TAG_NAME_OPEN,1,p +TAG_NAME_CLOSE,1,> +DATA_TEXT,83,Here's another paragraph of text. This one is inside a div with an id of "content". +TAG_CLOSE,1,p +TAG_NAME_OPEN,2,ul +TAG_NAME_CLOSE,1,> +TAG_NAME_OPEN,2,li +TAG_NAME_CLOSE,1,> +DATA_TEXT,11,List item 1 +TAG_CLOSE,2,li +TAG_NAME_OPEN,2,li +TAG_NAME_CLOSE,1,> +DATA_TEXT,11,List item 2 +TAG_CLOSE,2,li +TAG_NAME_OPEN,2,li +TAG_NAME_CLOSE,1,> +DATA_TEXT,11,List item 3 +TAG_CLOSE,2,li +TAG_CLOSE,2,ul +TAG_CLOSE,3,div +TAG_CLOSE,4,BODY From 7da392e6d59dde0209eec660178e2ec848f04dfd Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Sun, 28 Jan 2024 12:08:37 +0100 Subject: [PATCH 13/14] fix post merge --- xss_helpers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xss_helpers.go b/xss_helpers.go index 4eaa479..debf4a3 100644 --- a/xss_helpers.go +++ b/xss_helpers.go @@ -78,7 +78,7 @@ func htmlDecodeByteAt(s string) (int, int) { if s[2] == 'x' || s[2] == 'X' { if len(s) < 4 { - return '&' + return '&', 1 } ch := int(s[3]) ch = gsHexDecodeMap[ch] From 5a2e0efa47c9075745ef28fd48ee1ebb52fc332f Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Sun, 28 Jan 2024 12:23:42 +0100 Subject: [PATCH 14/14] increase coverage --- xss_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xss_test.go b/xss_test.go index 9e585c6..3fd4fbd 100644 --- a/xss_test.go +++ b/xss_test.go @@ -29,6 +29,8 @@ func TestIsXSS(t *testing.T) { "", "", "", + // Payload sample from https://github.com/payloadbox/xss-payload-list + "XSS\"\"\",\"XML namespace.\"),(\"\"\"<IMG SRC=\"javascript:javascript:alert(1)\">", } for _, example := range examples {