implement proper ruleset json serializer

This commit is contained in:
Kevin Pham
2023-12-04 22:53:38 -06:00
parent 6157d6543f
commit f8621e72ee
2 changed files with 55 additions and 153 deletions

View File

@@ -4,25 +4,35 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"ladder/proxychain" "ladder/proxychain"
"reflect" // _ "gopkg.in/yaml.v3"
"runtime"
"strings"
_ "gopkg.in/yaml.v3"
) )
type Rule struct { type Rule struct {
Domains []string Domains []string
RequestModifications []proxychain.RequestModification RequestModifications []proxychain.RequestModification
_rqms []_rqm // internal represenation of RequestModifications
ResponseModifications []proxychain.ResponseModification ResponseModifications []proxychain.ResponseModification
_rsms []_rsm // internal represenation of ResponseModifications
}
// internal represenation of ResponseModifications
type _rsm struct {
Name string `json:"name"`
Params []string `json:"params"`
}
// internal represenation of RequestModifications
type _rqm struct {
Name string `json:"name"`
Params []string `json:"params"`
} }
// implement type encoding/json/Marshaler // implement type encoding/json/Marshaler
func (rule *Rule) UnmarshalJSON(data []byte) error { func (rule *Rule) UnmarshalJSON(data []byte) error {
type Aux struct { type Aux struct {
Domains []string `json:"domains"` Domains []string `json:"domains"`
RequestModifications []string `json:"request_modifications"` RequestModifications []_rqm `json:"request_modifications"`
ResponseModifications []string `json:"response_modifications"` ResponseModifications []_rsm `json:"response_modifications"`
} }
aux := &Aux{} aux := &Aux{}
@@ -30,163 +40,41 @@ func (rule *Rule) UnmarshalJSON(data []byte) error {
return err return err
} }
//fmt.Println(aux.Domains)
rule.Domains = aux.Domains rule.Domains = aux.Domains
rule._rqms = aux.RequestModifications
rule._rsms = aux.ResponseModifications
// convert requestModification function call string into actual functional option // convert requestModification function call string into actual functional option
for _, rqmModStr := range aux.RequestModifications { for _, rqm := range aux.RequestModifications {
name, params, err := parseFuncCall(rqmModStr) f, exists := rqmModMap[rqm.Name]
if err != nil {
return fmt.Errorf("Rule::UnmarshalJSON invalid function call syntax => '%s'", err)
}
f, exists := rqmModMap[name]
if !exists { if !exists {
return fmt.Errorf("Rule::UnmarshalJSON => requestModifier '%s' does not exist, please check spelling", name) return fmt.Errorf("Rule::UnmarshalJSON => requestModifier '%s' does not exist, please check spelling", rqm.Name)
} }
rule.RequestModifications = append(rule.RequestModifications, f(params...)) rule.RequestModifications = append(rule.RequestModifications, f(rqm.Params...))
} }
// convert responseModification function call string into actual functional option // convert responseModification function call string into actual functional option
for _, rsmModStr := range aux.ResponseModifications { for _, rsm := range aux.ResponseModifications {
name, params, err := parseFuncCall(rsmModStr) f, exists := rsmModMap[rsm.Name]
if err != nil {
return fmt.Errorf("Rule::UnmarshalJSON invalid function call syntax => '%s'", err)
}
f, exists := rsmModMap[name]
if !exists { if !exists {
return fmt.Errorf("Rule::UnmarshalJSON => responseModifier '%s' does not exist, please check spelling", name) return fmt.Errorf("Rule::UnmarshalJSON => responseModifier '%s' does not exist, please check spelling", rsm.Name)
} }
rule.ResponseModifications = append(rule.ResponseModifications, f(params...)) rule.ResponseModifications = append(rule.ResponseModifications, f(rsm.Params...))
} }
return nil return nil
} }
// not fully possible to go from rule to JSON rule because
// reflection cannot get the parameters of the functional options
// of requestmodifiers and responsemodifiers
func (r *Rule) MarshalJSON() ([]byte, error) { func (r *Rule) MarshalJSON() ([]byte, error) {
type Aux struct { aux := struct {
Domains []string `json:"domains"` Domains []string `json:"domains"`
RequestModifications []string `json:"request_modifications"` RequestModifications []_rqm `json:"request_modifications"`
ResponseModifications []string `json:"response_modifications"` ResponseModifications []_rsm `json:"response_modifications"`
} }{
aux := &Aux{} Domains: r.Domains,
aux.Domains = r.Domains RequestModifications: r._rqms,
ResponseModifications: r._rsms,
for _, rqmMod := range r.RequestModifications {
fnName := getFunctionName(rqmMod)
aux.RequestModifications = append(aux.RequestModifications, fnName)
}
for _, rsmMod := range r.ResponseModifications {
fnName := getFunctionName(rsmMod)
aux.ResponseModifications = append(aux.ResponseModifications, fnName)
} }
return json.Marshal(aux) return json.Marshal(aux)
} }
// getFunctionName returns the name of the function
func getFunctionName(i interface{}) string {
// Get the value of the interface
val := reflect.ValueOf(i)
// Ensure it's a function
if val.Kind() != reflect.Func {
return "Not a function"
}
// Get the pointer to the function
ptr := val.Pointer()
// Get the function details from runtime
funcForPc := runtime.FuncForPC(ptr)
if funcForPc == nil {
return "Unknown"
}
// Return the name of the function
return extractShortName(funcForPc.Name())
}
// extractShortName extracts the short function name from the full name
func extractShortName(fullName string) string {
parts := strings.Split(fullName, ".")
if len(parts) > 0 {
// Assuming the function name is always the second last part
return parts[len(parts)-2]
}
return ""
}
// == YAML
// UnmarshalYAML implements the yaml.Unmarshaler interface for Rule
func (rule *Rule) UnmarshalYAML(unmarshal func(interface{}) error) error {
type Aux struct {
Domains []string `yaml:"domains"`
RequestModifications []string `yaml:"request_modifications"`
ResponseModifications []string `yaml:"response_modifications"`
}
aux := &Aux{}
if err := unmarshal(aux); err != nil {
return err
}
rule.Domains = aux.Domains
// Process requestModifications
for _, rqmModStr := range aux.RequestModifications {
name, params, err := parseFuncCall(rqmModStr)
if err != nil {
return fmt.Errorf("Rule::UnmarshalYAML invalid function call syntax => '%s'", err)
}
f, exists := rqmModMap[name]
if !exists {
return fmt.Errorf("Rule::UnmarshalYAML => requestModifier '%s' does not exist, please check spelling", name)
}
rule.RequestModifications = append(rule.RequestModifications, f(params...))
}
// Process responseModifications
for _, rsmModStr := range aux.ResponseModifications {
name, params, err := parseFuncCall(rsmModStr)
if err != nil {
return fmt.Errorf("Rule::UnmarshalYAML invalid function call syntax => '%s'", err)
}
f, exists := rsmModMap[name]
if !exists {
return fmt.Errorf("Rule::UnmarshalYAML => responseModifier '%s' does not exist, please check spelling", name)
}
rule.ResponseModifications = append(rule.ResponseModifications, f(params...))
}
return nil
}
func (r *Rule) MarshalYAML() (interface{}, error) {
type Aux struct {
Domains []string `yaml:"domains"`
RequestModifications []string `yaml:"request_modifications"`
ResponseModifications []string `yaml:"response_modifications"`
}
aux := &Aux{
Domains: r.Domains,
}
for _, rqmMod := range r.RequestModifications {
// Assuming getFunctionName returns a string representation of the function
fnName := getFunctionName(rqmMod)
aux.RequestModifications = append(aux.RequestModifications, fnName)
}
for _, rsmMod := range r.ResponseModifications {
fnName := getFunctionName(rsmMod)
aux.ResponseModifications = append(aux.ResponseModifications, fnName)
}
return aux, nil
}

