| Index: fuzzer/go/frontend/main.go
|
| diff --git a/fuzzer/go/frontend/main.go b/fuzzer/go/frontend/main.go
|
| index f3873a78e1d2944c0860a8c80b256c78c643e385..fe688d8a7c4e27d297e74bac9854345a9213df75 100644
|
| --- a/fuzzer/go/frontend/main.go
|
| +++ b/fuzzer/go/frontend/main.go
|
| @@ -4,213 +4,208 @@ import (
|
| "encoding/json"
|
| "flag"
|
| "fmt"
|
| - htemplate "html/template"
|
| - "math/rand"
|
| + "html/template"
|
| "net/http"
|
| - "os"
|
| "path/filepath"
|
| - "runtime"
|
| - "time"
|
| +)
|
|
|
| +import (
|
| "github.com/gorilla/mux"
|
| - metrics "github.com/rcrowley/go-metrics"
|
| "github.com/skia-dev/glog"
|
| - "go.skia.org/infra/fuzzer/go/config"
|
| - "go.skia.org/infra/go/auth"
|
| +)
|
| +
|
| +import (
|
| + "go.skia.org/infra/fuzzer/go/fuzz"
|
| "go.skia.org/infra/go/common"
|
| "go.skia.org/infra/go/login"
|
| "go.skia.org/infra/go/metadata"
|
| + "go.skia.org/infra/go/skiaversion"
|
| "go.skia.org/infra/go/util"
|
| - "google.golang.org/api/storage/v1"
|
| +)
|
| +
|
| +const (
|
| + // OAUTH2_CALLBACK_PATH is callback endpoint used for the Oauth2 flow.
|
| + OAUTH2_CALLBACK_PATH = "/oauth2callback/"
|
| )
|
|
|
| var (
|
| // indexTemplate is the main index.html page we serve.
|
| - indexTemplate *htemplate.Template = nil
|
| -
|
| - requestsCounter = metrics.NewRegisteredCounter("requests", metrics.DefaultRegistry)
|
| - router *mux.Router = mux.NewRouter()
|
| - client *http.Client = nil
|
| - store *storage.Service = nil
|
| + indexTemplate *template.Template = nil
|
| + detailsTemplate *template.Template = nil
|
| )
|
|
|
| // Command line flags.
|
| var (
|
| - configFilename = flag.String("config", "fuzzer.toml", "Configuration filename")
|
| -)
|
| -
|
| -const (
|
| - // OAUTH2_CALLBACK_PATH is callback endpoint used for the Oauth2 flow.
|
| - OAUTH2_CALLBACK_PATH = "/oauth2callback/"
|
| + graphiteServer = flag.String("graphite_server", "localhost:2003", "Where is Graphite metrics ingestion server running.")
|
| + host = flag.String("host", "localhost", "HTTP service host")
|
| + port = flag.String("port", ":80", "HTTP service port (e.g., ':8002')")
|
| + local = flag.Bool("local", false, "Running locally if true. As opposed to in production.")
|
| + resourcesDir = flag.String("resources_dir", "", "The directory to find templates, JS, and CSS files. If blank the current directory will be used.")
|
| +
|
| + authWhiteList = flag.String("auth_whitelist", login.DEFAULT_DOMAIN_WHITELIST, "White space separated list of domains and email addresses that are allowed to login.")
|
| + redirectURL = flag.String("redirect_url", "https://fuzzer.skia.org/oauth2callback/", "OAuth2 redirect url. Only used when local=false.")
|
| )
|
|
|
| func Init() {
|
| - defer common.LogPanic()
|
| + reloadTemplates()
|
| +}
|
|
|
| - rand.Seed(time.Now().UnixNano())
|
| +func reloadTemplates() {
|
| + indexTemplate = template.Must(template.ParseFiles(
|
| + filepath.Join(*resourcesDir, "templates/index.html"),
|
| + filepath.Join(*resourcesDir, "templates/header.html"),
|
| + ))
|
| +}
|
|
|
| - common.InitWithMetricsCB("fuzzer", func() string {
|
| - common.DecodeTomlFile(*configFilename, &config.Config)
|
| - return config.Config.FrontEnd.GraphiteServer
|
| - })
|
| +func main() {
|
| + defer common.LogPanic()
|
| + // Calls flag.Parse()
|
| + common.InitWithMetrics("fuzzer", graphiteServer)
|
|
|
| - if config.Config.Common.ResourcePath == "" {
|
| - _, filename, _, _ := runtime.Caller(0)
|
| - config.Config.Common.ResourcePath = filepath.Join(filepath.Dir(filename), "../..")
|
| - }
|
| + Init()
|
|
|
| - path, err := filepath.Abs(config.Config.Common.ResourcePath)
|
| - if err != nil {
|
| - glog.Fatalf("Couldn't get absolute path to fuzzer resources: %s", err)
|
| - }
|
| - if err := os.Chdir(path); err != nil {
|
| - glog.Fatal(err)
|
| - }
|
| + setupOAuth()
|
|
|
| - indexTemplate = htemplate.Must(htemplate.ParseFiles(
|
| - filepath.Join(path, "templates/index.html"),
|
| - filepath.Join(path, "templates/header.html"),
|
| - filepath.Join(path, "templates/titlebar.html"),
|
| - filepath.Join(path, "templates/footer.html"),
|
| - ))
|
| + runServer()
|
| +}
|
|
|
| - if client, err = auth.NewClient(config.Config.Common.DoOAuth, config.Config.Common.OAuthCacheFile, storage.DevstorageFullControlScope); err != nil {
|
| - glog.Fatalf("Failed to create authenticated HTTP client: %s", err)
|
| +func setupOAuth() {
|
| + var cookieSalt = "notverysecret"
|
| + // This clientID and clientSecret are only used for setting up a local server.
|
| + // Production id and secrets are in metadata and will be loaded from there.
|
| + var clientID = "31977622648-ubjke2f3staq6ouas64r31h8f8tcbiqp.apps.googleusercontent.com"
|
| + var clientSecret = "rK-kRY71CXmcg0v9I9KIgWci"
|
| + var useRedirectURL = fmt.Sprintf("http://localhost%s/oauth2callback/", *port)
|
| + if !*local {
|
| + cookieSalt = metadata.Must(metadata.ProjectGet(metadata.COOKIESALT))
|
| + clientID = metadata.Must(metadata.ProjectGet(metadata.CLIENT_ID))
|
| + clientSecret = metadata.Must(metadata.ProjectGet(metadata.CLIENT_SECRET))
|
| + useRedirectURL = *redirectURL
|
| }
|
| + login.Init(clientID, clientSecret, useRedirectURL, cookieSalt, login.DEFAULT_SCOPE, *authWhiteList, *local)
|
| +}
|
|
|
| - if store, err = storage.New(client); err != nil {
|
| - glog.Fatalf("Failed to create storage service client: %s", err)
|
| +func runServer() {
|
| + serverURL := "https://" + *host
|
| + if *local {
|
| + serverURL = "http://" + *host + *port
|
| }
|
| -}
|
|
|
| -type IndexContext struct {
|
| - LoadFuzzListURL string
|
| -}
|
| + r := mux.NewRouter()
|
| + r.PathPrefix("/res/").HandlerFunc(util.MakeResourceHandler(*resourcesDir))
|
|
|
| -func getURL(router *mux.Router, name string, pairs ...string) string {
|
| - route := router.Get(name)
|
| - if route == nil {
|
| - glog.Fatalf("Couldn't find any route named %s", name)
|
| - }
|
| + r.HandleFunc(OAUTH2_CALLBACK_PATH, login.OAuth2CallbackHandler)
|
| + r.HandleFunc("/", indexHandler)
|
| + r.HandleFunc("/details", detailHandler)
|
| + r.HandleFunc("/loginstatus/", login.StatusHandler)
|
| + r.HandleFunc("/logout/", login.LogoutHandler)
|
| + r.HandleFunc("/json/version", skiaversion.JsonHandler)
|
| + r.HandleFunc("/json/fuzz-list", fuzzListHandler)
|
|
|
| - routeURL, err := route.URL(pairs...)
|
| - if err != nil {
|
| - glog.Fatalf("Couldn't resolve route %s into a URL", routeURL)
|
| - }
|
| + rootHandler := login.ForceAuth(util.LoggingGzipRequestResponse(r), OAUTH2_CALLBACK_PATH)
|
|
|
| - return routeURL.String()
|
| + http.Handle("/", rootHandler)
|
| + glog.Infof("Ready to serve on %s", serverURL)
|
| + glog.Fatal(http.ListenAndServe(*port, nil))
|
| }
|
|
|
| -func mainHandler(w http.ResponseWriter, r *http.Request) {
|
| - glog.Infof("Main Handler: %q\n", r.URL.Path)
|
| - requestsCounter.Inc(1)
|
| - if r.Method == "GET" {
|
| - // Expand the template.
|
| - w.Header().Set("Content-Type", "text/html")
|
| - fuzzListURL := getURL(router, "fuzzListHandler")
|
| - context := IndexContext{
|
| - fuzzListURL,
|
| - }
|
| - if err := indexTemplate.Execute(w, context); err != nil {
|
| - glog.Errorf("Failed to expand template: %q\n", err)
|
| - }
|
| +func indexHandler(w http.ResponseWriter, r *http.Request) {
|
| + if *local {
|
| + reloadTemplates()
|
| }
|
| -}
|
|
|
| -// makeResourceHandler creates a static file handler that sets a caching policy.
|
| -func makeResourceHandler() func(http.ResponseWriter, *http.Request) {
|
| - fileServer := http.FileServer(http.Dir(config.Config.Common.ResourcePath))
|
| - return func(w http.ResponseWriter, r *http.Request) {
|
| - w.Header().Add("Cache-Control", string(300))
|
| - fileServer.ServeHTTP(w, r)
|
| - }
|
| -}
|
| + w.Header().Set("Content-Type", "text/html")
|
|
|
| -func getFuzzes(baseDir string) []string {
|
| - results := []string{}
|
| - glog.Infof("Opening bucket/directory: %s/%s", config.Config.Common.FuzzOutputGSBucket, baseDir)
|
| -
|
| - req := store.Objects.List(config.Config.Common.FuzzOutputGSBucket).Prefix(baseDir + "/").Delimiter("/")
|
| - for req != nil {
|
| - resp, err := req.Do()
|
| - if err != nil {
|
| - return results
|
| - }
|
| - for _, result := range resp.Prefixes {
|
| - results = append(results, result[len(baseDir)+1:len(result)-1])
|
| - }
|
| - if len(resp.NextPageToken) > 0 {
|
| - req.PageToken(resp.NextPageToken)
|
| - } else {
|
| - req = nil
|
| - }
|
| + if err := indexTemplate.Execute(w, nil); err != nil {
|
| + glog.Errorf("Failed to write or encode output: %s", err)
|
| }
|
| - return results
|
| }
|
|
|
| -func fuzzListHandler(w http.ResponseWriter, r *http.Request) {
|
| - if err := r.ParseForm(); err != nil {
|
| - util.ReportError(w, r, err, "Failed to parse form data.")
|
| - return
|
| +func detailHandler(w http.ResponseWriter, r *http.Request) {
|
| + if *local {
|
| + reloadTemplates()
|
| }
|
|
|
| - w.Header().Set("Content-Type", "application/json")
|
| - enc := json.NewEncoder(w)
|
| -
|
| - fuzzes := []string{}
|
| + w.Header().Set("Content-Type", "text/html")
|
|
|
| - failed := r.FormValue("failed")
|
| - passed := r.FormValue("passed")
|
| -
|
| - if failed == "true" {
|
| - fuzzes = append(fuzzes, getFuzzes("failed")...)
|
| - }
|
| - if passed == "true" {
|
| - fuzzes = append(fuzzes, getFuzzes("working")...)
|
| - }
|
| -
|
| - if err := enc.Encode(fuzzes); err != nil {
|
| + if err := detailsTemplate.Execute(w, nil); err != nil {
|
| glog.Errorf("Failed to write or encode output: %s", err)
|
| }
|
| }
|
|
|
| -func main() {
|
| - flag.Parse()
|
| - Init()
|
| +type fuzzReport []fuzzReportFile
|
|
|
| - // Set up login
|
| - var cookieSalt = "notverysecret"
|
| - var clientID = "31977622648-ubjke2f3staq6ouas64r31h8f8tcbiqp.apps.googleusercontent.com"
|
| - var clientSecret = "rK-kRY71CXmcg0v9I9KIgWci"
|
| - var useRedirectURL = fmt.Sprintf("http://localhost%s/oauth2callback/", config.Config.FrontEnd.Port)
|
| - if !config.Config.Common.Local {
|
| - cookieSalt = metadata.Must(metadata.ProjectGet(metadata.COOKIESALT))
|
| - clientID = metadata.Must(metadata.ProjectGet(metadata.CLIENT_ID))
|
| - clientSecret = metadata.Must(metadata.ProjectGet(metadata.CLIENT_SECRET))
|
| - useRedirectURL = config.Config.FrontEnd.RedirectURL
|
| - }
|
| +type fuzzReportFile struct {
|
| + FileName string `json:"fileName"`
|
| + BinaryCount int `json:"binaryCount"`
|
| + ApiCount int `json:"apiCount"`
|
| + Functions []fuzzReportFunction `json:"byFunction"`
|
| +}
|
|
|
| - login.Init(clientID, clientSecret, useRedirectURL, cookieSalt, login.DEFAULT_SCOPE, login.DEFAULT_DOMAIN_WHITELIST, config.Config.Common.Local)
|
| +type fuzzReportFunction struct {
|
| + FunctionName string `json:"functionName"`
|
| + BinaryCount int `json:"binaryCount"`
|
| + ApiCount int `json:"apiCount"`
|
| + LineNumbers []fuzzReportLineNumber `json:"byLineNumber"`
|
| +}
|
|
|
| - // Set up the login related resources.
|
| - router.HandleFunc(OAUTH2_CALLBACK_PATH, login.OAuth2CallbackHandler)
|
| - router.HandleFunc("/loginstatus/", login.StatusHandler)
|
| - router.HandleFunc("/logout/", login.LogoutHandler)
|
| +type fuzzReportLineNumber struct {
|
| + LineNumber int `json:"lineNumber"`
|
| + BinaryCount int `json:"binaryCount"`
|
| + ApiCount int `json:"apiCount"`
|
| + BinaryDetails []fuzzReportBinary `json:"binaryReports"`
|
| +}
|
|
|
| - router.HandleFunc("/", mainHandler)
|
| - router.HandleFunc("/failed", mainHandler)
|
| - router.HandleFunc("/passed", mainHandler)
|
| - router.PathPrefix("/res/").HandlerFunc(makeResourceHandler())
|
| +// We make this intermediate struct so we can control the json names and disentagle the backend structure from what is displayed/visualized
|
| +type fuzzReportBinary struct {
|
| + DebugStackTrace fuzz.StackTrace `json:"debugStackTrace"`
|
| + ReleaseStackTrace fuzz.StackTrace `json:"releaseStackTrace"`
|
| + HumanReadableFlags []string `json:"flags"`
|
| + BadBinaryName string `json:"binaryName"`
|
| + BinaryType string `json:"binaryType"`
|
| +}
|
|
|
| - jsonRouter := router.PathPrefix("/_").Subrouter()
|
| - jsonRouter.HandleFunc("/list", fuzzListHandler).Name("fuzzListHandler")
|
| +func fuzzListHandler(w http.ResponseWriter, r *http.Request) {
|
| + w.Header().Set("Content-Type", "application/json")
|
|
|
| - rootHandler := util.LoggingGzipRequestResponse(router)
|
| - if config.Config.FrontEnd.ForceLogin {
|
| - rootHandler = login.ForceAuth(rootHandler, OAUTH2_CALLBACK_PATH)
|
| + mockFuzzes := fuzzReport{
|
| + {
|
| + "foo.h", 30, 0, []fuzzReportFunction{
|
| + {
|
| + "frizzle()", 18, 0, []fuzzReportLineNumber{
|
| + {
|
| + 64, 17, 0, []fuzzReportBinary{},
|
| + },
|
| + {
|
| + 69, 1, 0, []fuzzReportBinary{},
|
| + },
|
| + },
|
| + }, {
|
| + "zizzle()", 12, 0, []fuzzReportLineNumber{
|
| + {
|
| + 123, 12, 0, []fuzzReportBinary{},
|
| + },
|
| + },
|
| + },
|
| + },
|
| + }, {
|
| + "bar.h", 15, 3, []fuzzReportFunction{
|
| + {
|
| + "frizzle()", 15, 3, []fuzzReportLineNumber{
|
| + {
|
| + 566, 15, 2, []fuzzReportBinary{},
|
| + },
|
| + {
|
| + 568, 0, 1, []fuzzReportBinary{},
|
| + },
|
| + },
|
| + },
|
| + },
|
| + },
|
| + }
|
| +
|
| + if err := json.NewEncoder(w).Encode(mockFuzzes); err != nil {
|
| + glog.Errorf("Failed to write or encode output: %s", err)
|
| + return
|
| }
|
| -
|
| - http.Handle("/", rootHandler)
|
| -
|
| - glog.Fatal(http.ListenAndServe(config.Config.FrontEnd.Port, nil))
|
| }
|
|
|