mirror of
https://gh-proxy.org/https://github.com/klinsmaya/new-api.git
synced 2026-11-08 01:17:09 +08:00
utils.fetch is a synchronous, read-only GET or HEAD to the channel Base URL host or the plugin's allowedHosts, through the channel proxy used for polling. It is available in buildSubmitRequest, the query and batch build and parse hooks, and buildContentRequest; every other hook throws. Each hook call gets at most 4 fetches, 15 s per fetch, 30 s in total and 1 MiB per body. Redirects are returned, not followed. responseType "bytes" returns an ArrayBuffer for binary data such as a media file header. While a fetch waits, the runtime gives up its execution slot and the watchdog pauses. BuildContentRequest now receives the request context, and the media proxy shares the plugin request header rules.
70 lines
2.3 KiB
Go
70 lines
2.3 KiB
Go
package jsplugin
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"golang.org/x/net/http/httpguts"
|
|
)
|
|
|
|
// ValidateRequestURL prevents plugins from directing a channel credential to
|
|
// hosts other than the configured base URL or an administrator-approved host.
|
|
func ValidateRequestURL(requestURL, baseURL string, allowedHosts []string) error {
|
|
request, err := url.Parse(requestURL)
|
|
if err != nil || request.Scheme == "" || request.Host == "" {
|
|
return fmt.Errorf("plugin request URL must be absolute")
|
|
}
|
|
base, err := url.Parse(baseURL)
|
|
if err != nil || base.Host == "" {
|
|
return fmt.Errorf("channel base URL is invalid")
|
|
}
|
|
requestHost := canonicalHost(request)
|
|
if requestHost == canonicalHost(base) {
|
|
return nil
|
|
}
|
|
for _, allowed := range allowedHosts {
|
|
// Parse with the request scheme so "host:443" matches an https request
|
|
// the same way an explicit default port on the request URL does.
|
|
allowedURL, parseErr := url.Parse(request.Scheme + "://" + strings.TrimSpace(allowed))
|
|
if parseErr == nil && requestHost == canonicalHost(allowedURL) {
|
|
return nil
|
|
}
|
|
}
|
|
return fmt.Errorf("plugin request host %q is not allowed", request.Host)
|
|
}
|
|
|
|
// ValidateRequestHeaders rejects plugin-chosen headers that the host
|
|
// transport owns or that change how a request is framed or proxied, and
|
|
// bounds their number and size.
|
|
func ValidateRequestHeaders(headers map[string]string) error {
|
|
if len(headers) > 64 {
|
|
return fmt.Errorf("at most 64 request headers are allowed")
|
|
}
|
|
for name, value := range headers {
|
|
name = strings.TrimSpace(name)
|
|
if !httpguts.ValidHeaderFieldName(name) {
|
|
return fmt.Errorf("request header name %q is invalid", name)
|
|
}
|
|
if !httpguts.ValidHeaderFieldValue(value) || len(value) > 8192 {
|
|
return fmt.Errorf("request header %q has an invalid or oversized value", name)
|
|
}
|
|
switch strings.ToLower(name) {
|
|
case "host", "content-length", "accept-encoding", "connection", "proxy-connection", "keep-alive",
|
|
"proxy-authorization", "te", "trailer", "transfer-encoding", "upgrade":
|
|
return fmt.Errorf("request header %q is not allowed", name)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func canonicalHost(value *url.URL) string {
|
|
host := strings.ToLower(value.Hostname())
|
|
port := value.Port()
|
|
if port == "" || port == "80" && value.Scheme == "http" || port == "443" && value.Scheme == "https" {
|
|
return host
|
|
}
|
|
return net.JoinHostPort(host, port)
|
|
}
|