mirror of
https://github.com/trezor/blockbook.git
synced 2026-03-22 23:47:21 +01:00
enhancement: avoid template.JSStr
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"html/template"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/trezor/blockbook/api"
|
||||
"github.com/trezor/blockbook/bchain"
|
||||
)
|
||||
|
||||
func Test_formatInt64(t *testing.T) {
|
||||
@@ -260,11 +262,11 @@ func Test_appendAmountSpanBitcoinType(t *testing.T) {
|
||||
|
||||
func Test_addressAliasSpan_XSS(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
address string
|
||||
td *TemplateData
|
||||
want string
|
||||
wantContains string // substring that must be present and properly escaped
|
||||
name string
|
||||
address string
|
||||
td *TemplateData
|
||||
want string
|
||||
wantContains string // substring that must be present and properly escaped
|
||||
wantNotContains string // substring that must NOT be present (raw XSS payload)
|
||||
}{
|
||||
{
|
||||
@@ -301,7 +303,7 @@ func Test_addressAliasSpan_XSS(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
wantContains: `alias-type="Contract" onclick="alert(1)" data="`,
|
||||
wantContains: `alias-type="Contract" onclick="alert(1)" data="`,
|
||||
wantNotContains: `onclick="alert(1)"`,
|
||||
},
|
||||
{
|
||||
@@ -317,7 +319,7 @@ func Test_addressAliasSpan_XSS(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
wantContains: `alias-type="<script>alert(1)</script>"`,
|
||||
wantContains: `alias-type="<script>alert(1)</script>"`,
|
||||
wantNotContains: `<script>`,
|
||||
},
|
||||
{
|
||||
@@ -333,7 +335,7 @@ func Test_addressAliasSpan_XSS(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
wantContains: `<img src=x onerror=alert(1)>`,
|
||||
wantContains: `<img src=x onerror=alert(1)>`,
|
||||
wantNotContains: `<img src=x onerror=alert(1)>`,
|
||||
},
|
||||
{
|
||||
@@ -349,7 +351,7 @@ func Test_addressAliasSpan_XSS(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
wantContains: `cc="0x1234"><script>alert(1)</script>"`,
|
||||
wantContains: `cc="0x1234"><script>alert(1)</script>"`,
|
||||
wantNotContains: `<script>alert(1)</script>`,
|
||||
},
|
||||
{
|
||||
@@ -365,7 +367,7 @@ func Test_addressAliasSpan_XSS(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
wantContains: `alias-type="Contract" onmouseover="alert('XSS')" data="`,
|
||||
wantContains: `alias-type="Contract" onmouseover="alert('XSS')" data="`,
|
||||
wantNotContains: `onmouseover="alert('XSS')"`,
|
||||
},
|
||||
}
|
||||
@@ -373,19 +375,19 @@ func Test_addressAliasSpan_XSS(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := addressAliasSpan(tt.address, tt.td)
|
||||
gotStr := string(got)
|
||||
|
||||
|
||||
if tt.want != "" {
|
||||
if gotStr != tt.want {
|
||||
t.Errorf("addressAliasSpan() = %v, want %v", gotStr, tt.want)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if tt.wantContains != "" {
|
||||
if !strings.Contains(gotStr, tt.wantContains) {
|
||||
t.Errorf("addressAliasSpan() = %v, should contain %v", gotStr, tt.wantContains)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if tt.wantNotContains != "" {
|
||||
if strings.Contains(gotStr, tt.wantNotContains) {
|
||||
t.Errorf("addressAliasSpan() = %v, should NOT contain raw XSS payload: %v", gotStr, tt.wantNotContains)
|
||||
@@ -394,3 +396,49 @@ func Test_addressAliasSpan_XSS(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func renderTokenDetailSpecific(t *testing.T, uri string) string {
|
||||
t.Helper()
|
||||
|
||||
tmpl := template.Must(template.New("tokenDetail.html").Funcs(template.FuncMap{
|
||||
"jsStr": jsStr,
|
||||
}).ParseFiles("./static/templates/tokenDetail.html"))
|
||||
|
||||
data := TemplateData{
|
||||
TokenId: "1",
|
||||
URI: uri,
|
||||
ContractInfo: &bchain.ContractInfo{
|
||||
Contract: "0x1234567890123456789012345678901234567890",
|
||||
Name: "Contract",
|
||||
Standard: bchain.ERC771TokenStandard,
|
||||
},
|
||||
}
|
||||
|
||||
var rendered bytes.Buffer
|
||||
if err := tmpl.ExecuteTemplate(&rendered, "specific", data); err != nil {
|
||||
t.Fatalf("ExecuteTemplate() error = %v", err)
|
||||
}
|
||||
return rendered.String()
|
||||
}
|
||||
|
||||
func Test_tokenDetailTemplateEscapesURIInJSContext(t *testing.T) {
|
||||
body := renderTokenDetailSpecific(t, `";console.log("XSS_EXEC_OK");//`)
|
||||
|
||||
if !strings.Contains(body, `const uri="\";console.log(\"XSS_EXEC_OK\");//";`) {
|
||||
t.Fatalf("escaped uri literal not found in output: %s", body)
|
||||
}
|
||||
if strings.Contains(body, `const uri="";console.log("XSS_EXEC_OK");//";`) {
|
||||
t.Fatalf("found unescaped JS breakout payload in output: %s", body)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_tokenDetailTemplateEscapesScriptEndTagInJSContext(t *testing.T) {
|
||||
body := renderTokenDetailSpecific(t, `";</script><script>alert(1)</script>//`)
|
||||
|
||||
if strings.Contains(body, `</script><script>alert(1)</script>`) {
|
||||
t.Fatalf("found unescaped script-end-tag payload in output: %s", body)
|
||||
}
|
||||
if !strings.Contains(body, `const uri="\";\u003c/script\u003e\u003cscript\u003ealert(1)\u003c/script\u003e//";`) {
|
||||
t.Fatalf("escaped script-end-tag payload not found in output: %s", body)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
}
|
||||
async function getMetadata(url) {
|
||||
try {
|
||||
const uri={{ jsStr $data.URI }};
|
||||
const uri={{ $data.URI }};
|
||||
if(uri) {
|
||||
const response = await fetch(uri);
|
||||
const contentType=response.headers.get('content-type');
|
||||
@@ -89,4 +89,4 @@
|
||||
}
|
||||
getMetadata();
|
||||
</script>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
Reference in New Issue
Block a user