From c38648025b27972261df14a72eb2707be4b69a8a Mon Sep 17 00:00:00 2001 From: lucamrgs <39555424+lucamrgs@users.noreply.github.com> Date: Tue, 14 May 2024 12:10:12 +0200 Subject: [PATCH] handle specific check for case agent-target address is url (#119) --- test/unittest/utils/http/http_test.go | 26 ++++++++++++++ utils/http/http.go | 50 ++++++++++++++++++++------- 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/test/unittest/utils/http/http_test.go b/test/unittest/utils/http/http_test.go index 59002843..2de434d7 100644 --- a/test/unittest/utils/http/http_test.go +++ b/test/unittest/utils/http/http_test.go @@ -470,6 +470,32 @@ func TestHttpPathParser(t *testing.T) { assert.Equal(t, parsedUrl, "https://godcapability.tno.nl") } +func TestHttpPathUrlComposition(t *testing.T) { + target := cacao.AgentTarget{ + Address: map[cacao.NetAddressType][]string{ + "url": {"https://godcapability.tno.nl/isp"}, + }, + } + + command := cacao.Command{ + Type: "http-api", + Command: "POST /isp/cst HTTP/1.1", + Headers: map[string][]string{"accept": {"application/json"}}, + } + httpOptions := http.HttpOptions{ + Target: &target, + Command: &command, + } + + parsedUrl, err := httpOptions.ExtractUrl() + if err != nil { + t.Error("failed test because: ", err) + } + // Duplication of path values if present is INTENDED behaviour and + // a warning will be issued + assert.Equal(t, parsedUrl, "https://godcapability.tno.nl/isp/isp/cst") +} + func TestHttpPathBreakingParser(t *testing.T) { target := cacao.AgentTarget{ Address: map[cacao.NetAddressType][]string{ diff --git a/utils/http/http.go b/utils/http/http.go index d2a51a5b..6d9c0337 100644 --- a/utils/http/http.go +++ b/utils/http/http.go @@ -23,6 +23,10 @@ var ( log *logger.Log ) +func init() { + log = logger.Logger(component, logger.Info, "", logger.Json) +} + type HttpOptions struct { Target *cacao.AgentTarget Command *cacao.Command @@ -52,7 +56,6 @@ func (httpRequest *HttpRequest) SkipCertificateValidation(skip bool) { } func (httpRequest *HttpRequest) Request(httpOptions HttpOptions) ([]byte, error) { - log = logger.Logger(component, logger.Info, "", logger.Json) request, err := httpOptions.setupRequest() if err != nil { return []byte{}, err @@ -201,9 +204,17 @@ func (httpOptions *HttpOptions) ExtractUrl() (string, error) { } } + // If for an http-api command the agent-target address is a URL, + // we should warn for misuse of the field when appendind command-specified path if len(target.Address["url"]) > 0 { if target.Address["url"][0] != "" { - return parsePathBasedUrl(target.Address["url"][0]) + urlObject, err := parsePathBasedUrl(target.Address["url"][0]) + if err != nil { + return "", err + } + if (urlObject.Path != "" && urlObject.Path != "/") && urlObject.Path == path { + log.Warn("possible http api invocation path duplication: agent-target url has same path of http-api command path") + } } } return buildSchemeAndHostname(path, target) @@ -213,11 +224,25 @@ func buildSchemeAndHostname(path string, target *cacao.AgentTarget) (string, err var hostname string scheme := setDefaultScheme(target) - hostname, err := extractHostname(scheme, target) + hostname, err := extractHostname(target) if err != nil { return "", err } + // If only URL is used to compose the URL target, then + // scheme and port are not considered + if len(target.Address["url"]) > 0 && + len(target.Address["dname"]) == 0 && + len(target.Address["ipv4"]) == 0 && + len(target.Address["ipv6"]) == 0 { + + url := strings.TrimSuffix(hostname, "/") + if len(path) > 1 && !strings.HasPrefix(path, "/") { + path = "/" + path + } + return strings.TrimSuffix(url+path, "/"), nil + } + parsedUrl := &url.URL{ Scheme: scheme, Host: fmt.Sprintf("%s:%s", hostname, target.Port), @@ -240,7 +265,7 @@ func setDefaultScheme(target *cacao.AgentTarget) string { return scheme } -func extractHostname(scheme string, target *cacao.AgentTarget) (string, error) { +func extractHostname(target *cacao.AgentTarget) (string, error) { var address string if len(target.Address["dname"]) > 0 { @@ -256,28 +281,29 @@ func extractHostname(scheme string, target *cacao.AgentTarget) (string, error) { return "", errors.New("failed regex rule for domain name") } address = target.Address["ipv4"][0] + } else if len(target.Address["url"]) > 0 { - match, _ := regexp.MatchString(ipv4Regex, target.Address["url"][0]) - if !match { - return "", errors.New("failed regex rule for domain name") + _, err := parsePathBasedUrl(target.Address["url"][0]) + if err != nil { + return "", err } address = target.Address["url"][0] + } else { return "", errors.New("unsupported target address type") } return address, nil } -func parsePathBasedUrl(httpUrl string) (string, error) { +func parsePathBasedUrl(httpUrl string) (*url.URL, error) { parsedUrl, err := url.ParseRequestURI(httpUrl) if err != nil { - return "", err + return parsedUrl, err } - if parsedUrl.Hostname() == "" { - return "", errors.New("no domain name") + return parsedUrl, errors.New("no domain name") } - return parsedUrl.String(), nil + return parsedUrl, nil } func validatePort(port string) error {