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

     1  package cwhub
     2  
     3  import (
     4  	"crypto/sha256"
     5  	"path/filepath"
     6  	"strings"
     7  
     8  	//"errors"
     9  	"fmt"
    10  	"io"
    11  	"os"
    12  
    13  	"github.com/enescakir/emoji"
    14  	"github.com/pkg/errors"
    15  	"golang.org/x/mod/semver"
    16  
    17  	log "github.com/sirupsen/logrus"
    18  )
    19  
    20  /*managed configuration types*/
    21  var PARSERS = "parsers"
    22  var PARSERS_OVFLW = "postoverflows"
    23  var SCENARIOS = "scenarios"
    24  var COLLECTIONS = "collections"
    25  var ItemTypes = []string{PARSERS, PARSERS_OVFLW, SCENARIOS, COLLECTIONS}
    26  
    27  var hubIdx map[string]map[string]Item
    28  
    29  var RawFileURLTemplate = "https://raw.githubusercontent.com/breakteam/hub/%s/%s"
    30  var HubBranch = "master"
    31  var HubIndexFile = ".index.json"
    32  
    33  type ItemVersion struct {
    34  	Digest     string
    35  	Deprecated bool
    36  }
    37  
    38  //Item can be : parsed, scenario, collection
    39  type Item struct {
    40  	/*descriptive info*/
    41  	Type                 string   `yaml:"type,omitempty"`                         //parser|postoverflows|scenario|collection(|enrich)
    42  	Stage                string   `json:"stage" yaml:"stage,omitempty,omitempty"` //Stage for parser|postoverflow : s00-raw/s01-...
    43  	Name                 string   //as seen in .config.json, usually "author/name"
    44  	FileName             string   //the filename, ie. apache2-logs.yaml
    45  	Description          string   `yaml:"description,omitempty"`            //as seen in .config.json
    46  	Author               string   `json:"author"`                           //as seen in .config.json
    47  	References           []string `yaml:"references,omitempty"`             //as seen in .config.json
    48  	BelongsToCollections []string `yaml:"belongs_to_collections,omitempty"` /*if it's part of collections, track name here*/
    49  
    50  	/*remote (hub) infos*/
    51  	RemoteURL  string                 `yaml:"remoteURL,omitempty"`               //the full remote uri of file in http
    52  	RemotePath string                 `json:"path" yaml:"remote_path,omitempty"` //the path relative to git ie. /parsers/stage/author/file.yaml
    53  	RemoteHash string                 `yaml:"hash,omitempty"`                    //the meow
    54  	Version    string                 `json:"version"`                           //the last version
    55  	Versions   map[string]ItemVersion `json:"versions" yaml:"-"`                 //the list of existing versions
    56  
    57  	/*local (deployed) infos*/
    58  	LocalPath string `yaml:"local_path,omitempty"` //the local path relative to ${CFG_DIR}
    59  	//LocalHubPath string
    60  	LocalVersion string
    61  	LocalHash    string //the local meow
    62  	Installed    bool
    63  	Downloaded   bool
    64  	UpToDate     bool
    65  	Tainted      bool //has it been locally modified
    66  	Local        bool //if it's a non versioned control one
    67  
    68  	/*if it's a collection, it not a single file*/
    69  	Parsers       []string `yaml:"parsers,omitempty"`
    70  	PostOverflows []string `yaml:"postoverflows,omitempty"`
    71  	Scenarios     []string `yaml:"scenarios,omitempty"`
    72  	Collections   []string `yaml:"collections,omitempty"`
    73  }
    74  
    75  var skippedLocal = 0
    76  var skippedTainted = 0
    77  
    78  /*To be used when reference(s) (is/are) missing in a collection*/
    79  var ReferenceMissingError = errors.New("Reference(s) missing in collection")
    80  var MissingHubIndex = errors.New("hub index can't be found")
    81  
    82  //GetVersionStatus : semver requires 'v' prefix
    83  func GetVersionStatus(v *Item) int {
    84  	return semver.Compare("v"+v.Version, "v"+v.LocalVersion)
    85  }
    86  
    87  // calculate sha256 of a file
    88  func getSHA256(filepath string) (string, error) {
    89  	/* Digest of file */
    90  	f, err := os.Open(filepath)
    91  	if err != nil {
    92  		return "", fmt.Errorf("unable to open '%s' : %s", filepath, err.Error())
    93  	}
    94  
    95  	defer f.Close()
    96  
    97  	h := sha256.New()
    98  	if _, err := io.Copy(h, f); err != nil {
    99  		return "", fmt.Errorf("unable to calculate sha256 of '%s': %s", filepath, err.Error())
   100  	}
   101  
   102  	return fmt.Sprintf("%x", h.Sum(nil)), nil
   103  }
   104  
   105  func GetItemMap(itemType string) map[string]Item {
   106  	var m map[string]Item
   107  	var ok bool
   108  
   109  	if m, ok = hubIdx[itemType]; !ok {
   110  		return nil
   111  	}
   112  	return m
   113  }
   114  
   115  //GetItemByPath retrieves the item from hubIdx based on the path. To achieve this it will resolve symlink to find associated hub item.
   116  func GetItemByPath(itemType string, itemPath string) (*Item, error) {
   117  	/*try to resolve symlink*/
   118  	finalName := ""
   119  	f, err := os.Lstat(itemPath)
   120  	if err != nil {
   121  		return nil, errors.Wrapf(err, "while performing lstat on %s", itemPath)
   122  	}
   123  
   124  	if f.Mode()&os.ModeSymlink == 0 {
   125  		/*it's not a symlink, it should be the filename itsef the key*/
   126  		finalName = filepath.Base(itemPath)
   127  	} else {
   128  		/*resolve the symlink to hub file*/
   129  		pathInHub, err := os.Readlink(itemPath)
   130  		if err != nil {
   131  			return nil, errors.Wrapf(err, "while reading symlink of %s", itemPath)
   132  		}
   133  		//extract author from path
   134  		fname := filepath.Base(pathInHub)
   135  		author := filepath.Base(filepath.Dir(pathInHub))
   136  		//trim yaml suffix
   137  		fname = strings.TrimSuffix(fname, ".yaml")
   138  		fname = strings.TrimSuffix(fname, ".yml")
   139  		finalName = fmt.Sprintf("%s/%s", author, fname)
   140  	}
   141  
   142  	/*it's not a symlink, it should be the filename itsef the key*/
   143  	if m := GetItemMap(itemType); m != nil {
   144  		if v, ok := m[finalName]; ok {
   145  			return &v, nil
   146  		} else {
   147  			return nil, fmt.Errorf("%s not found in %s", finalName, itemType)
   148  		}
   149  	} else {
   150  		return nil, fmt.Errorf("item type %s doesn't exist", itemType)
   151  	}
   152  
   153  }
   154  
   155  func GetItem(itemType string, itemName string) *Item {
   156  	if m, ok := GetItemMap(itemType)[itemName]; ok {
   157  		return &m
   158  	}
   159  	return nil
   160  }
   161  
   162  func AddItem(itemType string, item Item) error {
   163  	in := false
   164  	for _, itype := range ItemTypes {
   165  		if itype == itemType {
   166  			in = true
   167  		}
   168  	}
   169  	if !in {
   170  		return fmt.Errorf("ItemType %s is unknown", itemType)
   171  	}
   172  	hubIdx[itemType][item.Name] = item
   173  	return nil
   174  }
   175  
   176  func DisplaySummary() {
   177  	log.Printf("Loaded %d collecs, %d parsers, %d scenarios, %d post-overflow parsers", len(hubIdx[COLLECTIONS]),
   178  		len(hubIdx[PARSERS]), len(hubIdx[SCENARIOS]), len(hubIdx[PARSERS_OVFLW]))
   179  	if skippedLocal > 0 || skippedTainted > 0 {
   180  		log.Printf("unmanaged items : %d local, %d tainted", skippedLocal, skippedTainted)
   181  	}
   182  }
   183  
   184  //returns: human-text, Enabled, Warning, Unmanaged
   185  func ItemStatus(v Item) (string, bool, bool, bool) {
   186  	var Ok, Warning, Managed bool
   187  	var strret string
   188  
   189  	if !v.Installed {
   190  		strret = "disabled"
   191  		Ok = false
   192  	} else {
   193  		Ok = true
   194  		strret = "enabled"
   195  	}
   196  
   197  	if v.Local {
   198  		Managed = false
   199  		strret += ",local"
   200  	} else {
   201  		Managed = true
   202  	}
   203  
   204  	//tainted or out of date
   205  	if v.Tainted {
   206  		Warning = true
   207  		strret += ",tainted"
   208  	} else if !v.UpToDate && !v.Local {
   209  		strret += ",update-available"
   210  		Warning = true
   211  	}
   212  	return strret, Ok, Warning, Managed
   213  }
   214  
   215  func GetUpstreamInstalledScenariosAsString() ([]string, error) {
   216  	var retStr []string
   217  
   218  	items, err := GetUpstreamInstalledScenarios()
   219  	if err != nil {
   220  		return nil, errors.Wrap(err, "while fetching scenarios")
   221  	}
   222  	for _, it := range items {
   223  		retStr = append(retStr, it.Name)
   224  	}
   225  	return retStr, nil
   226  }
   227  
   228  func GetUpstreamInstalledScenarios() ([]Item, error) {
   229  	var retItems []Item
   230  
   231  	if _, ok := hubIdx[SCENARIOS]; !ok {
   232  		return nil, fmt.Errorf("no scenarios in hubIdx")
   233  	}
   234  	for _, item := range hubIdx[SCENARIOS] {
   235  		if item.Installed && !item.Tainted {
   236  			retItems = append(retItems, item)
   237  		}
   238  	}
   239  	return retItems, nil
   240  }
   241  
   242  //Returns a list of entries for packages : name, status, local_path, local_version, utf8_status (fancy)
   243  func HubStatus(itemType string, name string, all bool) []map[string]string {
   244  	if _, ok := hubIdx[itemType]; !ok {
   245  		log.Errorf("type %s doesn't exist", itemType)
   246  
   247  		return nil
   248  	}
   249  
   250  	var ret []map[string]string
   251  	/*remember, you do it for the user :)*/
   252  	for _, item := range hubIdx[itemType] {
   253  		if name != "" && name != item.Name {
   254  			//user has required a specific name
   255  			continue
   256  		}
   257  		//Only enabled items ?
   258  		if !all && !item.Installed {
   259  			continue
   260  		}
   261  		//Check the item status
   262  		status, ok, warning, managed := ItemStatus(item)
   263  		tmp := make(map[string]string)
   264  		tmp["name"] = item.Name
   265  		tmp["status"] = status
   266  		tmp["local_version"] = item.LocalVersion
   267  		tmp["local_path"] = item.LocalPath
   268  		tmp["description"] = item.Description
   269  		if !managed || !item.Installed {
   270  			tmp["utf8_status"] = fmt.Sprintf("%v  %s", emoji.Prohibited, status)
   271  		} else if warning {
   272  			tmp["utf8_status"] = fmt.Sprintf("%v  %s", emoji.Warning, status)
   273  		} else if ok {
   274  			tmp["utf8_status"] = fmt.Sprintf("%v  %s", emoji.CheckMark, status)
   275  		}
   276  		ret = append(ret, tmp)
   277  	}
   278  	return ret
   279  }