Skip to content

Commit

Permalink
added redirect ignore list and redirect allow list to configuration o…
Browse files Browse the repository at this point in the history
…ptions
  • Loading branch information
jacobm-splunk authored and daveshanley committed Mar 26, 2024
1 parent e150d40 commit a69fe02
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 46 deletions.
30 changes: 30 additions & 0 deletions cmd/root_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,16 @@ var (
printLoadedPathDelayConfigurations(config.PathDelays)
}

if len(config.IgnoreRedirects) > 0 {
config.CompileIgnoreRedirects()
printLoadedIgnoreRedirectPaths(config.IgnoreRedirects)
}

if len(config.RedirectAllowList) > 0 {
config.CompileRedirectAllowList()
printLoadedRedirectAllowList(config.RedirectAllowList)
}

// static headers
if config.Headers != nil && len(config.Headers.DropHeaders) > 0 {
pterm.Info.Printf("Dropping the following %d %s globally:\n", len(config.Headers.DropHeaders),
Expand Down Expand Up @@ -684,3 +694,23 @@ func printLoadedHarWhitelist(variables []string) {
}
pterm.Println()
}

func printLoadedIgnoreRedirectPaths(ignoreRedirects []string) {
pterm.Info.Printf("Loaded %d %s to ignore for redirects:\n", len(ignoreRedirects),
shared.Pluralize(len(ignoreRedirects), "path", "paths"))

for _, x := range ignoreRedirects {
pterm.Printf("🙈 Paths matching '%s' will be ignored for resolving redirects\n", pterm.LightCyan(x))
}
pterm.Println()
}

func printLoadedRedirectAllowList(allowRedirects []string) {
pterm.Info.Printf("Loaded %d allows listed redirect %s :\n", len(allowRedirects),
shared.Pluralize(len(allowRedirects), "path", "paths"))

for _, x := range allowRedirects {
pterm.Printf("🐵 Paths matching '%s' will always follow redirects, regardless of ignoreRedirect settings\n", pterm.LightCyan(x))
}
pterm.Println()
}
18 changes: 18 additions & 0 deletions config/paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,24 @@ func FindPathDelay(path string, configuration *shared.WiretapConfiguration) int
return foundMatch
}

func IgnoreRedirectOnPath(path string, configuration *shared.WiretapConfiguration) bool {
for _, redirectPath := range configuration.CompiledIgnoreRedirects {
if redirectPath.CompiledPath.Match(path) {
return true
}
}
return false
}

func PathRedirectAllowListed(path string, configuration *shared.WiretapConfiguration) bool {
for _, redirectPath := range configuration.CompiledRedirectAllowList {
if redirectPath.CompiledPath.Match(path) {
return true
}
}
return false
}

