-
Notifications
You must be signed in to change notification settings - Fork 16
Add body injection support for Vanta token revocation #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
d36f437
03fef17
eb9b2fb
d0aa630
ceaca72
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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" | ||
| ) | ||
|
|
||
|
|
@@ -37,6 +38,16 @@ 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 ( | ||
| // MaxBodySizeForInjection limits body size for placeholder replacement | ||
| // to prevent memory exhaustion (default: 10MB) | ||
| MaxBodySizeForInjection = 10 * 1024 * 1024 | ||
| ) | ||
|
|
||
| const ( | ||
|
|
@@ -54,7 +65,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"` | ||
| } | ||
|
|
@@ -65,8 +78,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: | ||
|
|
@@ -88,10 +105,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 | ||
|
|
@@ -198,6 +223,58 @@ 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)) | ||
|
|
||
| // Buffer the replaced content to calculate Content-Length | ||
| var buf bytes.Buffer | ||
|
||
| n, err := io.Copy(&buf, chain) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to replace placeholder in request body: %w", err) | ||
| } | ||
| r.Body.Close() | ||
|
|
||
| // Set the new body with updated Content-Length | ||
| r.Body = io.NopCloser(&buf) | ||
| r.ContentLength = n | ||
|
|
||
| return nil | ||
| }, nil | ||
| } | ||
|
|
||
| func (c *InjectBodyProcessorConfig) StripHazmat() ProcessorConfig { | ||
| return &InjectBodyProcessorConfig{ | ||
| Token: redactedStr, | ||
| Placeholder: c.Placeholder, | ||
| } | ||
| } | ||
|
|
||
| type OAuthProcessorConfig struct { | ||
| Token *OAuthToken `json:"token"` | ||
| } | ||
|
|
@@ -219,6 +296,34 @@ 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)) | ||
|
|
||
| // Buffer the replaced content to calculate Content-Length | ||
| var buf bytes.Buffer | ||
| n, err := io.Copy(&buf, chain) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to replace placeholder in request body: %w", err) | ||
| } | ||
| r.Body.Close() | ||
|
|
||
| // Set the new body with updated Content-Length | ||
| r.Body = io.NopCloser(&buf) | ||
| r.ContentLength = n | ||
|
|
||
| return nil | ||
| }, nil | ||
| } | ||
|
|
||
| // Default behavior: inject into Authorization header | ||
| return func(r *http.Request) error { | ||
| r.Header.Set("Authorization", "Bearer "+token) | ||
| return nil | ||
|
|
@@ -234,6 +339,66 @@ 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)) | ||
|
|
||
| // Buffer the replaced content to calculate Content-Length | ||
| var buf bytes.Buffer | ||
| n, err := io.Copy(&buf, chain) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to replace placeholder in request body: %w", err) | ||
| } | ||
| r.Body.Close() | ||
|
|
||
| // Set the new body with updated Content-Length | ||
| r.Body = io.NopCloser(&buf) | ||
| r.ContentLength = n | ||
|
|
||
| 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"` | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.