improve ua modifier with client side spoofing
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ ladder
|
|||||||
|
|
||||||
VERSION
|
VERSION
|
||||||
output.css
|
output.css
|
||||||
|
.aider*
|
||||||
|
|||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "proxychain/requestmodifers/vendor/ua-parser-js"]
|
||||||
|
path = proxychain/requestmodifers/vendor/ua-parser-js
|
||||||
|
url = https://github.com/faisalman/ua-parser-js.git
|
||||||
@@ -31,6 +31,7 @@ func NewProxySiteHandler(opts *ProxyOptions) fiber.Handler {
|
|||||||
SetFiberCtx(c).
|
SetFiberCtx(c).
|
||||||
SetDebugLogging(opts.Verbose).
|
SetDebugLogging(opts.Verbose).
|
||||||
SetRequestModifications(
|
SetRequestModifications(
|
||||||
|
rx.MasqueradeAsGoogleBot(),
|
||||||
rx.DeleteOutgoingCookies(),
|
rx.DeleteOutgoingCookies(),
|
||||||
// rx.RequestArchiveIs(),
|
// rx.RequestArchiveIs(),
|
||||||
rx.MasqueradeAsGoogleBot(),
|
rx.MasqueradeAsGoogleBot(),
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ type ProxyChain struct {
|
|||||||
Response *http.Response
|
Response *http.Response
|
||||||
requestModifications []RequestModification
|
requestModifications []RequestModification
|
||||||
onceRequestModifications []RequestModification
|
onceRequestModifications []RequestModification
|
||||||
onceResultModifications []ResponseModification
|
onceResponseModifications []ResponseModification
|
||||||
resultModifications []ResponseModification
|
resultModifications []ResponseModification
|
||||||
htmlTokenRewriters []rr.IHTMLTokenRewriter
|
htmlTokenRewriters []rr.IHTMLTokenRewriter
|
||||||
Ruleset *ruleset.RuleSet
|
Ruleset *ruleset.RuleSet
|
||||||
@@ -122,17 +122,17 @@ func (chain *ProxyChain) AddRequestModifications(mods ...RequestModification) *P
|
|||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddOnceRequestModification adds a request modifier to the ProxyChain that should only fire once
|
// AddOnceRequestModifications adds a request modifier to the ProxyChain that should only fire once
|
||||||
// the modifier will not fire until ProxyChain.Execute() is run and will be removed after it has been applied.
|
// the modifier will not fire until ProxyChain.Execute() is run and will be removed after it has been applied.
|
||||||
func (chain *ProxyChain) AddOnceRequestModification(mod ...RequestModification) *ProxyChain {
|
func (chain *ProxyChain) AddOnceRequestModifications(mods ...RequestModification) *ProxyChain {
|
||||||
chain.onceRequestModifications = append(chain.onceRequestModifications, mod...)
|
chain.onceRequestModifications = append(chain.onceRequestModifications, mods...)
|
||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddOnceResponseModification adds a response modifier to the ProxyChain that should only fire once
|
// AddOnceResponseModifications adds a response modifier to the ProxyChain that should only fire once
|
||||||
// the modifier will not fire until ProxyChain.Execute() is run and will be removed after it has been applied.
|
// the modifier will not fire until ProxyChain.Execute() is run and will be removed after it has been applied.
|
||||||
func (chain *ProxyChain) AddOnceResponseModification(mod ...ResponseModification) *ProxyChain {
|
func (chain *ProxyChain) AddOnceResponseModifications(mods ...ResponseModification) *ProxyChain {
|
||||||
chain.onceResultModifications = append(chain.onceResultModifications, mod...)
|
chain.onceResponseModifications = append(chain.onceResponseModifications, mods...)
|
||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,26 +188,25 @@ func (chain *ProxyChain) _initialize_request() (*http.Request, error) {
|
|||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
func reconstructUrlFromReferer(referer *url.URL, relativeUrl *url.URL) (*url.URL, error) {
|
func reconstructURLFromReferer(referer *url.URL, relativeURL *url.URL) (*url.URL, error) {
|
||||||
|
|
||||||
// Extract the real url from referer path
|
// Extract the real url from referer path
|
||||||
realUrl, err := url.Parse(strings.TrimPrefix(referer.Path, "/"))
|
realURL, err := url.Parse(strings.TrimPrefix(referer.Path, "/"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error parsing real URL from referer '%s': %v", referer.Path, err)
|
return nil, fmt.Errorf("error parsing real URL from referer '%s': %v", referer.Path, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if realUrl.Scheme == "" || realUrl.Host == "" {
|
if realURL.Scheme == "" || realURL.Host == "" {
|
||||||
return nil, fmt.Errorf("invalid referer URL: '%s' on request '%s", referer.String(), relativeUrl.String())
|
return nil, fmt.Errorf("invalid referer URL: '%s' on request '%s", referer.String(), relativeURL.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("rewrite relative URL using referer: '%s' -> '%s'\n", relativeUrl.String(), realUrl.String())
|
log.Printf("rewrite relative URL using referer: '%s' -> '%s'\n", relativeURL.String(), realURL.String())
|
||||||
|
|
||||||
return &url.URL{
|
return &url.URL{
|
||||||
Scheme: referer.Scheme,
|
Scheme: referer.Scheme,
|
||||||
Host: referer.Host,
|
Host: referer.Host,
|
||||||
Path: realUrl.Path,
|
Path: realURL.Path,
|
||||||
RawQuery: realUrl.RawQuery,
|
RawQuery: realURL.RawQuery,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,27 +226,27 @@ func preventRecursiveProxyRequest(urlQuery *url.URL, baseProxyURL string) *url.U
|
|||||||
return preventRecursiveProxyRequest(fixedURL, baseProxyURL)
|
return preventRecursiveProxyRequest(fixedURL, baseProxyURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractUrl extracts a URL from the request ctx. If the URL in the request
|
// extractURL extracts a URL from the request ctx. If the URL in the request
|
||||||
// is a relative path, it reconstructs the full URL using the referer header.
|
// is a relative path, it reconstructs the full URL using the referer header.
|
||||||
func (chain *ProxyChain) extractUrl() (*url.URL, error) {
|
func (chain *ProxyChain) extractURL() (*url.URL, error) {
|
||||||
reqUrl := chain.Context.Params("*")
|
reqURL := chain.Context.Params("*")
|
||||||
|
|
||||||
// sometimes client requests doubleroot '//'
|
// sometimes client requests doubleroot '//'
|
||||||
// there is a bug somewhere else, but this is a workaround until we find it
|
// there is a bug somewhere else, but this is a workaround until we find it
|
||||||
if strings.HasPrefix(reqUrl, "/") || strings.HasPrefix(reqUrl, `%2F`) {
|
if strings.HasPrefix(reqURL, "/") || strings.HasPrefix(reqURL, `%2F`) {
|
||||||
reqUrl = strings.TrimPrefix(reqUrl, "/")
|
reqURL = strings.TrimPrefix(reqURL, "/")
|
||||||
reqUrl = strings.TrimPrefix(reqUrl, `%2F`)
|
reqURL = strings.TrimPrefix(reqURL, `%2F`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// unescape url query
|
// unescape url query
|
||||||
uReqUrl, err := url.QueryUnescape(reqUrl)
|
uReqURL, err := url.QueryUnescape(reqURL)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
reqUrl = uReqUrl
|
reqURL = uReqURL
|
||||||
}
|
}
|
||||||
|
|
||||||
urlQuery, err := url.Parse(reqUrl)
|
urlQuery, err := url.Parse(reqURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error parsing request URL '%s': %v", reqUrl, err)
|
return nil, fmt.Errorf("error parsing request URL '%s': %v", reqURL, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// prevent recursive proxy requests
|
// prevent recursive proxy requests
|
||||||
@@ -269,7 +268,7 @@ func (chain *ProxyChain) extractUrl() (*url.URL, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error parsing referer URL from req: '%s': %v", relativePath, err)
|
return nil, fmt.Errorf("error parsing referer URL from req: '%s': %v", relativePath, err)
|
||||||
}
|
}
|
||||||
return reconstructUrlFromReferer(referer, relativePath)
|
return reconstructURLFromReferer(referer, relativePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFiberCtx takes the request ctx from the client
|
// SetFiberCtx takes the request ctx from the client
|
||||||
@@ -288,7 +287,7 @@ func (chain *ProxyChain) SetFiberCtx(ctx *fiber.Ctx) *ProxyChain {
|
|||||||
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.abortErr = chain.abort(err)
|
chain.abortErr = chain.abort(err)
|
||||||
}
|
}
|
||||||
@@ -307,9 +306,9 @@ func (chain *ProxyChain) validateCtxIsSet() error {
|
|||||||
return chain.abortErr
|
return chain.abortErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetHttpClient sets a new upstream http client transport
|
// SetHTTPClient sets a new upstream http client transport
|
||||||
// useful for modifying TLS
|
// useful for modifying TLS
|
||||||
func (chain *ProxyChain) SetHttpClient(httpClient *http.Client) *ProxyChain {
|
func (chain *ProxyChain) SetHTTPClient(httpClient *http.Client) *ProxyChain {
|
||||||
chain.Client = httpClient
|
chain.Client = httpClient
|
||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
@@ -373,22 +372,14 @@ func (chain *ProxyChain) _execute() (io.Reader, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply onceRequestModifications and onceResultModifications to proxychain and clear them
|
// Apply onceRequestModifications to proxychain and clear them
|
||||||
for _, applyOnceRequestModificationsTo := range chain.onceRequestModifications {
|
for _, applyOnceRequestModificationsTo := range chain.onceRequestModifications {
|
||||||
err := applyOnceRequestModificationsTo(chain)
|
err := applyOnceRequestModificationsTo(chain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, chain.abort(err)
|
return nil, chain.abort(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chain.onceRequestModifications = nil
|
chain.onceRequestModifications = []RequestModification{}
|
||||||
|
|
||||||
for _, applyOnceResultModificationsTo := range chain.onceResultModifications {
|
|
||||||
err := applyOnceResultModificationsTo(chain)
|
|
||||||
if err != nil {
|
|
||||||
return nil, chain.abort(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chain.onceResultModifications = nil
|
|
||||||
|
|
||||||
// Send Request Upstream
|
// Send Request Upstream
|
||||||
resp, err := chain.Client.Do(chain.Request)
|
resp, err := chain.Client.Do(chain.Request)
|
||||||
@@ -411,8 +402,16 @@ func (chain *ProxyChain) _execute() (io.Reader, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return chain.Response.Body, nil
|
// Apply onceResponseModifications to proxychain and clear them
|
||||||
|
for _, applyOnceResponseModificationsTo := range chain.onceResponseModifications {
|
||||||
|
err := applyOnceResponseModificationsTo(chain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, chain.abort(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chain.onceResponseModifications = []ResponseModification{}
|
||||||
|
|
||||||
|
return chain.Response.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
|
||||||
|
|||||||
@@ -20,12 +20,67 @@ func MasqueradeAsBingBot() proxychain.RequestModification {
|
|||||||
return masqueradeAsTrustedBot(botUA, botIP)
|
return masqueradeAsTrustedBot(botUA, botIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MasqueradeAsWaybackMachineBot modifies user agent and x-forwarded for
|
||||||
|
// to appear to be a archive.org (wayback machine) Bot
|
||||||
|
func MasqueradeAsWaybackMachineBot() proxychain.RequestModification {
|
||||||
|
const botUA string = "Mozilla/5.0 (compatible; archive.org_bot +http://www.archive.org/details/archive.org_bot)"
|
||||||
|
const botIP string = "207.241.235.164"
|
||||||
|
return masqueradeAsTrustedBot(botUA, botIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MasqueradeAsFacebookBot modifies user agent and x-forwarded for
|
||||||
|
// to appear to be a Facebook Bot (link previews?)
|
||||||
|
func MasqueradeAsFacebookBot() proxychain.RequestModification {
|
||||||
|
const botUA string = "facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)"
|
||||||
|
// 31.13.97.0/24, 31.13.99.0/24, 31.13.100.0/24, 66.220.144.0/20, 69.63.189.0/24, 69.63.190.0/24, 69.171.224.0/20, 69.171.240.0/21, 69.171.248.0/24, 173.252.73.0/24, 173.252.74.0/24, 173.252.77.0/24, 173.252.100.0/22, 173.252.104.0/21, 173.252.112.0/24, 2a03:2880:10::/48, 2a03:2880:10ff::/48, 2a03:2880:11::/48, 2a03:2880:11ff::/48, 2a03:2880:20::/48, 2a03:2880:20ff::/48, 2a03:2880:21ff::/48, 2a03:2880:30ff::/48, 2a03:2880:31ff::/48, 2a03:2880:1010::/48, 2a03:2880:1020::/48, 2a03:2880:2020::/48, 2a03:2880:2050::/48, 2a03:2880:2040::/48, 2a03:2880:2110::/48, 2a03:2880:2130::/48, 2a03:2880:3010::/48, 2a03:2880:3020::/48
|
||||||
|
const botIP string = "31.13.99.8"
|
||||||
|
return masqueradeAsTrustedBot(botUA, botIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MasqueradeAsYandexBot modifies user agent and x-forwarded for
|
||||||
|
// to appear to be a Yandex Spider Bot
|
||||||
|
func MasqueradeAsYandexBot() proxychain.RequestModification {
|
||||||
|
const botUA string = "Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)"
|
||||||
|
// 100.43.90.0/24, 37.9.115.0/24, 37.140.165.0/24, 77.88.22.0/25, 77.88.29.0/24, 77.88.31.0/24, 77.88.59.0/24, 84.201.146.0/24, 84.201.148.0/24, 84.201.149.0/24, 87.250.243.0/24, 87.250.253.0/24, 93.158.147.0/24, 93.158.148.0/24, 93.158.151.0/24, 93.158.153.0/32, 95.108.128.0/24, 95.108.138.0/24, 95.108.150.0/23, 95.108.158.0/24, 95.108.156.0/24, 95.108.188.128/25, 95.108.234.0/24, 95.108.248.0/24, 100.43.80.0/24, 130.193.62.0/24, 141.8.153.0/24, 178.154.165.0/24, 178.154.166.128/25, 178.154.173.29, 178.154.200.158, 178.154.202.0/24, 178.154.205.0/24, 178.154.239.0/24, 178.154.243.0/24, 37.9.84.253, 199.21.99.99, 178.154.162.29, 178.154.203.251, 178.154.211.250, 178.154.171.0/24, 178.154.200.0/24, 178.154.244.0/24, 178.154.246.0/24, 95.108.181.0/24, 95.108.246.252, 5.45.254.0/24, 5.255.253.0/24, 37.140.141.0/24, 37.140.188.0/24, 100.43.81.0/24, 100.43.85.0/24, 100.43.91.0/24, 199.21.99.0/24, 2a02:6b8:b000::/32, 2a02:6b8:b010::/32, 2a02:6b8:b011::/32, 2a02:6b8:c0e::/32
|
||||||
|
const botIP string = "37.9.115.9"
|
||||||
|
return masqueradeAsTrustedBot(botUA, botIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MasqueradeAsBaiduBot modifies user agent and x-forwarded for
|
||||||
|
// to appear to be a Baidu Spider Bot
|
||||||
|
func MasqueradeAsBaiduBot() proxychain.RequestModification {
|
||||||
|
const botUA string = "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
|
||||||
|
// 180.76.15.0/24, 119.63.196.0/24, 115.239.212./24, 119.63.199.0/24, 122.81.208.0/22, 123.125.71.0/24, 180.76.4.0/24, 180.76.5.0/24, 180.76.6.0/24, 185.10.104.0/24, 220.181.108.0/24, 220.181.51.0/24, 111.13.102.0/24, 123.125.67.144/29, 123.125.67.152/31, 61.135.169.0/24, 123.125.68.68/30, 123.125.68.72/29, 123.125.68.80/28, 123.125.68.96/30, 202.46.48.0/20, 220.181.38.0/24, 123.125.68.80/30, 123.125.68.84/31, 123.125.68.0/24
|
||||||
|
const botIP string = "180.76.15.7"
|
||||||
|
return masqueradeAsTrustedBot(botUA, botIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MasqueradeAsDuckDuckBot modifies user agent and x-forwarded for
|
||||||
|
// to appear to be a DuckDuckGo Bot
|
||||||
|
func MasqueradeAsDuckDuckBot() proxychain.RequestModification {
|
||||||
|
const botUA string = "DuckDuckBot/1.0; (+http://duckduckgo.com/duckduckbot.html)"
|
||||||
|
// 46.51.197.88, 46.51.197.89, 50.18.192.250, 50.18.192.251, 107.21.1.61, 176.34.131.233, 176.34.135.167, 184.72.106.52, 184.72.115.86
|
||||||
|
const botIP string = "46.51.197.88"
|
||||||
|
return masqueradeAsTrustedBot(botUA, botIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MasqueradeAsYahooBot modifies user agent and x-forwarded for
|
||||||
|
// to appear to be a Yahoo Bot
|
||||||
|
func MasqueradeAsYahooBot() proxychain.RequestModification {
|
||||||
|
const botUA string = "Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)"
|
||||||
|
// 5.255.250.0/24, 37.9.87.0/24, 67.195.37.0/24, 67.195.50.0/24, 67.195.110.0/24, 67.195.111.0/24, 67.195.112.0/23, 67.195.114.0/24, 67.195.115.0/24, 68.180.224.0/21, 72.30.132.0/24, 72.30.142.0/24, 72.30.161.0/24, 72.30.196.0/24, 72.30.198.0/24, 74.6.254.0/24, 74.6.8.0/24, 74.6.13.0/24, 74.6.17.0/24, 74.6.18.0/24, 74.6.22.0/24, 74.6.27.0/24, 74.6.168.0/24, 77.88.5.0/24, 77.88.47.0/24, 93.158.161.0/24, 98.137.72.0/24, 98.137.206.0/24, 98.137.207.0/24, 98.139.168.0/24, 114.111.95.0/24, 124.83.159.0/24, 124.83.179.0/24, 124.83.223.0/24, 141.8.144.0/24, 183.79.63.0/24, 183.79.92.0/24, 203.216.255.0/24, 211.14.11.0/24
|
||||||
|
const botIP string = "37.9.87.5"
|
||||||
|
return masqueradeAsTrustedBot(botUA, botIP)
|
||||||
|
}
|
||||||
|
|
||||||
func masqueradeAsTrustedBot(botUA string, botIP string) proxychain.RequestModification {
|
func masqueradeAsTrustedBot(botUA string, botIP string) proxychain.RequestModification {
|
||||||
return func(chain *proxychain.ProxyChain) error {
|
return func(chain *proxychain.ProxyChain) error {
|
||||||
chain.Request.Header.Set("user-agent", botUA)
|
chain.AddOnceRequestModifications(
|
||||||
chain.Request.Header.Set("x-forwarded-for", botIP)
|
SpoofUserAgent(botUA),
|
||||||
chain.Request.Header.Del("referrer")
|
SetRequestHeader("x-forwarded-for", botIP),
|
||||||
chain.Request.Header.Del("origin")
|
DeleteRequestHeader("referrer"),
|
||||||
|
DeleteRequestHeader("origin"),
|
||||||
|
)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package requestmodifers
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"ladder/proxychain"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ModifyDomainWithRegex(match regexp.Regexp, replacement string) proxychain.RequestModification {
|
func ModifyDomainWithRegex(match regexp.Regexp, replacement string) proxychain.RequestModification {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package requestmodifers
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"ladder/proxychain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetOutgoingCookie modifes a specific cookie name
|
// SetOutgoingCookie modifes a specific cookie name
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package requestmodifers
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"ladder/proxychain"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ModifyPathWithRegex(match regexp.Regexp, replacement string) proxychain.RequestModification {
|
func ModifyPathWithRegex(match regexp.Regexp, replacement string) proxychain.RequestModification {
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
package requestmodifers
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
"ladder/proxychain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ModifyQueryParams replaces query parameter values in URL's query params in a ProxyChain's URL.
|
// ModifyQueryParams replaces query parameter values in URL's query params in a ProxyChain's URL.
|
||||||
// If the query param key doesn't exist, it is created.
|
// If the query param key doesn't exist, it is created.
|
||||||
func ModifyQueryParams(key string, value string) proxychain.RequestModification {
|
func ModifyQueryParams(key string, value string) proxychain.RequestModification {
|
||||||
return func(px *proxychain.ProxyChain) error {
|
return func(chain *proxychain.ProxyChain) error {
|
||||||
q := px.Request.URL.Query()
|
q := chain.Request.URL.Query()
|
||||||
px.Request.URL.RawQuery = modifyQueryParams(key, value, q)
|
chain.Request.URL.RawQuery = modifyQueryParams(key, value, q)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,28 @@
|
|||||||
package requestmodifers
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
"ladder/proxychain"
|
||||||
)
|
)
|
||||||
|
|
||||||
const archivistUrl string = "https://archive.is/latest/"
|
const archivistUrl string = "https://archive.is/latest"
|
||||||
|
|
||||||
// RequestArchiveIs modifies a ProxyChain's URL to request an archived version from archive.is
|
// RequestArchiveIs modifies a ProxyChain's URL to request an archived version from archive.is
|
||||||
func RequestArchiveIs() proxychain.RequestModification {
|
func RequestArchiveIs() proxychain.RequestModification {
|
||||||
return func(px *proxychain.ProxyChain) error {
|
return func(chain *proxychain.ProxyChain) error {
|
||||||
px.Request.URL.RawQuery = ""
|
chain.Request.URL.RawQuery = ""
|
||||||
newURLString := archivistUrl + px.Request.URL.String()
|
newURL, err := url.Parse(fmt.Sprintf("%s/%s", archivistUrl, chain.Request.URL.String()))
|
||||||
newURL, err := url.Parse(newURLString)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// archivist seems to sabotage requests from cloudflare's DNS
|
// archivist seems to sabotage requests from cloudflare's DNS
|
||||||
// bypass this just in case
|
// bypass this just in case
|
||||||
px.AddRequestModifications(ResolveWithGoogleDoH())
|
chain.AddOnceRequestModifications(ResolveWithGoogleDoH())
|
||||||
|
|
||||||
px.Request.URL = newURL
|
chain.Request.URL = newURL
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package requestmodifers
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
"ladder/proxychain"
|
||||||
)
|
)
|
||||||
|
|
||||||
const googleCacheUrl string = "https://webcache.googleusercontent.com/search?q=cache:"
|
const googleCacheUrl string = "https://webcache.googleusercontent.com/search?q=cache:"
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package requestmodifers
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ladder/proxychain"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
"ladder/proxychain"
|
||||||
)
|
)
|
||||||
|
|
||||||
const waybackUrl string = "https://web.archive.org/web/"
|
const waybackUrl string = "https://web.archive.org/web/"
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"ladder/proxychain"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"ladder/proxychain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// resolveWithGoogleDoH resolves DNS using Google's DNS-over-HTTPS
|
// resolveWithGoogleDoH resolves DNS using Google's DNS-over-HTTPS
|
||||||
|
|||||||
@@ -1,20 +1,29 @@
|
|||||||
package requestmodifers
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
|
tx "ladder/proxychain/responsemodifers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SpoofReferrer modifies the referrer header
|
// SpoofReferrer modifies the referrer header.
|
||||||
// useful if the page can be accessed from a search engine
|
// It is useful if the page can be accessed from a search engine
|
||||||
// or social media site, but not by browsing the website itself
|
// or social media site, but not by browsing the website itself.
|
||||||
// if url is "", then the referrer header is removed
|
// if url is "", then the referrer header is removed.
|
||||||
func SpoofReferrer(url string) proxychain.RequestModification {
|
func SpoofReferrer(url string) proxychain.RequestModification {
|
||||||
return func(px *proxychain.ProxyChain) error {
|
return func(chain *proxychain.ProxyChain) error {
|
||||||
|
|
||||||
|
// change refer on client side js
|
||||||
|
script := fmt.Sprintf(`document.referrer = "%s"`, url)
|
||||||
|
chain.AddOnceResponseModifications(
|
||||||
|
tx.InjectScriptBeforeDOMContentLoaded(script),
|
||||||
|
)
|
||||||
|
|
||||||
if url == "" {
|
if url == "" {
|
||||||
px.Request.Header.Del("referrer")
|
chain.Request.Header.Del("referrer")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
px.Request.Header.Set("referrer", url)
|
chain.Request.Header.Set("referrer", url)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ package requestmodifers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"ladder/proxychain"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"ladder/proxychain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SpoofReferrerFromBaiduSearch modifies the referrer header
|
// SpoofReferrerFromBaiduSearch modifies the referrer header
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
// pretending to be from a bing search site
|
// pretending to be from a bing search site
|
||||||
func SpoofReferrerFromBingSearch() proxychain.RequestModification {
|
func SpoofReferrerFromBingSearch() proxychain.RequestModification {
|
||||||
return func(chain *proxychain.ProxyChain) error {
|
return func(chain *proxychain.ProxyChain) error {
|
||||||
chain.AddRequestModifications(
|
chain.AddOnceRequestModifications(
|
||||||
SpoofReferrer("https://www.bing.com/"),
|
SpoofReferrer("https://www.bing.com/"),
|
||||||
SetRequestHeader("sec-fetch-site", "cross-site"),
|
SetRequestHeader("sec-fetch-site", "cross-site"),
|
||||||
SetRequestHeader("sec-fetch-dest", "document"),
|
SetRequestHeader("sec-fetch-dest", "document"),
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ import (
|
|||||||
// pretending to be from a google search site
|
// pretending to be from a google search site
|
||||||
func SpoofReferrerFromGoogleSearch() proxychain.RequestModification {
|
func SpoofReferrerFromGoogleSearch() proxychain.RequestModification {
|
||||||
return func(chain *proxychain.ProxyChain) error {
|
return func(chain *proxychain.ProxyChain) error {
|
||||||
chain.Request.Header.Set("referrer", "https://www.google.com/")
|
chain.AddOnceRequestModifications(
|
||||||
chain.Request.Header.Set("sec-fetch-site", "cross-site")
|
SpoofReferrer("https://www.google.com"),
|
||||||
chain.Request.Header.Set("sec-fetch-dest", "document")
|
SetRequestHeader("sec-fetch-site", "cross-site"),
|
||||||
chain.Request.Header.Set("sec-fetch-mode", "navigate")
|
SetRequestHeader("sec-fetch-dest", "document"),
|
||||||
ModifyQueryParams("utm_source", "google")
|
SetRequestHeader("sec-fetch-mode", "navigate"),
|
||||||
|
ModifyQueryParams("utm_source", "google"),
|
||||||
|
)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
// pretending to be from a linkedin post
|
// pretending to be from a linkedin post
|
||||||
func SpoofReferrerFromLinkedInPost() proxychain.RequestModification {
|
func SpoofReferrerFromLinkedInPost() proxychain.RequestModification {
|
||||||
return func(chain *proxychain.ProxyChain) error {
|
return func(chain *proxychain.ProxyChain) error {
|
||||||
chain.AddRequestModifications(
|
chain.AddOnceRequestModifications(
|
||||||
SpoofReferrer("https://www.linkedin.com/"),
|
SpoofReferrer("https://www.linkedin.com/"),
|
||||||
SetRequestHeader("sec-fetch-site", "cross-site"),
|
SetRequestHeader("sec-fetch-site", "cross-site"),
|
||||||
SetRequestHeader("sec-fetch-dest", "document"),
|
SetRequestHeader("sec-fetch-dest", "document"),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package requestmodifers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
// pretending to be from a tumblr post
|
// pretending to be from a tumblr post
|
||||||
func SpoofReferrerFromTumblrPost() proxychain.RequestModification {
|
func SpoofReferrerFromTumblrPost() proxychain.RequestModification {
|
||||||
return func(chain *proxychain.ProxyChain) error {
|
return func(chain *proxychain.ProxyChain) error {
|
||||||
chain.AddRequestModifications(
|
chain.AddOnceRequestModifications(
|
||||||
SpoofReferrer("https://www.tumblr.com/"),
|
SpoofReferrer("https://www.tumblr.com/"),
|
||||||
SetRequestHeader("sec-fetch-site", "cross-site"),
|
SetRequestHeader("sec-fetch-site", "cross-site"),
|
||||||
SetRequestHeader("sec-fetch-dest", "document"),
|
SetRequestHeader("sec-fetch-dest", "document"),
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
// pretending to be from a twitter post
|
// pretending to be from a twitter post
|
||||||
func SpoofReferrerFromTwitterPost() proxychain.RequestModification {
|
func SpoofReferrerFromTwitterPost() proxychain.RequestModification {
|
||||||
return func(chain *proxychain.ProxyChain) error {
|
return func(chain *proxychain.ProxyChain) error {
|
||||||
chain.AddRequestModifications(
|
chain.AddOnceRequestModifications(
|
||||||
SpoofReferrer("https://t.co/"),
|
SpoofReferrer("https://t.co/"),
|
||||||
SetRequestHeader("sec-fetch-site", "cross-site"),
|
SetRequestHeader("sec-fetch-site", "cross-site"),
|
||||||
SetRequestHeader("sec-fetch-dest", "document"),
|
SetRequestHeader("sec-fetch-dest", "document"),
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
// pretending to be from a vkontakte post (popular in Russia)
|
// pretending to be from a vkontakte post (popular in Russia)
|
||||||
func SpoofReferrerFromVkontaktePost() proxychain.RequestModification {
|
func SpoofReferrerFromVkontaktePost() proxychain.RequestModification {
|
||||||
return func(chain *proxychain.ProxyChain) error {
|
return func(chain *proxychain.ProxyChain) error {
|
||||||
chain.AddRequestModifications(
|
chain.AddOnceRequestModifications(
|
||||||
SpoofReferrer("https://away.vk.com/"),
|
SpoofReferrer("https://away.vk.com/"),
|
||||||
SetRequestHeader("sec-fetch-site", "cross-site"),
|
SetRequestHeader("sec-fetch-site", "cross-site"),
|
||||||
SetRequestHeader("sec-fetch-dest", "document"),
|
SetRequestHeader("sec-fetch-dest", "document"),
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ package requestmodifers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"ladder/proxychain"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
|
"ladder/proxychain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SpoofReferrerFromWeiboPost modifies the referrer header
|
// SpoofReferrerFromWeiboPost modifies the referrer header
|
||||||
|
|||||||
@@ -1,13 +1,39 @@
|
|||||||
package requestmodifers
|
package requestmodifers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
|
tx "ladder/proxychain/responsemodifers"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// https://github.com/faisalman/ua-parser-js/tree/master
|
||||||
|
// update using:
|
||||||
|
// git submodule update --remote --merge
|
||||||
|
//
|
||||||
|
//go:embed vendor/ua-parser-js/dist/ua-parser.min.js
|
||||||
|
var UAParserJS string
|
||||||
|
|
||||||
|
// note: spoof_user_agent.js has a dependency on ua-parser.min.js
|
||||||
|
// ua-parser.min.js should be loaded first.
|
||||||
|
//
|
||||||
|
//go:embed spoof_user_agent.js
|
||||||
|
var spoofUserAgentJS string
|
||||||
|
|
||||||
// SpoofUserAgent modifies the user agent
|
// SpoofUserAgent modifies the user agent
|
||||||
func SpoofUserAgent(ua string) proxychain.RequestModification {
|
func SpoofUserAgent(ua string) proxychain.RequestModification {
|
||||||
return func(px *proxychain.ProxyChain) error {
|
return func(chain *proxychain.ProxyChain) error {
|
||||||
px.Request.Header.Set("user-agent", ua)
|
// modify ua headers
|
||||||
|
chain.AddOnceRequestModifications(
|
||||||
|
SetRequestHeader("user-agent", ua),
|
||||||
|
)
|
||||||
|
|
||||||
|
script := strings.ReplaceAll(spoofUserAgentJS, "{{USER_AGENT}}", ua)
|
||||||
|
chain.AddOnceResponseModifications(
|
||||||
|
tx.InjectScriptBeforeDOMContentLoaded(script),
|
||||||
|
tx.InjectScriptBeforeDOMContentLoaded(UAParserJS),
|
||||||
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
100
proxychain/requestmodifers/spoof_user_agent.js
Normal file
100
proxychain/requestmodifers/spoof_user_agent.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
(() => {
|
||||||
|
const UA = "{{USER_AGENT}}";
|
||||||
|
|
||||||
|
// monkey-patch navigator.userAgent
|
||||||
|
{
|
||||||
|
const { get } = Object.getOwnPropertyDescriptor(
|
||||||
|
Navigator.prototype,
|
||||||
|
"userAgent",
|
||||||
|
);
|
||||||
|
Object.defineProperty(Navigator.prototype, "userAgent", {
|
||||||
|
get: new Proxy(get, {
|
||||||
|
apply() {
|
||||||
|
return UA;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// monkey-patch navigator.appVersion
|
||||||
|
{
|
||||||
|
const { get } = Object.getOwnPropertyDescriptor(
|
||||||
|
Navigator.prototype,
|
||||||
|
"appVersion",
|
||||||
|
);
|
||||||
|
Object.defineProperty(Navigator.prototype, "appVersion", {
|
||||||
|
get: new Proxy(get, {
|
||||||
|
apply() {
|
||||||
|
return UA.replace("Mozilla/", "");
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// monkey-patch navigator.UserAgentData
|
||||||
|
// Assuming UAParser is already loaded and available
|
||||||
|
function spoofUserAgentData(uaString) {
|
||||||
|
// Parse the user-agent string
|
||||||
|
const parser = new UAParser(uaString);
|
||||||
|
const parsedData = parser.getResult();
|
||||||
|
|
||||||
|
// Extracted data
|
||||||
|
const platform = parsedData.os.name;
|
||||||
|
const browserName = parsedData.browser.name;
|
||||||
|
const browserMajorVersion = parsedData.browser.major;
|
||||||
|
const isMobile =
|
||||||
|
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
||||||
|
uaString,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Overwrite navigator.userAgentData
|
||||||
|
self.NavigatorUAData = self.NavigatorUAData || new class NavigatorUAData {
|
||||||
|
brands = [{
|
||||||
|
brand: browserName,
|
||||||
|
version: browserMajorVersion,
|
||||||
|
}];
|
||||||
|
mobile = isMobile;
|
||||||
|
platform = platform;
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
brands: this.brands,
|
||||||
|
mobile: this.mobile,
|
||||||
|
platform: this.platform,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
getHighEntropyValues(hints) {
|
||||||
|
const result = this.toJSON();
|
||||||
|
// Add additional high entropy values based on hints
|
||||||
|
// Modify these as per your requirements
|
||||||
|
if (hints.includes("architecture")) {
|
||||||
|
result.architecture = "x86";
|
||||||
|
}
|
||||||
|
if (hints.includes("bitness")) {
|
||||||
|
result.bitness = "64";
|
||||||
|
}
|
||||||
|
if (hints.includes("model")) {
|
||||||
|
result.model = "";
|
||||||
|
}
|
||||||
|
if (hints.includes("platformVersion")) {
|
||||||
|
result.platformVersion = "10.0.0"; // Example value
|
||||||
|
}
|
||||||
|
if (hints.includes("uaFullVersion")) {
|
||||||
|
result.uaFullVersion = browserMajorVersion;
|
||||||
|
}
|
||||||
|
if (hints.includes("fullVersionList")) {
|
||||||
|
result.fullVersionList = this.brands;
|
||||||
|
}
|
||||||
|
return Promise.resolve(result);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
// Apply the monkey patch
|
||||||
|
Object.defineProperty(navigator, "userAgentData", {
|
||||||
|
value: new self.NavigatorUAData(),
|
||||||
|
writable: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
spoofUserAgentData(UA);
|
||||||
|
// TODO: use hideMonkeyPatch to hide overrides
|
||||||
|
})();
|
||||||
1
proxychain/requestmodifers/vendor/ua-parser-js
vendored
Submodule
1
proxychain/requestmodifers/vendor/ua-parser-js
vendored
Submodule
Submodule proxychain/requestmodifers/vendor/ua-parser-js added at 5173a5442f
@@ -2,9 +2,10 @@ package responsemodifers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
"ladder/proxychain/responsemodifers/rewriters"
|
"ladder/proxychain/responsemodifers/rewriters"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// injectScript modifies HTTP responses
|
// injectScript modifies HTTP responses
|
||||||
@@ -25,16 +26,16 @@ func injectScript(js string, execTime rewriters.ScriptExecTime) proxychain.Respo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// InjectScriptBeforeDOMContentLoaded modifies HTTP responses to inject a JS before DOM Content is loaded (script tag in head)
|
// InjectScriptBeforeDOMContentLoaded modifies HTTP responses to inject a JS before DOM Content is loaded (script tag in head)
|
||||||
func InjectScriptBeforeDOMContentLoaded(js string, execTime rewriters.ScriptExecTime) proxychain.ResponseModification {
|
func InjectScriptBeforeDOMContentLoaded(js string) proxychain.ResponseModification {
|
||||||
return injectScript(js, rewriters.BeforeDOMContentLoaded)
|
return injectScript(js, rewriters.BeforeDOMContentLoaded)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InjectScriptAfterDOMContentLoaded modifies HTTP responses to inject a JS after DOM Content is loaded (script tag in head)
|
// InjectScriptAfterDOMContentLoaded modifies HTTP responses to inject a JS after DOM Content is loaded (script tag in head)
|
||||||
func InjectScriptAfterDOMContentLoaded(js string, execTime rewriters.ScriptExecTime) proxychain.ResponseModification {
|
func InjectScriptAfterDOMContentLoaded(js string) proxychain.ResponseModification {
|
||||||
return injectScript(js, rewriters.AfterDOMContentLoaded)
|
return injectScript(js, rewriters.AfterDOMContentLoaded)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InjectScriptAfterDOMIdle modifies HTTP responses to inject a JS after the DOM is idle (ie: js framework loaded)
|
// InjectScriptAfterDOMIdle modifies HTTP responses to inject a JS after the DOM is idle (ie: js framework loaded)
|
||||||
func InjectScriptAfterDOMIdle(js string, execTime rewriters.ScriptExecTime) proxychain.ResponseModification {
|
func InjectScriptAfterDOMIdle(js string) proxychain.ResponseModification {
|
||||||
return injectScript(js, rewriters.AfterDOMIdle)
|
return injectScript(js, rewriters.AfterDOMIdle)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ package responsemodifers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"ladder/proxychain"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"ladder/proxychain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DeleteIncomingCookies prevents ALL cookies from being sent from the proxy server
|
// DeleteIncomingCookies prevents ALL cookies from being sent from the proxy server
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ package responsemodifers
|
|||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
"ladder/proxychain/responsemodifers/rewriters"
|
"ladder/proxychain/responsemodifers/rewriters"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed patch_dynamic_resource_urls.js
|
//go:embed patch_dynamic_resource_urls.js
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// Also overrides the attribute setter prototype to modify the request URLs
|
// Also overrides the attribute setter prototype to modify the request URLs
|
||||||
// fetch("/relative_script.js") -> fetch("http://localhost:8080/relative_script.js")
|
// fetch("/relative_script.js") -> fetch("http://localhost:8080/relative_script.js")
|
||||||
(() => {
|
(() => {
|
||||||
|
|
||||||
// ============== PARAMS ===========================
|
// ============== PARAMS ===========================
|
||||||
// if the original request was: http://localhost:8080/http://proxiedsite.com/foo/bar
|
// if the original request was: http://localhost:8080/http://proxiedsite.com/foo/bar
|
||||||
// proxyOrigin is http://localhost:8080
|
// proxyOrigin is http://localhost:8080
|
||||||
@@ -29,16 +28,20 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
function rewriteURL(url) {
|
function rewriteURL(url) {
|
||||||
const oldUrl = url
|
const oldUrl = url;
|
||||||
if (!url) return url
|
if (!url) return url;
|
||||||
let isStr = (typeof url.startsWith === 'function')
|
let isStr = typeof url.startsWith === "function";
|
||||||
if (!isStr) return url
|
if (!isStr) return url;
|
||||||
|
|
||||||
// don't rewrite special URIs
|
// don't rewrite special URIs
|
||||||
if (blacklistedSchemes.includes(url)) return url;
|
if (blacklistedSchemes.includes(url)) return url;
|
||||||
|
|
||||||
// don't rewrite invalid URIs
|
// don't rewrite invalid URIs
|
||||||
try { new URL(url, origin) } catch { return url }
|
try {
|
||||||
|
new URL(url, origin);
|
||||||
|
} catch {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
// don't double rewrite
|
// don't double rewrite
|
||||||
if (url.startsWith(proxyOrigin)) return url;
|
if (url.startsWith(proxyOrigin)) return url;
|
||||||
@@ -57,22 +60,23 @@
|
|||||||
} else if (url.startsWith("/")) {
|
} else if (url.startsWith("/")) {
|
||||||
url = `/${origin}/${encodeURIComponent(url.substring(1))}`;
|
url = `/${origin}/${encodeURIComponent(url.substring(1))}`;
|
||||||
} else if (url.startsWith(origin)) {
|
} else if (url.startsWith(origin)) {
|
||||||
url = `/${encodeURIComponent(url)}`
|
url = `/${encodeURIComponent(url)}`;
|
||||||
} else if (url.startsWith("http://") || url.startsWith("https://")) {
|
} else if (url.startsWith("http://") || url.startsWith("https://")) {
|
||||||
url = `/${proxyOrigin}/${encodeURIComponent(url)}`;
|
url = `/${proxyOrigin}/${encodeURIComponent(url)}`;
|
||||||
}
|
}
|
||||||
console.log(`proxychain: rewrite JS URL: ${oldUrl} -> ${url}`)
|
console.log(`proxychain: rewrite JS URL: ${oldUrl} -> ${url}`);
|
||||||
return url;
|
return url;
|
||||||
};
|
}
|
||||||
|
|
||||||
// sometimes anti-bot protections like cloudflare or akamai bot manager check if JS is hooked
|
// sometimes anti-bot protections like cloudflare or akamai bot manager check if JS is hooked
|
||||||
function hideMonkeyPatch(objectOrName, method, originalToString) {
|
function hideMonkeyPatch(objectOrName, method, originalToString) {
|
||||||
let obj;
|
let obj;
|
||||||
let isGlobalFunction = false;
|
let isGlobalFunction = false;
|
||||||
|
|
||||||
if (typeof objectOrName === 'string') {
|
if (typeof objectOrName === "string") {
|
||||||
obj = globalThis[objectOrName];
|
obj = globalThis[objectOrName];
|
||||||
isGlobalFunction = (typeof obj === 'function') && (method === objectOrName);
|
isGlobalFunction = (typeof obj === "function") &&
|
||||||
|
(method === objectOrName);
|
||||||
} else {
|
} else {
|
||||||
obj = objectOrName;
|
obj = objectOrName;
|
||||||
}
|
}
|
||||||
@@ -83,71 +87,101 @@
|
|||||||
return originalFunction.apply(this, args);
|
return originalFunction.apply(this, args);
|
||||||
};
|
};
|
||||||
globalThis[objectOrName].toString = () => originalToString;
|
globalThis[objectOrName].toString = () => originalToString;
|
||||||
} else if (obj && typeof obj[method] === 'function') {
|
} else if (obj && typeof obj[method] === "function") {
|
||||||
const originalMethod = obj[method];
|
const originalMethod = obj[method];
|
||||||
obj[method] = function(...args) {
|
obj[method] = function(...args) {
|
||||||
return originalMethod.apply(this, args);
|
return originalMethod.apply(this, args);
|
||||||
};
|
};
|
||||||
obj[method].toString = () => originalToString;
|
obj[method].toString = () => originalToString;
|
||||||
} else {
|
} else {
|
||||||
console.warn(`proxychain: cannot hide monkey patch: ${method} is not a function on the provided object.`);
|
console.warn(
|
||||||
|
`proxychain: cannot hide monkey patch: ${method} is not a function on the provided object.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// monkey patch fetch
|
// monkey patch fetch
|
||||||
const oldFetch = fetch;
|
const oldFetch = fetch;
|
||||||
fetch = async (url, init) => {
|
fetch = async (url, init) => {
|
||||||
return oldFetch(rewriteURL(url), init)
|
return oldFetch(rewriteURL(url), init);
|
||||||
}
|
};
|
||||||
hideMonkeyPatch('fetch', 'fetch', 'function fetch() { [native code] }')
|
hideMonkeyPatch("fetch", "fetch", "function fetch() { [native code] }");
|
||||||
|
|
||||||
// monkey patch xmlhttprequest
|
// monkey patch xmlhttprequest
|
||||||
const oldOpen = XMLHttpRequest.prototype.open;
|
const oldOpen = XMLHttpRequest.prototype.open;
|
||||||
XMLHttpRequest.prototype.open = function(method, url, async = true, user = null, password = null) {
|
XMLHttpRequest.prototype.open = function(
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
async = true,
|
||||||
|
user = null,
|
||||||
|
password = null,
|
||||||
|
) {
|
||||||
return oldOpen.call(this, method, rewriteURL(url), async, user, password);
|
return oldOpen.call(this, method, rewriteURL(url), async, user, password);
|
||||||
};
|
};
|
||||||
hideMonkeyPatch(XMLHttpRequest.prototype, 'open', 'function(){if("function"==typeof eo)return eo.apply(this,arguments)}');
|
hideMonkeyPatch(
|
||||||
|
XMLHttpRequest.prototype,
|
||||||
|
"open",
|
||||||
|
'function(){if("function"==typeof eo)return eo.apply(this,arguments)}',
|
||||||
|
);
|
||||||
|
|
||||||
const oldSend = XMLHttpRequest.prototype.send;
|
const oldSend = XMLHttpRequest.prototype.send;
|
||||||
XMLHttpRequest.prototype.send = function(method, url) {
|
XMLHttpRequest.prototype.send = function(method, url) {
|
||||||
return oldSend.call(this, method, rewriteURL(url));
|
return oldSend.call(this, method, rewriteURL(url));
|
||||||
};
|
};
|
||||||
hideMonkeyPatch(XMLHttpRequest.prototype, 'send', 'function(){if("function"==typeof eo)return eo.apply(this,arguments)}');
|
hideMonkeyPatch(
|
||||||
|
XMLHttpRequest.prototype,
|
||||||
|
"send",
|
||||||
|
'function(){if("function"==typeof eo)return eo.apply(this,arguments)}',
|
||||||
|
);
|
||||||
|
|
||||||
// monkey patch service worker registration
|
// monkey patch service worker registration
|
||||||
const oldRegister = ServiceWorkerContainer.prototype.register;
|
const oldRegister = ServiceWorkerContainer.prototype.register;
|
||||||
ServiceWorkerContainer.prototype.register = function(scriptURL, options) {
|
ServiceWorkerContainer.prototype.register = function(scriptURL, options) {
|
||||||
return oldRegister.call(this, rewriteURL(scriptURL), options)
|
return oldRegister.call(this, rewriteURL(scriptURL), options);
|
||||||
}
|
};
|
||||||
hideMonkeyPatch(ServiceWorkerContainer.prototype, 'register', 'function register() { [native code] }')
|
hideMonkeyPatch(
|
||||||
|
ServiceWorkerContainer.prototype,
|
||||||
|
"register",
|
||||||
|
"function register() { [native code] }",
|
||||||
|
);
|
||||||
|
|
||||||
// monkey patch URL.toString() method
|
// monkey patch URL.toString() method
|
||||||
const oldToString = URL.prototype.toString
|
const oldToString = URL.prototype.toString;
|
||||||
URL.prototype.toString = function() {
|
URL.prototype.toString = function() {
|
||||||
let originalURL = oldToString.call(this)
|
let originalURL = oldToString.call(this);
|
||||||
return rewriteURL(originalURL)
|
return rewriteURL(originalURL);
|
||||||
}
|
};
|
||||||
hideMonkeyPatch(URL.prototype, 'toString', 'function toString() { [native code] }')
|
hideMonkeyPatch(
|
||||||
|
URL.prototype,
|
||||||
|
"toString",
|
||||||
|
"function toString() { [native code] }",
|
||||||
|
);
|
||||||
|
|
||||||
// monkey patch URL.toJSON() method
|
// monkey patch URL.toJSON() method
|
||||||
const oldToJson = URL.prototype.toString
|
const oldToJson = URL.prototype.toString;
|
||||||
URL.prototype.toString = function() {
|
URL.prototype.toString = function() {
|
||||||
let originalURL = oldToJson.call(this)
|
let originalURL = oldToJson.call(this);
|
||||||
return rewriteURL(originalURL)
|
return rewriteURL(originalURL);
|
||||||
}
|
};
|
||||||
hideMonkeyPatch(URL.prototype, 'toString', 'function toJSON() { [native code] }')
|
hideMonkeyPatch(
|
||||||
|
URL.prototype,
|
||||||
|
"toString",
|
||||||
|
"function toJSON() { [native code] }",
|
||||||
|
);
|
||||||
|
|
||||||
// Monkey patch URL.href getter and setter
|
// Monkey patch URL.href getter and setter
|
||||||
const originalHrefDescriptor = Object.getOwnPropertyDescriptor(URL.prototype, 'href');
|
const originalHrefDescriptor = Object.getOwnPropertyDescriptor(
|
||||||
Object.defineProperty(URL.prototype, 'href', {
|
URL.prototype,
|
||||||
|
"href",
|
||||||
|
);
|
||||||
|
Object.defineProperty(URL.prototype, "href", {
|
||||||
get: function() {
|
get: function() {
|
||||||
let originalHref = originalHrefDescriptor.get.call(this);
|
let originalHref = originalHrefDescriptor.get.call(this);
|
||||||
return rewriteURL(originalHref)
|
return rewriteURL(originalHref);
|
||||||
},
|
},
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
originalHrefDescriptor.set.call(this, rewriteURL(newValue));
|
originalHrefDescriptor.set.call(this, rewriteURL(newValue));
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: do one more pass of this by manually traversing the DOM
|
// TODO: do one more pass of this by manually traversing the DOM
|
||||||
@@ -155,34 +189,34 @@
|
|||||||
|
|
||||||
// Monkey patch setter
|
// Monkey patch setter
|
||||||
const elements = [
|
const elements = [
|
||||||
{ tag: 'a', attribute: 'href' },
|
{ tag: "a", attribute: "href" },
|
||||||
{ tag: 'img', attribute: 'src' },
|
{ tag: "img", attribute: "src" },
|
||||||
// { tag: 'img', attribute: 'srcset' }, // TODO: handle srcset
|
// { tag: 'img', attribute: 'srcset' }, // TODO: handle srcset
|
||||||
{ tag: 'script', attribute: 'src' },
|
{ tag: "script", attribute: "src" },
|
||||||
{ tag: 'link', attribute: 'href' },
|
{ tag: "link", attribute: "href" },
|
||||||
{ tag: 'link', attribute: 'icon' },
|
{ tag: "link", attribute: "icon" },
|
||||||
{ tag: 'iframe', attribute: 'src' },
|
{ tag: "iframe", attribute: "src" },
|
||||||
{ tag: 'audio', attribute: 'src' },
|
{ tag: "audio", attribute: "src" },
|
||||||
{ tag: 'video', attribute: 'src' },
|
{ tag: "video", attribute: "src" },
|
||||||
{ tag: 'source', attribute: 'src' },
|
{ tag: "source", attribute: "src" },
|
||||||
// { tag: 'source', attribute: 'srcset' }, // TODO: handle srcset
|
// { tag: 'source', attribute: 'srcset' }, // TODO: handle srcset
|
||||||
{ tag: 'embed', attribute: 'src' },
|
{ tag: "embed", attribute: "src" },
|
||||||
{ tag: 'embed', attribute: 'pluginspage' },
|
{ tag: "embed", attribute: "pluginspage" },
|
||||||
{ tag: 'html', attribute: 'manifest' },
|
{ tag: "html", attribute: "manifest" },
|
||||||
{ tag: 'object', attribute: 'src' },
|
{ tag: "object", attribute: "src" },
|
||||||
{ tag: 'input', attribute: 'src' },
|
{ tag: "input", attribute: "src" },
|
||||||
{ tag: 'track', attribute: 'src' },
|
{ tag: "track", attribute: "src" },
|
||||||
{ tag: 'form', attribute: 'action' },
|
{ tag: "form", attribute: "action" },
|
||||||
{ tag: 'area', attribute: 'href' },
|
{ tag: "area", attribute: "href" },
|
||||||
{ tag: 'base', attribute: 'href' },
|
{ tag: "base", attribute: "href" },
|
||||||
{ tag: 'blockquote', attribute: 'cite' },
|
{ tag: "blockquote", attribute: "cite" },
|
||||||
{ tag: 'del', attribute: 'cite' },
|
{ tag: "del", attribute: "cite" },
|
||||||
{ tag: 'ins', attribute: 'cite' },
|
{ tag: "ins", attribute: "cite" },
|
||||||
{ tag: 'q', attribute: 'cite' },
|
{ tag: "q", attribute: "cite" },
|
||||||
{ tag: 'button', attribute: 'formaction' },
|
{ tag: "button", attribute: "formaction" },
|
||||||
{ tag: 'input', attribute: 'formaction' },
|
{ tag: "input", attribute: "formaction" },
|
||||||
{ tag: 'meta', attribute: 'content' },
|
{ tag: "meta", attribute: "content" },
|
||||||
{ tag: 'object', attribute: 'data' },
|
{ tag: "object", attribute: "data" },
|
||||||
];
|
];
|
||||||
|
|
||||||
elements.forEach(({ tag, attribute }) => {
|
elements.forEach(({ tag, attribute }) => {
|
||||||
@@ -195,7 +229,7 @@
|
|||||||
// calling rewriteURL will end up calling a setter for href,
|
// calling rewriteURL will end up calling a setter for href,
|
||||||
// leading to a recusive loop and a Maximum call stack size exceeded
|
// leading to a recusive loop and a Maximum call stack size exceeded
|
||||||
// error, so we guard against this with a local semaphore flag
|
// error, so we guard against this with a local semaphore flag
|
||||||
const isRewritingSetKey = Symbol.for('isRewritingSet');
|
const isRewritingSetKey = Symbol.for("isRewritingSet");
|
||||||
if (!this[isRewritingSetKey]) {
|
if (!this[isRewritingSetKey]) {
|
||||||
this[isRewritingSetKey] = true;
|
this[isRewritingSetKey] = true;
|
||||||
descriptor.set.call(this, rewriteURL(value));
|
descriptor.set.call(this, rewriteURL(value));
|
||||||
@@ -207,44 +241,43 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
get() {
|
get() {
|
||||||
const isRewritingGetKey = Symbol.for('isRewritingGet');
|
const isRewritingGetKey = Symbol.for("isRewritingGet");
|
||||||
if (!this[isRewritingGetKey]) {
|
if (!this[isRewritingGetKey]) {
|
||||||
this[isRewritingGetKey] = true;
|
this[isRewritingGetKey] = true;
|
||||||
let oldURL = descriptor.get.call(this);
|
let oldURL = descriptor.get.call(this);
|
||||||
let newURL = rewriteURL(oldURL);
|
let newURL = rewriteURL(oldURL);
|
||||||
this[isRewritingGetKey] = false;
|
this[isRewritingGetKey] = false;
|
||||||
return newURL
|
return newURL;
|
||||||
} else {
|
} else {
|
||||||
return descriptor.get.call(this);
|
return descriptor.get.call(this);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// sometimes, libraries will set the Element.innerHTML or Element.outerHTML directly with a string instead of setters.
|
// sometimes, libraries will set the Element.innerHTML or Element.outerHTML directly with a string instead of setters.
|
||||||
// in this case, we intercept it, create a fake DOM, parse it and then rewrite all attributes that could
|
// in this case, we intercept it, create a fake DOM, parse it and then rewrite all attributes that could
|
||||||
// contain a URL. Then we return the replacement innerHTML/outerHTML with redirected links.
|
// contain a URL. Then we return the replacement innerHTML/outerHTML with redirected links.
|
||||||
function rewriteInnerHTML(html, elements) {
|
function rewriteInnerHTML(html, elements) {
|
||||||
const isRewritingHTMLKey = Symbol.for('isRewritingHTML');
|
const isRewritingHTMLKey = Symbol.for("isRewritingHTML");
|
||||||
|
|
||||||
// Check if already processing
|
// Check if already processing
|
||||||
if (document[isRewritingHTMLKey]) {
|
if (document[isRewritingHTMLKey]) {
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tempContainer = document.createElement('div');
|
const tempContainer = document.createElement("div");
|
||||||
document[isRewritingHTMLKey] = true;
|
document[isRewritingHTMLKey] = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
tempContainer.innerHTML = html;
|
tempContainer.innerHTML = html;
|
||||||
|
|
||||||
// Create a map for quick lookup
|
// Create a map for quick lookup
|
||||||
const elementsMap = new Map(elements.map(e => [e.tag, e.attribute]));
|
const elementsMap = new Map(elements.map((e) => [e.tag, e.attribute]));
|
||||||
|
|
||||||
// Loop-based DOM traversal
|
// Loop-based DOM traversal
|
||||||
const nodes = [...tempContainer.querySelectorAll('*')];
|
const nodes = [...tempContainer.querySelectorAll("*")];
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
const attribute = elementsMap.get(node.tagName.toLowerCase());
|
const attribute = elementsMap.get(node.tagName.toLowerCase());
|
||||||
if (attribute && node.hasAttribute(attribute)) {
|
if (attribute && node.hasAttribute(attribute)) {
|
||||||
@@ -261,19 +294,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Store original setters
|
// Store original setters
|
||||||
const originalSetters = {};
|
const originalSetters = {};
|
||||||
|
|
||||||
['innerHTML', 'outerHTML'].forEach(property => {
|
["innerHTML", "outerHTML"].forEach((property) => {
|
||||||
const descriptor = Object.getOwnPropertyDescriptor(Element.prototype, property);
|
const descriptor = Object.getOwnPropertyDescriptor(
|
||||||
|
Element.prototype,
|
||||||
|
property,
|
||||||
|
);
|
||||||
if (descriptor && descriptor.set) {
|
if (descriptor && descriptor.set) {
|
||||||
originalSetters[property] = descriptor.set;
|
originalSetters[property] = descriptor.set;
|
||||||
|
|
||||||
Object.defineProperty(Element.prototype, property, {
|
Object.defineProperty(Element.prototype, property, {
|
||||||
...descriptor,
|
...descriptor,
|
||||||
set(value) {
|
set(value) {
|
||||||
const isRewritingHTMLKey = Symbol.for('isRewritingHTML');
|
const isRewritingHTMLKey = Symbol.for("isRewritingHTML");
|
||||||
if (!this[isRewritingHTMLKey]) {
|
if (!this[isRewritingHTMLKey]) {
|
||||||
this[isRewritingHTMLKey] = true;
|
this[isRewritingHTMLKey] = true;
|
||||||
try {
|
try {
|
||||||
@@ -286,40 +321,8 @@ const originalSetters = {};
|
|||||||
// Use original setter in recursive call
|
// Use original setter in recursive call
|
||||||
originalSetters[property].call(this, value);
|
originalSetters[property].call(this, value);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(() => {
|
|
||||||
document.addEventListener('DOMContentLoaded', (event) => {
|
|
||||||
initIdleMutationObserver();
|
|
||||||
});
|
|
||||||
|
|
||||||
function initIdleMutationObserver() {
|
|
||||||
let debounceTimer;
|
|
||||||
const debounceDelay = 500; // adjust the delay as needed
|
|
||||||
|
|
||||||
const observer = new MutationObserver((mutations) => {
|
|
||||||
// Clear the previous timer and set a new one
|
|
||||||
clearTimeout(debounceTimer);
|
|
||||||
debounceTimer = setTimeout(() => {
|
|
||||||
execute();
|
|
||||||
observer.disconnect(); // Disconnect after first execution
|
|
||||||
}, debounceDelay);
|
|
||||||
});
|
|
||||||
|
|
||||||
const config = { attributes: false, childList: true, subtree: true };
|
|
||||||
observer.observe(document.body, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
function execute() {
|
|
||||||
console.log('DOM is now idle. Executing...');
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ package responsemodifers
|
|||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"ladder/proxychain"
|
"ladder/proxychain"
|
||||||
"ladder/proxychain/responsemodifers/rewriters"
|
"ladder/proxychain/responsemodifers/rewriters"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// RewriteHTMLResourceURLs modifies HTTP responses
|
// RewriteHTMLResourceURLs modifies HTTP responses
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ func (r *HTMLRewriter) Close() error {
|
|||||||
|
|
||||||
// Read processes the HTML content, rewriting URLs and managing the state of tokens.
|
// Read processes the HTML content, rewriting URLs and managing the state of tokens.
|
||||||
func (r *HTMLRewriter) Read(p []byte) (int, error) {
|
func (r *HTMLRewriter) Read(p []byte) (int, error) {
|
||||||
|
|
||||||
if r.currentToken == nil || r.currentToken.Data == "" || r.currentTokenProcessed {
|
if r.currentToken == nil || r.currentToken.Data == "" || r.currentTokenProcessed {
|
||||||
tokenType := r.tokenizer.Next()
|
tokenType := r.tokenizer.Next()
|
||||||
|
|
||||||
|
|||||||
@@ -3,18 +3,21 @@ package rewriters
|
|||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/net/html/atom"
|
|
||||||
"log"
|
"log"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/html/atom"
|
||||||
|
|
||||||
"golang.org/x/net/html"
|
"golang.org/x/net/html"
|
||||||
)
|
)
|
||||||
|
|
||||||
var rewriteAttrs map[string]map[string]bool
|
var (
|
||||||
var specialRewriteAttrs map[string]map[string]bool
|
rewriteAttrs map[string]map[string]bool
|
||||||
var schemeBlacklist map[string]bool
|
specialRewriteAttrs map[string]map[string]bool
|
||||||
|
schemeBlacklist map[string]bool
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// define all tag/attributes which might contain URLs
|
// define all tag/attributes which might contain URLs
|
||||||
@@ -66,7 +69,6 @@ func init() {
|
|||||||
"wss": true,
|
"wss": true,
|
||||||
"ftp": true,
|
"ftp": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTMLTokenURLRewriter implements HTMLTokenRewriter
|
// HTMLTokenURLRewriter implements HTMLTokenRewriter
|
||||||
@@ -162,7 +164,7 @@ func handleRootRelativePath(attr *html.Attribute, baseURL *url.URL) {
|
|||||||
// log.Printf("BASEURL patch: %s\n", baseURL)
|
// log.Printf("BASEURL patch: %s\n", baseURL)
|
||||||
|
|
||||||
attr.Val = fmt.Sprintf(
|
attr.Val = fmt.Sprintf(
|
||||||
"/%s://%s/%s",
|
"%s://%s/%s",
|
||||||
baseURL.Scheme,
|
baseURL.Scheme,
|
||||||
baseURL.Host,
|
baseURL.Host,
|
||||||
strings.TrimPrefix(attr.Val, "/"),
|
strings.TrimPrefix(attr.Val, "/"),
|
||||||
@@ -184,7 +186,7 @@ func handleDocumentRelativePath(attr *html.Attribute, baseURL *url.URL) {
|
|||||||
strings.Trim(attr.Val, "/"),
|
strings.Trim(attr.Val, "/"),
|
||||||
)
|
)
|
||||||
attr.Val = escape(attr.Val)
|
attr.Val = escape(attr.Val)
|
||||||
attr.Val = fmt.Sprintf("/%s", attr.Val)
|
attr.Val = fmt.Sprintf("%s://%s/%s", baseURL.Scheme, baseURL.Host, attr.Val)
|
||||||
log.Printf("doc rel url rewritten-> '%s'='%s'", attr.Key, attr.Val)
|
log.Printf("doc rel url rewritten-> '%s'='%s'", attr.Key, attr.Val)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +201,7 @@ func handleAbsolutePath(attr *html.Attribute, baseURL *url.URL) {
|
|||||||
if !(u.Scheme == "http" || u.Scheme == "https") {
|
if !(u.Scheme == "http" || u.Scheme == "https") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
attr.Val = fmt.Sprintf("/%s", escape(strings.TrimPrefix(attr.Val, "/")))
|
attr.Val = fmt.Sprintf("%s://%s/%s", baseURL.Scheme, baseURL.Host, escape(strings.TrimPrefix(attr.Val, "/")))
|
||||||
log.Printf("abs url rewritten-> '%s'='%s'", attr.Key, attr.Val)
|
log.Printf("abs url rewritten-> '%s'='%s'", attr.Key, attr.Val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user