Skip to content

Commit

Permalink
feat(#3): Add security enforcement for tables
Browse files Browse the repository at this point in the history
  • Loading branch information
borisershov committed May 22, 2024
1 parent a667c7c commit f89170e
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 29 deletions.
34 changes: 33 additions & 1 deletion ctx/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ type confOpts struct {

Progress progressConf `conf:"progress"`
Filters map[string]filterConf `conf:"filters"`
MySQL *mysqlConf `conf:"mysql"`
Security securityConf `conf:"security"`

MySQL *mysqlConf `conf:"mysql"`
}

type progressConf struct {
Expand All @@ -31,6 +33,22 @@ type columnFilterConf struct {
Unique bool `conf:"unique"`
}

type securityConf struct {
Policy securityPolicyConf `conf:"policy"`
Exceptions securityExceptionsConf `conf:"exceptions"`
Defaults filterConf `conf:"defaults"`
}

type securityPolicyConf struct {
Tables string `conf:"tables" conf_extraopts:"default=pass"`
Columns string `conf:"columns" conf_extraopts:"default=pass"`
}

type securityExceptionsConf struct {
Tables []string `conf:"tables"`
Columns []string `conf:"columns"`
}

type mysqlConf struct {
Host string `conf:"host" conf_extraopts:"required"`
Port int `conf:"port" conf_extraopts:"required"`
Expand Down Expand Up @@ -60,5 +78,19 @@ func confRead(confPath string) (confOpts, error) {
}
}

if misc.SecurityPolicyTablesTypeFromString(c.Security.Policy.Tables) == misc.SecurityPolicyTablesUnknown {
return c, fmt.Errorf("conf read: unknown security policy tables type")
}

if misc.SecurityPolicyColumnsTypeFromString(c.Security.Policy.Columns) == misc.SecurityPolicyColumnsUnknown {
return c, fmt.Errorf("conf read: unknown security policy columns type")
}

for _, cf := range c.Security.Defaults.Columns {
if misc.ValueTypeFromString(cf.Type) == misc.ValueTypeUnknown {
return c, fmt.Errorf("conf read: unknown default filter type")
}
}

return c, nil
}
39 changes: 39 additions & 0 deletions ctx/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Ctx struct {
Output io.Writer
Rules relfilter.Rules
Progress progressCtx
Security SecurityCtx
DB DBCtx
}

Expand Down Expand Up @@ -49,6 +50,21 @@ type progressCtx struct {
Humanize bool
}

type SecurityCtx struct {
Policy securityPolicyCtx
Exceptions securityExceptionsCtx
}

type securityPolicyCtx struct {
Tables misc.SecurityPolicyTablesType
Columns misc.SecurityPolicyColumnsType
}

type securityExceptionsCtx struct {
Tables map[string]any
Columns map[string]any
}

// Init initiates application custom context
func AppCtxInit() (any, error) {

Expand Down Expand Up @@ -155,6 +171,29 @@ func AppCtxInit() (any, error) {
return nil, err
}

c.Security = SecurityCtx{
Policy: securityPolicyCtx{
Tables: misc.SecurityPolicyTablesTypeFromString(conf.Security.Policy.Tables),
Columns: misc.SecurityPolicyColumnsTypeFromString(conf.Security.Policy.Columns),
},
Exceptions: securityExceptionsCtx{
Tables: func() map[string]any {
v := make(map[string]any)
for _, e := range conf.Security.Exceptions.Tables {
v[e] = nil
}
return v
}(),
Columns: func() map[string]any {
v := make(map[string]any)
for _, e := range conf.Security.Exceptions.Columns {
v[e] = nil
}
return v
}(),
},
}

return c, nil
}

Expand Down
47 changes: 47 additions & 0 deletions misc/security.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package misc

type SecurityPolicyTablesType string

const (
SecurityPolicyTablesUnknown SecurityPolicyTablesType = "unknown"
SecurityPolicyTablesPass SecurityPolicyTablesType = "pass"
SecurityPolicyTablesSkip SecurityPolicyTablesType = "skip"
)

func (v SecurityPolicyTablesType) String() string {
return string(v)
}

func SecurityPolicyTablesTypeFromString(v string) SecurityPolicyTablesType {
switch v {
case string(SecurityPolicyTablesPass):
return SecurityPolicyTablesPass
case string(SecurityPolicyTablesSkip):
return SecurityPolicyTablesSkip
default:
return SecurityPolicyTablesUnknown
}
}

type SecurityPolicyColumnsType string

const (
SecurityPolicyColumnsUnknown SecurityPolicyColumnsType = "unknown"
SecurityPolicyColumnsPass SecurityPolicyColumnsType = "pass"
SecurityPolicyColumnsRandomize SecurityPolicyColumnsType = "randomize"
)

func (v SecurityPolicyColumnsType) String() string {
return string(v)
}

func SecurityPolicyColumnsTypeFromString(v string) SecurityPolicyColumnsType {
switch v {
case string(SecurityPolicyColumnsPass):
return SecurityPolicyColumnsPass
case string(SecurityPolicyColumnsRandomize):
return SecurityPolicyColumnsRandomize
default:
return SecurityPolicyColumnsUnknown
}
}
109 changes: 107 additions & 2 deletions modules/anonymizers/mysql/dh.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,74 @@ import (
"fmt"
"strings"

"github.com/nixys/nxs-data-anonymizer/misc"
"github.com/nixys/nxs-data-anonymizer/modules/filters/relfilter"
)

func dhCreateTableName(usrCtx any, deferred, token []byte) ([]byte, error) {
func dhSecurityCreateTable(usrCtx any, deferred, token []byte) ([]byte, error) {

uctx := usrCtx.(*userCtx)

uctx.security.tmpBuf = append(uctx.security.tmpBuf, token...)

return deferred, nil
}

func dhSecurityCreateTableName(usrCtx any, deferred, token []byte) ([]byte, error) {

uctx := usrCtx.(*userCtx)

uctx.security.tmpBuf = append(uctx.security.tmpBuf, deferred...)
uctx.security.tmpBuf = append(uctx.security.tmpBuf, token...)

return []byte{}, nil
}

func dhSecurityNil(usrCtx any, deferred, token []byte) ([]byte, error) {

uctx := usrCtx.(*userCtx)
uctx.filter.TableCreate(string(deferred))

if uctx.security.isSkip == true {
return []byte{}, nil
}

return append(deferred, token...), nil
}

func dhCreateTableName(usrCtx any, deferred, token []byte) ([]byte, error) {

uctx := usrCtx.(*userCtx)

tn := string(deferred)

// Check table pass through security rules
if !securityPolicyCheck(uctx, tn) {

// If not: table will be skipped from result dump

uctx.security.isSkip = true
uctx.security.tmpBuf = []byte{}
return []byte{}, nil
}

uctx.filter.TableCreate(tn)

d := append(uctx.security.tmpBuf, append(deferred, token...)...)

uctx.security.isSkip = false
uctx.security.tmpBuf = []byte{}

return d, nil
}

func dhCreateTableFieldName(usrCtx any, deferred, token []byte) ([]byte, error) {

uctx := usrCtx.(*userCtx)

if uctx.security.isSkip == true {
return []byte{}, nil
}

uctx.column.name = string(deferred)

return append(deferred, token...), nil
Expand All @@ -28,6 +82,10 @@ func dhCreateTableColumnTypeAdd(usrCtx any, deferred, token []byte) ([]byte, err

uctx := usrCtx.(*userCtx)

if uctx.security.isSkip == true {
return []byte{}, nil
}

for k, v := range typeKeys {
if k == "generated" {
if k == string(token) || strings.ToUpper(k) == string(token) {
Expand All @@ -53,6 +111,10 @@ func dhCreateTableColumnAdd(usrCtx any, deferred, token []byte) ([]byte, error)

uctx := usrCtx.(*userCtx)

if uctx.security.isSkip == true {
return []byte{}, nil
}

if uctx.column.isSkip == false {
uctx.filter.ColumnAdd(uctx.column.name, uctx.column.columnType)
}
Expand All @@ -66,6 +128,10 @@ func dhInsertIntoTableName(usrCtx any, deferred, token []byte) ([]byte, error) {

uctx := usrCtx.(*userCtx)

if uctx.security.isSkip == true {
return []byte{}, nil
}

// Check insert into table name
if bytes.Compare([]byte(uctx.filter.TableNameGet()), deferred) != 0 {
return append(deferred, token...), fmt.Errorf("`create` and `insert into` table names are mismatch (create table: '%s', insert into table: '%s')", uctx.filter.TableNameGet(), string(deferred))
Expand All @@ -78,6 +144,10 @@ func dhCreateTableValues(usrCtx any, deferred, token []byte) ([]byte, error) {

uctx := usrCtx.(*userCtx)

if uctx.security.isSkip == true {
return []byte{}, nil
}

if bytes.Compare(deferred, []byte("NULL")) == 0 {
uctx.filter.ValueAdd(nil)
} else {
Expand All @@ -91,6 +161,10 @@ func dhCreateTableValuesString(usrCtx any, deferred, token []byte) ([]byte, erro

uctx := usrCtx.(*userCtx)

if uctx.security.isSkip == true {
return []byte{}, nil
}

uctx.filter.ValueAdd(deferred)

return []byte{}, nil
Expand All @@ -100,6 +174,10 @@ func dhCreateTableValuesEnd(usrCtx any, deferred, token []byte) ([]byte, error)

uctx := usrCtx.(*userCtx)

if uctx.security.isSkip == true {
return []byte{}, nil
}

if bytes.Compare(deferred, []byte("NULL")) == 0 {
uctx.filter.ValueAdd(nil)
} else {
Expand All @@ -118,6 +196,10 @@ func dhCreateTableValuesStringEnd(usrCtx any, deferred, token []byte) ([]byte, e

uctx := usrCtx.(*userCtx)

if uctx.security.isSkip == true {
return []byte{}, nil
}

// Apply filter for row
if err := uctx.filter.Apply(); err != nil {
return []byte{}, err
Expand Down Expand Up @@ -154,3 +236,26 @@ func rowDataGen(filter *relfilter.Filter) []byte {

return []byte(fmt.Sprintf("(%s)", out))
}

// SecurityPolicyCheck checks the table passes the security rules
// true: pass
// false: skip
func securityPolicyCheck(uctx *userCtx, tname string) bool {

// Continue if security policy is `skip`
if uctx.security.policy.tables != misc.SecurityPolicyTablesSkip {
return true
}

// Check rules for specified table name
if _, b := uctx.filter.TableNameLookup(tname); b == true {
return true
}

// Check specified table name in exceptions
if _, b := uctx.security.exceptions.tables[tname]; b == true {
return true
}

return false
}
Loading

0 comments on commit f89170e

Please sign in to comment.