update
Some checks failed
Pipeline: Test, Lint, Build / Get version info (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test JS code (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint i18n files (push) Has been cancelled
Pipeline: Test, Lint, Build / Check Docker configuration (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v5) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v6) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v7) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to GHCR (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to Docker Hub (push) Has been cancelled
Pipeline: Test, Lint, Build / Cleanup digest artifacts (push) Has been cancelled
Pipeline: Test, Lint, Build / Build Windows installers (push) Has been cancelled
Pipeline: Test, Lint, Build / Package/Release (push) Has been cancelled
Pipeline: Test, Lint, Build / Upload Linux PKG (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled
POEditor import / update-translations (push) Has been cancelled
Some checks failed
Pipeline: Test, Lint, Build / Get version info (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test JS code (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint i18n files (push) Has been cancelled
Pipeline: Test, Lint, Build / Check Docker configuration (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v5) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v6) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v7) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to GHCR (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to Docker Hub (push) Has been cancelled
Pipeline: Test, Lint, Build / Cleanup digest artifacts (push) Has been cancelled
Pipeline: Test, Lint, Build / Build Windows installers (push) Has been cancelled
Pipeline: Test, Lint, Build / Package/Release (push) Has been cancelled
Pipeline: Test, Lint, Build / Upload Linux PKG (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled
POEditor import / update-translations (push) Has been cancelled
This commit is contained in:
192
plugins/host_network_permissions_base.go
Normal file
192
plugins/host_network_permissions_base.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NetworkPermissionsBase contains common functionality for network-based permissions
|
||||
type networkPermissionsBase struct {
|
||||
Reason string `json:"reason"`
|
||||
AllowLocalNetwork bool `json:"allowLocalNetwork,omitempty"`
|
||||
}
|
||||
|
||||
// URLMatcher provides URL pattern matching functionality
|
||||
type urlMatcher struct{}
|
||||
|
||||
// newURLMatcher creates a new URL matcher instance
|
||||
func newURLMatcher() *urlMatcher {
|
||||
return &urlMatcher{}
|
||||
}
|
||||
|
||||
// checkURLPolicy performs common checks for a URL against network policies.
|
||||
func checkURLPolicy(requestURL string, allowLocalNetwork bool) (*url.URL, error) {
|
||||
parsedURL, err := url.Parse(requestURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid URL: %w", err)
|
||||
}
|
||||
|
||||
// Check local network restrictions
|
||||
if !allowLocalNetwork {
|
||||
if err := checkLocalNetwork(parsedURL); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return parsedURL, nil
|
||||
}
|
||||
|
||||
// MatchesURLPattern checks if a URL matches a given pattern
|
||||
func (m *urlMatcher) MatchesURLPattern(requestURL, pattern string) bool {
|
||||
// Handle wildcard pattern
|
||||
if pattern == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Parse both URLs to handle path matching correctly
|
||||
reqURL, err := url.Parse(requestURL)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
patternURL, err := url.Parse(pattern)
|
||||
if err != nil {
|
||||
// If pattern is not a valid URL, treat it as a simple string pattern
|
||||
regexPattern := m.urlPatternToRegex(pattern)
|
||||
matched, err := regexp.MatchString(regexPattern, requestURL)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return matched
|
||||
}
|
||||
|
||||
// Match scheme
|
||||
if patternURL.Scheme != "" && patternURL.Scheme != reqURL.Scheme {
|
||||
return false
|
||||
}
|
||||
|
||||
// Match host with wildcard support
|
||||
if !m.matchesHost(reqURL.Host, patternURL.Host) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Match path with wildcard support
|
||||
// Special case: if pattern URL has empty path and contains wildcards, allow any path (domain-only wildcard matching)
|
||||
if (patternURL.Path == "" || patternURL.Path == "/") && strings.Contains(pattern, "*") {
|
||||
// This is a domain-only wildcard pattern, allow any path
|
||||
return true
|
||||
}
|
||||
if !m.matchesPath(reqURL.Path, patternURL.Path) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// urlPatternToRegex converts a URL pattern with wildcards to a regex pattern
|
||||
func (m *urlMatcher) urlPatternToRegex(pattern string) string {
|
||||
// Escape special regex characters except *
|
||||
escaped := regexp.QuoteMeta(pattern)
|
||||
|
||||
// Replace escaped \* with regex pattern for wildcard matching
|
||||
// For subdomain: *.example.com -> [^.]*\.example\.com
|
||||
// For path: /api/* -> /api/.*
|
||||
escaped = strings.ReplaceAll(escaped, "\\*", ".*")
|
||||
|
||||
// Anchor the pattern to match the full URL
|
||||
return "^" + escaped + "$"
|
||||
}
|
||||
|
||||
// matchesHost checks if a host matches a pattern with wildcard support
|
||||
func (m *urlMatcher) matchesHost(host, pattern string) bool {
|
||||
if pattern == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
if pattern == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Handle wildcard patterns anywhere in the host
|
||||
if strings.Contains(pattern, "*") {
|
||||
patterns := []string{
|
||||
strings.ReplaceAll(regexp.QuoteMeta(pattern), "\\*", "[0-9.]+"), // IP pattern
|
||||
strings.ReplaceAll(regexp.QuoteMeta(pattern), "\\*", "[^.]*"), // Domain pattern
|
||||
}
|
||||
|
||||
for _, regexPattern := range patterns {
|
||||
fullPattern := "^" + regexPattern + "$"
|
||||
if matched, err := regexp.MatchString(fullPattern, host); err == nil && matched {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return host == pattern
|
||||
}
|
||||
|
||||
// matchesPath checks if a path matches a pattern with wildcard support
|
||||
func (m *urlMatcher) matchesPath(path, pattern string) bool {
|
||||
// Normalize empty paths to "/"
|
||||
if path == "" {
|
||||
path = "/"
|
||||
}
|
||||
if pattern == "" {
|
||||
pattern = "/"
|
||||
}
|
||||
|
||||
if pattern == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Handle wildcard paths
|
||||
if strings.HasSuffix(pattern, "/*") {
|
||||
prefix := pattern[:len(pattern)-2] // Remove "/*"
|
||||
if prefix == "" {
|
||||
prefix = "/"
|
||||
}
|
||||
return strings.HasPrefix(path, prefix)
|
||||
}
|
||||
|
||||
return path == pattern
|
||||
}
|
||||
|
||||
// CheckLocalNetwork checks if the URL is accessing local network resources
|
||||
func checkLocalNetwork(parsedURL *url.URL) error {
|
||||
host := parsedURL.Hostname()
|
||||
|
||||
// Check for localhost variants
|
||||
if host == "localhost" || host == "127.0.0.1" || host == "::1" {
|
||||
return fmt.Errorf("requests to localhost are not allowed")
|
||||
}
|
||||
|
||||
// Try to parse as IP address
|
||||
ip := net.ParseIP(host)
|
||||
if ip != nil && isPrivateIP(ip) {
|
||||
return fmt.Errorf("requests to private IP addresses are not allowed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsPrivateIP checks if an IP is loopback, private, or link-local (IPv4/IPv6).
|
||||
func isPrivateIP(ip net.IP) bool {
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
if ip.IsLoopback() || ip.IsPrivate() {
|
||||
return true
|
||||
}
|
||||
// IPv4 link-local: 169.254.0.0/16
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return ip4[0] == 169 && ip4[1] == 254
|
||||
}
|
||||
// IPv6 link-local: fe80::/10
|
||||
if ip16 := ip.To16(); ip16 != nil && ip.To4() == nil {
|
||||
return ip16[0] == 0xfe && (ip16[1]&0xc0) == 0x80
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user