diff --git a/builtins/parse_config.go b/builtins/parse_config.go index 110e2e670f..003c3fd816 100644 --- a/builtins/parse_config.go +++ b/builtins/parse_config.go @@ -24,7 +24,7 @@ func registerParseConfig() { Name: "parse_config", Decl: types.NewFunction( types.Args(types.S, types.S), // parser name, configuration - types.NewObject(nil, types.NewDynamicProperty(types.S, types.NewAny())), // map[string]interface{} aka JSON + types.NewObject(nil, types.NewDynamicProperty(types.S, types.NewAny())), // map[string]any aka JSON ), } rego.RegisterBuiltin2(&decl, parseConfig) @@ -35,7 +35,7 @@ func registerParseConfigFile() { Name: "parse_config_file", Decl: types.NewFunction( types.Args(types.S), // path to configuration file - types.NewObject(nil, types.NewDynamicProperty(types.S, types.NewAny())), // map[string]interface{} aka JSON + types.NewObject(nil, types.NewDynamicProperty(types.S, types.NewAny())), // map[string]any aka JSON ), } rego.RegisterBuiltin1(&decl, parseConfigFile) @@ -46,7 +46,7 @@ func registerParseCombinedConfigFiles() { Name: "parse_combined_config_files", Decl: types.NewFunction( types.Args(types.NewArray(nil, types.S)), // paths to configuration files - types.NewObject(nil, types.NewDynamicProperty(types.S, types.NewAny())), // map[string]interface{} aka JSON + types.NewObject(nil, types.NewDynamicProperty(types.S, types.NewAny())), // map[string]any aka JSON ), } rego.RegisterBuiltin1(&decl, parseCombinedConfigFiles) @@ -56,24 +56,17 @@ func registerParseCombinedConfigFiles() { // parsed configuration as a Rego object. This can be used to parse all of the // configuration formats conftest supports in-line in Rego policies. func parseConfig(bctx rego.BuiltinContext, op1, op2 *ast.Term) (*ast.Term, error) { - args, err := decodeArgs([]*ast.Term{op1, op2}) + args, err := decodeTypedArgs("", op1, op2) if err != nil { return nil, fmt.Errorf("decode args: %w", err) } - parserName, ok := args[0].(string) - if !ok { - return nil, fmt.Errorf("parser name %v [%T] is not expected type string", args[0], args[0]) - } - config, ok := args[1].(string) - if !ok { - return nil, fmt.Errorf("config %v [%T] is not expected type string", args[1], args[1]) - } + parserName, config := args[0], args[1] + parser, err := parser.New(parserName) if err != nil { return nil, fmt.Errorf("create config parser: %w", err) } - - var cfg map[string]interface{} + var cfg map[string]any if err := parser.Unmarshal([]byte(config), &cfg); err != nil { return nil, fmt.Errorf("unmarshal config: %w", err) } @@ -84,15 +77,12 @@ func parseConfig(bctx rego.BuiltinContext, op1, op2 *ast.Term) (*ast.Term, error // parseConfigFile takes a config file path, parses the config file, and // returns the parsed configuration as a Rego object. func parseConfigFile(bctx rego.BuiltinContext, op1 *ast.Term) (*ast.Term, error) { - args, err := decodeArgs([]*ast.Term{op1}) + args, err := decodeTypedArgs("", op1) if err != nil { return nil, fmt.Errorf("decode args: %w", err) } - file, ok := args[0].(string) - if !ok { - return nil, fmt.Errorf("file %v [%T] is not expected type string", args[0], args[0]) - } - filePath := filepath.Join(filepath.Dir(bctx.Location.File), file) + filePath := filepath.Join(filepath.Dir(bctx.Location.File), args[0]) + parser, err := parser.NewFromPath(filePath) if err != nil { return nil, fmt.Errorf("create config parser: %w", err) @@ -102,7 +92,7 @@ func parseConfigFile(bctx rego.BuiltinContext, op1 *ast.Term) (*ast.Term, error) return nil, fmt.Errorf("read config file %s: %w", filePath, err) } - var cfg map[string]interface{} + var cfg map[string]any if err := parser.Unmarshal(contents, &cfg); err != nil { return nil, fmt.Errorf("unmarshal config: %w", err) } @@ -110,46 +100,58 @@ func parseConfigFile(bctx rego.BuiltinContext, op1 *ast.Term) (*ast.Term, error) return toAST(bctx, cfg, contents) } -// parseCombinedConfigFiles +// parseCombinedConfigFiles takes multiple config file paths, parses the configs, +// combines them, and returns that as a Rego object. func parseCombinedConfigFiles(bctx rego.BuiltinContext, op1 *ast.Term) (*ast.Term, error) { - args, err := decodeArgs([]*ast.Term{op1}) + iface, err := ast.ValueToInterface(op1.Value, nil) if err != nil { - return nil, fmt.Errorf("decode args: %w", err) + return nil, fmt.Errorf("ast.ValueToInterface: %w", err) + } + slice, ok := iface.([]any) + if !ok { + return nil, fmt.Errorf("argument is not a slice") } - filePaths := []string{} - fileList := args[0].([]interface{}) - for _, file := range fileList { - filePaths = append(filePaths, filepath.Join(filepath.Dir(bctx.Location.File), file.(string))) + var paths []string + for i, s := range slice { + path, ok := s.(string) + if !ok { + return nil, fmt.Errorf("index %d is not expected type string", i) + } + paths = append(paths, filepath.Join(filepath.Dir(bctx.Location.File), path)) } - cfg, err := parser.ParseConfigurations(filePaths) + cfg, err := parser.ParseConfigurations(paths) if err != nil { - return nil, fmt.Errorf("failed to parse combine configurations: %w", err) + return nil, fmt.Errorf("parse combine configurations: %w", err) } - combinedCfg := parser.CombineConfigurations(cfg) - combinedContent, err := json.Marshal(combinedCfg) + combined := parser.CombineConfigurations(cfg) + content, err := json.Marshal(combined) if err != nil { - return nil, fmt.Errorf("failed to marshal combined content: %w", err) + return nil, fmt.Errorf("marshal combined content: %w", err) } - return toAST(bctx, combinedCfg["Combined"], combinedContent) + return toAST(bctx, combined["Combined"], content) } -func decodeArgs(args []*ast.Term) ([]interface{}, error) { - decoded := make([]interface{}, len(args)) +func decodeTypedArgs[T any](ty T, args ...*ast.Term) ([]T, error) { + decoded := make([]T, len(args)) for i, arg := range args { iface, err := ast.ValueToInterface(arg.Value, nil) if err != nil { return nil, fmt.Errorf("ast.ValueToInterface: %w", err) } - decoded[i] = iface + v, ok := iface.(T) + if !ok { + return nil, fmt.Errorf("argument %d is not type %T, have %T", i, ty, iface) + } + decoded[i] = v } return decoded, nil } -func toAST(bctx rego.BuiltinContext, cfg interface{}, contents []byte) (*ast.Term, error) { +func toAST(bctx rego.BuiltinContext, cfg any, contents []byte) (*ast.Term, error) { val, err := ast.InterfaceToValue(cfg) if err != nil { return nil, fmt.Errorf("convert config to ast.Value: %w", err) diff --git a/examples/kubernetes/combine/combine.rego b/examples/kubernetes/combine/combine.rego index 6f80cdcaa4..3954b952fb 100644 --- a/examples/kubernetes/combine/combine.rego +++ b/examples/kubernetes/combine/combine.rego @@ -11,4 +11,4 @@ service_selects_app(app) { input[i].contents.kind == "Service" service := input[i].contents service.spec.selector.app == app -} \ No newline at end of file +} diff --git a/parser/cyclonedx/cyclonedx_test.go b/parser/cyclonedx/cyclonedx_test.go index ce8a870504..2df9b59f5c 100644 --- a/parser/cyclonedx/cyclonedx_test.go +++ b/parser/cyclonedx/cyclonedx_test.go @@ -49,6 +49,7 @@ func TestCycloneDXParserValid(t *testing.T) { t.Error("There should be information parsed but its nil") } + //#nosec until https://github.com/securego/gosec/issues/1001 is fixed expectedSHA256 := "sha256:d7ec60cf8390612b360c857688b383068b580d9a6ab78417c9493170ad3f1616" metadata := input.(map[string]interface{})["metadata"] @@ -103,6 +104,7 @@ func TestCycloneDXParserInValid(t *testing.T) { t.Error("There should be information parsed but its nil") } + //#nosec until https://github.com/securego/gosec/issues/1001 is fixed expectedSHA256 := "sha256:d7ec60cf8390612b360c857688b383068b580d9a6ab78417c9493170ad3f1616" metadata := input.(map[string]interface{})["metadata"]