refactor wip

This commit is contained in:
Kevin Pham
2023-11-19 15:03:11 -06:00
parent 98fa53287b
commit ee9066dedb
27 changed files with 377 additions and 210 deletions

2
go.mod
View File

@@ -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
) )

View File

@@ -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
View File

View 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
} }

View 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
}

View File

@@ -1,4 +1,4 @@
package rqm // ReQuestModifier package requestmodifers
import ( import (
"ladder/proxychain" "ladder/proxychain"

View File

@@ -1,4 +1,4 @@
package rqm // ReQuestModifier package requestmodifers
import ( import (
"ladder/proxychain" "ladder/proxychain"

View File

@@ -1,4 +1,4 @@
package rqm // ReQuestModifier package requestmodifers
import ( import (
"ladder/proxychain" "ladder/proxychain"

View File

@@ -1,4 +1,4 @@
package rqm // ReQuestModifier package requestmodifers
import ( import (
"ladder/proxychain" "ladder/proxychain"

View File

@@ -1,4 +1,4 @@
package rqm // ReQuestModifier package requestmodifers
import ( import (
"ladder/proxychain" "ladder/proxychain"

View 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
}
}

View File

@@ -1,4 +1,4 @@
package rqm package requestmodifers
import ( import (
"ladder/proxychain" "ladder/proxychain"

View File

@@ -1,4 +1,4 @@
package rqm // ReQuestModifier package requestmodifers
import ( import (
"ladder/proxychain" "ladder/proxychain"

View File

@@ -1,4 +1,4 @@
package rqm // ReQuestModifier package requestmodifers
import ( import (
"ladder/proxychain" "ladder/proxychain"

View File

@@ -1,4 +1,4 @@
package rqm package requestmodifers
import ( import (
"context" "context"

View File

@@ -1,4 +1,4 @@
package rqm // ReQuestModifier package requestmodifers
import ( import (
"ladder/proxychain" "ladder/proxychain"

View File

@@ -1,4 +1,4 @@
package rqm // ReQuestModifier package requestmodifers
import ( import (
"ladder/proxychain" "ladder/proxychain"

View File

@@ -1,4 +1,4 @@
package rqm // ReQuestModifier package requestmodifers
import ( import (
"ladder/proxychain" "ladder/proxychain"

View File

@@ -1,4 +1,4 @@
package rqm // ReQuestModifier package requestmodifers
import ( import (
"ladder/proxychain" "ladder/proxychain"

View 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
}
}

View 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
}
}

View 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
}
}

View File

@@ -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
} }

View 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
}

View File

@@ -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]
}

View File

@@ -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
}
}

View File

@@ -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
}
}