Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/file/request_test.go
2848 views
1
package file
2
3
import (
4
"archive/zip"
5
"bytes"
6
"compress/gzip"
7
"context"
8
"os"
9
"path/filepath"
10
"sync"
11
"sync/atomic"
12
"testing"
13
"time"
14
15
"github.com/stretchr/testify/require"
16
17
"github.com/projectdiscovery/nuclei/v3/pkg/model"
18
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
19
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
20
"github.com/projectdiscovery/nuclei/v3/pkg/operators/extractors"
21
"github.com/projectdiscovery/nuclei/v3/pkg/operators/matchers"
22
"github.com/projectdiscovery/nuclei/v3/pkg/output"
23
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
24
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
25
permissionutil "github.com/projectdiscovery/utils/permission"
26
)
27
28
func zipFile(t *testing.T, fileName string, data []byte) []byte {
29
var b bytes.Buffer
30
w := zip.NewWriter(&b)
31
w1, err := w.Create(fileName)
32
require.NoError(t, err)
33
_, err = w1.Write(data)
34
require.NoError(t, err)
35
err = w.Close()
36
require.NoError(t, err)
37
return b.Bytes()
38
}
39
40
func gzipFile(t *testing.T, data []byte) []byte {
41
var b bytes.Buffer
42
w := gzip.NewWriter(&b)
43
_, err := w.Write(data)
44
require.NoError(t, err)
45
err = w.Close()
46
require.NoError(t, err)
47
return b.Bytes()
48
}
49
50
func TestFileExecuteWithResults(t *testing.T) {
51
var testCaseBase = []byte("TEST\r\n1.1.1.1\r\n")
52
const testCaseBaseFilename = "config.yaml"
53
var testCases = []struct {
54
fileName string
55
data []byte
56
}{
57
{
58
fileName: testCaseBaseFilename,
59
data: testCaseBase,
60
},
61
{
62
fileName: testCaseBaseFilename + ".gz",
63
data: gzipFile(t, testCaseBase),
64
},
65
{
66
fileName: "config.yaml.zip",
67
data: zipFile(t, testCaseBaseFilename, testCaseBase),
68
},
69
}
70
71
for _, tt := range testCases {
72
options := testutils.DefaultOptions
73
74
testutils.Init(options)
75
templateID := "testing-file"
76
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
77
ID: templateID,
78
Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
79
})
80
81
request := &Request{
82
ID: templateID,
83
MaxSize: "1Gb",
84
NoRecursive: false,
85
Extensions: []string{"all"},
86
DenyList: []string{".go"},
87
Archive: true,
88
Operators: operators.Operators{
89
Matchers: []*matchers.Matcher{{
90
Name: "test",
91
Part: "raw",
92
Type: matchers.MatcherTypeHolder{MatcherType: matchers.WordsMatcher},
93
Words: []string{"1.1.1.1"},
94
}},
95
Extractors: []*extractors.Extractor{{
96
Part: "raw",
97
Type: extractors.ExtractorTypeHolder{ExtractorType: extractors.RegexExtractor},
98
Regex: []string{"[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+"},
99
}},
100
},
101
options: executerOpts,
102
}
103
err := request.Compile(executerOpts)
104
require.Nil(t, err, "could not compile file request")
105
106
tempDir, err := os.MkdirTemp("", "test-*")
107
require.Nil(t, err, "could not create temporary directory")
108
defer func() {
109
_ = os.RemoveAll(tempDir)
110
}()
111
112
files := map[string][]byte{
113
tt.fileName: tt.data,
114
}
115
for k, v := range files {
116
err = os.WriteFile(filepath.Join(tempDir, k), v, permissionutil.TempFilePermission)
117
require.Nil(t, err, "could not write temporary file")
118
}
119
120
var finalEvent *output.InternalWrappedEvent
121
t.Run("valid", func(t *testing.T) {
122
metadata := make(output.InternalEvent)
123
previous := make(output.InternalEvent)
124
ctxArgs := contextargs.NewWithInput(context.Background(), tempDir)
125
err := request.ExecuteWithResults(ctxArgs, metadata, previous, func(event *output.InternalWrappedEvent) {
126
finalEvent = event
127
})
128
require.Nil(t, err, "could not execute file request")
129
})
130
require.NotNil(t, finalEvent, "could not get event output from request")
131
require.Equal(t, 1, len(finalEvent.Results), "could not get correct number of results")
132
require.Equal(t, "test", finalEvent.Results[0].MatcherName, "could not get correct matcher name of results")
133
require.Equal(t, 1, len(finalEvent.Results[0].ExtractedResults), "could not get correct number of extracted results")
134
require.Equal(t, "1.1.1.1", finalEvent.Results[0].ExtractedResults[0], "could not get correct extracted results")
135
finalEvent = nil
136
}
137
}
138
139
func TestFileProtocolConcurrentExecution(t *testing.T) {
140
tempDir, err := os.MkdirTemp("", "nuclei-test-*")
141
require.NoError(t, err)
142
143
defer func() {
144
_ = os.RemoveAll(tempDir)
145
}()
146
147
numFiles := 5
148
for i := range numFiles {
149
content := "TEST_CONTENT_MATCH_DATA"
150
filePath := filepath.Join(tempDir, "test_"+string(rune('0'+i))+".txt")
151
err := os.WriteFile(filePath, []byte(content), permissionutil.TempFilePermission)
152
require.NoError(t, err)
153
}
154
155
options := testutils.DefaultOptions
156
testutils.Init(options)
157
templateID := "testing-file-concurrent"
158
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
159
ID: templateID,
160
Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
161
})
162
163
var timesMutex sync.Mutex
164
var processedFiles int64
165
166
request := &Request{
167
ID: templateID,
168
MaxSize: "1Gb",
169
NoRecursive: false,
170
Extensions: []string{"txt"},
171
Archive: false,
172
Operators: operators.Operators{
173
Matchers: []*matchers.Matcher{{
174
Name: "test",
175
Part: "raw",
176
Type: matchers.MatcherTypeHolder{MatcherType: matchers.WordsMatcher},
177
Words: []string{"TEST_CONTENT_MATCH_DATA"},
178
}},
179
},
180
options: executerOpts,
181
}
182
183
err = request.Compile(executerOpts)
184
require.NoError(t, err)
185
186
input := contextargs.NewWithInput(context.Background(), tempDir)
187
var results []*output.InternalWrappedEvent
188
var resultMutex sync.Mutex
189
190
startTime := time.Now()
191
err = request.ExecuteWithResults(input, make(output.InternalEvent), make(output.InternalEvent), func(event *output.InternalWrappedEvent) {
192
atomic.AddInt64(&processedFiles, 1)
193
resultMutex.Lock()
194
results = append(results, event)
195
resultMutex.Unlock()
196
197
// small delay to make timing differences more observable
198
time.Sleep(10 * time.Millisecond)
199
})
200
totalTime := time.Since(startTime)
201
require.NoError(t, err)
202
203
finalProcessedFiles := atomic.LoadInt64(&processedFiles)
204
t.Logf("Total execution time: %v", totalTime)
205
t.Logf("Files processed: %d", finalProcessedFiles)
206
t.Logf("Results returned: %d", len(results))
207
208
// test 1: all files should be processed
209
require.Equal(t, int64(numFiles), finalProcessedFiles, "Not all files were processed")
210
211
// test 2: verify callback invocation timing shows concurrency
212
timesMutex.Lock()
213
defer timesMutex.Unlock()
214
}
215
216