bitbucket.org/Aishee/synsec@v0.0.0-20210414005726-236fc01a153d/pkg/cwhub/cwhub_test.go (about)

     1  package cwhub
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"net/http"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  	"testing"
    11  
    12  	"bitbucket.org/Aishee/synsec/pkg/csconfig"
    13  	log "github.com/sirupsen/logrus"
    14  )
    15  
    16  /*
    17   To test :
    18    - Download 'first' hub index
    19    - Update hub index
    20    - Install collection + list content
    21    - Taint existing parser + list
    22    - Upgrade collection
    23  */
    24  
    25  var testDataFolder = "."
    26  
    27  func TestItemStatus(t *testing.T) {
    28  	cfg := test_prepenv()
    29  
    30  	err := UpdateHubIdx(cfg.Hub)
    31  	//DownloadHubIdx()
    32  	if err != nil {
    33  		t.Fatalf("failed to download index : %s", err)
    34  	}
    35  	if err := GetHubIdx(cfg.Hub); err != nil {
    36  		t.Fatalf("failed to load hub index : %s", err)
    37  	}
    38  
    39  	//get existing map
    40  	x := GetItemMap(COLLECTIONS)
    41  	if len(x) == 0 {
    42  		t.Fatalf("expected non empty result")
    43  	}
    44  
    45  	//Get item : good and bad
    46  	for k, _ := range x {
    47  		item := GetItem(COLLECTIONS, k)
    48  		if item == nil {
    49  			t.Fatalf("expected item")
    50  		}
    51  		item.Installed = true
    52  		item.UpToDate = false
    53  		item.Local = false
    54  		item.Tainted = false
    55  		txt, _, _, _ := ItemStatus(*item)
    56  		if txt != "enabled,update-available" {
    57  			log.Fatalf("got '%s'", txt)
    58  		}
    59  
    60  		item.Installed = false
    61  		item.UpToDate = false
    62  		item.Local = true
    63  		item.Tainted = false
    64  		txt, _, _, _ = ItemStatus(*item)
    65  		if txt != "disabled,local" {
    66  			log.Fatalf("got '%s'", txt)
    67  		}
    68  
    69  		break
    70  	}
    71  	DisplaySummary()
    72  }
    73  
    74  func TestGetters(t *testing.T) {
    75  	cfg := test_prepenv()
    76  
    77  	err := UpdateHubIdx(cfg.Hub)
    78  	//DownloadHubIdx()
    79  	if err != nil {
    80  		t.Fatalf("failed to download index : %s", err)
    81  	}
    82  	if err := GetHubIdx(cfg.Hub); err != nil {
    83  		t.Fatalf("failed to load hub index : %s", err)
    84  	}
    85  
    86  	//get non existing map
    87  	empty := GetItemMap("ratata")
    88  	if empty != nil {
    89  		t.Fatalf("expected nil result")
    90  	}
    91  	//get existing map
    92  	x := GetItemMap(COLLECTIONS)
    93  	if len(x) == 0 {
    94  		t.Fatalf("expected non empty result")
    95  	}
    96  
    97  	//Get item : good and bad
    98  	for k, _ := range x {
    99  		empty := GetItem(COLLECTIONS, k+"nope")
   100  		if empty != nil {
   101  			t.Fatalf("expected empty item")
   102  		}
   103  
   104  		item := GetItem(COLLECTIONS, k)
   105  		if item == nil {
   106  			t.Fatalf("expected non empty item")
   107  		}
   108  
   109  		//Add item and get it
   110  		item.Name += "nope"
   111  		if err := AddItem(COLLECTIONS, *item); err != nil {
   112  			t.Fatalf("didn't expect error : %s", err)
   113  		}
   114  
   115  		newitem := GetItem(COLLECTIONS, item.Name)
   116  		if newitem == nil {
   117  			t.Fatalf("expected non empty item")
   118  		}
   119  
   120  		//Add bad item
   121  		if err := AddItem("ratata", *item); err != nil {
   122  			if fmt.Sprintf("%s", err) != "ItemType ratata is unknown" {
   123  				t.Fatalf("unexpected error")
   124  			}
   125  		} else {
   126  			t.Fatalf("Expected error")
   127  		}
   128  
   129  		break
   130  	}
   131  
   132  }
   133  
   134  func TestIndexDownload(t *testing.T) {
   135  
   136  	cfg := test_prepenv()
   137  
   138  	err := UpdateHubIdx(cfg.Hub)
   139  	//DownloadHubIdx()
   140  	if err != nil {
   141  		t.Fatalf("failed to download index : %s", err)
   142  	}
   143  	if err := GetHubIdx(cfg.Hub); err != nil {
   144  		t.Fatalf("failed to load hub index : %s", err)
   145  	}
   146  }
   147  
   148  func test_prepenv() *csconfig.Config {
   149  	log.SetLevel(log.DebugLevel)
   150  
   151  	var cfg = &csconfig.Config{}
   152  	cfg.Hub = &csconfig.Hub{}
   153  	cfg.Hub.ConfigDir, _ = filepath.Abs("./install")
   154  	cfg.Hub.HubDir, _ = filepath.Abs("./hubdir")
   155  	cfg.Hub.HubIndexFile = filepath.Clean("./hubdir/.index.json")
   156  
   157  	//Mock the http client
   158  	http.DefaultClient.Transport = newMockTransport()
   159  
   160  	if err := os.RemoveAll(cfg.Hub.ConfigDir); err != nil {
   161  		log.Fatalf("failed to remove %s : %s", cfg.Hub.ConfigDir, err)
   162  	}
   163  
   164  	if err := os.MkdirAll(cfg.Hub.ConfigDir, 0700); err != nil {
   165  		log.Fatalf("mkdir : %s", err)
   166  	}
   167  
   168  	if err := os.RemoveAll(cfg.Hub.HubDir); err != nil {
   169  		log.Fatalf("failed to remove %s : %s", cfg.Hub.HubDir, err)
   170  	}
   171  	if err := os.MkdirAll(cfg.Hub.HubDir, 0700); err != nil {
   172  		log.Fatalf("failed to mkdir %s : %s", cfg.Hub.HubDir, err)
   173  	}
   174  
   175  	if err := UpdateHubIdx(cfg.Hub); err != nil {
   176  		log.Fatalf("failed to download index : %s", err)
   177  	}
   178  
   179  	// if err := os.RemoveAll(cfg.Hub.InstallDir); err != nil {
   180  	// 	log.Fatalf("failed to remove %s : %s", cfg.Hub.InstallDir, err)
   181  	// }
   182  	// if err := os.MkdirAll(cfg.Hub.InstallDir, 0700); err != nil {
   183  	// 	log.Fatalf("failed to mkdir %s : %s", cfg.Hub.InstallDir, err)
   184  	// }
   185  	return cfg
   186  
   187  }
   188  
   189  func testInstallItem(cfg *csconfig.Hub, t *testing.T, item Item) {
   190  
   191  	//Install the parser
   192  	item, err := DownloadLatest(cfg, item, false)
   193  	if err != nil {
   194  		t.Fatalf("error while downloading %s : %v", item.Name, err)
   195  	}
   196  	if err, _ := LocalSync(cfg); err != nil {
   197  		t.Fatalf("taint: failed to run localSync : %s", err)
   198  	}
   199  	if !hubIdx[item.Type][item.Name].UpToDate {
   200  		t.Fatalf("download: %s should be up-to-date", item.Name)
   201  	}
   202  	if hubIdx[item.Type][item.Name].Installed {
   203  		t.Fatalf("download: %s should not be install", item.Name)
   204  	}
   205  	if hubIdx[item.Type][item.Name].Tainted {
   206  		t.Fatalf("download: %s should not be tainted", item.Name)
   207  	}
   208  
   209  	item, err = EnableItem(cfg, item)
   210  	if err != nil {
   211  		t.Fatalf("error while enabled %s : %v.", item.Name, err)
   212  	}
   213  	if err, _ := LocalSync(cfg); err != nil {
   214  		t.Fatalf("taint: failed to run localSync : %s", err)
   215  	}
   216  	if !hubIdx[item.Type][item.Name].Installed {
   217  		t.Fatalf("install: %s should be install", item.Name)
   218  	}
   219  }
   220  
   221  func testTaintItem(cfg *csconfig.Hub, t *testing.T, item Item) {
   222  	if hubIdx[item.Type][item.Name].Tainted {
   223  		t.Fatalf("pre-taint: %s should not be tainted", item.Name)
   224  	}
   225  	f, err := os.OpenFile(item.LocalPath, os.O_APPEND|os.O_WRONLY, 0600)
   226  	if err != nil {
   227  		t.Fatalf("(taint) opening %s (%s) : %s", item.LocalPath, item.Name, err)
   228  	}
   229  
   230  	if _, err = f.WriteString("tainted"); err != nil {
   231  		t.Fatalf("tainting %s : %s", item.Name, err)
   232  	}
   233  	f.Close()
   234  	//Local sync and check status
   235  	if err, _ := LocalSync(cfg); err != nil {
   236  		t.Fatalf("taint: failed to run localSync : %s", err)
   237  	}
   238  	if !hubIdx[item.Type][item.Name].Tainted {
   239  		t.Fatalf("taint: %s should be tainted", item.Name)
   240  	}
   241  }
   242  
   243  func testUpdateItem(cfg *csconfig.Hub, t *testing.T, item Item) {
   244  
   245  	if hubIdx[item.Type][item.Name].UpToDate {
   246  		t.Fatalf("update: %s should NOT be up-to-date", item.Name)
   247  	}
   248  	//Update it + check status
   249  	item, err := DownloadLatest(cfg, item, true)
   250  	if err != nil {
   251  		t.Fatalf("failed to update %s : %s", item.Name, err)
   252  	}
   253  	//Local sync and check status
   254  	if err, _ := LocalSync(cfg); err != nil {
   255  		t.Fatalf("failed to run localSync : %s", err)
   256  	}
   257  	if !hubIdx[item.Type][item.Name].UpToDate {
   258  		t.Fatalf("update: %s should be up-to-date", item.Name)
   259  	}
   260  	if hubIdx[item.Type][item.Name].Tainted {
   261  		t.Fatalf("update: %s should not be tainted anymore", item.Name)
   262  	}
   263  }
   264  
   265  func testDisableItem(cfg *csconfig.Hub, t *testing.T, item Item) {
   266  	if !item.Installed {
   267  		t.Fatalf("disable: %s should be installed", item.Name)
   268  	}
   269  	//Remove
   270  	item, err := DisableItem(cfg, item, false, false)
   271  	if err != nil {
   272  		t.Fatalf("failed to disable item : %v", err)
   273  	}
   274  	//Local sync and check status
   275  	if err, warns := LocalSync(cfg); err != nil || len(warns) > 0 {
   276  		t.Fatalf("failed to run localSync : %s (%+v)", err, warns)
   277  	}
   278  	if hubIdx[item.Type][item.Name].Tainted {
   279  		t.Fatalf("disable: %s should not be tainted anymore", item.Name)
   280  	}
   281  	if hubIdx[item.Type][item.Name].Installed {
   282  		t.Fatalf("disable: %s should not be installed anymore", item.Name)
   283  	}
   284  	if !hubIdx[item.Type][item.Name].Downloaded {
   285  		t.Fatalf("disable: %s should still be downloaded", item.Name)
   286  	}
   287  	//Purge
   288  	item, err = DisableItem(cfg, item, true, false)
   289  	if err != nil {
   290  		t.Fatalf("failed to purge item : %v", err)
   291  	}
   292  	//Local sync and check status
   293  	if err, warns := LocalSync(cfg); err != nil || len(warns) > 0 {
   294  		t.Fatalf("failed to run localSync : %s (%+v)", err, warns)
   295  	}
   296  	if hubIdx[item.Type][item.Name].Installed {
   297  		t.Fatalf("disable: %s should not be installed anymore", item.Name)
   298  	}
   299  	if hubIdx[item.Type][item.Name].Downloaded {
   300  		t.Fatalf("disable: %s should not be downloaded", item.Name)
   301  	}
   302  }
   303  
   304  func TestInstallParser(t *testing.T) {
   305  
   306  	/*
   307  	 - install a random parser
   308  	 - check its status
   309  	 - taint it
   310  	 - check its status
   311  	 - force update it
   312  	 - check its status
   313  	 - remove it
   314  	*/
   315  	cfg := test_prepenv()
   316  
   317  	if err := GetHubIdx(cfg.Hub); err != nil {
   318  		t.Fatalf("failed to load hub index")
   319  	}
   320  	//map iteration is random by itself
   321  	for _, it := range hubIdx[PARSERS] {
   322  		testInstallItem(cfg.Hub, t, it)
   323  		it = hubIdx[PARSERS][it.Name]
   324  		_ = HubStatus(PARSERS, it.Name, false)
   325  		testTaintItem(cfg.Hub, t, it)
   326  		it = hubIdx[PARSERS][it.Name]
   327  		_ = HubStatus(PARSERS, it.Name, false)
   328  		testUpdateItem(cfg.Hub, t, it)
   329  		it = hubIdx[PARSERS][it.Name]
   330  		testDisableItem(cfg.Hub, t, it)
   331  		it = hubIdx[PARSERS][it.Name]
   332  
   333  		break
   334  	}
   335  }
   336  
   337  func TestInstallCollection(t *testing.T) {
   338  
   339  	/*
   340  	 - install a random parser
   341  	 - check its status
   342  	 - taint it
   343  	 - check its status
   344  	 - force update it
   345  	 - check its status
   346  	 - remove it
   347  	*/
   348  	cfg := test_prepenv()
   349  
   350  	if err := GetHubIdx(cfg.Hub); err != nil {
   351  		t.Fatalf("failed to load hub index")
   352  	}
   353  	//map iteration is random by itself
   354  	for _, it := range hubIdx[COLLECTIONS] {
   355  		testInstallItem(cfg.Hub, t, it)
   356  		it = hubIdx[COLLECTIONS][it.Name]
   357  		testTaintItem(cfg.Hub, t, it)
   358  		it = hubIdx[COLLECTIONS][it.Name]
   359  		testUpdateItem(cfg.Hub, t, it)
   360  		it = hubIdx[COLLECTIONS][it.Name]
   361  		testDisableItem(cfg.Hub, t, it)
   362  
   363  		it = hubIdx[COLLECTIONS][it.Name]
   364  		x := HubStatus(COLLECTIONS, it.Name, false)
   365  		log.Printf("%+v", x)
   366  		break
   367  	}
   368  }
   369  
   370  type mockTransport struct{}
   371  
   372  func newMockTransport() http.RoundTripper {
   373  	return &mockTransport{}
   374  }
   375  
   376  // Implement http.RoundTripper
   377  func (t *mockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
   378  	// Create mocked http.Response
   379  	response := &http.Response{
   380  		Header:     make(http.Header),
   381  		Request:    req,
   382  		StatusCode: http.StatusOK,
   383  	}
   384  	response.Header.Set("Content-Type", "application/json")
   385  	responseBody := ""
   386  	log.Printf("---> %s", req.URL.Path)
   387  
   388  	/*FAKE PARSER*/
   389  	if req.URL.Path == "/breakteam/hub/master/parsers/s01-parse/breakteam/foobar_parser.yaml" {
   390  		responseBody = `onsuccess: next_stage
   391  filter: evt.Parsed.program == 'foobar_parser'
   392  name: breakteam/foobar_parser
   393  #debug: true
   394  description: A parser for foobar_parser WAF
   395  grok:
   396    name: foobar_parser
   397    apply_on: message
   398  `
   399  
   400  	} else if req.URL.Path == "/breakteam/hub/master/parsers/s01-parse/breakteam/foobar_subparser.yaml" {
   401  		responseBody = `onsuccess: next_stage
   402  filter: evt.Parsed.program == 'foobar_parser'
   403  name: breakteam/foobar_parser
   404  #debug: true
   405  description: A parser for foobar_parser WAF
   406  grok:
   407    name: foobar_parser
   408    apply_on: message
   409  `
   410  		/*FAKE SCENARIO*/
   411  
   412  	} else if req.URL.Path == "/breakteam/hub/master/scenarios/breakteam/foobar_scenario.yaml" {
   413  		responseBody = `filter: true
   414  name: breakteam/foobar_scenario`
   415  		/*FAKE COLLECTIONS*/
   416  	} else if req.URL.Path == "/breakteam/hub/master/collections/breakteam/foobar.yaml" {
   417  		responseBody = `
   418  blah: blalala
   419  qwe: jejwejejw`
   420  	} else if req.URL.Path == "/breakteam/hub/master/collections/breakteam/foobar_subcollection.yaml" {
   421  		responseBody = `
   422  blah: blalala
   423  qwe: jejwejejw`
   424  	} else if req.URL.Path == "/breakteam/hub/master/.index.json" {
   425  		responseBody =
   426  			`{
   427  				"collections": {
   428  				 "breakteam/foobar": {
   429  				  "path": "collections/breakteam/foobar.yaml",
   430  				  "version": "0.1",
   431  				  "versions": {
   432  				   "0.1": {
   433  					"digest": "786c9490e4dd234453e53aa9bb7d28c60668e31c3c0c71a7dd6d0abbfa60261a",
   434  					"deprecated": false
   435  				   }
   436  				  },
   437  				  "long_description": "bG9uZyBkZXNjcmlwdGlvbgo=",
   438  				  "content": "bG9uZyBkZXNjcmlwdGlvbgo=",
   439  				  "description": "foobar collection : foobar",
   440  				  "author": "breakteam",
   441  				  "labels": null,
   442  				  "collections" : ["breakteam/foobar_subcollection"],
   443  				  "parsers": [
   444  				   "breakteam/foobar_parser"
   445  				  ],
   446  				  "scenarios": [
   447  				   "breakteam/foobar_scenario"
   448  				  ]
   449  				 },
   450  				 "breakteam/foobar_subcollection": {
   451  					"path": "collections/breakteam/foobar_subcollection.yaml",
   452  					"version": "0.1",
   453  					"versions": {
   454  					 "0.1": {
   455  					  "digest": "786c9490e4dd234453e53aa9bb7d28c60668e31c3c0c71a7dd6d0abbfa60261a",
   456  					  "deprecated": false
   457  					 }
   458  					},
   459  					"long_description": "bG9uZyBkZXNjcmlwdGlvbgo=",
   460  					"content": "bG9uZyBkZXNjcmlwdGlvbgo=",
   461  					"description": "foobar collection : foobar",
   462  					"author": "breakteam",
   463  					"labels": null,
   464  					"parsers": [
   465  					 "breakteam/foobar_subparser"
   466  					]
   467  				   }
   468  				},
   469  				"parsers": {
   470  				 "breakteam/foobar_parser": {
   471  				  "path": "parsers/s01-parse/breakteam/foobar_parser.yaml",
   472  				  "stage": "s01-parse",
   473  				  "version": "0.1",
   474  				  "versions": {
   475  				   "0.1": {
   476  					"digest": "7d72765baa7227095d8e83803d81f2a8f383e5808f1a4d72deb425352afd59ae",
   477  					"deprecated": false
   478  				   }
   479  				  },
   480  				  "long_description": "bG9uZyBkZXNjcmlwdGlvbgo=",
   481  				  "content": "bG9uZyBkZXNjcmlwdGlvbgo=",
   482  				  "description": "A foobar parser",
   483  				  "author": "breakteam",
   484  				  "labels": null
   485  				 },
   486  				 "breakteam/foobar_subparser": {
   487  					"path": "parsers/s01-parse/breakteam/foobar_subparser.yaml",
   488  					"stage": "s01-parse",
   489  					"version": "0.1",
   490  					"versions": {
   491  					 "0.1": {
   492  					  "digest": "7d72765baa7227095d8e83803d81f2a8f383e5808f1a4d72deb425352afd59ae",
   493  					  "deprecated": false
   494  					 }
   495  					},
   496  					"long_description": "bG9uZyBkZXNjcmlwdGlvbgo=",
   497  					"content": "bG9uZyBkZXNjcmlwdGlvbgo=",
   498  					"description": "A foobar parser",
   499  					"author": "breakteam",
   500  					"labels": null
   501  				   }
   502  				},
   503  				"postoverflows": {
   504  				},
   505  				"scenarios": {
   506  					"breakteam/foobar_scenario": {
   507  						"path": "scenarios/breakteam/foobar_scenario.yaml",
   508  						"version": "0.1",
   509  						"versions": {
   510  						 "0.1": {
   511  						  "digest": "a76b389db944ca7a9e5a3f3ae61ee2d4ee98167164ec9b971174b1d44f5a01c6",
   512  						  "deprecated": false
   513  						 }
   514  						},
   515  						"long_description": "bG9uZyBkZXNjcmlwdGlvbgo=",
   516  						"content": "bG9uZyBkZXNjcmlwdGlvbgo=",
   517  						"description": "a foobar scenario",
   518  						"author": "breakteam",
   519  						"labels": {
   520  						 "remediation": "true",
   521  						 "scope": "ip",
   522  						 "service": "http",
   523  						 "type": "web_attack"
   524  						}
   525  					   }
   526  				}
   527  			   }
   528  			   `
   529  	} else {
   530  		log.Fatalf("unexpected url :/")
   531  	}
   532  
   533  	response.Body = ioutil.NopCloser(strings.NewReader(responseBody))
   534  	return response, nil
   535  }