-
Notifications
You must be signed in to change notification settings - Fork 101
/
main.go
153 lines (122 loc) · 4.12 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package main
import (
"bufio"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"time"
"github.com/003random/getJS/v2/runner"
)
func main() {
options, err := setup()
if err != nil {
log.Fatal(fmt.Errorf("parsing flags: %w", err))
}
if err := runner.New(options).Run(); err != nil {
log.Fatal(err)
}
}
func setup() (options *runner.Options, err error) {
options = &runner.Options{}
flag.StringVar(&options.Request.Method, "method", "GET", "The request method that should be used to make fetch the remote contents.")
flag.DurationVar(&options.Request.Timeout, "timeout", 5*time.Second, "The request timeout used while fetching the remote contents.")
flag.BoolVar(&options.Complete, "complete", false, "Complete/Autofil relative URLs by adding the current origin.")
flag.BoolVar(&options.Resolve, "resolve", false, "Resolve the JavaScript files. Can only be used in combination with '--resolve'. Unresolvable hosts are not included in the results.")
flag.IntVar(&options.Threads, "threads", 2, "The amount of processing threads to spawn.")
flag.BoolVar(&options.Verbose, "verbose", false, "Print verbose runtime information and errors.")
var (
url string
input arrayFlags
output arrayFlags
header arrayFlags
)
flag.Var(&header, "header", "The optional request headers to add to the requests. This flag can be used multiple times with a new header each time.")
flag.StringVar(&url, "url", "", "The URL where the JavaScript sources should be extracted from.")
flag.Var(&input, "input", "The optional URLs input files. Each URL should be on a new line in plain text format. This flag can be used multiple times with different files.")
flag.Var(&output, "output", "The optional output file where the results are written to.")
flag.Parse()
options.Request.Headers = headers(header)
options.Inputs = inputs(input)
options.Outputs = outputs(output)
// Add an input for the single URL option, if set.
if len(url) > 0 {
options.Inputs = append(options.Inputs, runner.Input{
Type: runner.InputURL,
Data: strings.NewReader(url),
})
}
stat, err := os.Stdin.Stat()
if err != nil {
log.Fatal(fmt.Errorf("error reading stdin: %v", err))
}
if (stat.Mode() & os.ModeCharDevice) == 0 {
// Read the first line of stdin to detect its format
reader := bufio.NewReader(os.Stdin)
firstLine, err := reader.ReadString('\n')
if err != nil && err != io.EOF {
log.Fatal(fmt.Errorf("error reading first line of stdin: %v", err))
}
if isURL(strings.TrimSpace(firstLine)) {
// Treat as URL input.
options.Inputs = append(options.Inputs, runner.Input{
Type: runner.InputURL,
Data: io.MultiReader(strings.NewReader(firstLine), reader),
})
} else {
// Treat as HTTP response body.
options.Inputs = append(options.Inputs, runner.Input{
Type: runner.InputResponse,
Data: io.MultiReader(strings.NewReader(firstLine), reader),
})
}
}
return
}
func isURL(str string) bool {
return strings.HasPrefix(str, "http://") || strings.HasPrefix(str, "https://")
}
func outputs(names []string) []io.Writer {
outputs := append([]io.Writer{}, os.Stdout)
for _, n := range names {
file, err := os.OpenFile(n, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
log.Fatal(fmt.Errorf("error parsing output file flag: %v", err))
}
outputs = append(outputs, file)
}
return outputs
}
func inputs(names []string) []runner.Input {
inputs := []runner.Input{}
for _, n := range names {
file, err := os.Open(n)
if err != nil {
log.Fatal(fmt.Errorf("error reading from file %s: %v", n, err))
}
inputs = append(inputs, runner.Input{Type: runner.InputURL, Data: file})
}
return inputs
}
func headers(args []string) http.Header {
headers := make(http.Header)
for _, s := range args {
parts := strings.Split(s, ":")
if len(parts) <= 1 {
log.Fatal(fmt.Errorf("invalid header %s", s))
}
headers[strings.TrimSpace(parts[0])] = []string{strings.TrimSpace(strings.Join(parts[1:], ":"))}
}
return headers
}
type arrayFlags []string
func (a *arrayFlags) Set(value string) error {
*a = append(*a, value)
return nil
}
func (a *arrayFlags) String() string {
return strings.Join(*a, ",")
}