github.com/crowdsecurity/crowdsec@v1.6.1/pkg/alertcontext/config.go (about) 1 package alertcontext 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "io/fs" 8 "os" 9 "path/filepath" 10 "slices" 11 12 log "github.com/sirupsen/logrus" 13 "gopkg.in/yaml.v3" 14 15 "github.com/crowdsecurity/crowdsec/pkg/csconfig" 16 "github.com/crowdsecurity/crowdsec/pkg/cwhub" 17 ) 18 19 var ErrNoContextData = errors.New("no context to send") 20 21 // this file is here to avoid circular dependencies between the configuration and the hub 22 23 // HubItemWrapper is a wrapper around a hub item to unmarshal only the context part 24 // because there are other fields like name etc. 25 type HubItemWrapper struct { 26 Context map[string][]string `yaml:"context"` 27 } 28 29 // mergeContext adds the context from src to dest. 30 func mergeContext(dest map[string][]string, src map[string][]string) error { 31 if len(src) == 0 { 32 return ErrNoContextData 33 } 34 35 for k, v := range src { 36 if _, ok := dest[k]; !ok { 37 dest[k] = make([]string, 0) 38 } 39 40 for _, s := range v { 41 if !slices.Contains(dest[k], s) { 42 dest[k] = append(dest[k], s) 43 } 44 } 45 } 46 47 return nil 48 } 49 50 // addContextFromItem merges the context from an item into the context to send to the console. 51 func addContextFromItem(toSend map[string][]string, item *cwhub.Item) error { 52 filePath := item.State.LocalPath 53 log.Tracef("loading console context from %s", filePath) 54 55 content, err := os.ReadFile(filePath) 56 if err != nil { 57 return err 58 } 59 60 wrapper := &HubItemWrapper{} 61 62 err = yaml.Unmarshal(content, wrapper) 63 if err != nil { 64 return fmt.Errorf("%s: %w", filePath, err) 65 } 66 67 err = mergeContext(toSend, wrapper.Context) 68 if err != nil { 69 // having an empty hub item deserves an error 70 log.Errorf("while merging context from %s: %s. Note that context data should be under the 'context:' key, the top-level is metadata.", filePath, err) 71 } 72 73 return nil 74 } 75 76 // addContextFromFile merges the context from a file into the context to send to the console. 77 func addContextFromFile(toSend map[string][]string, filePath string) error { 78 log.Tracef("loading console context from %s", filePath) 79 80 content, err := os.ReadFile(filePath) 81 if err != nil { 82 return err 83 } 84 85 newContext := make(map[string][]string, 0) 86 87 err = yaml.Unmarshal(content, newContext) 88 if err != nil { 89 return fmt.Errorf("%s: %w", filePath, err) 90 } 91 92 err = mergeContext(toSend, newContext) 93 if err != nil && !errors.Is(err, ErrNoContextData) { 94 // having an empty console/context.yaml is not an error 95 return err 96 } 97 98 return nil 99 } 100 101 102 // LoadConsoleContext loads the context from the hub (if provided) and the file console_context_path. 103 func LoadConsoleContext(c *csconfig.Config, hub *cwhub.Hub) error { 104 c.Crowdsec.ContextToSend = make(map[string][]string, 0) 105 106 if hub != nil { 107 items, err := hub.GetInstalledItems(cwhub.CONTEXTS) 108 if err != nil { 109 return err 110 } 111 112 for _, item := range items { 113 // context in item files goes under the key 'context' 114 if err = addContextFromItem(c.Crowdsec.ContextToSend, item); err != nil { 115 return err 116 } 117 } 118 } 119 120 ignoreMissing := false 121 122 if c.Crowdsec.ConsoleContextPath != "" { 123 // if it's provided, it must exist 124 if _, err := os.Stat(c.Crowdsec.ConsoleContextPath); err != nil { 125 return fmt.Errorf("while checking console_context_path: %w", err) 126 } 127 } else { 128 c.Crowdsec.ConsoleContextPath = filepath.Join(c.ConfigPaths.ConfigDir, "console", "context.yaml") 129 ignoreMissing = true 130 } 131 132 if err := addContextFromFile(c.Crowdsec.ContextToSend, c.Crowdsec.ConsoleContextPath); err != nil { 133 if !errors.Is(err, fs.ErrNotExist) { 134 return err 135 } else if !ignoreMissing { 136 log.Warningf("while merging context from %s: %s", c.Crowdsec.ConsoleContextPath, err) 137 } 138 } 139 140 feedback, err := json.Marshal(c.Crowdsec.ContextToSend) 141 if err != nil { 142 return fmt.Errorf("marshaling console context: %s", err) 143 } 144 145 log.Debugf("console context to send: %s", feedback) 146 147 return nil 148 }