refactor wip
This commit is contained in:
2
go.mod
2
go.mod
@@ -24,7 +24,7 @@ require (
|
|||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.50.0 // indirect
|
github.com/valyala/fasthttp v1.50.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||||
golang.org/x/net v0.18.0 // indirect
|
golang.org/x/net v0.18.0
|
||||||
golang.org/x/sys v0.14.0 // indirect
|
golang.org/x/sys v0.14.0 // indirect
|
||||||
golang.org/x/term v0.14.0
|
golang.org/x/term v0.14.0
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import (
|
|||||||
|
|
||||||
"ladder/pkg/ruleset"
|
"ladder/pkg/ruleset"
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
"ladder/proxychain/rqm"
|
rx "ladder/proxychain/requestmodifers"
|
||||||
"ladder/proxychain/rsm"
|
tx "ladder/proxychain/responsemodifers"
|
||||||
|
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
@@ -49,19 +49,20 @@ func NewProxySiteHandler(opts *ProxyOptions) fiber.Handler {
|
|||||||
rs = r
|
rs = r
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
proxychain := proxychain.
|
||||||
|
NewProxyChain().
|
||||||
|
SetDebugLogging(opts.Verbose).
|
||||||
|
SetRequestModifications(
|
||||||
|
rx.DeleteOutgoingCookies(),
|
||||||
|
).
|
||||||
|
AddResponseModifications(
|
||||||
|
tx.DeleteIncomingCookies(),
|
||||||
|
)
|
||||||
|
|
||||||
return func(c *fiber.Ctx) error {
|
return func(c *fiber.Ctx) error {
|
||||||
return proxychain.NewProxyChain().
|
return proxychain.SetFiberCtx(c).Execute()
|
||||||
SetCtx(c).
|
|
||||||
//AddRuleset(&rs).
|
|
||||||
SetRequestModifications(
|
|
||||||
rqm.BlockOutgoingCookies(),
|
|
||||||
).
|
|
||||||
SetResultModifications(
|
|
||||||
rsm.BlockIncomingCookies(),
|
|
||||||
).
|
|
||||||
Execute()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func modifyURL(uri string, rule ruleset.Rule) (string, error) {
|
func modifyURL(uri string, rule ruleset.Rule) (string, error) {
|
||||||
|
|||||||
0
proxychain/cache/memcache.go
vendored
Normal file
0
proxychain/cache/memcache.go
vendored
Normal file
@@ -14,14 +14,6 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultClient *http.Client
|
|
||||||
|
|
||||||
func DefaultClient() {
|
|
||||||
defaultClient = &http.Client{
|
|
||||||
Timeout: 15,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ProxyChain manages the process of forwarding an HTTP request to an upstream server,
|
ProxyChain manages the process of forwarding an HTTP request to an upstream server,
|
||||||
applying request and response modifications along the way.
|
applying request and response modifications along the way.
|
||||||
@@ -40,21 +32,22 @@ applying request and response modifications along the way.
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
||||||
"ladder/internal/proxychain/rqm"
|
rx "ladder/pkg/proxychain/requestmodifers"
|
||||||
"ladder/internal/proxychain/rsm"
|
tx "ladder/pkg/proxychain/responsemodifers"
|
||||||
"ladder/internal/proxychain"
|
"ladder/internal/proxychain"
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
proxychain.NewProxyChain().
|
proxychain.NewProxyChain().
|
||||||
|
|
||||||
SetCtx(c).
|
SetFiberCtx(c).
|
||||||
AddRuleset(&rs).
|
|
||||||
SetRequestModifications(
|
SetRequestModifications(
|
||||||
rqm.BlockOutgoingCookies(),
|
rx.BlockOutgoingCookies(),
|
||||||
|
rx.SpoofOrigin(),
|
||||||
|
rx.SpoofReferrer(),
|
||||||
).
|
).
|
||||||
SetResultModifications(
|
SetResultModifications(
|
||||||
rsm.BlockIncomingCookies(),
|
tx.BlockIncomingCookies(),
|
||||||
).
|
).
|
||||||
Execute()
|
Execute()
|
||||||
|
|
||||||
@@ -90,12 +83,12 @@ type ProxyChain struct {
|
|||||||
Client *http.Client
|
Client *http.Client
|
||||||
Request *http.Request
|
Request *http.Request
|
||||||
Response *http.Response
|
Response *http.Response
|
||||||
Body []byte
|
Body io.Reader
|
||||||
requestModifications []RequestModification
|
requestModifications []RequestModification
|
||||||
resultModifications []ResponseModification
|
resultModifications []ResponseModification
|
||||||
ruleset *ruleset.RuleSet
|
Ruleset *ruleset.RuleSet
|
||||||
verbose bool
|
debugMode bool
|
||||||
_abort_err error
|
abortErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
// a ProxyStrategy is a pre-built proxychain with purpose-built defaults
|
// a ProxyStrategy is a pre-built proxychain with purpose-built defaults
|
||||||
@@ -123,23 +116,16 @@ func (chain *ProxyChain) AddRequestModifications(mods ...RequestModification) *P
|
|||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetResultModifications sets the ProxyChain's response modifers
|
// AddResponseModifications sets the ProxyChain's response modifers
|
||||||
// the modifier will not fire until ProxyChain.Execute() is run.
|
// the modifier will not fire until ProxyChain.Execute() is run.
|
||||||
func (chain *ProxyChain) SetResultModifications(mods ...ResponseModification) *ProxyChain {
|
func (chain *ProxyChain) AddResponseModifications(mods ...ResponseModification) *ProxyChain {
|
||||||
chain.resultModifications = mods
|
chain.resultModifications = mods
|
||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddResultModifications adds to the ProxyChain's response modifers
|
|
||||||
// the modifier will not fire until ProxyChain.Execute() is run.
|
|
||||||
func (chain *ProxyChain) AddResultModifications(mods ...ResponseModification) *ProxyChain {
|
|
||||||
chain.resultModifications = append(chain.resultModifications, mods...)
|
|
||||||
return chain
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds a ruleset to ProxyChain
|
// Adds a ruleset to ProxyChain
|
||||||
func (chain *ProxyChain) AddRuleset(rs *ruleset.RuleSet) *ProxyChain {
|
func (chain *ProxyChain) AddRuleset(rs *ruleset.RuleSet) *ProxyChain {
|
||||||
chain.ruleset = rs
|
chain.Ruleset = rs
|
||||||
// TODO: add _applyRuleset method
|
// TODO: add _applyRuleset method
|
||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
@@ -178,20 +164,16 @@ func (chain *ProxyChain) _initialize_request() (*http.Request, error) {
|
|||||||
|
|
||||||
// _execute sends the request for the ProxyChain and returns the raw body only
|
// _execute sends the request for the ProxyChain and returns the raw body only
|
||||||
// the caller is responsible for returning a response back to the requestor
|
// the caller is responsible for returning a response back to the requestor
|
||||||
// the caller is also responsible for calling pxc._reset() when they are done with the body
|
// the caller is also responsible for calling chain._reset() when they are done with the body
|
||||||
func (chain *ProxyChain) _execute() (*[]byte, error) {
|
func (chain *ProxyChain) _execute() (io.Reader, error) {
|
||||||
chain._validate_ctx_is_set()
|
if chain.validateCtxIsSet() != nil {
|
||||||
if chain._abort_err != nil {
|
return nil, chain.abortErr
|
||||||
return nil, chain._abort_err
|
|
||||||
}
|
|
||||||
if chain.Context == nil {
|
|
||||||
return nil, errors.New("request ctx not set. Use ProxyChain.SetCtx()")
|
|
||||||
}
|
}
|
||||||
if chain.Request.URL.Scheme == "" {
|
if chain.Request.URL.Scheme == "" {
|
||||||
return nil, errors.New("request url not set or invalid. Check ProxyChain ReqMods for issues")
|
return nil, errors.New("request url not set or invalid. Check ProxyChain ReqMods for issues")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply requestModifications to proxychain (pxc)
|
// Apply requestModifications to proxychain
|
||||||
for _, applyRequestModificationsTo := range chain.requestModifications {
|
for _, applyRequestModificationsTo := range chain.requestModifications {
|
||||||
err := applyRequestModificationsTo(chain)
|
err := applyRequestModificationsTo(chain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -205,13 +187,8 @@ func (chain *ProxyChain) _execute() (*[]byte, error) {
|
|||||||
return nil, chain.abort(err)
|
return nil, chain.abort(err)
|
||||||
}
|
}
|
||||||
chain.Response = resp
|
chain.Response = resp
|
||||||
|
chain.Body = chain.Response.Body
|
||||||
|
|
||||||
// Buffer response into memory
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, chain.abort(err)
|
|
||||||
}
|
|
||||||
chain.Body = body
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
/* todo: move to rsm
|
/* todo: move to rsm
|
||||||
@@ -220,7 +197,7 @@ func (chain *ProxyChain) _execute() (*[]byte, error) {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Apply ResponseModifiers to proxychain (pxc)
|
// Apply ResponseModifiers to proxychain
|
||||||
for _, applyResultModificationsTo := range chain.resultModifications {
|
for _, applyResultModificationsTo := range chain.resultModifications {
|
||||||
err := applyResultModificationsTo(chain)
|
err := applyResultModificationsTo(chain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -228,7 +205,7 @@ func (chain *ProxyChain) _execute() (*[]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &chain.Body, nil
|
return chain.Body, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute sends the request for the ProxyChain and returns the request to the sender
|
// Execute sends the request for the ProxyChain and returns the request to the sender
|
||||||
@@ -242,22 +219,7 @@ func (chain *ProxyChain) Execute() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Return request back to client
|
// Return request back to client
|
||||||
return chain.Context.Send(*body)
|
return chain.Context.SendStream(body)
|
||||||
}
|
|
||||||
|
|
||||||
// ExecuteAPIContent sends the request for the ProxyChain and returns the response body as
|
|
||||||
// a structured API response to the client
|
|
||||||
// if any step in the ProxyChain fails, the request will abort and a 500 error will
|
|
||||||
// be returned to the client
|
|
||||||
func (chain *ProxyChain) ExecuteAPIContent() error {
|
|
||||||
defer chain._reset()
|
|
||||||
body, err := chain._execute()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// TODO: implement reader API
|
|
||||||
// Return request back to client
|
|
||||||
return chain.Context.Send(*body)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reconstructUrlFromReferer reconstructs the URL using the referer's scheme, host, and the relative path / queries
|
// reconstructUrlFromReferer reconstructs the URL using the referer's scheme, host, and the relative path / queries
|
||||||
@@ -312,50 +274,51 @@ func (chain *ProxyChain) extractUrl() (*url.URL, error) {
|
|||||||
return reconstructUrlFromReferer(referer, relativePath)
|
return reconstructUrlFromReferer(referer, relativePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCtx takes the request ctx from the client
|
// SetFiberCtx takes the request ctx from the client
|
||||||
// for the modifiers and execute function to use.
|
// for the modifiers and execute function to use.
|
||||||
// it must be set everytime a new request comes through
|
// it must be set everytime a new request comes through
|
||||||
// if the upstream request url cannot be extracted from the ctx,
|
// if the upstream request url cannot be extracted from the ctx,
|
||||||
// a 500 error will be sent back to the client
|
// a 500 error will be sent back to the client
|
||||||
func (chain *ProxyChain) SetCtx(ctx *fiber.Ctx) *ProxyChain {
|
func (chain *ProxyChain) SetFiberCtx(ctx *fiber.Ctx) *ProxyChain {
|
||||||
chain.Context = ctx
|
chain.Context = ctx
|
||||||
|
|
||||||
// initialize the request and prepare it for modification
|
// initialize the request and prepare it for modification
|
||||||
req, err := chain._initialize_request()
|
req, err := chain._initialize_request()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
chain._abort_err = chain.abort(err)
|
chain.abortErr = chain.abort(err)
|
||||||
}
|
}
|
||||||
chain.Request = req
|
chain.Request = req
|
||||||
|
|
||||||
// extract the URL for the request and add it to the new request
|
// extract the URL for the request and add it to the new request
|
||||||
url, err := chain.extractUrl()
|
url, err := chain.extractUrl()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
chain._abort_err = chain.abort(err)
|
chain.abortErr = chain.abort(err)
|
||||||
}
|
}
|
||||||
chain.Request.URL = url
|
chain.Request.URL = url
|
||||||
|
|
||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxc *ProxyChain) _validate_ctx_is_set() {
|
func (chain *ProxyChain) validateCtxIsSet() error {
|
||||||
if pxc.Context != nil {
|
if chain.Context != nil {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
err := errors.New("proxyChain was called without setting a fiber Ctx. Use ProxyChain.SetCtx()")
|
err := errors.New("proxyChain was called without setting a fiber Ctx. Use ProxyChain.SetCtx()")
|
||||||
pxc._abort_err = pxc.abort(err)
|
chain.abortErr = chain.abort(err)
|
||||||
|
return chain.abortErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetClient sets a new upstream http client transport
|
// SetHttpClient sets a new upstream http client transport
|
||||||
// useful for modifying TLS
|
// useful for modifying TLS
|
||||||
func (pxc *ProxyChain) SetClient(httpClient *http.Client) *ProxyChain {
|
func (chain *ProxyChain) SetHttpClient(httpClient *http.Client) *ProxyChain {
|
||||||
pxc.Client = httpClient
|
chain.Client = httpClient
|
||||||
return pxc
|
return chain
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetVerbose changes the logging behavior to print
|
// SetVerbose changes the logging behavior to print
|
||||||
// the modification steps and applied rulesets for debugging
|
// the modification steps and applied rulesets for debugging
|
||||||
func (chain *ProxyChain) SetVerbose() *ProxyChain {
|
func (chain *ProxyChain) SetDebugLogging(isDebugMode bool) *ProxyChain {
|
||||||
chain.verbose = true
|
chain.debugMode = isDebugMode
|
||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,8 +326,8 @@ func (chain *ProxyChain) SetVerbose() *ProxyChain {
|
|||||||
// this will prevent Execute from firing and reset the state
|
// this will prevent Execute from firing and reset the state
|
||||||
// returns the initial error enriched with context
|
// returns the initial error enriched with context
|
||||||
func (chain *ProxyChain) abort(err error) error {
|
func (chain *ProxyChain) abort(err error) error {
|
||||||
defer chain._reset()
|
//defer chain._reset()
|
||||||
chain._abort_err = err
|
chain.abortErr = err
|
||||||
chain.Context.Response().SetStatusCode(500)
|
chain.Context.Response().SetStatusCode(500)
|
||||||
e := fmt.Errorf("ProxyChain error for '%s': %s", chain.Request.URL.String(), err.Error())
|
e := fmt.Errorf("ProxyChain error for '%s': %s", chain.Request.URL.String(), err.Error())
|
||||||
chain.Context.SendString(e.Error())
|
chain.Context.SendString(e.Error())
|
||||||
@@ -374,7 +337,7 @@ func (chain *ProxyChain) abort(err error) error {
|
|||||||
|
|
||||||
// internal function to reset state of ProxyChain for reuse
|
// internal function to reset state of ProxyChain for reuse
|
||||||
func (chain *ProxyChain) _reset() {
|
func (chain *ProxyChain) _reset() {
|
||||||
chain._abort_err = nil
|
chain.abortErr = nil
|
||||||
chain.Body = nil
|
chain.Body = nil
|
||||||
chain.Request = nil
|
chain.Request = nil
|
||||||
chain.Response = nil
|
chain.Response = nil
|
||||||
@@ -384,7 +347,6 @@ func (chain *ProxyChain) _reset() {
|
|||||||
// NewProxyChain initializes a new ProxyChain
|
// NewProxyChain initializes a new ProxyChain
|
||||||
func NewProxyChain() *ProxyChain {
|
func NewProxyChain() *ProxyChain {
|
||||||
chain := new(ProxyChain)
|
chain := new(ProxyChain)
|
||||||
//px.Client = defaultClient
|
|
||||||
chain.Client = http.DefaultClient
|
chain.Client = http.DefaultClient
|
||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
|
|||||||
19
proxychain/proxychain_cache.go
Normal file
19
proxychain/proxychain_cache.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package proxychain
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Cache provides an interface for caching mechanisms.
|
||||||
|
// It supports operations to get, set, and invalidate cache entries.
|
||||||
|
// Implementations should ensure thread safety, efficiency
|
||||||
|
type Cache interface {
|
||||||
|
// Get Retrieves a cached value by its key. Returns the value and a boolean indicating
|
||||||
|
Get(key string) (value interface{}, found bool)
|
||||||
|
|
||||||
|
// Set - Stores a value associated with a key in the cache for a specified time-to-live (ttl).
|
||||||
|
// If ttl is zero, the cache item has no expiration.
|
||||||
|
Set(key string, value interface{}, ttl time.Duration)
|
||||||
|
|
||||||
|
// Invalidate - Removes a value from the cache by its key. If the key does not exist,
|
||||||
|
// it should perform a no-op or return a suitable error.
|
||||||
|
Invalidate(key string) error
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package rqm // ReQuestModifier
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package rqm // ReQuestModifier
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package rqm // ReQuestModifier
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package rqm // ReQuestModifier
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package rqm // ReQuestModifier
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
23
proxychain/requestmodifers/modify_request_headers.go
Normal file
23
proxychain/requestmodifers/modify_request_headers.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package requestmodifers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ladder/proxychain"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetRequestHeader modifies a specific outgoing header
|
||||||
|
// This is the header that the upstream server will see.
|
||||||
|
func SetRequestHeader(name string, val string) proxychain.RequestModification {
|
||||||
|
return func(px *proxychain.ProxyChain) error {
|
||||||
|
px.Request.Header.Set(name, val)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRequestHeader modifies a specific outgoing header
|
||||||
|
// This is the header that the upstream server will see.
|
||||||
|
func DeleteRequestHeader(name string) proxychain.RequestModification {
|
||||||
|
return func(px *proxychain.ProxyChain) error {
|
||||||
|
px.Request.Header.Del(name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package rqm
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package rqm // ReQuestModifier
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package rqm // ReQuestModifier
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package rqm
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package rqm // ReQuestModifier
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package rqm // ReQuestModifier
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package rqm // ReQuestModifier
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package rqm // ReQuestModifier
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
21
proxychain/responsemodifers/bypass_cors.go
Normal file
21
proxychain/responsemodifers/bypass_cors.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package responsemodifers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ladder/proxychain"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BypassCORS modifies response headers to prevent the browser
|
||||||
|
// from enforcing any CORS restrictions. This should run at the end of the chain.
|
||||||
|
func BypassCORS() proxychain.ResponseModification {
|
||||||
|
return func(chain *proxychain.ProxyChain) error {
|
||||||
|
chain.AddResponseModifications(
|
||||||
|
SetResponseHeader("Access-Control-Allow-Origin", "*"),
|
||||||
|
SetResponseHeader("Access-Control-Expose-Headers", "*"),
|
||||||
|
SetResponseHeader("Access-Control-Allow-Credentials", "true"),
|
||||||
|
SetResponseHeader("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, HEAD, OPTIONS, PATCH"),
|
||||||
|
SetResponseHeader("Access-Control-Allow-Headers", "*"),
|
||||||
|
DeleteResponseHeader("X-Frame-Options"),
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
27
proxychain/responsemodifers/bypass_csp.go
Normal file
27
proxychain/responsemodifers/bypass_csp.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package responsemodifers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ladder/proxychain"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BypassContentSecurityPolicy modifies response headers to prevent the browser
|
||||||
|
// from enforcing any CSP restrictions. This should run at the end of the chain.
|
||||||
|
func BypassContentSecurityPolicy() proxychain.ResponseModification {
|
||||||
|
return func(chain *proxychain.ProxyChain) error {
|
||||||
|
chain.AddResponseModifications(
|
||||||
|
DeleteResponseHeader("Content-Security-Policy"),
|
||||||
|
DeleteResponseHeader("Content-Security-Policy-Report-Only"),
|
||||||
|
DeleteResponseHeader("X-Content-Security-Policy"),
|
||||||
|
DeleteResponseHeader("X-WebKit-CSP"),
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetContentSecurityPolicy modifies response headers to a specific CSP
|
||||||
|
func SetContentSecurityPolicy(csp string) proxychain.ResponseModification {
|
||||||
|
return func(chain *proxychain.ProxyChain) error {
|
||||||
|
chain.Response.Header.Set("Content-Security-Policy", csp)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
102
proxychain/responsemodifers/modify_incoming_cookies.go
Normal file
102
proxychain/responsemodifers/modify_incoming_cookies.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package responsemodifers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"ladder/proxychain"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeleteIncomingCookies prevents ALL cookies from being sent from the proxy server
|
||||||
|
// back down to the client.
|
||||||
|
func DeleteIncomingCookies(whitelist ...string) proxychain.ResponseModification {
|
||||||
|
return func(px *proxychain.ProxyChain) error {
|
||||||
|
px.Response.Header.Del("Set-Cookie")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteIncomingCookiesExcept prevents non-whitelisted cookies from being sent from the proxy server
|
||||||
|
// to the client. Cookies whose names are in the whitelist are not removed.
|
||||||
|
func DeleteIncomingCookiesExcept(whitelist ...string) proxychain.ResponseModification {
|
||||||
|
return func(px *proxychain.ProxyChain) error {
|
||||||
|
// Convert whitelist slice to a map for efficient lookups
|
||||||
|
whitelistMap := make(map[string]struct{})
|
||||||
|
for _, cookieName := range whitelist {
|
||||||
|
whitelistMap[cookieName] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the response has no cookies, return early
|
||||||
|
if px.Response.Header == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter the cookies in the response
|
||||||
|
filteredCookies := []string{}
|
||||||
|
for _, cookieStr := range px.Response.Header["Set-Cookie"] {
|
||||||
|
cookie := parseCookie(cookieStr)
|
||||||
|
if _, found := whitelistMap[cookie.Name]; found {
|
||||||
|
filteredCookies = append(filteredCookies, cookieStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the Set-Cookie header with the filtered cookies
|
||||||
|
if len(filteredCookies) > 0 {
|
||||||
|
px.Response.Header["Set-Cookie"] = filteredCookies
|
||||||
|
} else {
|
||||||
|
px.Response.Header.Del("Set-Cookie")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseCookie parses a cookie string and returns an http.Cookie object.
|
||||||
|
func parseCookie(cookieStr string) *http.Cookie {
|
||||||
|
header := http.Header{}
|
||||||
|
header.Add("Set-Cookie", cookieStr)
|
||||||
|
request := http.Request{Header: header}
|
||||||
|
return request.Cookies()[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIncomingCookies adds a raw cookie string being sent from the proxy server down to the client
|
||||||
|
func SetIncomingCookies(cookies string) proxychain.ResponseModification {
|
||||||
|
return func(px *proxychain.ProxyChain) error {
|
||||||
|
px.Response.Header.Set("Set-Cookie", cookies)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIncomingCookie modifies a specific cookie in the response from the proxy server to the client.
|
||||||
|
func SetIncomingCookie(name string, val string) proxychain.ResponseModification {
|
||||||
|
return func(px *proxychain.ProxyChain) error {
|
||||||
|
if px.Response.Header == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedCookies := []string{}
|
||||||
|
found := false
|
||||||
|
|
||||||
|
// Iterate over existing cookies and modify the one that matches the cookieName
|
||||||
|
for _, cookieStr := range px.Response.Header["Set-Cookie"] {
|
||||||
|
cookie := parseCookie(cookieStr)
|
||||||
|
if cookie.Name == name {
|
||||||
|
// Replace the cookie with the new value
|
||||||
|
updatedCookies = append(updatedCookies, fmt.Sprintf("%s=%s", name, val))
|
||||||
|
found = true
|
||||||
|
} else {
|
||||||
|
// Keep the cookie as is
|
||||||
|
updatedCookies = append(updatedCookies, cookieStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the specified cookie wasn't found, add it
|
||||||
|
if !found {
|
||||||
|
updatedCookies = append(updatedCookies, fmt.Sprintf("%s=%s", name, val))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the Set-Cookie header
|
||||||
|
px.Response.Header["Set-Cookie"] = updatedCookies
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,12 @@
|
|||||||
package rsm // ReSponseModifers
|
package responsemodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ModifyResponseHeader modifies response headers from the upstream server
|
// SetResponseHeader modifies response headers from the upstream server
|
||||||
// if value is "", then the response header is deleted.
|
func SetResponseHeader(key string, value string) proxychain.ResponseModification {
|
||||||
func ModifyResponseHeader(key string, value string) proxychain.ResponseModification {
|
|
||||||
return func(px *proxychain.ProxyChain) error {
|
return func(px *proxychain.ProxyChain) error {
|
||||||
if value == "" {
|
|
||||||
px.Context.Response().Header.Del(key)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
px.Context.Response().Header.Set(key, value)
|
px.Context.Response().Header.Set(key, value)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
114
proxychain/responsemodifers/rewrite_http_resource_urls.go
Normal file
114
proxychain/responsemodifers/rewrite_http_resource_urls.go
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package responsemodifers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"ladder/proxychain"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/html"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HTMLResourceURLRewriter struct {
|
||||||
|
src io.Reader
|
||||||
|
buffer *bytes.Buffer // buffer to temporarily hold rewritten output for the reader
|
||||||
|
proxyURL *url.URL // proxyURL is the URL of the proxy, not the upstream URL
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHTMLResourceURLRewriter(src io.Reader, proxyURL *url.URL) *HTMLResourceURLRewriter {
|
||||||
|
return &HTMLResourceURLRewriter{
|
||||||
|
src: src,
|
||||||
|
buffer: new(bytes.Buffer),
|
||||||
|
proxyURL: proxyURL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rewriteToken(token *html.Token, baseURL *url.URL) {
|
||||||
|
attrsToRewrite := map[string]bool{"href": true, "src": true, "action": true, "srcset": true}
|
||||||
|
for i := range token.Attr {
|
||||||
|
attr := &token.Attr[i]
|
||||||
|
if attrsToRewrite[attr.Key] && strings.HasPrefix(attr.Val, "/") {
|
||||||
|
// Make URL absolute
|
||||||
|
attr.Val = "/https://" + baseURL.Host + attr.Val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HTMLResourceURLRewriter) Read(p []byte) (int, error) {
|
||||||
|
if r.buffer.Len() != 0 {
|
||||||
|
return r.buffer.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenizer := html.NewTokenizer(r.src)
|
||||||
|
for {
|
||||||
|
tokenType := tokenizer.Next()
|
||||||
|
if tokenType == html.ErrorToken {
|
||||||
|
err := tokenizer.Err()
|
||||||
|
if err == io.EOF {
|
||||||
|
return 0, io.EOF // End of document
|
||||||
|
}
|
||||||
|
return 0, err // Actual error
|
||||||
|
}
|
||||||
|
token := tokenizer.Token()
|
||||||
|
if tokenType == html.StartTagToken || tokenType == html.SelfClosingTagToken {
|
||||||
|
rewriteToken(&token, r.url)
|
||||||
|
}
|
||||||
|
r.buffer.WriteString(token.String())
|
||||||
|
if r.buffer.Len() > 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// RewriteHTMLResourceURLs updates src/href attributes in HTML content to route through the proxy.
|
||||||
|
func RewriteHTMLResourceURLs() proxychain.ResponseModification {
|
||||||
|
return func(chain *proxychain.ProxyChain) error {
|
||||||
|
ct := chain.Response.Header.Get("content-type")
|
||||||
|
if ct != "text/html" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse dom
|
||||||
|
tokenizer := html.NewTokenizer(chain.Body)
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
|
||||||
|
// traverse dom and proxify existing src/img resource links
|
||||||
|
for {
|
||||||
|
tokenType := tokenizer.Next()
|
||||||
|
switch tokenType {
|
||||||
|
case html.ErrorToken:
|
||||||
|
// End of the document, set the new body
|
||||||
|
chain.Body = io.ReaderFrom(buffer)
|
||||||
|
return nil
|
||||||
|
case html.StartTagToken, html.SelfClosingTagToken:
|
||||||
|
token := tokenizer.Token()
|
||||||
|
// Rewrite the necessary attributes
|
||||||
|
token = rewriteToken(token, u)
|
||||||
|
buffer.WriteString(token.String())
|
||||||
|
case html.TextToken, html.CommentToken, html.DoctypeToken, html.EndTagToken:
|
||||||
|
// Write the token to the buffer as is
|
||||||
|
buffer.WriteString(tokenizer.Token().String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// rewriteToken rewrites the tokens with URLs to point to the proxy server.
|
||||||
|
func rewriteToken(token html.Token, u *url.URL) html.Token {
|
||||||
|
// Define attributes to rewrite, add more as needed such as "srcset"
|
||||||
|
rewriteAttrs := map[string]bool{"href": true, "src": true, "action": true, "srcset": true}
|
||||||
|
|
||||||
|
for i, attr := range token.Attr {
|
||||||
|
_, shouldRewrite := rewriteAttrs[attr.Key]
|
||||||
|
if shouldRewrite {
|
||||||
|
val := attr.Val
|
||||||
|
if strings.HasPrefix(val, "/") {
|
||||||
|
token.Attr[i].Val = "/https://" + u.Host + val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return token
|
||||||
|
}
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
package rsm // ReSponseModifers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"ladder/proxychain"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BlockIncomingCookies prevents ALL cookies from being sent from the proxy server
|
|
||||||
// to the client.
|
|
||||||
func BlockIncomingCookies(whitelist ...string) proxychain.ResponseModification {
|
|
||||||
return func(px *proxychain.ProxyChain) error {
|
|
||||||
px.Response.Header.Del("Set-Cookie")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockIncomingCookiesExcept prevents non-whitelisted cookies from being sent from the proxy server
|
|
||||||
// to the client. Cookies whose names are in the whitelist are not removed.
|
|
||||||
func BlockIncomingCookiesExcept(whitelist ...string) proxychain.ResponseModification {
|
|
||||||
return func(px *proxychain.ProxyChain) error {
|
|
||||||
// Convert whitelist slice to a map for efficient lookups
|
|
||||||
whitelistMap := make(map[string]struct{})
|
|
||||||
for _, cookieName := range whitelist {
|
|
||||||
whitelistMap[cookieName] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the response has no cookies, return early
|
|
||||||
if px.Response.Header == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter the cookies in the response
|
|
||||||
filteredCookies := []string{}
|
|
||||||
for _, cookieStr := range px.Response.Header["Set-Cookie"] {
|
|
||||||
cookie := parseCookie(cookieStr)
|
|
||||||
if _, found := whitelistMap[cookie.Name]; found {
|
|
||||||
filteredCookies = append(filteredCookies, cookieStr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the Set-Cookie header with the filtered cookies
|
|
||||||
if len(filteredCookies) > 0 {
|
|
||||||
px.Response.Header["Set-Cookie"] = filteredCookies
|
|
||||||
} else {
|
|
||||||
px.Response.Header.Del("Set-Cookie")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseCookie parses a cookie string and returns an http.Cookie object.
|
|
||||||
func parseCookie(cookieStr string) *http.Cookie {
|
|
||||||
header := http.Header{}
|
|
||||||
header.Add("Set-Cookie", cookieStr)
|
|
||||||
request := http.Request{Header: header}
|
|
||||||
return request.Cookies()[0]
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package rsm // ReSponseModifers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"ladder/proxychain"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BypassCORs modifies response headers to prevent the browser
|
|
||||||
// from enforcing any CORS restrictions
|
|
||||||
func BypassCORS() proxychain.ResponseModification {
|
|
||||||
return func(px *proxychain.ProxyChain) error {
|
|
||||||
px.AddResultModifications(
|
|
||||||
ModifyResponseHeader("Access-Control-Allow-Origin", "*"),
|
|
||||||
ModifyResponseHeader("Access-Control-Expose-Headers", "*"),
|
|
||||||
ModifyResponseHeader("Access-Control-Allow-Credentials", "true"),
|
|
||||||
ModifyResponseHeader("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, HEAD, OPTIONS, PATCH"),
|
|
||||||
DeleteResponseHeader("X-Frame-Options"),
|
|
||||||
)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package rsm // ReSponseModifers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"ladder/proxychain"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BypassCSP modifies response headers to prevent the browser
|
|
||||||
// from enforcing any CORS restrictions
|
|
||||||
func BypassCSP() proxychain.ResponseModification {
|
|
||||||
return func(px *proxychain.ProxyChain) error {
|
|
||||||
px.AddResultModifications(
|
|
||||||
ModifyResponseHeader("Access-Control-Allow-Origin", "*"),
|
|
||||||
ModifyResponseHeader("Access-Control-Expose-Headers", "*"),
|
|
||||||
ModifyResponseHeader("Access-Control-Allow-Credentials", "true"),
|
|
||||||
ModifyResponseHeader("Access-Control-Allow-Methods", ""),
|
|
||||||
)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user