Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/js/compiler/compiler.go
2070 views
1
// Package compiler provides a compiler for the goja runtime.
2
package compiler
3
4
import (
5
"context"
6
"fmt"
7
8
"github.com/Mzack9999/goja"
9
"github.com/kitabisa/go-ci"
10
11
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
12
"github.com/projectdiscovery/nuclei/v3/pkg/types"
13
contextutil "github.com/projectdiscovery/utils/context"
14
"github.com/projectdiscovery/utils/errkit"
15
stringsutil "github.com/projectdiscovery/utils/strings"
16
)
17
18
var (
19
// ErrJSExecDeadline is the error returned when alloted time for script execution exceeds
20
ErrJSExecDeadline = errkit.New("js engine execution deadline exceeded").SetKind(errkit.ErrKindDeadline).Build()
21
)
22
23
// Compiler provides a runtime to execute goja runtime
24
// based javascript scripts efficiently while also
25
// providing them access to custom modules defined in libs/.
26
type Compiler struct{}
27
28
// New creates a new compiler for the goja runtime.
29
func New() *Compiler {
30
return &Compiler{}
31
}
32
33
// ExecuteOptions provides options for executing a script.
34
type ExecuteOptions struct {
35
// ExecutionId is the id of the execution
36
ExecutionId string
37
38
// Callback can be used to register new runtime helper functions
39
// ex: export etc
40
Callback func(runtime *goja.Runtime) error
41
42
// Cleanup is extra cleanup function to be called after execution
43
Cleanup func(runtime *goja.Runtime)
44
45
// Source is original source of the script
46
Source *string
47
48
Context context.Context
49
50
TimeoutVariants *types.Timeouts
51
52
// Manually exported objects
53
exports map[string]interface{}
54
}
55
56
// ExecuteArgs is the arguments to pass to the script.
57
type ExecuteArgs struct {
58
Args map[string]interface{} //these are protocol variables
59
TemplateCtx map[string]interface{} // templateCtx contains template scoped variables
60
}
61
62
// Map returns a merged map of the TemplateCtx and Args fields.
63
func (e *ExecuteArgs) Map() map[string]interface{} {
64
return generators.MergeMaps(e.TemplateCtx, e.Args)
65
}
66
67
// NewExecuteArgs returns a new execute arguments.
68
func NewExecuteArgs() *ExecuteArgs {
69
return &ExecuteArgs{
70
Args: make(map[string]interface{}),
71
TemplateCtx: make(map[string]interface{}),
72
}
73
}
74
75
// ExecuteResult is the result of executing a script.
76
type ExecuteResult map[string]interface{}
77
78
// Map returns the map representation of the ExecuteResult
79
func (e ExecuteResult) Map() map[string]interface{} {
80
if e == nil {
81
return make(map[string]interface{})
82
}
83
return e
84
}
85
86
// NewExecuteResult returns a new execute result instance
87
func NewExecuteResult() ExecuteResult {
88
return make(map[string]interface{})
89
}
90
91
// GetSuccess returns whether the script was successful or not.
92
func (e ExecuteResult) GetSuccess() bool {
93
if e == nil {
94
return false
95
}
96
val, ok := e["success"].(bool)
97
if !ok {
98
return false
99
}
100
return val
101
}
102
103
// ExecuteWithOptions executes a script with the provided options.
104
func (c *Compiler) ExecuteWithOptions(program *goja.Program, args *ExecuteArgs, opts *ExecuteOptions) (ExecuteResult, error) {
105
if opts == nil {
106
opts = &ExecuteOptions{Context: context.Background()}
107
}
108
if args == nil {
109
args = NewExecuteArgs()
110
}
111
// handle nil maps
112
if args.TemplateCtx == nil {
113
args.TemplateCtx = make(map[string]interface{})
114
}
115
if args.Args == nil {
116
args.Args = make(map[string]interface{})
117
}
118
// merge all args into templatectx
119
args.TemplateCtx = generators.MergeMaps(args.TemplateCtx, args.Args)
120
121
// execute with context and timeout
122
123
ctx, cancel := context.WithTimeoutCause(opts.Context, opts.TimeoutVariants.JsCompilerExecutionTimeout, ErrJSExecDeadline)
124
defer cancel()
125
// execute the script
126
results, err := contextutil.ExecFuncWithTwoReturns(ctx, func() (val goja.Value, err error) {
127
// TODO(dwisiswant0): remove this once we get the RCA.
128
defer func() {
129
if ci.IsCI() {
130
return
131
}
132
133
if r := recover(); r != nil {
134
err = fmt.Errorf("panic: %v", r)
135
}
136
}()
137
138
return ExecuteProgram(program, args, opts)
139
})
140
if err != nil {
141
if val, ok := err.(*goja.Exception); ok {
142
if x := val.Unwrap(); x != nil {
143
err = x
144
}
145
}
146
e := NewExecuteResult()
147
e["error"] = err.Error()
148
return e, err
149
}
150
var res ExecuteResult
151
if opts.exports != nil {
152
res = ExecuteResult(opts.exports)
153
opts.exports = nil
154
} else {
155
res = NewExecuteResult()
156
}
157
res["response"] = results.Export()
158
res["success"] = results.ToBoolean()
159
return res, nil
160
}
161
162
// if the script uses export/ExportAS tokens then we can run it in IIFE mode
163
// but if not we can't run it
164
func CanRunAsIIFE(script string) bool {
165
return stringsutil.ContainsAny(script, exportAsToken, exportToken)
166
}
167
168
// SourceIIFEMode is a mode where the script is wrapped in a function and compiled.
169
// This is used when the script is not exported or exported as a function.
170
func SourceIIFEMode(script string, strict bool) (*goja.Program, error) {
171
val := fmt.Sprintf(`
172
(function() {
173
%s
174
})()
175
`, script)
176
return goja.Compile("", val, strict)
177
}
178
179
// SourceAutoMode is a mode where the script is wrapped in a function and compiled.
180
// This is used when the script is exported or exported as a function.
181
func SourceAutoMode(script string, strict bool) (*goja.Program, error) {
182
if !CanRunAsIIFE(script) {
183
// this will not be run in a pooled runtime
184
return goja.Compile("", script, strict)
185
}
186
val := fmt.Sprintf(`
187
(function() {
188
%s
189
})()
190
`, script)
191
return goja.Compile("", val, strict)
192
}
193
194