go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/mqlc/invariants.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package mqlc
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"go.mondoo.com/cnquery/llx"
    10  	"go.mondoo.com/cnquery/utils/multierr"
    11  )
    12  
    13  // An Invariant is a condition that we expect compiled code to hold.
    14  // This is used to find inconsistencies in our compiler and not for
    15  // meant to be user facing
    16  type Invariant struct {
    17  	ShortName   string
    18  	Description string
    19  	Issues      []string
    20  	// Checker returns true if the invariant holds
    21  	Checker func(*llx.CodeBundle) bool
    22  }
    23  
    24  type InvariantFailed struct {
    25  	ShortName string
    26  	Source    string
    27  }
    28  
    29  func (e InvariantFailed) Error() string {
    30  	return fmt.Sprintf("Invariant %q failed: Source => \n%+v", e.ShortName, e.Source)
    31  }
    32  
    33  type InvariantList []Invariant
    34  
    35  func (l InvariantList) Check(cb *llx.CodeBundle) error {
    36  	var err multierr.Errors
    37  	for _, i := range l {
    38  		if !i.Checker(cb) {
    39  			err.Add(InvariantFailed{
    40  				ShortName: i.ShortName,
    41  				Source:    cb.Source,
    42  			})
    43  		}
    44  	}
    45  
    46  	return err.Deduplicate()
    47  }
    48  
    49  var Invariants = InvariantList{
    50  	{
    51  		ShortName:   "code is not nil",
    52  		Description: `Make sure any compiled code is never just nil`,
    53  		Checker: func(cb *llx.CodeBundle) bool {
    54  			return cb.CodeV2 != nil
    55  		},
    56  	},
    57  	{
    58  		ShortName: "return-entrypoints-singular",
    59  		Description: `
    60  			The return statement indicates that the following expression
    61  			is to be used for the value of the block. Our execution code
    62  			assumes that only 1 value will be reported for the block.
    63  
    64  			This means that there can only be 1 entrypoint. Further, it
    65  			also means that num_entrypoints + num_datapoints == 1. The
    66  			restriction on datapoints is just an artifact of the way things
    67  			are written and can be changed, however the entrypoint should
    68  			be the return value. I mention this as a reminder that not all
    69  			parts of this invariant need to be this way forever and can be
    70  			changed
    71  		`,
    72  		Issues: []string{
    73  			"https://gitlab.com/mondoolabs/mondoo/-/issues/716",
    74  		},
    75  		Checker: func(cb *llx.CodeBundle) bool {
    76  			return checkReturnEntrypointsV2(cb.CodeV2)
    77  		},
    78  	},
    79  }
    80  
    81  func checkReturnEntrypointsV2(code *llx.CodeV2) bool {
    82  	for i := range code.Blocks {
    83  		block := code.Blocks[i]
    84  
    85  		if block.SingleValue {
    86  			if len(code.Entrypoints())+len(code.Datapoints()) != 1 {
    87  				return false
    88  			}
    89  		}
    90  	}
    91  
    92  	return true
    93  }
    94  
    95  func checkReturnEntrypointsV1(code *llx.CodeV1) bool {
    96  	if code.SingleValue {
    97  		if len(code.Entrypoints)+len(code.Datapoints) != 1 {
    98  			return false
    99  		}
   100  	}
   101  
   102  	for _, c := range code.Functions {
   103  		if checkReturnEntrypointsV1(c) == false {
   104  			return false
   105  		}
   106  	}
   107  
   108  	return true
   109  }