diff --git a/aspects/schema.go b/aspects/schema.go index a35173abba0..7a230ded8da 100644 --- a/aspects/schema.go +++ b/aspects/schema.go @@ -246,6 +246,10 @@ func (v *mapSchema) Validate(raw []byte) error { return validationErrorf(`cannot accept null value for "map" type`) } + if err := validMapKeys(mapValue); err != nil { + return validationErrorFrom(err) + } + if v.entrySchemas != nil { for key := range mapValue { if _, ok := v.entrySchemas[key]; !ok { @@ -323,6 +327,16 @@ func (v *mapSchema) Validate(raw []byte) error { return nil } +func validMapKeys(v map[string]json.RawMessage) error { + for k := range v { + if !validSubkey.Match([]byte(k)) { + return fmt.Errorf(`key %q doesn't conform to required format`, k) + } + } + + return nil +} + func (v *mapSchema) parseConstraints(constraints map[string]json.RawMessage) error { err := checkExclusiveMapConstraints(constraints) if err != nil { @@ -336,6 +350,10 @@ func (v *mapSchema) parseConstraints(constraints map[string]json.RawMessage) err return fmt.Errorf(`cannot parse map's "schema" constraint: %v`, err) } + if err := validMapKeys(entries); err != nil { + return fmt.Errorf(`cannot parse map: %w`, err) + } + v.entrySchemas = make(map[string]Schema, len(entries)) for key, value := range entries { entrySchema, err := v.topSchema.parse(value) diff --git a/aspects/schema_test.go b/aspects/schema_test.go index c9382a9d1f9..f0a646dba60 100644 --- a/aspects/schema_test.go +++ b/aspects/schema_test.go @@ -441,6 +441,34 @@ func (*schemaSuite) TestMapSchemaRequiredNotInSchema(c *C) { c.Assert(err, ErrorMatches, `cannot parse map's "required" constraint: required key "baz" must have schema entry`) } +func (*schemaSuite) TestMapSchemaWithInvalidKeyFormat(c *C) { + schemaStr := []byte(`{ + "schema": { + "-foo": "string" + } +}`) + + _, err := aspects.ParseSchema(schemaStr) + c.Assert(err, ErrorMatches, `cannot parse map: key "-foo" doesn't conform to required format`) +} + +func (*schemaSuite) TestMapRejectsInputMapWithInvalidKeyFormat(c *C) { + schemaStr := []byte(`{ + "schema": { + "foo": "int" + } +}`) + + schema, err := aspects.ParseSchema(schemaStr) + c.Assert(err, IsNil) + + input := []byte(`{ + "-foo": 1 +}`) + err = schema.Validate(input) + c.Assert(err, ErrorMatches, `cannot accept top level element: key "-foo" doesn't conform to required format`) +} + func (*schemaSuite) TestMapInvalidConstraintCombos(c *C) { type testcase struct { name string