7
7
"fmt"
8
8
"strings"
9
9
10
- "github.com/xeipuuv/gojsonschema"
10
+ "github.com/santhosh-tekuri/jsonschema/v6"
11
+ "google.golang.org/protobuf/types/known/structpb"
11
12
12
13
minderv1 "github.com/mindersec/minder/pkg/api/protobuf/go/minder/v1"
13
14
)
@@ -18,32 +19,32 @@ type RuleValidator struct {
18
19
ruleTypeName string
19
20
20
21
// schema is the schema that this rule type must conform to
21
- schema * gojsonschema .Schema
22
+ schema * jsonschema .Schema
22
23
// paramSchema is the schema that the parameters for this rule type must conform to
23
- paramSchema * gojsonschema .Schema
24
+ paramSchema * jsonschema .Schema
24
25
}
25
26
26
27
// NewRuleValidator creates a new rule validator
27
28
func NewRuleValidator (rt * minderv1.RuleType ) (* RuleValidator , error ) {
28
- // Load schemas
29
- schemaLoader := gojsonschema .NewGoLoader (rt .Def .RuleSchema )
30
- schema , err := gojsonschema .NewSchema (schemaLoader )
29
+ if rt .GetDef ().GetRuleSchema () == nil {
30
+ return nil , fmt .Errorf ("rule type %s does not have a rule schema" , rt .Name )
31
+ }
32
+ // Create a new schema compiler
33
+ // Compile the main rule schema
34
+ mainSchema , err := compileSchemaFromPB (rt .GetDef ().GetRuleSchema ())
31
35
if err != nil {
32
36
return nil , fmt .Errorf ("cannot create json schema: %w" , err )
33
37
}
34
38
35
- var paramSchema * gojsonschema.Schema
36
- if rt .Def .ParamSchema != nil {
37
- paramSchemaLoader := gojsonschema .NewGoLoader (rt .Def .ParamSchema )
38
- paramSchema , err = gojsonschema .NewSchema (paramSchemaLoader )
39
- if err != nil {
40
- return nil , fmt .Errorf ("cannot create json schema for params: %w" , err )
41
- }
39
+ // Compile the parameter schema if it exists
40
+ paramSchema , err := compileSchemaFromPB (rt .GetDef ().GetParamSchema ())
41
+ if err != nil {
42
+ return nil , fmt .Errorf ("cannot create json schema for params: %w" , err )
42
43
}
43
44
44
45
return & RuleValidator {
45
46
ruleTypeName : rt .Name ,
46
- schema : schema ,
47
+ schema : mainSchema ,
47
48
paramSchema : paramSchema ,
48
49
}, nil
49
50
}
@@ -57,7 +58,7 @@ func (r *RuleValidator) ValidateRuleDefAgainstSchema(contextualProfile map[strin
57
58
Err : err .Error (),
58
59
}
59
60
}
60
-
61
+ applyDefaults ( r . schema , contextualProfile )
61
62
return nil
62
63
}
63
64
@@ -67,36 +68,66 @@ func (r *RuleValidator) ValidateParamsAgainstSchema(params map[string]any) error
67
68
if r .paramSchema == nil {
68
69
return nil
69
70
}
70
-
71
71
if err := validateAgainstSchema (r .paramSchema , params ); err != nil {
72
72
return & RuleValidationError {
73
73
RuleType : r .ruleTypeName ,
74
74
Err : err .Error (),
75
75
}
76
76
}
77
-
77
+ applyDefaults ( r . paramSchema , params )
78
78
return nil
79
79
}
80
80
81
- func validateAgainstSchema (schema * gojsonschema.Schema , obj map [string ]any ) error {
82
- documentLoader := gojsonschema .NewGoLoader (obj )
83
- result , err := schema .Validate (documentLoader )
84
- if err != nil {
85
- return fmt .Errorf ("cannot validate json schema: %s" , err )
81
+ func compileSchemaFromPB (schemaData * structpb.Struct ) (* jsonschema.Schema , error ) {
82
+ if schemaData == nil {
83
+ return nil , nil
86
84
}
87
85
88
- if ! result .Valid () {
89
- return buildValidationError (result .Errors ())
86
+ return compileSchemaFromMap (schemaData .AsMap ())
87
+ }
88
+
89
+ func compileSchemaFromMap (schemaData map [string ]any ) (* jsonschema.Schema , error ) {
90
+ compiler := jsonschema .NewCompiler ()
91
+ if err := compiler .AddResource ("schema.json" , schemaData ); err != nil {
92
+ return nil , fmt .Errorf ("invalid schema: %w" , err )
90
93
}
94
+ return compiler .Compile ("schema.json" )
95
+ }
91
96
97
+ func validateAgainstSchema (schema * jsonschema.Schema , obj map [string ]any ) error {
98
+ if err := schema .Validate (obj ); err != nil {
99
+ if verror , ok := err .(* jsonschema.ValidationError ); ok {
100
+ return buildValidationError (verror .Causes )
101
+ }
102
+ return fmt .Errorf ("invalid json schema: %s" , err )
103
+ }
92
104
return nil
93
105
}
94
106
95
- func buildValidationError (errs []gojsonschema. ResultError ) error {
107
+ func buildValidationError (errs []* jsonschema. ValidationError ) error {
96
108
problems := make ([]string , 0 , len (errs ))
97
109
for _ , desc := range errs {
98
- problems = append (problems , desc .String ())
110
+ problems = append (problems , desc .Error ())
99
111
}
100
-
101
112
return fmt .Errorf ("invalid json schema: %s" , strings .TrimSpace (strings .Join (problems , "\n " )))
102
113
}
114
+
115
+ // applyDefaults recursively applies default values from the schema to the object.
116
+ func applyDefaults (schema * jsonschema.Schema , obj map [string ]any ) {
117
+ for key , def := range schema .Properties {
118
+ // If the key does not exist in obj, apply the default value from the schema if present
119
+ if _ , exists := obj [key ]; ! exists && def .Default != nil {
120
+ obj [key ] = * def .Default
121
+ }
122
+
123
+ // If def has properties, apply defaults to the nested object
124
+ if def .Properties != nil {
125
+ o , ok := obj [key ].(map [string ]any )
126
+ if ! ok {
127
+ // cannot apply defaults to non-object types
128
+ continue
129
+ }
130
+ applyDefaults (def , o )
131
+ }
132
+ }
133
+ }
0 commit comments