Files
releases/plugins/logs/ptr.go
T
Torin Sandall c0cc24676d logs: Update decision logger to support masking
These changes enhance the in-built decision logger to support masking
of input and result fields for cases where sensitive information is
passed to OPA (or generated by the policy.)

Also, fix rego package to avoid panic-ing on programmatically created
queriest that lack Location information.

Fixes #779

Signed-off-by: Torin Sandall <torinsandall@gmail.com>
2019-06-04 16:14:46 -07:00

140 lines
2.2 KiB
Go

package logs
import (
"encoding/json"
"fmt"
"net/url"
"strconv"
"strings"
"github.com/open-policy-agent/opa/util"
)
type ptr []string
func parsePtr(str string) (ptr, error) {
if len(str) == 0 {
return nil, fmt.Errorf("mask must be non-empty")
} else if str[0] != '/' {
return nil, fmt.Errorf("mask must be slash-prefixed")
}
parts := strings.Split(str[1:], "/")
if parts[0] != "input" && parts[0] != "result" {
return nil, fmt.Errorf("mask prefix not allowed")
}
for i := range parts {
var err error
parts[i], err = url.QueryUnescape(parts[i])
if err != nil {
return nil, err
}
}
return ptr(parts), nil
}
func (p ptr) String() string {
escaped := make([]string, len(p))
for i := range escaped {
escaped[i] = url.QueryEscape(p[i])
}
return "/" + strings.Join(escaped, "/")
}
func (p ptr) Erase(event *EventV1) {
if len(p) == 1 {
switch p[0] {
case "input":
event.Input = nil
case "result":
event.Result = nil
default:
panic("illegal value")
}
} else {
var node interface{}
switch p[0] {
case "input":
if event.Input != nil {
node = *event.Input
}
case "result":
if event.Result != nil {
node = *event.Result
}
}
parent := p[1 : len(p)-1].lookup(node)
parentObj, ok := parent.(map[string]interface{})
if !ok {
return
}
fld := p[len(p)-1]
if _, ok := parentObj[fld]; !ok {
return
}
delete(parentObj, fld)
}
event.Erased = append(event.Erased, p.String())
}
func (p ptr) lookup(node interface{}) interface{} {
for i := 0; i < len(p); i++ {
switch v := node.(type) {
case map[string]interface{}:
var ok bool
if node, ok = v[p[i]]; !ok {
return nil
}
case []interface{}:
idx, err := strconv.Atoi(p[i])
if err != nil {
return nil
} else if idx < 0 || idx >= len(v) {
return nil
}
node = v[idx]
default:
return nil
}
}
return node
}
func resultValueToPtrs(rv interface{}) ([]ptr, error) {
bs, err := json.Marshal(rv)
if err != nil {
return nil, err
}
var value []string
if err := util.Unmarshal(bs, &value); err != nil {
return nil, err
}
result := make([]ptr, len(value))
for i := range result {
if result[i], err = parsePtr(value[i]); err != nil {
return nil, err
}
}
return result, nil
}