Skip to content

Commit

Permalink
Object keys uniqueness check (#461)
Browse files Browse the repository at this point in the history
* fix: only handle keys in certain attribute object values case-insensitively

* test for uniqueness check

* changelog entry

* Update CHANGELOG.md

Co-authored-by: Joe Afflerbach <joe.afflerbach@avenga.com>

Co-authored-by: Joe Afflerbach <joe.afflerbach@avenga.com>
  • Loading branch information
johakoch and afflerbach authored Apr 4, 2022
1 parent 72609bc commit d97e210
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Unreleased changes are available as `avenga/couper:edge` container.
* Couper [reads and merges configuration files](./docs/CLI.md#global-options) from a given directory ([#437](https://github.com/avenga/couper/pull/437))
* provided via `-d` command-line flag or `COUPER_FILE_DIRECTORY` environment variable

* **Fixed**
* Keys in object type attribute values are only handled case-insensitively if reasonable (e.g. they represent HTTP methods or header field values) ([#461](https://github.com/avenga/couper/pull/461))

---

## [1.8.0](https://github.com/avenga/couper/releases/tag/v1.8.0)
Expand Down
9 changes: 7 additions & 2 deletions config/configload/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,18 @@ func completeSchemaComponents(body hcl.Body, schema *hcl.BodySchema, attrs hcl.A
continue
}

keyName := strings.ToLower(seetie.ValueToString(k))
keyName := seetie.ValueToString(k)
switch name {
case "add_request_headers", "add_response_headers", "beta_scope", "headers", "set_request_headers", "set_response_headers":
// header field names, method names: handle object keys case-insensitively
keyName = strings.ToLower(keyName)
}
if _, ok := unique[keyName]; ok {
errors = errors.Append(&hcl.Diagnostic{
Subject: &expr.SrcRange,
Severity: hcl.DiagError,
Summary: fmt.Sprintf("key in an attribute must be unique: '%s'", keyName),
Detail: "Key must be unique for " + string(keyName) + ".",
Detail: "Key must be unique for " + keyName + ".",
})
}

Expand Down
219 changes: 219 additions & 0 deletions config/configload/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,222 @@ func TestLabels(t *testing.T) {
})
}
}

func TestAttributeObjectKeys(t *testing.T) {
tests := []struct {
name string
hcl string
error string
}{
{
"add_request_headers",
`server {
api {
endpoint "/a" {
add_request_headers = {
a = "a"
A = "A"
}
}
}
}`,
"couper.hcl:4,29-7,8: key in an attribute must be unique: 'a'; Key must be unique for a.",
},
{
"add_response_headers",
`server {
api {
endpoint "/a" {
add_response_headers = {
a = "a"
A = "A"
}
}
}
}`,
"couper.hcl:4,30-7,8: key in an attribute must be unique: 'a'; Key must be unique for a.",
},
{
"beta_scope",
`server {
api {
endpoint "/a" {
beta_scope = {
get = "a"
GeT = "A"
}
}
}
}`,
"couper.hcl:4,20-7,8: key in an attribute must be unique: 'get'; Key must be unique for get.",
},
{
"headers",
`server {
api {
endpoint "/a" {
request {
headers = {
a = "a"
A = "A"
}
}
}
}
}`,
"couper.hcl:5,19-8,10: key in an attribute must be unique: 'a'; Key must be unique for a.",
},
{
"set_request_headers",
`server {
api {
endpoint "/a" {
set_request_headers = {
a = "a"
A = "A"
}
}
}
}`,
"couper.hcl:4,29-7,8: key in an attribute must be unique: 'a'; Key must be unique for a.",
},
{
"set_response_headers",
`server {
api {
endpoint "/a" {
set_response_headers = {
a = "a"
A = "A"
}
}
}
}`,
"couper.hcl:4,30-7,8: key in an attribute must be unique: 'a'; Key must be unique for a.",
},
{
"json_body",
`server {
api {
endpoint "/a" {
request {
json_body = {
a = "a"
A = "A"
}
}
}
}
}`,
"",
},
{
"form_body",
`server {
api {
endpoint "/a" {
request {
form_body = {
a = "a"
A = "A"
}
}
}
}
}`,
"",
},
{
"beta_roles_map",
`server {}
definitions {
jwt "a" {
signature_algorithm = "HS256"
key = "asdf"
beta_roles_map = {
a = []
A = []
}
}
}`,
"",
},
{
"beta_scope_map",
`server {}
definitions {
jwt "a" {
signature_algorithm = "HS256"
key = "asdf"
beta_scope_map = {
a = []
A = []
}
}
}`,
"",
},
{
"claims",
`server {}
definitions {
jwt "a" {
signature_algorithm = "HS256"
key = "asdf"
claims = {
a = "a"
A = "A"
}
}
}`,
"",
},
{
"custom_log_fields",
`server {}
definitions {
jwt "a" {
signature_algorithm = "HS256"
key = "asdf"
custom_log_fields = {
a = "a"
A = "A"
}
}
}`,
"",
},
{
"environment_variables",
`server {}
defaults {
environment_variables = {
a = "a"
A = "A"
}
}`,
"",
},
}

logger, _ := logrustest.NewNullLogger()
log := logger.WithContext(context.TODO())

for _, tt := range tests {
t.Run(tt.name, func(subT *testing.T) {
conf, err := LoadBytes([]byte(tt.hcl), "couper.hcl")
if conf != nil {
_, err = runtime.NewServerConfiguration(conf, log, nil)
}

var errMsg string
if err != nil {
errMsg = err.Error()
}

if tt.error != errMsg {
subT.Errorf("%q: Unexpected configuration error:\n\tWant: %q\n\tGot: %q", tt.name, tt.error, errMsg)
}
})
}
}

0 comments on commit d97e210

Please sign in to comment.