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 }