Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/common/protocolstate/headless.go
2072 views
1
package protocolstate
2
3
import (
4
"context"
5
"net"
6
"strings"
7
8
"github.com/go-rod/rod"
9
"github.com/go-rod/rod/lib/proto"
10
"github.com/projectdiscovery/networkpolicy"
11
"github.com/projectdiscovery/nuclei/v3/pkg/types"
12
"github.com/projectdiscovery/utils/errkit"
13
stringsutil "github.com/projectdiscovery/utils/strings"
14
urlutil "github.com/projectdiscovery/utils/url"
15
"go.uber.org/multierr"
16
)
17
18
// initialize state of headless protocol
19
20
var (
21
ErrURLDenied = errkit.New("headless: url dropped by rule")
22
ErrHostDenied = errorTemplate{format: "host %v dropped by network policy"}
23
)
24
25
// errorTemplate provides a way to create formatted errors like the old errorutil.NewWithFmt
26
type errorTemplate struct {
27
format string
28
}
29
30
func (e errorTemplate) Msgf(args ...interface{}) error {
31
return errkit.Newf(e.format, args...)
32
}
33
34
func GetNetworkPolicy(ctx context.Context) *networkpolicy.NetworkPolicy {
35
execCtx := GetExecutionContext(ctx)
36
if execCtx == nil {
37
return nil
38
}
39
dialers, ok := dialers.Get(execCtx.ExecutionID)
40
if !ok || dialers == nil {
41
return nil
42
}
43
return dialers.NetworkPolicy
44
}
45
46
// ValidateNFailRequest validates and fails request
47
// if the request does not respect the rules, it will be canceled with reason
48
func ValidateNFailRequest(options *types.Options, page *rod.Page, e *proto.FetchRequestPaused) error {
49
reqURL := e.Request.URL
50
normalized := strings.ToLower(reqURL) // normalize url to lowercase
51
normalized = strings.TrimSpace(normalized) // trim leading & trailing whitespaces
52
if !IsLfaAllowed(options) && stringsutil.HasPrefixI(normalized, "file:") {
53
return multierr.Combine(FailWithReason(page, e), errkit.Newf("headless: url %v dropped by rule: %v", reqURL, "use of file:// protocol disabled use '-lfa' to enable"))
54
}
55
// validate potential invalid schemes
56
// javascript protocol is allowed for xss fuzzing
57
if stringsutil.HasPrefixAnyI(normalized, "ftp:", "externalfile:", "chrome:", "chrome-extension:") {
58
return multierr.Combine(FailWithReason(page, e), errkit.Newf("headless: url %v dropped by rule: %v", reqURL, "protocol blocked by network policy"))
59
}
60
if !isValidHost(options, reqURL) {
61
return multierr.Combine(FailWithReason(page, e), errkit.Newf("headless: url %v dropped by rule: %v", reqURL, "address blocked by network policy"))
62
}
63
return nil
64
}
65
66
// FailWithReason fails request with AccessDenied reason
67
func FailWithReason(page *rod.Page, e *proto.FetchRequestPaused) error {
68
m := proto.FetchFailRequest{
69
RequestID: e.RequestID,
70
ErrorReason: proto.NetworkErrorReasonAccessDenied,
71
}
72
return m.Call(page)
73
}
74
75
// InitHeadless initializes headless protocol state
76
func InitHeadless(options *types.Options) {
77
dialers, ok := dialers.Get(options.ExecutionId)
78
if ok && dialers != nil {
79
dialers.Lock()
80
dialers.LocalFileAccessAllowed = options.AllowLocalFileAccess
81
dialers.RestrictLocalNetworkAccess = options.RestrictLocalNetworkAccess
82
dialers.Unlock()
83
}
84
}
85
86
func IsRestrictLocalNetworkAccess(options *types.Options) bool {
87
dialers, ok := dialers.Get(options.ExecutionId)
88
if ok && dialers != nil {
89
dialers.Lock()
90
defer dialers.Unlock()
91
92
return dialers.RestrictLocalNetworkAccess
93
}
94
return false
95
}
96
97
// isValidHost checks if the host is valid (only limited to http/https protocols)
98
func isValidHost(options *types.Options, targetUrl string) bool {
99
if !stringsutil.HasPrefixAny(targetUrl, "http:", "https:") {
100
return true
101
}
102
103
dialers, ok := dialers.Get(options.ExecutionId)
104
if !ok {
105
return true
106
}
107
108
np := dialers.NetworkPolicy
109
if !ok || np == nil {
110
return true
111
}
112
113
urlx, err := urlutil.Parse(targetUrl)
114
if err != nil {
115
// not a valid url
116
return false
117
}
118
targetUrl = urlx.Hostname()
119
_, ok = np.ValidateHost(targetUrl)
120
return ok
121
}
122
123
// IsHostAllowed checks if the host is allowed by network policy
124
func IsHostAllowed(executionId string, targetUrl string) bool {
125
dialers, ok := dialers.Get(executionId)
126
if !ok {
127
return true
128
}
129
130
np := dialers.NetworkPolicy
131
if !ok || np == nil {
132
return true
133
}
134
135
sepCount := strings.Count(targetUrl, ":")
136
if sepCount > 1 {
137
// most likely a ipv6 address (parse url and validate host)
138
return np.Validate(targetUrl)
139
}
140
if sepCount == 1 {
141
host, _, _ := net.SplitHostPort(targetUrl)
142
if _, ok := np.ValidateHost(host); !ok {
143
return false
144
}
145
return true
146
}
147
// just a hostname or ip without port
148
_, ok = np.ValidateHost(targetUrl)
149
return ok
150
}
151
152