420 lines
11 KiB
Go
420 lines
11 KiB
Go
package cwhub
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
/*
|
|
To test :
|
|
- Download 'first' hub index
|
|
- Update hub index
|
|
- Install collection + list content
|
|
- Taint existing parser + list
|
|
- Upgrade collection
|
|
*/
|
|
|
|
var testDataFolder = "."
|
|
|
|
func TestIndexDownload(t *testing.T) {
|
|
|
|
os.RemoveAll(Cfgdir)
|
|
test_prepenv()
|
|
|
|
err := LoadHubIdx()
|
|
//DownloadHubIdx()
|
|
if err != nil {
|
|
t.Fatalf("failed to download index")
|
|
}
|
|
if err := GetHubIdx(); err != nil {
|
|
t.Fatalf("failed to load hub index")
|
|
}
|
|
}
|
|
|
|
func test_prepenv() {
|
|
log.SetLevel(log.DebugLevel)
|
|
|
|
Cfgdir = filepath.Clean("./cscli")
|
|
Installdir = filepath.Clean("./install")
|
|
Hubdir = filepath.Clean("./hubdir")
|
|
|
|
//Mock the http client
|
|
http.DefaultClient.Transport = newMockTransport()
|
|
|
|
if err := os.RemoveAll(Cfgdir); err != nil {
|
|
log.Fatalf("failed to remove %s : %s", Installdir, err)
|
|
}
|
|
|
|
if err := os.MkdirAll(Cfgdir, 0700); err != nil {
|
|
log.Fatalf("mkdir : %s", err)
|
|
}
|
|
if err := UpdateHubIdx(); err != nil {
|
|
log.Fatalf("failed to download index : %s", err)
|
|
}
|
|
|
|
if err := os.RemoveAll(Installdir); err != nil {
|
|
log.Fatalf("failed to remove %s : %s", Installdir, err)
|
|
}
|
|
if err := os.MkdirAll(Installdir, 0700); err != nil {
|
|
log.Fatalf("failed to mkdir %s : %s", Installdir, err)
|
|
}
|
|
if err := os.RemoveAll(Hubdir); err != nil {
|
|
log.Fatalf("failed to remove %s : %s", Hubdir, err)
|
|
}
|
|
if err := os.MkdirAll(Hubdir, 0700); err != nil {
|
|
log.Fatalf("failed to mkdir %s : %s", Hubdir, err)
|
|
}
|
|
|
|
}
|
|
|
|
func testInstallItem(t *testing.T, item Item) {
|
|
//Install the parser
|
|
item, err := DownloadLatest(item, Hubdir, false, testDataFolder)
|
|
if err != nil {
|
|
t.Fatalf("error while downloading %s : %v", item.Name, err)
|
|
}
|
|
if err := LocalSync(); err != nil {
|
|
t.Fatalf("taint: failed to run localSync : %s", err)
|
|
}
|
|
if !HubIdx[item.Type][item.Name].UpToDate {
|
|
t.Fatalf("download: %s should be up-to-date", item.Name)
|
|
}
|
|
if HubIdx[item.Type][item.Name].Installed {
|
|
t.Fatalf("download: %s should not be install", item.Name)
|
|
}
|
|
if HubIdx[item.Type][item.Name].Tainted {
|
|
t.Fatalf("download: %s should not be tainted", item.Name)
|
|
}
|
|
|
|
item, err = EnableItem(item, Installdir, Hubdir)
|
|
if err != nil {
|
|
t.Fatalf("error while enabled %s : %v.", item.Name, err)
|
|
}
|
|
if err := LocalSync(); err != nil {
|
|
t.Fatalf("taint: failed to run localSync : %s", err)
|
|
}
|
|
if !HubIdx[item.Type][item.Name].Installed {
|
|
t.Fatalf("install: %s should be install", item.Name)
|
|
}
|
|
}
|
|
|
|
func testTaintItem(t *testing.T, item Item) {
|
|
if HubIdx[item.Type][item.Name].Tainted {
|
|
t.Fatalf("pre-taint: %s should not be tainted", item.Name)
|
|
}
|
|
f, err := os.OpenFile(item.LocalPath, os.O_APPEND|os.O_WRONLY, 0600)
|
|
if err != nil {
|
|
t.Fatalf("(taint) opening %s (%s) : %s", item.LocalPath, item.Name, err)
|
|
}
|
|
|
|
if _, err = f.WriteString("tainted"); err != nil {
|
|
t.Fatalf("tainting %s : %s", item.Name, err)
|
|
}
|
|
f.Close()
|
|
//Local sync and check status
|
|
if err := LocalSync(); err != nil {
|
|
t.Fatalf("taint: failed to run localSync : %s", err)
|
|
}
|
|
if !HubIdx[item.Type][item.Name].Tainted {
|
|
t.Fatalf("taint: %s should be tainted", item.Name)
|
|
}
|
|
}
|
|
|
|
func testUpdateItem(t *testing.T, item Item) {
|
|
|
|
if HubIdx[item.Type][item.Name].UpToDate {
|
|
t.Fatalf("update: %s should NOT be up-to-date", item.Name)
|
|
}
|
|
//Update it + check status
|
|
item, err := DownloadLatest(item, Hubdir, true, testDataFolder)
|
|
if err != nil {
|
|
t.Fatalf("failed to update %s : %s", item.Name, err)
|
|
}
|
|
//Local sync and check status
|
|
if err := LocalSync(); err != nil {
|
|
t.Fatalf("failed to run localSync : %s", err)
|
|
}
|
|
if !HubIdx[item.Type][item.Name].UpToDate {
|
|
t.Fatalf("update: %s should be up-to-date", item.Name)
|
|
}
|
|
if HubIdx[item.Type][item.Name].Tainted {
|
|
t.Fatalf("update: %s should not be tainted anymore", item.Name)
|
|
}
|
|
}
|
|
|
|
func testDisableItem(t *testing.T, item Item) {
|
|
if !item.Installed {
|
|
t.Fatalf("disable: %s should be installed", item.Name)
|
|
}
|
|
//Remove
|
|
item, err := DisableItem(item, Installdir, Hubdir, false)
|
|
if err != nil {
|
|
t.Fatalf("failed to disable item : %v", err)
|
|
}
|
|
//Local sync and check status
|
|
if err := LocalSync(); err != nil {
|
|
t.Fatalf("failed to run localSync : %s", err)
|
|
}
|
|
if HubIdx[item.Type][item.Name].Tainted {
|
|
t.Fatalf("disable: %s should not be tainted anymore", item.Name)
|
|
}
|
|
if HubIdx[item.Type][item.Name].Installed {
|
|
t.Fatalf("disable: %s should not be installed anymore", item.Name)
|
|
}
|
|
if !HubIdx[item.Type][item.Name].Downloaded {
|
|
t.Fatalf("disable: %s should still be downloaded", item.Name)
|
|
}
|
|
//Purge
|
|
item, err = DisableItem(item, Installdir, Hubdir, true)
|
|
if err != nil {
|
|
t.Fatalf("failed to purge item : %v", err)
|
|
}
|
|
//Local sync and check status
|
|
if err := LocalSync(); err != nil {
|
|
t.Fatalf("failed to run localSync : %s", err)
|
|
}
|
|
if HubIdx[item.Type][item.Name].Installed {
|
|
t.Fatalf("disable: %s should not be installed anymore", item.Name)
|
|
}
|
|
if HubIdx[item.Type][item.Name].Downloaded {
|
|
t.Fatalf("disable: %s should not be downloaded", item.Name)
|
|
}
|
|
}
|
|
|
|
func TestInstallParser(t *testing.T) {
|
|
|
|
/*
|
|
- install a random parser
|
|
- check its status
|
|
- taint it
|
|
- check its status
|
|
- force update it
|
|
- check its status
|
|
- remove it
|
|
*/
|
|
test_prepenv()
|
|
|
|
if err := GetHubIdx(); err != nil {
|
|
t.Fatalf("failed to load hub index")
|
|
}
|
|
//map iteration is random by itself
|
|
for _, it := range HubIdx[PARSERS] {
|
|
testInstallItem(t, it)
|
|
it = HubIdx[PARSERS][it.Name]
|
|
_ = HubStatus(PARSERS, it.Name, false)
|
|
testTaintItem(t, it)
|
|
it = HubIdx[PARSERS][it.Name]
|
|
_ = HubStatus(PARSERS, it.Name, false)
|
|
testUpdateItem(t, it)
|
|
it = HubIdx[PARSERS][it.Name]
|
|
testDisableItem(t, it)
|
|
it = HubIdx[PARSERS][it.Name]
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
func TestInstallCollection(t *testing.T) {
|
|
|
|
/*
|
|
- install a random parser
|
|
- check its status
|
|
- taint it
|
|
- check its status
|
|
- force update it
|
|
- check its status
|
|
- remove it
|
|
*/
|
|
test_prepenv()
|
|
|
|
if err := GetHubIdx(); err != nil {
|
|
t.Fatalf("failed to load hub index")
|
|
}
|
|
//map iteration is random by itself
|
|
for _, it := range HubIdx[COLLECTIONS] {
|
|
testInstallItem(t, it)
|
|
it = HubIdx[COLLECTIONS][it.Name]
|
|
testTaintItem(t, it)
|
|
it = HubIdx[COLLECTIONS][it.Name]
|
|
testUpdateItem(t, it)
|
|
it = HubIdx[COLLECTIONS][it.Name]
|
|
testDisableItem(t, it)
|
|
it = HubIdx[COLLECTIONS][it.Name]
|
|
x := HubStatus(COLLECTIONS, it.Name, false)
|
|
log.Printf("%+v", x)
|
|
break
|
|
}
|
|
}
|
|
|
|
type mockTransport struct{}
|
|
|
|
func newMockTransport() http.RoundTripper {
|
|
return &mockTransport{}
|
|
}
|
|
|
|
// Implement http.RoundTripper
|
|
func (t *mockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
// Create mocked http.Response
|
|
response := &http.Response{
|
|
Header: make(http.Header),
|
|
Request: req,
|
|
StatusCode: http.StatusOK,
|
|
}
|
|
response.Header.Set("Content-Type", "application/json")
|
|
responseBody := ""
|
|
log.Printf("---> %s", req.URL.Path)
|
|
|
|
/*FAKE PARSER*/
|
|
if req.URL.Path == "/crowdsecurity/hub/master/parsers/s01-parse/crowdsecurity/foobar_parser.yaml" {
|
|
responseBody = `onsuccess: next_stage
|
|
filter: evt.Parsed.program == 'foobar_parser'
|
|
name: crowdsecurity/foobar_parser
|
|
#debug: true
|
|
description: A parser for foobar_parser WAF
|
|
grok:
|
|
name: foobar_parser
|
|
apply_on: message
|
|
`
|
|
|
|
} else if req.URL.Path == "/crowdsecurity/hub/master/parsers/s01-parse/crowdsecurity/foobar_subparser.yaml" {
|
|
responseBody = `onsuccess: next_stage
|
|
filter: evt.Parsed.program == 'foobar_parser'
|
|
name: crowdsecurity/foobar_parser
|
|
#debug: true
|
|
description: A parser for foobar_parser WAF
|
|
grok:
|
|
name: foobar_parser
|
|
apply_on: message
|
|
`
|
|
/*FAKE SCENARIO*/
|
|
|
|
} else if req.URL.Path == "/crowdsecurity/hub/master/scenarios/crowdsecurity/foobar_scenario.yaml" {
|
|
responseBody = `filter: true
|
|
name: crowdsecurity/foobar_scenario`
|
|
/*FAKE COLLECTIONS*/
|
|
} else if req.URL.Path == "/crowdsecurity/hub/master/collections/crowdsecurity/foobar.yaml" {
|
|
responseBody = `
|
|
blah: blalala
|
|
qwe: jejwejejw`
|
|
} else if req.URL.Path == "/crowdsecurity/hub/master/collections/crowdsecurity/foobar_subcollection.yaml" {
|
|
responseBody = `
|
|
blah: blalala
|
|
qwe: jejwejejw`
|
|
} else if req.URL.Path == "/crowdsecurity/hub/master/.index.json" {
|
|
responseBody =
|
|
`{
|
|
"collections": {
|
|
"crowdsecurity/foobar": {
|
|
"path": "collections/crowdsecurity/foobar.yaml",
|
|
"version": "0.1",
|
|
"versions": {
|
|
"0.1": {
|
|
"digest": "786c9490e4dd234453e53aa9bb7d28c60668e31c3c0c71a7dd6d0abbfa60261a",
|
|
"deprecated": false
|
|
}
|
|
},
|
|
"long_description": "bG9uZyBkZXNjcmlwdGlvbgo=",
|
|
"content": "bG9uZyBkZXNjcmlwdGlvbgo=",
|
|
"description": "foobar collection : foobar",
|
|
"author": "crowdsecurity",
|
|
"labels": null,
|
|
"collections" : ["crowdsecurity/foobar_subcollection"],
|
|
"parsers": [
|
|
"crowdsecurity/foobar_parser"
|
|
],
|
|
"scenarios": [
|
|
"crowdsecurity/foobar_scenario"
|
|
]
|
|
},
|
|
"crowdsecurity/foobar_subcollection": {
|
|
"path": "collections/crowdsecurity/foobar_subcollection.yaml",
|
|
"version": "0.1",
|
|
"versions": {
|
|
"0.1": {
|
|
"digest": "786c9490e4dd234453e53aa9bb7d28c60668e31c3c0c71a7dd6d0abbfa60261a",
|
|
"deprecated": false
|
|
}
|
|
},
|
|
"long_description": "bG9uZyBkZXNjcmlwdGlvbgo=",
|
|
"content": "bG9uZyBkZXNjcmlwdGlvbgo=",
|
|
"description": "foobar collection : foobar",
|
|
"author": "crowdsecurity",
|
|
"labels": null,
|
|
"parsers": [
|
|
"crowdsecurity/foobar_subparser"
|
|
]
|
|
}
|
|
},
|
|
"parsers": {
|
|
"crowdsecurity/foobar_parser": {
|
|
"path": "parsers/s01-parse/crowdsecurity/foobar_parser.yaml",
|
|
"stage": "s01-parse",
|
|
"version": "0.1",
|
|
"versions": {
|
|
"0.1": {
|
|
"digest": "7d72765baa7227095d8e83803d81f2a8f383e5808f1a4d72deb425352afd59ae",
|
|
"deprecated": false
|
|
}
|
|
},
|
|
"long_description": "bG9uZyBkZXNjcmlwdGlvbgo=",
|
|
"content": "bG9uZyBkZXNjcmlwdGlvbgo=",
|
|
"description": "A foobar parser",
|
|
"author": "crowdsecurity",
|
|
"labels": null
|
|
},
|
|
"crowdsecurity/foobar_subparser": {
|
|
"path": "parsers/s01-parse/crowdsecurity/foobar_subparser.yaml",
|
|
"stage": "s01-parse",
|
|
"version": "0.1",
|
|
"versions": {
|
|
"0.1": {
|
|
"digest": "7d72765baa7227095d8e83803d81f2a8f383e5808f1a4d72deb425352afd59ae",
|
|
"deprecated": false
|
|
}
|
|
},
|
|
"long_description": "bG9uZyBkZXNjcmlwdGlvbgo=",
|
|
"content": "bG9uZyBkZXNjcmlwdGlvbgo=",
|
|
"description": "A foobar parser",
|
|
"author": "crowdsecurity",
|
|
"labels": null
|
|
}
|
|
},
|
|
"postoverflows": {
|
|
},
|
|
"scenarios": {
|
|
"crowdsecurity/foobar_scenario": {
|
|
"path": "scenarios/crowdsecurity/foobar_scenario.yaml",
|
|
"version": "0.1",
|
|
"versions": {
|
|
"0.1": {
|
|
"digest": "a76b389db944ca7a9e5a3f3ae61ee2d4ee98167164ec9b971174b1d44f5a01c6",
|
|
"deprecated": false
|
|
}
|
|
},
|
|
"long_description": "bG9uZyBkZXNjcmlwdGlvbgo=",
|
|
"content": "bG9uZyBkZXNjcmlwdGlvbgo=",
|
|
"description": "a foobar scenario",
|
|
"author": "crowdsecurity",
|
|
"labels": {
|
|
"remediation": "true",
|
|
"scope": "ip",
|
|
"service": "http",
|
|
"type": "web_attack"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`
|
|
} else {
|
|
log.Fatalf("unexpected url :/")
|
|
}
|
|
|
|
response.Body = ioutil.NopCloser(strings.NewReader(responseBody))
|
|
return response, nil
|
|
}
|