coupergateway / couper

Couper is a lightweight API gateway designed to support developers in building and operating API-driven Web projects
https://couper.io
MIT License
84 stars 15 forks source link

Validate `Inline` struct attributes #830

Open johakoch opened 2 months ago

johakoch commented 2 months ago

"Regular" config struct attributes are validated at config load. Several Inline attributes, however, are not validated at all.

E.g. proxy:

func (p Proxy) Inline() interface{} {
    type Inline struct {
        meta.RequestHeadersAttributes
        meta.ResponseHeadersAttributes
        meta.FormParamsAttributes
        meta.QueryParamsAttributes
        Backend        *Backend    `hcl:"backend,block" ...`
        ExpectedStatus []int       `hcl:"expected_status,optional" ...`
        URL            string      `hcl:"url,optional" ...`
        Websockets     *Websockets `hcl:"websockets,block" ...`
    }

    return &Inline{}
}

proxy {
  url = 42
}

URL is validated at runtime by producer.NewURLFromAttribute(). OK


proxy {
  expected_status = 200
}

ExpectedStatus with an invalid type (e.g. int instead of []int) is quietly ignored: handler/proxy.go

    expStatusVal, err := eval.ValueFromBodyAttribute(hclCtx, p.context, "expected_status")
    if err != nil {
        return nil, err
    }

    outCtx = context.WithValue(outCtx, request.EndpointExpectedStatus, seetie.ValueToIntSlice(expStatusVal))

with

func ValueToIntSlice(src cty.Value) []int64 {
    var n []int64
    if !src.IsKnown() || src.IsNull() || !src.CanIterateElements() {
        return n
    }

returning an empty slice, then ignored in handler/producer/result.go because len() of the empty slice is not > 0

    if expStatus, ok := req.Context().Value(request.EndpointExpectedStatus).([]int64); beresp != nil &&
        ok && len(expStatus) > 0 {
...

proxy {
  set_request_headers = 42
}

even panics.


As it seems, the Inline is only used to create another BodySchema to find unsupported attributes/blocks in (config/configload/schema.go)

func completeSchemaComponents(body hcl.Body, schema *hcl.BodySchema,
    blocks hcl.Blocks, errors hcl.Diagnostics) (hcl.Blocks, hcl.Diagnostics) {

    content, diags := body.Content(schema)

How can we use the type information from the Inline structs returned by .Inline() to validate attribute values?