Files
upstream-corteza/server/compose/service/values/sanitizer_test.go
upstream-mirror 7e385fc126
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
2026-09-18 01:22:23 +05:00

403 lines
11 KiB
Go

package values
import (
"fmt"
"reflect"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/cortezaproject/corteza/server/compose/types"
)
func Test_sanitizer_Run(t *testing.T) {
tests := []struct {
name string
kind string
options map[string]interface{}
input string
output string
outref uint64
}{
{
name: "numbers should be trimmed",
kind: "Number",
input: " 42 ",
output: "42",
},
{
name: "object reference should be processed",
kind: "Record",
input: " 133569629112020995 ",
output: "133569629112020995",
outref: 133569629112020995,
},
{
name: "object reference should be numeric",
kind: "Record",
input: " foo ",
output: "",
},
{
name: "user reference should be processed",
kind: "User",
input: " 133569629112020995 ",
output: "133569629112020995",
outref: 133569629112020995,
},
{
name: "user reference should be numeric",
kind: "User",
input: " foo ",
output: "",
},
{
name: "strings should be kept intact",
kind: "String",
input: " The answer ",
output: " The answer ",
},
{
name: "booleans should be converted (t)",
kind: "Bool",
input: "t",
output: "1",
},
{
name: "booleans should be converted (false)",
kind: "Bool",
input: "false",
output: "",
},
{
name: "booleans should be converted (garbage)",
kind: "Bool",
input: "%%#)%)')$)'",
output: "",
},
{
name: "dates should be converted to ISO",
kind: "DateTime",
input: "Mon Jan 2 15:04:05 2006",
output: "2006-01-02T15:04:05Z",
},
{
name: "dates should be converted to UTC",
kind: "DateTime",
input: "2020-03-02T20:20:20+05:00",
output: "2020-03-02T15:20:20Z",
},
{
name: "micro/mili seconds should be cut off",
kind: "DateTime",
input: "2020-03-11T11:20:08.471Z",
output: "2020-03-11T11:20:08Z",
},
{
name: "number space trim",
kind: "Number",
input: " 42 ",
output: "42",
},
{
name: "number negative",
kind: "Number",
input: "-42",
output: "-42",
},
{
name: "number positive",
kind: "Number",
input: "+42",
output: "42",
},
{
name: "number precision",
kind: "Number",
options: map[string]interface{}{"precision": 3},
input: "42.44455",
output: "42.445",
},
{
name: "number precision; bigger precision then provided fracture",
kind: "Number",
options: map[string]interface{}{"precision": 3},
input: "42.4",
output: "42.4",
},
{
name: "number precision; default to 0",
kind: "Number",
options: map[string]interface{}{},
input: "42.4",
output: "42",
},
{
name: "number precision; clamped between [0, 6]",
kind: "Number",
options: map[string]interface{}{"precision": 10},
input: "42.5555555555",
output: "42.555556",
},
{
name: "number precision; round",
kind: "Number",
options: map[string]interface{}{"precision": 0},
input: "41.6",
output: "42",
},
{
name: "number precision; trailing .00",
kind: "Number",
options: map[string]interface{}{"precision": 2},
input: "42.00",
output: "42",
},
{
name: "number precision; trailing .040",
kind: "Number",
options: map[string]interface{}{"precision": 2},
input: "42.040",
output: "42.04",
},
{
name: "string escaping; html",
kind: "String",
options: map[string]interface{}{},
input: "<span onerror=alert()>Title here</span>",
output: "<span>Title here</span>",
},
{
name: "string escaping; html a.href with javascript alert",
kind: "String",
options: map[string]interface{}{},
input: `<a href="javascript:alert('XSS1')" onmouseover="alert('XSS2')">XSS<a>`,
output: "XSS",
},
{
name: "string escaping; a.href with javascript",
kind: "String",
options: map[string]interface{}{},
input: `<a href="javascript:document.location='https://cortezaproject.org/'">XSS</A>`,
output: "XSS",
},
{
name: "string escaping; script with script",
kind: "String",
options: map[string]interface{}{},
input: `<script>document.write("<scri");</script>pt src="https://cortezaproject.org/script.js"></script>`,
output: `pt src="https://cortezaproject.org/script.js">`,
},
{
name: "string escaping; inline styles unchanged",
kind: "String",
options: map[string]interface{}{},
input: `<span style="background-color: #00ff00"><span style="color: #4a86e8">nasty looking content</span></span>`,
output: `<span style="background-color: #00ff00"><span style="color: #4a86e8">nasty looking content</span></span>`,
},
{
name: "string escaping; script with a",
kind: "String",
options: map[string]interface{}{},
input: `<script a=">'>" src="https://cortezaproject.org/xss.js"></script>`,
output: "",
},
{
name: "string escaping; meta with script",
kind: "String",
options: map[string]interface{}{},
input: `<meta http-equiv="set-cookie" content="<script>alert('xss')</script>">`,
output: "",
},
{
name: "string escaping; object",
kind: "String",
options: map[string]interface{}{},
input: `<object type="text/x-scriptlet" data="https://cortezaproject.org/xss.html"></object>`,
output: "",
},
{
name: "string escaping; base href",
kind: "String",
options: map[string]interface{}{},
input: `<base href="javascript:alert('xss');//">`,
output: "",
},
{
name: "string escaping; script",
kind: "String",
options: map[string]interface{}{},
input: `<!--[if gte ie 4]><script>alert('xss');</script><![endif]-->`,
output: "",
},
{
name: "string escaping; div",
kind: "String",
options: map[string]interface{}{},
input: `<div style="background-image: url(javascript:alert('XSS'))"></div>`,
output: "<div></div>",
},
{
name: "string escaping; frameset",
kind: "String",
options: map[string]interface{}{},
input: `<frameset><frame src="javascript:alert('XSS');"></frameset>`,
output: "",
},
{
name: "string escaping; iframe",
kind: "String",
options: map[string]interface{}{},
input: `<iframe src=# onmouseover="alert(document.cookie)"></iframe>`,
output: "",
},
{
name: "string escaping; meta",
kind: "String",
options: map[string]interface{}{},
input: `<meta http-equiv="refresh" content="0; url=https://;url=javascript:alert('xss');">`,
output: "",
},
{
name: "string escaping; br",
kind: "String",
options: map[string]interface{}{},
input: `<br size="&{alert('XSS')}">`,
output: "<br>",
},
{
name: "string escaping; bgsound",
kind: "String",
options: map[string]interface{}{},
input: `<bgsound src="javascript:alert('XSS');">`,
output: "",
},
{
name: "string escaping; input type image",
kind: "String",
options: map[string]interface{}{},
input: `<input type="image" src="javascript:alert('XSS');">`,
output: "",
},
{
name: "string escaping; style",
kind: "String",
options: map[string]interface{}{},
input: `<style>@import 'https://cortezaproject.org/xss.css';</style>`,
output: "",
},
{
name: "string escaping; link",
kind: "String",
options: map[string]interface{}{},
input: `<link rel="stylesheet" href="javascript:alert('xss');">`,
output: "",
},
{
name: "string escaping; html body onload event",
kind: "String",
options: map[string]interface{}{},
input: `<body onload=alert('XSS')>`,
output: "",
},
{
name: "string escaping; xss element",
kind: "String",
options: map[string]interface{}{},
input: `'';!--"<XSS>=&{()}`,
output: `'';!--"=&{()}`,
},
{
name: "string escaping; xss element",
kind: "String",
options: map[string]interface{}{},
input: `Hello <span class="><script src='https://cortezaproject.org/XSS.js'></script>">there</span> world.`,
output: "Hello <span>there</span> world.",
},
{
name: "string escaping; xss element",
kind: "String",
options: map[string]interface{}{},
input: `<tag1>cor<tag2></tag2>teza</tag1><tag1>server</tag1><tag2>123</tag2>`,
output: "cortezaserver123",
},
{
name: "string escaping; preserve necessary chars",
kind: "String",
options: map[string]interface{}{},
input: `a < b ; "'"`,
output: `a < b ; "'"`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := sanitizer{}
m := &types.Module{Fields: types.ModuleFieldSet{&types.ModuleField{Name: "testField", Kind: tt.kind, Options: tt.options}}}
v := types.RecordValueSet{&types.RecordValue{Name: "testField", Value: tt.input}}
o := types.RecordValueSet{&types.RecordValue{Name: "testField", Value: tt.output, Ref: tt.outref}}
// Need to mark values as updated to trigger sanitization.
v.SetUpdatedFlag(true)
o.SetUpdatedFlag(true)
if sanitized := s.Run(m, v); !reflect.DeepEqual(sanitized, o) {
t.Errorf("\ninput value:\n%v\n\nresult of sanitization:\n%v\n\nexpected:\n%v\n", tt.input, sanitized, o)
}
})
}
}
func TestSanitizerExpr(t *testing.T) {
tests := []struct {
name string
kind string
expr []string
input string
output string
outref uint64
}{
{
name: "cap numbers",
kind: "Number",
expr: []string{`value > 42 ? 42 : value`},
input: "43",
output: "42",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := sanitizer{}
m := &types.Module{Fields: types.ModuleFieldSet{&types.ModuleField{Name: "testField", Kind: tt.kind}}}
m.Fields.FindByName("testField").Expressions.Sanitizers = tt.expr
v := types.RecordValueSet{&types.RecordValue{Name: "testField", Value: tt.input}}
o := types.RecordValueSet{&types.RecordValue{Name: "testField", Value: tt.output, Ref: tt.outref}}
// Need to mark values as updated to trigger sanitization.
v.SetUpdatedFlag(true)
o.SetUpdatedFlag(true)
if sanitized := s.Run(m, v); !reflect.DeepEqual(sanitized, o) {
t.Errorf("\ninput value:\n%v\n\nresult of sanitization:\n%v\n\nexpected:\n%v\n", tt.input, sanitized, o)
}
})
}
}
func TestDatetimeSanitizer(t *testing.T) {
tests := []struct {
input interface{}
onlyDate, onlyTime bool
rval string
}{
{time.Date(1999, 9, 9, 9, 9, 9, 9, time.UTC), false, false, "1999-09-09T09:09:09Z"},
{"2021-03-23T20:21:15Z", false, false, "2021-03-23T20:21:15Z"},
{"2021-03-23T20:21:15+01:00", false, false, "2021-03-23T19:21:15Z"},
{"2021-03-23", true, false, "2021-03-23"},
}
for _, tt := range tests {
t.Run(fmt.Sprintf("%v", tt.input), func(t *testing.T) {
assert.New(t).Equal(tt.rval, sDatetime(tt.input, tt.onlyDate, tt.onlyTime))
})
}
}