github.com/newrelic/go-agent@v3.26.0+incompatible/internal/security_policies.go (about)

     1  // Copyright 2020 New Relic Corporation. All rights reserved.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package internal
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"reflect"
    10  )
    11  
    12  // Security policies documentation:
    13  // https://source.datanerd.us/agents/agent-specs/blob/master/Language-Agent-Security-Policies.md
    14  
    15  // SecurityPolicies contains the security policies.
    16  type SecurityPolicies struct {
    17  	RecordSQL                 securityPolicy `json:"record_sql"`
    18  	AttributesInclude         securityPolicy `json:"attributes_include"`
    19  	AllowRawExceptionMessages securityPolicy `json:"allow_raw_exception_messages"`
    20  	CustomEvents              securityPolicy `json:"custom_events"`
    21  	CustomParameters          securityPolicy `json:"custom_parameters"`
    22  }
    23  
    24  // PointerIfPopulated returns a reference to the security policies if they have
    25  // been populated from JSON.
    26  func (sp *SecurityPolicies) PointerIfPopulated() *SecurityPolicies {
    27  	emptyPolicies := SecurityPolicies{}
    28  	if nil != sp && *sp != emptyPolicies {
    29  		return sp
    30  	}
    31  	return nil
    32  }
    33  
    34  type securityPolicy struct {
    35  	EnabledVal *bool `json:"enabled"`
    36  }
    37  
    38  func (p *securityPolicy) Enabled() bool           { return nil == p.EnabledVal || *p.EnabledVal }
    39  func (p *securityPolicy) SetEnabled(enabled bool) { p.EnabledVal = &enabled }
    40  func (p *securityPolicy) IsSet() bool             { return nil != p.EnabledVal }
    41  
    42  type policyer interface {
    43  	SetEnabled(bool)
    44  	IsSet() bool
    45  }
    46  
    47  // UnmarshalJSON decodes security policies sent from the preconnect endpoint.
    48  func (sp *SecurityPolicies) UnmarshalJSON(data []byte) (er error) {
    49  	defer func() {
    50  		// Zero out all fields if there is an error to ensure that the
    51  		// populated check works.
    52  		if er != nil {
    53  			*sp = SecurityPolicies{}
    54  		}
    55  	}()
    56  
    57  	var raw map[string]struct {
    58  		Enabled  bool `json:"enabled"`
    59  		Required bool `json:"required"`
    60  	}
    61  	err := json.Unmarshal(data, &raw)
    62  	if err != nil {
    63  		return fmt.Errorf("unable to unmarshal security policies: %v", err)
    64  	}
    65  
    66  	knownPolicies := make(map[string]policyer)
    67  
    68  	spv := reflect.ValueOf(sp).Elem()
    69  	for i := 0; i < spv.NumField(); i++ {
    70  		fieldAddress := spv.Field(i).Addr()
    71  		field := fieldAddress.Interface().(policyer)
    72  		name := spv.Type().Field(i).Tag.Get("json")
    73  		knownPolicies[name] = field
    74  	}
    75  
    76  	for name, policy := range raw {
    77  		p, ok := knownPolicies[name]
    78  		if !ok {
    79  			if policy.Required {
    80  				return errUnknownRequiredPolicy{name: name}
    81  			}
    82  		} else {
    83  			p.SetEnabled(policy.Enabled)
    84  		}
    85  	}
    86  	for name, policy := range knownPolicies {
    87  		if !policy.IsSet() {
    88  			return errUnsetPolicy{name: name}
    89  		}
    90  	}
    91  	return nil
    92  }
    93  
    94  type errUnknownRequiredPolicy struct{ name string }
    95  
    96  func (err errUnknownRequiredPolicy) Error() string {
    97  	return fmt.Sprintf("policy '%s' is unrecognized, please check for a newer agent version or contact support", err.name)
    98  }
    99  
   100  type errUnsetPolicy struct{ name string }
   101  
   102  func (err errUnsetPolicy) Error() string {
   103  	return fmt.Sprintf("policy '%s' not received, please contact support", err.name)
   104  }
   105  
   106  func isDisconnectSecurityPolicyError(e error) bool {
   107  	if _, ok := e.(errUnknownRequiredPolicy); ok {
   108  		return true
   109  	}
   110  	if _, ok := e.(errUnsetPolicy); ok {
   111  		return true
   112  	}
   113  	return false
   114  }