commit f6ce674e922dc6e54e26ebd3b0d6098f6274ca57 from: Sergey Bronnikov date: Sun Apr 16 17:08:49 2023 UTC server: initial version commit - 4bdba0c89beaf1b4416b25d6d4ce09cfa21b35e1 commit + f6ce674e922dc6e54e26ebd3b0d6098f6274ca57 blob - /dev/null blob + 7ae56b610b25acf6875284f89c2baa424d7d0f3e (mode 644) --- /dev/null +++ server/README.md @@ -0,0 +1,35 @@ +This is a sample semgrep rule server for serving individual rules and packs. + +To run: + +```sh +$ ./server -listen=:8080 -d=/path/to/semgrep-rules/rules -packs=packs.yml +``` + +The `packs.yml` file contains rule packs. + +```yaml +packs: + tarantool: + - box_cfg_raw_access + - grant_guest_full_access + - missed_if_not_exist + - set_trigger_once + - insecure-hash-algorithm + - insecure-hash-algorithm + - bad_hash_func +``` + +The rule names must be the `id`s of the rules, *not* the filenames. + +To serve and run a rule pack: + +```sh +$ semgrep --config=http://localhost:8080/p/tarantool +``` + +To serve and run an individual rule: + +```sh +$ semgrep --config=http://localhost:8080/r/box_cfg_raw_access +``` blob - /dev/null blob + 5024f02b2ebe88f27a3bf0887212ac4bbdca2c6a (mode 644) --- /dev/null +++ server/main.go @@ -0,0 +1,144 @@ +// Author: Damian Gryski + +package main + +import ( + "flag" + "io/ioutil" + "log" + "net/http" + "os" + "path/filepath" + "strings" + + "gopkg.in/yaml.v2" +) + +type RuleFile struct { + Rules []Rule `yaml:"rules"` +} + +type Rule map[string]interface{} + +type PackFile struct { + Packs map[string][]string `yaml:"packs"` +} + +func main() { + + dir := flag.String("dir", ".", "directory to scan for yaml files") + listen := flag.String("listen", ":8080", "address to listen on") + packyml := flag.String("packs", "packs.yml", "list of rule packs") + + flag.Parse() + + rules := make(map[string]Rule) + + filepath.Walk(*dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if filepath.Ext(path) != ".yml" { + return nil + } + + b, err := ioutil.ReadFile(path) + if err != nil { + log.Println("error reading", path, ":", err) + return nil + } + + var r RuleFile + if err := yaml.Unmarshal(b, &r); err != nil { + log.Println("error loading", path, ":", err) + return nil + } + + for _, rr := range r.Rules { + rules[rr["id"].(string)] = rr + } + + return nil + }) + + log.Println("loaded", len(rules), "rules") + + var packs PackFile + + if *packyml != "" { + b, err := ioutil.ReadFile(*packyml) + if err != nil { + log.Fatalln("error reading", *packyml, ":", err) + } + + if err := yaml.Unmarshal(b, &packs); err != nil { + log.Fatalln("error loading", *packyml, ":", err) + } + + // ensure all referenced rules exist + for _, v := range packs.Packs { + for _, r := range v { + if _, ok := rules[r]; !ok { + log.Printf("error: pack %q contained unknown rule %q", v, r) + } + } + } + } + + log.Println("loaded", len(packs.Packs), "packs") + + handler := rulesHandler{rules, packs.Packs} + + http.HandleFunc("/r/", handler.HandleRule) + http.HandleFunc("/p/", handler.HandlePack) + + log.Println("listening on", *listen) + http.ListenAndServe(*listen, nil) +} + +type rulesHandler struct { + rules map[string]Rule + packs map[string][]string +} + +// TODO(dgryski): These responses should really be precomputed and cached rather than computed on-the-fly + +func (rh rulesHandler) HandleRule(w http.ResponseWriter, r *http.Request) { + p := strings.TrimPrefix(r.RequestURI, "/r/") + log.Println("rule", p) + + rule, ok := rh.rules[p] + if !ok { + http.NotFound(w, r) + return + } + + w.Header().Set("Content-Type", "text/yaml") + + y := yaml.NewEncoder(w) + defer y.Close() + y.Encode(RuleFile{Rules: []Rule{rule}}) +} + +func (rh rulesHandler) HandlePack(w http.ResponseWriter, r *http.Request) { + p := strings.TrimPrefix(r.RequestURI, "/p/") + log.Println("pack", p) + + rules, ok := rh.packs[p] + if !ok { + http.NotFound(w, r) + return + } + + var f RuleFile + for _, rule := range rules { + f.Rules = append(f.Rules, rh.rules[rule]) + } + + w.Header().Set("Content-Type", "text/yaml") + + y := yaml.NewEncoder(w) + defer y.Close() + y.Encode(f) +}