Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/catalog/index/index_test.go
2848 views
1
package index
2
3
import (
4
"fmt"
5
"os"
6
"path/filepath"
7
"testing"
8
"time"
9
10
"github.com/projectdiscovery/nuclei/v3/pkg/model"
11
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
12
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/stringslice"
13
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/code"
14
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/headless"
15
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http"
16
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
17
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
18
"github.com/stretchr/testify/require"
19
)
20
21
func TestNewIndex(t *testing.T) {
22
t.Run("with custom directory", func(t *testing.T) {
23
tmpDir := t.TempDir()
24
cache, err := NewIndex(tmpDir)
25
require.NoError(t, err, "Failed to create cache with custom directory")
26
require.NotNil(t, cache, "Cache should not be nil")
27
require.Equal(t, filepath.Join(tmpDir, IndexFileName), cache.cacheFile)
28
require.Equal(t, IndexVersion, cache.version)
29
})
30
31
t.Run("with default directory", func(t *testing.T) {
32
cache, err := NewDefaultIndex()
33
require.NoError(t, err, "Failed to create cache with default directory")
34
require.NotNil(t, cache, "Cache should not be nil")
35
})
36
}
37
38
func TestCacheBasicOperations(t *testing.T) {
39
tmpDir := t.TempDir()
40
cache, err := NewIndex(tmpDir)
41
require.NoError(t, err)
42
43
metadata := &Metadata{
44
ID: "concurrent-test",
45
FilePath: "/tmp/concurrent.yaml",
46
}
47
48
t.Run("Set and Has", func(t *testing.T) {
49
cache.Set(metadata.FilePath, metadata)
50
require.Equal(t, 1, cache.Size(), "Cache size should be 1 after Set")
51
require.True(t, cache.Has(metadata.FilePath), "Cache should contain the path after Set")
52
require.False(t, cache.Has("/nonexistent"), "Cache should not contain nonexistent path")
53
})
54
55
t.Run("Get with validation", func(t *testing.T) {
56
// Get should fail validation for nonexistent file
57
retrieved, found := cache.Get(metadata.FilePath)
58
require.False(t, found, "Get should fail validation for nonexistent file")
59
require.Nil(t, retrieved, "Retrieved metadata should be nil for invalid entry")
60
})
61
62
t.Run("Delete", func(t *testing.T) {
63
cache.Set(metadata.FilePath, metadata)
64
require.True(t, cache.Has(metadata.FilePath), "Cache should contain path before Delete")
65
66
cache.Delete(metadata.FilePath)
67
require.False(t, cache.Has(metadata.FilePath), "Cache should not contain path after Delete")
68
})
69
70
t.Run("Clear", func(t *testing.T) {
71
cache.Set(metadata.FilePath, metadata)
72
cache.Set("/tmp/test2.yaml", &Metadata{ID: "test2", FilePath: "/tmp/test2.yaml"})
73
require.True(t, cache.Size() > 0, "Cache should have entries before Clear")
74
75
cache.Clear()
76
require.Equal(t, 0, cache.Size(), "Cache should be empty after Clear")
77
})
78
}
79
80
func TestCachePersistence(t *testing.T) {
81
tmpDir := t.TempDir()
82
83
metadata1 := &Metadata{
84
ID: "persist-test-1",
85
FilePath: "/tmp/persist1.yaml",
86
Name: "Persistence Test 1",
87
Authors: []string{"tester"},
88
Tags: []string{"test"},
89
Severity: "medium",
90
ProtocolType: "dns",
91
}
92
93
metadata2 := &Metadata{
94
ID: "persist-test-2",
95
FilePath: "/tmp/persist2.yaml",
96
Name: "Persistence Test 2",
97
Authors: []string{"tester2"},
98
Tags: []string{"cve"},
99
Severity: "critical",
100
ProtocolType: "http",
101
}
102
103
t.Run("Save and Load", func(t *testing.T) {
104
// Create cache and add entries
105
cache1, err := NewIndex(tmpDir)
106
require.NoError(t, err)
107
108
cache1.Set(metadata1.FilePath, metadata1)
109
cache1.Set(metadata2.FilePath, metadata2)
110
require.Equal(t, 2, cache1.Size())
111
112
// Save to disk
113
err = cache1.Save()
114
require.NoError(t, err, "Failed to save cache")
115
116
// Verify cache file exists
117
cacheFile := filepath.Join(tmpDir, IndexFileName)
118
stat, err := os.Stat(cacheFile)
119
require.NoError(t, err, "Cache file should exist")
120
require.Greater(t, stat.Size(), int64(0), "Cache file should not be empty")
121
122
// Create new cache and load
123
cache2, err := NewIndex(tmpDir)
124
require.NoError(t, err)
125
require.Equal(t, 0, cache2.Size(), "New cache should be empty before Load")
126
127
err = cache2.Load()
128
require.NoError(t, err, "Failed to load cache")
129
130
// Verify data was loaded
131
require.Equal(t, 2, cache2.Size(), "Loaded cache should have 2 entries")
132
require.True(t, cache2.Has(metadata1.FilePath), "Loaded cache should contain first entry")
133
require.True(t, cache2.Has(metadata2.FilePath), "Loaded cache should contain second entry")
134
})
135
136
t.Run("Load non-existent cache", func(t *testing.T) {
137
emptyDir := t.TempDir()
138
cache, err := NewIndex(emptyDir)
139
require.NoError(t, err)
140
141
// Loading non-existent cache should not error
142
err = cache.Load()
143
require.NoError(t, err, "Loading non-existent cache should not error")
144
require.Equal(t, 0, cache.Size(), "Cache should be empty after loading non-existent file")
145
})
146
147
t.Run("Atomic save", func(t *testing.T) {
148
cache, err := NewIndex(tmpDir)
149
require.NoError(t, err)
150
151
cache.Set(metadata1.FilePath, metadata1)
152
err = cache.Save()
153
require.NoError(t, err)
154
155
// Verify no .tmp file left behind
156
tmpFile := filepath.Join(tmpDir, IndexFileName+".tmp")
157
_, err = os.Stat(tmpFile)
158
require.True(t, os.IsNotExist(err), "Temporary file should not exist after save")
159
160
// Verify actual cache file exists
161
cacheFile := filepath.Join(tmpDir, IndexFileName)
162
_, err = os.Stat(cacheFile)
163
require.NoError(t, err, "Cache file should exist")
164
})
165
}
166
167
func TestIndexVersionMismatch(t *testing.T) {
168
tmpDir := t.TempDir()
169
170
// Create cache with current version
171
cache1, err := NewIndex(tmpDir)
172
require.NoError(t, err)
173
174
metadata := &Metadata{
175
ID: "version-test",
176
FilePath: "/tmp/version.yaml",
177
}
178
cache1.Set(metadata.FilePath, metadata)
179
180
// Save with current version
181
err = cache1.Save()
182
require.NoError(t, err)
183
184
// Manually modify version and save again
185
cache1.version = 999
186
err = cache1.Save()
187
require.NoError(t, err)
188
189
// Try to load with different version
190
cache2, err := NewIndex(tmpDir)
191
require.NoError(t, err)
192
193
// Load should succeed but cache should be empty (version mismatch)
194
err = cache2.Load()
195
require.NoError(t, err, "Load should not error on version mismatch")
196
require.Equal(t, 0, cache2.Size(), "Cache should be empty after version mismatch")
197
}
198
199
func TestCacheCorruptedFile(t *testing.T) {
200
tmpDir := t.TempDir()
201
cacheFile := filepath.Join(tmpDir, IndexFileName)
202
203
// Create corrupted cache file
204
err := os.WriteFile(cacheFile, []byte("corrupted data that is not valid gob"), 0644)
205
require.NoError(t, err)
206
207
// Try to load corrupted cache
208
cache, err := NewIndex(tmpDir)
209
require.NoError(t, err)
210
211
err = cache.Load()
212
require.NoError(t, err, "Load should not error on corrupted cache")
213
require.Equal(t, 0, cache.Size(), "Cache should be empty after loading corrupted file")
214
215
// Corrupted file should be removed
216
_, err = os.Stat(cacheFile)
217
require.True(t, os.IsNotExist(err), "Corrupted cache file should be removed")
218
}
219
220
func TestMetadataValidation(t *testing.T) {
221
tmpDir := t.TempDir()
222
tmpFile := filepath.Join(tmpDir, "test.yaml")
223
224
t.Run("Valid metadata", func(t *testing.T) {
225
// Create a test file
226
err := os.WriteFile(tmpFile, []byte("id: test\ninfo:\n name: Test"), 0644)
227
require.NoError(t, err)
228
229
info, err := os.Stat(tmpFile)
230
require.NoError(t, err)
231
232
// Create metadata with correct checksum
233
metadata := &Metadata{
234
ID: "test",
235
FilePath: tmpFile,
236
ModTime: info.ModTime(),
237
}
238
239
// Should be valid
240
require.True(t, metadata.IsValid(), "Metadata should be valid for unchanged file")
241
})
242
243
t.Run("Invalid metadata after file modification", func(t *testing.T) {
244
// Create the test file first to ensure it exists in this subtest
245
err := os.WriteFile(tmpFile, []byte("id: test\ninfo:\n name: Test"), 0644)
246
require.NoError(t, err)
247
248
// Set file ModTime to past to ensure modification is detectable
249
oldTime := time.Now().Add(-2 * time.Second)
250
err = os.Chtimes(tmpFile, oldTime, oldTime)
251
require.NoError(t, err)
252
253
info, err := os.Stat(tmpFile)
254
require.NoError(t, err)
255
256
metadata := &Metadata{
257
ID: "test",
258
FilePath: tmpFile,
259
ModTime: info.ModTime(),
260
}
261
262
// Modify file
263
err = os.WriteFile(tmpFile, []byte("id: test\ninfo:\n name: Modified"), 0644)
264
require.NoError(t, err)
265
266
// Should now be invalid
267
require.False(t, metadata.IsValid(), "Metadata should be invalid after file modification")
268
})
269
270
t.Run("Invalid metadata for deleted file", func(t *testing.T) {
271
// Create the test file first to ensure it exists in this subtest
272
err := os.WriteFile(tmpFile, []byte("id: test\ninfo:\n name: Test"), 0644)
273
require.NoError(t, err)
274
275
info, err := os.Stat(tmpFile)
276
require.NoError(t, err)
277
278
metadata := &Metadata{
279
ID: "test",
280
FilePath: tmpFile,
281
ModTime: info.ModTime(),
282
}
283
284
// Delete file
285
err = os.Remove(tmpFile)
286
require.NoError(t, err)
287
288
// Should be invalid
289
require.False(t, metadata.IsValid(), "Metadata should be invalid for deleted file")
290
})
291
}
292
293
func TestSetFromTemplate(t *testing.T) {
294
tmpDir := t.TempDir()
295
tmpFile := filepath.Join(tmpDir, "extract.yaml")
296
297
// Create a test file
298
err := os.WriteFile(tmpFile, []byte("id: extract-test"), 0644)
299
require.NoError(t, err)
300
301
cache, err := NewIndex(tmpDir)
302
require.NoError(t, err)
303
304
t.Run("Basic metadata extraction", func(t *testing.T) {
305
template := &templates.Template{
306
ID: "extract-test",
307
Info: model.Info{
308
Name: "Extract Test Template",
309
Authors: stringslice.StringSlice{Value: "author1,author2"},
310
Tags: stringslice.StringSlice{Value: "tag1,tag2"},
311
Description: "Test description",
312
SeverityHolder: severity.Holder{
313
Severity: severity.High,
314
},
315
},
316
SelfContained: true,
317
Verified: true,
318
TemplateVerifier: "test-verifier",
319
}
320
321
metadata, ok := cache.SetFromTemplate(tmpFile, template)
322
require.True(t, ok, "Failed to set metadata from template")
323
require.NotNil(t, metadata, "Metadata should not be nil")
324
325
// Verify core fields
326
require.Equal(t, "extract-test", metadata.ID)
327
require.Equal(t, tmpFile, metadata.FilePath)
328
329
// Verify Info fields
330
require.Equal(t, "Extract Test Template", metadata.Name)
331
require.Equal(t, []string{"author1,author2"}, metadata.Authors)
332
require.Equal(t, []string{"tag1,tag2"}, metadata.Tags)
333
require.Equal(t, "high", metadata.Severity)
334
335
// Verify flags
336
require.True(t, metadata.Verified)
337
require.Equal(t, "test-verifier", metadata.TemplateVerifier)
338
})
339
340
t.Run("HTTP protocol detection", func(t *testing.T) {
341
// Create a separate test file for this test
342
httpFile := filepath.Join(tmpDir, "http-test.yaml")
343
err := os.WriteFile(httpFile, []byte("id: http-test"), 0644)
344
require.NoError(t, err)
345
346
template := &templates.Template{
347
ID: "http-test",
348
Info: model.Info{
349
Name: "HTTP Test",
350
Authors: stringslice.StringSlice{Value: "tester"},
351
SeverityHolder: severity.Holder{
352
Severity: severity.Medium,
353
},
354
},
355
RequestsHTTP: []*http.Request{{Method: http.HTTPMethodTypeHolder{MethodType: http.HTTPGet}}},
356
}
357
358
metadata, ok := cache.SetFromTemplate(httpFile, template)
359
require.True(t, ok)
360
require.NotNil(t, metadata)
361
require.Equal(t, "http", metadata.ProtocolType)
362
})
363
364
t.Run("Extract with missing file", func(t *testing.T) {
365
template := &templates.Template{
366
ID: "missing-test",
367
Info: model.Info{
368
Name: "Missing File Test",
369
Authors: stringslice.StringSlice{Value: "tester"},
370
SeverityHolder: severity.Holder{
371
Severity: severity.Low,
372
},
373
},
374
}
375
376
metadata, ok := cache.SetFromTemplate("/nonexistent/file.yaml", template)
377
require.False(t, ok, "Should return false for nonexistent file")
378
require.NotNil(t, metadata, "Metadata should still be returned")
379
})
380
}
381
382
func TestMetadataMatchingHelpers(t *testing.T) {
383
metadata := &Metadata{
384
Tags: []string{"cve", "rce", "apache"},
385
Authors: []string{"pdteam", "geeknik"},
386
Severity: "critical",
387
ProtocolType: "http",
388
}
389
390
t.Run("HasTag", func(t *testing.T) {
391
require.True(t, metadata.HasTag("cve"))
392
require.True(t, metadata.HasTag("rce"))
393
require.True(t, metadata.HasTag("apache"))
394
require.False(t, metadata.HasTag("xxe"))
395
require.False(t, metadata.HasTag(""))
396
})
397
398
t.Run("HasAuthor", func(t *testing.T) {
399
require.True(t, metadata.HasAuthor("pdteam"))
400
require.True(t, metadata.HasAuthor("geeknik"))
401
require.False(t, metadata.HasAuthor("unknown"))
402
require.False(t, metadata.HasAuthor(""))
403
})
404
405
t.Run("MatchesSeverity", func(t *testing.T) {
406
require.True(t, metadata.MatchesSeverity(severity.Critical))
407
require.False(t, metadata.MatchesSeverity(severity.High))
408
require.False(t, metadata.MatchesSeverity(severity.Medium))
409
require.False(t, metadata.MatchesSeverity(severity.Low))
410
require.False(t, metadata.MatchesSeverity(severity.Info))
411
})
412
413
t.Run("MatchesProtocol", func(t *testing.T) {
414
require.True(t, metadata.MatchesProtocol(types.HTTPProtocol))
415
require.False(t, metadata.MatchesProtocol(types.DNSProtocol))
416
require.False(t, metadata.MatchesProtocol(types.FileProtocol))
417
require.False(t, metadata.MatchesProtocol(types.NetworkProtocol))
418
})
419
420
t.Run("Empty metadata", func(t *testing.T) {
421
emptyMetadata := &Metadata{}
422
require.False(t, emptyMetadata.HasTag("any"))
423
require.False(t, emptyMetadata.HasAuthor("any"))
424
})
425
}
426
427
func TestCacheConcurrency(t *testing.T) {
428
tmpDir := t.TempDir()
429
cache, err := NewIndex(tmpDir)
430
require.NoError(t, err)
431
432
// Test concurrent writes
433
t.Run("Concurrent Set", func(t *testing.T) {
434
done := make(chan bool)
435
for i := 0; i < 10; i++ {
436
go func(id int) {
437
metadata := &Metadata{
438
ID: string(rune('a' + id)),
439
FilePath: filepath.Join("/tmp", string(rune('a'+id))+".yaml"),
440
}
441
cache.Set(metadata.FilePath, metadata)
442
done <- true
443
}(i)
444
}
445
446
// Wait for all goroutines
447
for i := 0; i < 10; i++ {
448
<-done
449
}
450
451
require.Equal(t, 10, cache.Size(), "All concurrent writes should succeed")
452
})
453
454
// Test concurrent reads
455
t.Run("Concurrent Has", func(t *testing.T) {
456
metadata := &Metadata{
457
ID: "concurrent-test",
458
FilePath: "/tmp/concurrent.yaml",
459
}
460
cache.Set(metadata.FilePath, metadata)
461
462
done := make(chan bool)
463
for i := 0; i < 20; i++ {
464
go func() {
465
_ = cache.Has(metadata.FilePath)
466
done <- true
467
}()
468
}
469
470
// Wait for all goroutines
471
for i := 0; i < 20; i++ {
472
<-done
473
}
474
})
475
}
476
477
func TestCacheSize(t *testing.T) {
478
tmpDir := t.TempDir()
479
cache, err := NewIndex(tmpDir)
480
require.NoError(t, err)
481
482
require.Equal(t, 0, cache.Size(), "New cache should have size 0")
483
484
// Add entries
485
for i := 0; i < 5; i++ {
486
metadata := &Metadata{
487
ID: string(rune('a' + i)),
488
FilePath: filepath.Join("/tmp", string(rune('a'+i))+".yaml"),
489
}
490
cache.Set(metadata.FilePath, metadata)
491
}
492
493
require.Equal(t, 5, cache.Size(), "Cache should have size 5 after adding 5 entries")
494
495
// Delete entries
496
cache.Delete(filepath.Join("/tmp", "a.yaml"))
497
cache.Delete(filepath.Join("/tmp", "b.yaml"))
498
499
require.Equal(t, 3, cache.Size(), "Cache should have size 3 after deleting 2 entries")
500
501
// Clear cache
502
cache.Clear()
503
require.Equal(t, 0, cache.Size(), "Cache should have size 0 after Clear")
504
}
505
506
func TestCacheGetWithValidFile(t *testing.T) {
507
tmpDir := t.TempDir()
508
cache, err := NewIndex(tmpDir)
509
require.NoError(t, err)
510
511
// Create a real file for testing validation
512
tmpFile := filepath.Join(tmpDir, "test.yaml")
513
err = os.WriteFile(tmpFile, []byte("id: test"), 0644)
514
require.NoError(t, err)
515
516
info, err := os.Stat(tmpFile)
517
require.NoError(t, err)
518
519
metadata := &Metadata{
520
ID: "test",
521
FilePath: tmpFile,
522
ModTime: info.ModTime(),
523
Name: "Test Template",
524
}
525
526
// Set and get should work with valid file
527
cache.Set(metadata.FilePath, metadata)
528
retrieved, found := cache.Get(metadata.FilePath)
529
require.True(t, found, "Should find entry with valid file")
530
require.NotNil(t, retrieved, "Retrieved metadata should not be nil")
531
require.Equal(t, metadata.ID, retrieved.ID)
532
}
533
534
func TestCacheSaveErrorHandling(t *testing.T) {
535
tmpDir := t.TempDir()
536
cache, err := NewIndex(tmpDir)
537
require.NoError(t, err)
538
539
metadata := &Metadata{
540
ID: "test",
541
FilePath: filepath.Join("/tmp", "test.yaml"),
542
}
543
cache.Set(metadata.FilePath, metadata)
544
545
// Create a directory where the temp file would be created to force an error
546
// The Save method creates a file at cacheFile + ".tmp"
547
conflictPath := filepath.Join(tmpDir, IndexFileName+".tmp")
548
err = os.Mkdir(conflictPath, 0755)
549
require.NoError(t, err)
550
551
err = cache.Save()
552
require.Error(t, err, "Save should fail when temp file cannot be created")
553
}
554
555
func TestNewCacheWithInvalidDirectory(t *testing.T) {
556
// Try to create cache in a file path (should fail)
557
tmpFile := filepath.Join(t.TempDir(), "file.txt")
558
err := os.WriteFile(tmpFile, []byte("test"), 0644)
559
require.NoError(t, err)
560
561
cache, err := NewIndex(tmpFile)
562
require.Error(t, err, "NewCache should fail when path is a file")
563
require.Nil(t, cache, "Cache should be nil on error")
564
}
565
566
func TestCacheLoadCorruptedRemoval(t *testing.T) {
567
tmpDir := t.TempDir()
568
cacheFile := filepath.Join(tmpDir, IndexFileName)
569
570
// Create corrupted cache file with invalid gob data
571
err := os.WriteFile(cacheFile, []byte("this is not valid gob encoding at all!"), 0644)
572
require.NoError(t, err)
573
574
// Verify file exists before Load
575
_, err = os.Stat(cacheFile)
576
require.NoError(t, err, "Corrupted file should exist")
577
578
// Load should not error but should remove corrupted file
579
cache, err := NewIndex(tmpDir)
580
require.NoError(t, err)
581
582
err = cache.Load()
583
require.NoError(t, err, "Load should not return error for corrupted file")
584
585
// Verify corrupted file was removed
586
_, err = os.Stat(cacheFile)
587
require.True(t, os.IsNotExist(err), "Corrupted file should be removed")
588
require.Equal(t, 0, cache.Size(), "Cache should be empty after loading corrupted file")
589
}
590
591
func TestMetadataExtractionWithNilClassification(t *testing.T) {
592
tmpDir := t.TempDir()
593
tmpFile := filepath.Join(tmpDir, "test.yaml")
594
err := os.WriteFile(tmpFile, []byte("id: test"), 0644)
595
require.NoError(t, err)
596
597
template := &templates.Template{
598
ID: "nil-classification",
599
Info: model.Info{
600
Name: "Template without classification",
601
Authors: stringslice.StringSlice{Value: "tester"},
602
SeverityHolder: severity.Holder{
603
Severity: severity.Medium,
604
},
605
Classification: nil, // Explicitly nil
606
},
607
}
608
609
cache, err := NewIndex(tmpDir)
610
require.NoError(t, err)
611
612
metadata, ok := cache.SetFromTemplate(tmpFile, template)
613
require.True(t, ok)
614
require.NotNil(t, metadata)
615
}
616
617
func TestCachePersistenceWithLargeDataset(t *testing.T) {
618
tmpDir := t.TempDir()
619
cache, err := NewIndex(tmpDir)
620
require.NoError(t, err)
621
622
// Add 100 entries to test bulk operations
623
for i := 0; i < 100; i++ {
624
metadata := &Metadata{
625
ID: fmt.Sprintf("template-%d", i),
626
FilePath: filepath.Join("/tmp", fmt.Sprintf("template-%d.yaml", i)),
627
Name: fmt.Sprintf("Template %d", i),
628
Authors: []string{fmt.Sprintf("author%d", i)},
629
Tags: []string{"tag1", "tag2", "tag3"},
630
Severity: "high",
631
}
632
cache.Set(metadata.FilePath, metadata)
633
}
634
635
require.Equal(t, 100, cache.Size(), "Cache should contain 100 entries")
636
637
// Save to disk
638
err = cache.Save()
639
require.NoError(t, err)
640
641
// Load into new cache
642
cache2, err := NewIndex(tmpDir)
643
require.NoError(t, err)
644
err = cache2.Load()
645
require.NoError(t, err)
646
647
require.Equal(t, 100, cache2.Size(), "Loaded cache should contain 100 entries")
648
649
// Verify a sample entry
650
found := cache2.Has(filepath.Join("/tmp", "template-50.yaml"))
651
require.True(t, found, "Should find sample entry")
652
}
653
654
func TestMetadataHelperMethods(t *testing.T) {
655
metadata := &Metadata{
656
ID: "helper-test",
657
Tags: []string{},
658
Authors: []string{},
659
Severity: "",
660
ProtocolType: "",
661
}
662
663
t.Run("Empty tags", func(t *testing.T) {
664
require.False(t, metadata.HasTag("anytag"))
665
})
666
667
t.Run("Empty authors", func(t *testing.T) {
668
require.False(t, metadata.HasAuthor("anyauthor"))
669
})
670
671
t.Run("Empty severity", func(t *testing.T) {
672
require.False(t, metadata.MatchesSeverity(severity.Critical))
673
})
674
675
t.Run("Empty protocol", func(t *testing.T) {
676
require.False(t, metadata.MatchesProtocol(types.HTTPProtocol))
677
})
678
}
679
680
func TestMultipleProtocolsDetection(t *testing.T) {
681
tmpDir := t.TempDir()
682
tmpFile := filepath.Join(tmpDir, "multi.yaml")
683
err := os.WriteFile(tmpFile, []byte("id: multi"), 0644)
684
require.NoError(t, err)
685
686
// Template with multiple protocol types
687
template := &templates.Template{
688
ID: "multi-protocol",
689
Info: model.Info{
690
Name: "Multi Protocol Template",
691
Authors: stringslice.StringSlice{Value: "tester"},
692
SeverityHolder: severity.Holder{
693
Severity: severity.High,
694
},
695
},
696
RequestsHTTP: []*http.Request{{Method: http.HTTPMethodTypeHolder{MethodType: http.HTTPGet}}},
697
RequestsHeadless: []*headless.Request{{}},
698
RequestsCode: []*code.Request{{}},
699
}
700
701
cache, err := NewIndex(tmpDir)
702
require.NoError(t, err)
703
704
metadata, ok := cache.SetFromTemplate(tmpFile, template)
705
require.True(t, ok)
706
require.NotNil(t, metadata)
707
require.Equal(t, "http", metadata.ProtocolType, "Primary protocol should be http")
708
}
709
710
func TestNewMetadataFromTemplate(t *testing.T) {
711
tmpl := &templates.Template{
712
ID: "test-template",
713
Info: model.Info{
714
Name: "Test Template",
715
Authors: stringslice.StringSlice{Value: []string{"author"}},
716
Tags: stringslice.StringSlice{Value: []string{"tag"}},
717
SeverityHolder: severity.Holder{
718
Severity: severity.Low,
719
},
720
},
721
Verified: true,
722
TemplateVerifier: "verifier",
723
}
724
725
path := "/tmp/test.yaml"
726
metadata := NewMetadataFromTemplate(path, tmpl)
727
728
require.Equal(t, tmpl.ID, metadata.ID)
729
require.Equal(t, path, metadata.FilePath)
730
require.Equal(t, tmpl.Info.Name, metadata.Name)
731
require.Equal(t, tmpl.Info.Authors.ToSlice(), metadata.Authors)
732
require.Equal(t, tmpl.Info.Tags.ToSlice(), metadata.Tags)
733
require.Equal(t, tmpl.Info.SeverityHolder.Severity.String(), metadata.Severity)
734
require.Equal(t, tmpl.Type().String(), metadata.ProtocolType)
735
require.Equal(t, tmpl.Verified, metadata.Verified)
736
require.Equal(t, tmpl.TemplateVerifier, metadata.TemplateVerifier)
737
}
738
739