package main import ( "bytes" "fmt" "io/ioutil" "log" "net/http" "net/http/httputil" "regexp" "strings" ) //Zexpressions is the set of regexp being used by zardoz var Zexpressions = []string{ `[[:alpha:]]{4,32}`, // alpha digit token `[ ]([A-Za-z0-9-_]{4,}\.)+\w+`, // domain name `[ ]/[A-Za-z0-9-_/.]{4,}[ ]`, // URI path (also partial) `[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}`, // IP address `[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}`, // UUID } func passAndLearn(resp *http.Response) error { ProxyFlow.response = resp ProxyFlow.seniority++ req := ProxyFlow.request switch { case isAuth(resp): log.Println("401: We don't want to store credentials") case isError(resp): buf := bytes.NewBufferString(BlockMessage) resp.Body = ioutil.NopCloser(buf) resp.Status = "403 Forbidden" resp.StatusCode = 403 resp.Header["Content-Length"] = []string{fmt.Sprint(buf.Len())} resp.Header.Set("Content-Encoding", "none") resp.Header.Set("Cache-Control", "no-cache, no-store") log.Println("Filing inside bad class") feedRequest(req, "BAD") ControPlane.StatsTokens <- "DOWNGRADE" case isSuccess(resp): log.Println("Filing inside Good Class: ", resp.StatusCode) feedRequest(req, "GOOD") } return nil } func blockAndlearn(resp *http.Response) error { ProxyFlow.response = resp ProxyFlow.seniority++ req := ProxyFlow.request buf := bytes.NewBufferString(BlockMessage) resp.Body = ioutil.NopCloser(buf) resp.Status = "403 Forbidden" resp.StatusCode = 403 resp.Header["Content-Length"] = []string{fmt.Sprint(buf.Len())} resp.Header.Set("Content-Encoding", "none") resp.Header.Set("Cache-Control", "no-cache, no-store") switch { case isAuth(resp): log.Println("401: We don't want to store credentials") case isError(resp): log.Println("Filing inside bad class") feedRequest(req, "BAD") case isSuccess(resp): log.Println("Filing inside Good Class: ", resp.StatusCode) ControPlane.StatsTokens <- "UPGRADED" feedRequest(req, "GOOD") } return nil } func sanitizeHeaders(s string) string { var collect []string ss := strings.ToLower(s) for _, zregexp := range Zexpressions { re, rerr := regexp.Compile(zregexp) if rerr != nil { log.Println("Error Compiling regular expression: ", zregexp) } matched := re.FindAllString(ss, -1) if matched == nil { matched = []string{"null"} } collect = append(collect, matched...) } uMatched := Unique(collect) log.Println("Matched: ", uMatched) return strings.Join(uMatched, " ") } func feedRequest(req *http.Request, class string) { feed := formatRequest(req) if class == "BAD" { log.Println("Feeding BAD token: ", feed) ControPlane.BadTokens <- sanitizeHeaders(feed) } if class == "GOOD" { log.Println("Feeding GOOD Token:", feed) ControPlane.GoodTokens <- sanitizeHeaders(feed) } } func formatRequest(req *http.Request) string { requestDump, err := httputil.DumpRequest(req, false) if err != nil { fmt.Println(err) } return fmt.Sprintf("%s\n", requestDump) } //Unique returns unique elements in the string func Unique(slice []string) []string { // create a map with all the values as key uniqMap := make(map[string]struct{}) for _, v := range slice { uniqMap[v] = struct{}{} } // turn the map keys into a slice uniqSlice := make([]string, 0, len(uniqMap)) for v := range uniqMap { uniqSlice = append(uniqSlice, v) } return uniqSlice } func isSuccess(resp *http.Response) bool { return resp.StatusCode <= 299 } func isAuth(resp *http.Response) bool { return resp.StatusCode == 401 } func isError(resp *http.Response) bool { return resp.StatusCode >= 400 && resp.StatusCode != 401 }