View File

@@ -3,7 +3,7 @@ package ruleset_v2
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
yaml "gopkg.in/yaml.v3" //yaml "gopkg.in/yaml.v3"
"testing" "testing"
) )
@@ -13,13 +13,25 @@ func TestRuleUnmarshalJSON(t *testing.T) {
"example.com", "example.com",
"www.example.com" "www.example.com"
], ],
"request_modifications": [
"SpoofUserAgent(\"googlebot\")"
],
"response_modifications": [ "response_modifications": [
"APIContent()", {
"SetContentSecurityPolicy(\"foobar\")", "name": "APIContent",
"SetIncomingCookie(\"authorization-bearer\", \"hunter2\")" "params": []
},
{
"name": "SetContentSecurityPolicy",
"params": ["foobar"]
},
{
"name": "SetIncomingCookie",
"params": ["authorization-bearer", "hunter2"]
}
],
"request_modifications": [
{
"name": "ForwardRequestHeaders",
"params": []
}
] ]
}` }`
@@ -54,6 +66,7 @@ func TestRuleUnmarshalJSON(t *testing.T) {
fmt.Println(string(jsonRule)) fmt.Println(string(jsonRule))
} }
/*
func TestRuleUnmarshalYAML(t *testing.T) { func TestRuleUnmarshalYAML(t *testing.T) {
ruleYAML := ` ruleYAML := `
domains: domains:
@@ -95,3 +108,4 @@ response_modifications:
} }
fmt.Println(string(yamlRule)) fmt.Println(string(yamlRule))
} }
*/