Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ require (
github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/icholy/replace v0.6.0 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.31.0 // indirect
)
16 changes: 16 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,22 @@ github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027 h1:1L0aalTpPz7YlMx
github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/icholy/replace v0.6.0 h1:EBiD2pGqZIOJAbEaf/5GVRaD/Pmbb4n+K3LrBdXd4dw=
github.com/icholy/replace v0.6.0/go.mod h1:zzi8pxElj2t/5wHHHYmH45D+KxytX/t4w3ClY5nlK+g=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
Expand All @@ -37,13 +42,24 @@ github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
138 changes: 138 additions & 0 deletions processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"github.com/aws/aws-sdk-go-v2/aws"
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
"github.com/icholy/replace"
"golang.org/x/exp/slices"
)

Expand All @@ -37,6 +38,10 @@ const (
// ParamSubtoken optionally specifies which subtoken should be used.
// Default is SubtokenAccessToken.
ParamSubtoken = "st"

// ParamPlaceholder specifies the placeholder pattern to replace in the
// request body with the token value. Used by InjectBodyProcessor.
ParamPlaceholder = "placeholder"
)

const (
Expand All @@ -54,7 +59,9 @@ type ProcessorConfig interface {
type wireProcessor struct {
InjectProcessorConfig *InjectProcessorConfig `json:"inject_processor,omitempty"`
InjectHMACProcessorConfig *InjectHMACProcessorConfig `json:"inject_hmac_processor,omitempty"`
InjectBodyProcessorConfig *InjectBodyProcessorConfig `json:"inject_body_processor,omitempty"`
OAuthProcessorConfig *OAuthProcessorConfig `json:"oauth2_processor,omitempty"`
OAuthBodyProcessorConfig *OAuthBodyProcessorConfig `json:"oauth2_body_processor,omitempty"`
Sigv4ProcessorConfig *Sigv4ProcessorConfig `json:"sigv4_processor,omitempty"`
MultiProcessorConfig *MultiProcessorConfig `json:"multi_processor,omitempty"`
}
Expand All @@ -65,8 +72,12 @@ func newWireProcessor(p ProcessorConfig) (wireProcessor, error) {
return wireProcessor{InjectProcessorConfig: p}, nil
case *InjectHMACProcessorConfig:
return wireProcessor{InjectHMACProcessorConfig: p}, nil
case *InjectBodyProcessorConfig:
return wireProcessor{InjectBodyProcessorConfig: p}, nil
case *OAuthProcessorConfig:
return wireProcessor{OAuthProcessorConfig: p}, nil
case *OAuthBodyProcessorConfig:
return wireProcessor{OAuthBodyProcessorConfig: p}, nil
case *Sigv4ProcessorConfig:
return wireProcessor{Sigv4ProcessorConfig: p}, nil
case *MultiProcessorConfig:
Expand All @@ -88,10 +99,18 @@ func (wp *wireProcessor) getProcessorConfig() (ProcessorConfig, error) {
np += 1
p = wp.InjectHMACProcessorConfig
}
if wp.InjectBodyProcessorConfig != nil {
np += 1
p = wp.InjectBodyProcessorConfig
}
if wp.OAuthProcessorConfig != nil {
np += 1
p = wp.OAuthProcessorConfig
}
if wp.OAuthBodyProcessorConfig != nil {
np += 1
p = wp.OAuthBodyProcessorConfig
}
if wp.Sigv4ProcessorConfig != nil {
np += 1
p = wp.Sigv4ProcessorConfig
Expand Down Expand Up @@ -198,6 +217,51 @@ func (c *InjectHMACProcessorConfig) StripHazmat() ProcessorConfig {
}
}

type InjectBodyProcessorConfig struct {
Token string `json:"token"`
Placeholder string `json:"placeholder,omitempty"`
}

var _ ProcessorConfig = new(InjectBodyProcessorConfig)

func (c *InjectBodyProcessorConfig) Processor(params map[string]string) (RequestProcessor, error) {
if c.Token == "" {
return nil, errors.New("missing token")
}

// Get placeholder from params or use config default or fallback
placeholder := c.Placeholder
if paramPlaceholder, ok := params[ParamPlaceholder]; ok {
placeholder = paramPlaceholder
}
if placeholder == "" {
placeholder = "{{ACCESS_TOKEN}}"
}

return func(r *http.Request) error {
if r.Body == nil {
return nil
}

// Use streaming replacement to avoid loading entire body into memory
chain := replace.Chain(r.Body, replace.String(placeholder, c.Token))

// Set body to the replacement chain for true streaming with chunked encoding
// Setting ContentLength to 0 triggers chunked transfer encoding
r.Body = io.NopCloser(chain)
r.ContentLength = 0

return nil
}, nil
}

func (c *InjectBodyProcessorConfig) StripHazmat() ProcessorConfig {
return &InjectBodyProcessorConfig{
Token: redactedStr,
Placeholder: c.Placeholder,
}
}

type OAuthProcessorConfig struct {
Token *OAuthToken `json:"token"`
}
Expand All @@ -219,6 +283,27 @@ func (c *OAuthProcessorConfig) Processor(params map[string]string) (RequestProce
return nil, errors.New("missing token")
}

// Check if placeholder parameter is present for body injection
if placeholder, ok := params[ParamPlaceholder]; ok && placeholder != "" {
// Inject token into request body by replacing placeholder
return func(r *http.Request) error {
if r.Body == nil {
return nil
}

// Use streaming replacement to avoid loading entire body into memory
chain := replace.Chain(r.Body, replace.String(placeholder, token))

// Set body to the replacement chain for true streaming with chunked encoding
// Setting ContentLength to 0 triggers chunked transfer encoding
r.Body = io.NopCloser(chain)
r.ContentLength = 0

return nil
}, nil
}

// Default behavior: inject into Authorization header
return func(r *http.Request) error {
r.Header.Set("Authorization", "Bearer "+token)
return nil
Expand All @@ -234,6 +319,59 @@ func (c *OAuthProcessorConfig) StripHazmat() ProcessorConfig {
}
}

type OAuthBodyProcessorConfig struct {
Token *OAuthToken `json:"token"`
Placeholder string `json:"placeholder,omitempty"`
}

var _ ProcessorConfig = (*OAuthBodyProcessorConfig)(nil)

func (c *OAuthBodyProcessorConfig) Processor(params map[string]string) (RequestProcessor, error) {
token := c.Token.AccessToken
if params[ParamSubtoken] == SubtokenRefresh {
token = c.Token.RefreshToken
}

if token == "" {
return nil, errors.New("missing token")
}

// Get placeholder from params or use config default or fallback
placeholder := c.Placeholder
if paramPlaceholder, ok := params[ParamPlaceholder]; ok {
placeholder = paramPlaceholder
}
if placeholder == "" {
placeholder = "{{ACCESS_TOKEN}}"
}

return func(r *http.Request) error {
if r.Body == nil {
return nil
}

// Use streaming replacement to avoid loading entire body into memory
chain := replace.Chain(r.Body, replace.String(placeholder, token))

// Set body to the replacement chain for true streaming with chunked encoding
// Setting ContentLength to 0 triggers chunked transfer encoding
r.Body = io.NopCloser(chain)
r.ContentLength = 0

return nil
}, nil
}

func (c *OAuthBodyProcessorConfig) StripHazmat() ProcessorConfig {
return &OAuthBodyProcessorConfig{
Token: &OAuthToken{
AccessToken: redactedStr,
RefreshToken: redactedStr,
},
Placeholder: c.Placeholder,
}
}

type Sigv4ProcessorConfig struct {
AccessKey string `json:"access_key"`
SecretKey string `json:"secret_key"`
Expand Down
Loading