func RewritePath(path string, configuration *shared.WiretapConfiguration) string {
paths := FindPaths(path, configuration)
var replaced string = path
Expand Down
94 changes: 94 additions & 0 deletions config/paths_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,97 @@ func TestLocatePathDelay(t *testing.T) {
assert.Equal(t, 0, delay)

}

func TestIgnoreRedirect(t *testing.T) {

config := `ignoreRedirects:
- /pb33f/test/**
- /pb33f/cakes/123
- /*/test/123`

var c shared.WiretapConfiguration
_ = yaml.Unmarshal([]byte(config), &c)

c.CompileIgnoreRedirects()

ignore := IgnoreRedirectOnPath("/pb33f/test/burgers/fries?1234=no", &c)
assert.True(t, ignore)

ignore = IgnoreRedirectOnPath("/pb33f/cakes/123", &c)
assert.True(t, ignore)

ignore = IgnoreRedirectOnPath("/roastbeef/test/123", &c)
assert.True(t, ignore)

ignore = IgnoreRedirectOnPath("/not-registered", &c)
assert.False(t, ignore)

}

func TestIgnoreRedirect_NoPathsRegistered(t *testing.T) {

var c shared.WiretapConfiguration
_ = yaml.Unmarshal([]byte(""), &c)

c.CompileIgnoreRedirects()

ignore := IgnoreRedirectOnPath("/pb33f/test/burgers/fries?1234=no", &c)
assert.False(t, ignore)

ignore = IgnoreRedirectOnPath("/pb33f/cakes/123", &c)
assert.False(t, ignore)

ignore = IgnoreRedirectOnPath("/roastbeef/test/123", &c)
assert.False(t, ignore)

ignore = IgnoreRedirectOnPath("/not-registered", &c)
assert.False(t, ignore)

}

func TestRedirectAllowList(t *testing.T) {

config := `ignoreRedirects:
- /pb33f/test/**
- /pb33f/cakes/123
- /*/test/123`

var c shared.WiretapConfiguration
_ = yaml.Unmarshal([]byte(config), &c)

c.CompileIgnoreRedirects()

ignore := IgnoreRedirectOnPath("/pb33f/test/burgers/fries?1234=no", &c)
assert.True(t, ignore)

ignore = IgnoreRedirectOnPath("/pb33f/cakes/123", &c)
assert.True(t, ignore)

ignore = IgnoreRedirectOnPath("/roastbeef/test/123", &c)
assert.True(t, ignore)

ignore = IgnoreRedirectOnPath("/not-registered", &c)
assert.False(t, ignore)

}

func TestRedirectAllowList_NoPathsRegistered(t *testing.T) {

var c shared.WiretapConfiguration
_ = yaml.Unmarshal([]byte(""), &c)

c.CompileIgnoreRedirects()

ignore := IgnoreRedirectOnPath("/pb33f/test/burgers/fries?1234=no", &c)
assert.False(t, ignore)

ignore = IgnoreRedirectOnPath("/pb33f/cakes/123", &c)
assert.False(t, ignore)

ignore = IgnoreRedirectOnPath("/roastbeef/test/123", &c)
assert.False(t, ignore)

ignore = IgnoreRedirectOnPath("/not-registered", &c)
assert.False(t, ignore)

}
23 changes: 17 additions & 6 deletions daemon/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ package daemon

import (
"crypto/tls"
"net/http"
"net/url"

"github.com/pb33f/wiretap/config"
"github.com/pterm/pterm"
"net/http"
"net/url"

"github.com/pb33f/wiretap/shared"
)
Expand Down Expand Up @@ -40,9 +39,6 @@ func (c *wiretapTransport) RoundTrip(r *http.Request) (*http.Response, error) {

func (ws *WiretapService) callAPI(req *http.Request) (*http.Response, error) {

tr := newWiretapTransport()
client := &http.Client{Transport: tr}

configStore, _ := ws.controlsStore.Get(shared.ConfigKey)

// create a new request from the original request, but replace the path
Expand All @@ -59,6 +55,21 @@ func (ws *WiretapService) callAPI(req *http.Request) (*http.Response, error) {
req.URL = newUrl
}

tr := newWiretapTransport()
var client *http.Client

// create a client based on if wiretap should redirect on the path or not
if config.IgnoreRedirectOnPath(req.URL.Path, wiretapConfig) && !config.PathRedirectAllowListed(req.URL.Path, wiretapConfig) {
client = &http.Client{
Transport: tr,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
} else {
client = &http.Client{Transport: tr}
}

// re-write referer
if req.Header.Get("Referer") != "" {
// retain original referer for logging
Expand Down
108 changes: 68 additions & 40 deletions shared/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,46 +13,50 @@ import (
)

type WiretapConfiguration struct {
Contract string `json:"-" yaml:"-"`
RedirectHost string `json:"redirectHost,omitempty" yaml:"redirectHost,omitempty"`
RedirectPort string `json:"redirectPort,omitempty" yaml:"redirectPort,omitempty"`
RedirectBasePath string `json:"redirectBasePath,omitempty" yaml:"redirectBasePath,omitempty"`
RedirectProtocol string `json:"redirectProtocol,omitempty" yaml:"redirectProtocol,omitempty"`
RedirectURL string `json:"redirectURL,omitempty" yaml:"redirectURL,omitempty"`
Port string `json:"port,omitempty" yaml:"port,omitempty"`
MonitorPort string `json:"monitorPort,omitempty" yaml:"monitorPort,omitempty"`
WebSocketHost string `json:"webSocketHost,omitempty" yaml:"webSocketHost,omitempty"`
WebSocketPort string `json:"webSocketPort,omitempty" yaml:"webSocketPort,omitempty"`
GlobalAPIDelay int `json:"globalAPIDelay,omitempty" yaml:"globalAPIDelay,omitempty"`
StaticDir string `json:"staticDir,omitempty" yaml:"staticDir,omitempty"`
StaticIndex string `json:"staticIndex,omitempty" yaml:"staticIndex,omitempty"`
PathConfigurations map[string]*WiretapPathConfig `json:"paths,omitempty" yaml:"paths,omitempty"`
Headers *WiretapHeaderConfig `json:"headers,omitempty" yaml:"headers,omitempty"`
StaticPaths []string `json:"staticPaths,omitempty" yaml:"staticPaths,omitempty"`
Variables map[string]string `json:"variables,omitempty" yaml:"variables,omitempty"`
Spec string `json:"contract,omitempty" yaml:"contract,omitempty"`
Certificate string `json:"certificate,omitempty" yaml:"certificate,omitempty"`
CertificateKey string `json:"certificateKey,omitempty" yaml:"certificateKey,omitempty"`
HardErrors bool `json:"hardValidation,omitempty" yaml:"hardValidation,omitempty"`
HardErrorCode int `json:"hardValidationCode,omitempty" yaml:"hardValidationCode,omitempty"`
HardErrorReturnCode int `json:"hardValidationReturnCode,omitempty" yaml:"hardValidationReturnCode,omitempty"`
PathDelays map[string]int `json:"pathDelays,omitempty" yaml:"pathDelays,omitempty"`
MockMode bool `json:"mockMode,omitempty" yaml:"mockMode,omitempty"`
MockModePretty bool `json:"mockModePretty,omitempty" yaml:"mockModePretty,omitempty"`
Base string `json:"base,omitempty" yaml:"base,omitempty"`
HAR string `json:"har,omitempty" yaml:"har,omitempty"`
HARValidate bool `json:"harValidate,omitempty" yaml:"harValidate,omitempty"`
HARPathAllowList []string `json:"harPathAllowList,omitempty" yaml:"harPathAllowList,omitempty"`
StreamReport bool `json:"streamReport,omitempty" yaml:"streamReport,omitempty"`
ReportFile string `json:"reportFilename,omitempty" yaml:"reportFilename,omitempty"`
HARFile *harhar.HAR `json:"-" yaml:"-"`
CompiledPathDelays map[string]*CompiledPathDelay `json:"-" yaml:"-"`
CompiledVariables map[string]*CompiledVariable `json:"-" yaml:"-"`
Version string `json:"-" yaml:"-"`
StaticPathsCompiled []glob.Glob `json:"-" yaml:"-"`
CompiledPaths map[string]*CompiledPath `json:"-"`
FS embed.FS `json:"-"`
Logger *slog.Logger
Contract string `json:"-" yaml:"-"`
RedirectHost string `json:"redirectHost,omitempty" yaml:"redirectHost,omitempty"`
RedirectPort string `json:"redirectPort,omitempty" yaml:"redirectPort,omitempty"`
RedirectBasePath string `json:"redirectBasePath,omitempty" yaml:"redirectBasePath,omitempty"`
RedirectProtocol string `json:"redirectProtocol,omitempty" yaml:"redirectProtocol,omitempty"`
RedirectURL string `json:"redirectURL,omitempty" yaml:"redirectURL,omitempty"`
Port string `json:"port,omitempty" yaml:"port,omitempty"`
MonitorPort string `json:"monitorPort,omitempty" yaml:"monitorPort,omitempty"`
WebSocketHost string `json:"webSocketHost,omitempty" yaml:"webSocketHost,omitempty"`
WebSocketPort string `json:"webSocketPort,omitempty" yaml:"webSocketPort,omitempty"`
GlobalAPIDelay int `json:"globalAPIDelay,omitempty" yaml:"globalAPIDelay,omitempty"`
StaticDir string `json:"staticDir,omitempty" yaml:"staticDir,omitempty"`
StaticIndex string `json:"staticIndex,omitempty" yaml:"staticIndex,omitempty"`
PathConfigurations map[string]*WiretapPathConfig `json:"paths,omitempty" yaml:"paths,omitempty"`
Headers *WiretapHeaderConfig `json:"headers,omitempty" yaml:"headers,omitempty"`
StaticPaths []string `json:"staticPaths,omitempty" yaml:"staticPaths,omitempty"`
Variables map[string]string `json:"variables,omitempty" yaml:"variables,omitempty"`
Spec string `json:"contract,omitempty" yaml:"contract,omitempty"`
Certificate string `json:"certificate,omitempty" yaml:"certificate,omitempty"`
CertificateKey string `json:"certificateKey,omitempty" yaml:"certificateKey,omitempty"`
HardErrors bool `json:"hardValidation,omitempty" yaml:"hardValidation,omitempty"`
HardErrorCode int `json:"hardValidationCode,omitempty" yaml:"hardValidationCode,omitempty"`
HardErrorReturnCode int `json:"hardValidationReturnCode,omitempty" yaml:"hardValidationReturnCode,omitempty"`
PathDelays map[string]int `json:"pathDelays,omitempty" yaml:"pathDelays,omitempty"`
MockMode bool `json:"mockMode,omitempty" yaml:"mockMode,omitempty"`
MockModePretty bool `json:"mockModePretty,omitempty" yaml:"mockModePretty,omitempty"`
Base string `json:"base,omitempty" yaml:"base,omitempty"`
HAR string `json:"har,omitempty" yaml:"har,omitempty"`
HARValidate bool `json:"harValidate,omitempty" yaml:"harValidate,omitempty"`
HARPathAllowList []string `json:"harPathAllowList,omitempty" yaml:"harPathAllowList,omitempty"`
StreamReport bool `json:"streamReport,omitempty" yaml:"streamReport,omitempty"`
ReportFile string `json:"reportFilename,omitempty" yaml:"reportFilename,omitempty"`
IgnoreRedirects []string `json:"ignoreRedirects,omitempty" yaml:"ignoreRedirects,omitempty"`
RedirectAllowList []string `json:"redirectAllowList,omitempty" yaml:"redirectAllowList,omitempty"`
HARFile *harhar.HAR `json:"-" yaml:"-"`
CompiledPathDelays map[string]*CompiledPathDelay `json:"-" yaml:"-"`
CompiledVariables map[string]*CompiledVariable `json:"-" yaml:"-"`
Version string `json:"-" yaml:"-"`
StaticPathsCompiled []glob.Glob `json:"-" yaml:"-"`
CompiledPaths map[string]*CompiledPath `json:"-"`
CompiledIgnoreRedirects []*CompiledRedirect `json:"-" yaml:"-"`
CompiledRedirectAllowList []*CompiledRedirect `json:"-" yaml:"-"`
FS embed.FS `json:"-"`
Logger *slog.Logger
}

func (wtc *WiretapConfiguration) CompilePaths() {
Expand Down Expand Up @@ -91,6 +95,26 @@ func (wtc *WiretapConfiguration) CompileVariables() {
}
}

func (wtc *WiretapConfiguration) CompileIgnoreRedirects() {
wtc.CompiledIgnoreRedirects = make([]*CompiledRedirect, 0)
for _, x := range wtc.IgnoreRedirects {
compiled := &CompiledRedirect{
CompiledPath: glob.MustCompile(wtc.ReplaceWithVariables(x)),
}
wtc.CompiledIgnoreRedirects = append(wtc.CompiledIgnoreRedirects, compiled)
}
}

func (wtc *WiretapConfiguration) CompileRedirectAllowList() {
wtc.CompiledRedirectAllowList = make([]*CompiledRedirect, 0)
for _, x := range wtc.RedirectAllowList {
compiled := &CompiledRedirect{
CompiledPath: glob.MustCompile(wtc.ReplaceWithVariables(x)),
}
wtc.CompiledRedirectAllowList = append(wtc.CompiledRedirectAllowList, compiled)
}
}

func (wtc *WiretapConfiguration) ReplaceWithVariables(input string) string {
for x := range wtc.Variables {
if wtc.Variables[x] != "" && wtc.CompiledVariables[x] != nil {
Expand Down Expand Up @@ -135,6 +159,10 @@ type CompiledPathRewrite struct {
CompiledTarget glob.Glob
}

type CompiledRedirect struct {
CompiledPath glob.Glob
}

type WiretapHeaderConfig struct {
DropHeaders []string `json:"drop,omitempty" yaml:"drop,omitempty"`
InjectHeaders map[string]string `json:"inject,omitempty" yaml:"inject,omitempty"`
Expand Down

0 comments on commit a69fe02

Please sign in to comment.