Added support for query names to the allow.list
This commit is contained in:
parent
f518d5fc69
commit
aff2a13ba4
|
@ -152,7 +152,7 @@ mutation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query {
|
query getProducts {
|
||||||
products {
|
products {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
|
|
@ -18,6 +18,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type allowItem struct {
|
type allowItem struct {
|
||||||
|
name string
|
||||||
|
hash string
|
||||||
uri string
|
uri string
|
||||||
gql string
|
gql string
|
||||||
vars json.RawMessage
|
vars json.RawMessage
|
||||||
|
@ -94,7 +96,7 @@ func initAllowList(cpath string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (al *allowList) add(req *gqlReq) {
|
func (al *allowList) add(req *gqlReq) {
|
||||||
if al.active == false || len(req.ref) == 0 || len(req.Query) == 0 {
|
if len(req.ref) == 0 || len(req.Query) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,11 +121,39 @@ func (al *allowList) add(req *gqlReq) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (al *allowList) load() {
|
func (al *allowList) upsert(query, vars []byte, uri string) {
|
||||||
if al.active == false {
|
q := string(query)
|
||||||
return
|
hash := gqlHash(q, vars, "")
|
||||||
|
name := gqlName(q)
|
||||||
|
|
||||||
|
var key string
|
||||||
|
|
||||||
|
if len(name) == 0 {
|
||||||
|
key = hash
|
||||||
|
} else {
|
||||||
|
key = name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if i, ok := al.index[key]; !ok {
|
||||||
|
al.list = append(al.list, &allowItem{
|
||||||
|
name: name,
|
||||||
|
hash: hash,
|
||||||
|
uri: uri,
|
||||||
|
gql: q,
|
||||||
|
vars: vars,
|
||||||
|
})
|
||||||
|
al.index[key] = len(al.list) - 1
|
||||||
|
} else {
|
||||||
|
item := al.list[i]
|
||||||
|
item.name = name
|
||||||
|
item.hash = hash
|
||||||
|
item.gql = q
|
||||||
|
item.vars = vars
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (al *allowList) load() {
|
||||||
b, err := ioutil.ReadFile(al.filepath)
|
b, err := ioutil.ReadFile(al.filepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -172,21 +202,7 @@ func (al *allowList) load() {
|
||||||
|
|
||||||
if c == 0 {
|
if c == 0 {
|
||||||
if ty == AL_QUERY {
|
if ty == AL_QUERY {
|
||||||
q := string(b[s:(e + 1)])
|
al.upsert(b[s:(e+1)], varBytes, uri)
|
||||||
key := gqlHash(q, varBytes, "")
|
|
||||||
|
|
||||||
if idx, ok := al.index[key]; !ok {
|
|
||||||
al.list = append(al.list, &allowItem{
|
|
||||||
uri: uri,
|
|
||||||
gql: q,
|
|
||||||
vars: varBytes,
|
|
||||||
})
|
|
||||||
al.index[key] = len(al.list) - 1
|
|
||||||
} else {
|
|
||||||
item := al.list[idx]
|
|
||||||
item.gql = q
|
|
||||||
item.vars = varBytes
|
|
||||||
}
|
|
||||||
varBytes = nil
|
varBytes = nil
|
||||||
|
|
||||||
} else if ty == AL_VARS {
|
} else if ty == AL_VARS {
|
||||||
|
@ -204,11 +220,11 @@ func (al *allowList) load() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (al *allowList) save(item *allowItem) {
|
func (al *allowList) save(item *allowItem) {
|
||||||
if al.active == false {
|
item.hash = gqlHash(item.gql, item.vars, "")
|
||||||
return
|
item.name = gqlName(item.gql)
|
||||||
}
|
|
||||||
|
|
||||||
key := gqlHash(item.gql, item.vars, "")
|
if len(item.name) == 0 {
|
||||||
|
key := item.hash
|
||||||
|
|
||||||
if _, ok := al.index[key]; ok {
|
if _, ok := al.index[key]; ok {
|
||||||
return
|
return
|
||||||
|
@ -217,6 +233,20 @@ func (al *allowList) save(item *allowItem) {
|
||||||
al.list = append(al.list, item)
|
al.list = append(al.list, item)
|
||||||
al.index[key] = len(al.list) - 1
|
al.index[key] = len(al.list) - 1
|
||||||
|
|
||||||
|
} else {
|
||||||
|
key := item.name
|
||||||
|
|
||||||
|
if i, ok := al.index[key]; ok {
|
||||||
|
if al.list[i].hash == item.hash {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
al.list[i] = item
|
||||||
|
} else {
|
||||||
|
al.list = append(al.list, item)
|
||||||
|
al.index[key] = len(al.list) - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
f, err := os.Create(al.filepath)
|
f, err := os.Create(al.filepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warn().Err(err).Msgf("Failed to write allow list: %s", al.filepath)
|
logger.Warn().Err(err).Msgf("Failed to write allow list: %s", al.filepath)
|
||||||
|
|
|
@ -246,9 +246,9 @@ func (c *coreContext) resolveSQL() ([]byte, *stmt, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if conf.Production == false {
|
if conf.Production == false {
|
||||||
// _allowList.add(&c.req)
|
_allowList.add(&c.req)
|
||||||
// }
|
}
|
||||||
|
|
||||||
if len(stmts) > 1 {
|
if len(stmts) > 1 {
|
||||||
if st = findStmt(role, stmts); st == nil {
|
if st = findStmt(role, stmts); st == nil {
|
||||||
|
|
|
@ -4,6 +4,7 @@ package serv
|
||||||
|
|
||||||
func Fuzz(data []byte) int {
|
func Fuzz(data []byte) int {
|
||||||
gql := string(data)
|
gql := string(data)
|
||||||
|
gqlName(gql)
|
||||||
gqlHash(gql, nil, "")
|
gqlHash(gql, nil, "")
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
|
@ -10,6 +10,7 @@ func TestFuzzCrashers(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range crashers {
|
for _, f := range crashers {
|
||||||
|
gqlName(f)
|
||||||
gqlHash(f, nil, "")
|
gqlHash(f, nil, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,12 @@ func prepareStmt(c context.Context, gql string, vars []byte) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(vars) == 0 {
|
||||||
|
logger.Debug().Msgf("Building prepared statement for:\n %s", gql)
|
||||||
|
} else {
|
||||||
|
logger.Debug().Msgf("Building prepared statement:\n %s\n%s", vars, gql)
|
||||||
|
}
|
||||||
|
|
||||||
if err := tx.Commit(c); err != nil {
|
if err := tx.Commit(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,6 +106,30 @@ func al(b byte) bool {
|
||||||
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')
|
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func gqlName(b string) string {
|
||||||
|
state, s := 0, 0
|
||||||
|
|
||||||
|
for i := 0; i < len(b); i++ {
|
||||||
|
switch {
|
||||||
|
case state == 2 && b[i] == '{':
|
||||||
|
return b[s:i]
|
||||||
|
case state == 2 && b[i] == ' ':
|
||||||
|
return b[s:i]
|
||||||
|
case state == 1 && b[i] == '{':
|
||||||
|
return ""
|
||||||
|
case state == 1 && b[i] != ' ':
|
||||||
|
s = i
|
||||||
|
state = 2
|
||||||
|
case state == 1 && b[i] == ' ':
|
||||||
|
continue
|
||||||
|
case i != 0 && b[i] == ' ' && (b[i-1] == 'n' || b[i-1] == 'y'):
|
||||||
|
state = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func findStmt(role string, stmts []stmt) *stmt {
|
func findStmt(role string, stmts []stmt) *stmt {
|
||||||
for i := range stmts {
|
for i := range stmts {
|
||||||
if stmts[i].role.Name != role {
|
if stmts[i].role.Name != role {
|
||||||
|
|
|
@ -229,3 +229,80 @@ func TestGQLHashWithVars2(t *testing.T) {
|
||||||
t.Fatal("Hashes don't match they should")
|
t.Fatal("Hashes don't match they should")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGQLName1(t *testing.T) {
|
||||||
|
var q = `
|
||||||
|
query {
|
||||||
|
products(
|
||||||
|
distinct: [price]
|
||||||
|
where: { id: { and: { greater_or_equals: 20, lt: 28 } } }
|
||||||
|
) { id name } }`
|
||||||
|
|
||||||
|
name := gqlName(q)
|
||||||
|
|
||||||
|
if len(name) != 0 {
|
||||||
|
t.Fatal("Name should be empty, not ", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGQLName2(t *testing.T) {
|
||||||
|
var q = `
|
||||||
|
query hakuna_matata {
|
||||||
|
products(
|
||||||
|
distinct: [price]
|
||||||
|
where: { id: { and: { greater_or_equals: 20, lt: 28 } } }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
name := gqlName(q)
|
||||||
|
|
||||||
|
if name != "hakuna_matata" {
|
||||||
|
t.Fatal("Name should be 'hakuna_matata', not ", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGQLName3(t *testing.T) {
|
||||||
|
var q = `
|
||||||
|
mutation means{ users { id } }`
|
||||||
|
|
||||||
|
// var v2 = ` { products( limit: 30, order_by: { price: desc }, distinct: [ price ] where: { id: { and: { greater_or_equals: 20, lt: 28 } } }) { id name price user { id email } } } `
|
||||||
|
|
||||||
|
name := gqlName(q)
|
||||||
|
|
||||||
|
if name != "means" {
|
||||||
|
t.Fatal("Name should be 'means', not ", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGQLName4(t *testing.T) {
|
||||||
|
var q = `
|
||||||
|
query no_worries
|
||||||
|
users {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
name := gqlName(q)
|
||||||
|
|
||||||
|
if name != "no_worries" {
|
||||||
|
t.Fatal("Name should be 'no_worries', not ", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGQLName5(t *testing.T) {
|
||||||
|
var q = `
|
||||||
|
{
|
||||||
|
users {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
name := gqlName(q)
|
||||||
|
|
||||||
|
if len(name) != 0 {
|
||||||
|
t.Fatal("Name should be empty, not ", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue