initial commit

This commit is contained in:
2024-01-13 00:27:56 -06:00
parent 6d3a276bf0
commit 98efa97678
18 changed files with 1392 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
package webserver
import (
"fmt"
"log"
"regexp"
"strconv"
"strings"
"time"
"compress/gzip"
"net/http"
"istheinternetonfire.app/assets"
"istheinternetonfire.app/internal/config"
)
const (
TYPE_APPLICATION_PEM string = "application/x-pem-file"
TYPE_APPLICATION_JSON string = "application/json"
TYPE_AUDIO_MPEG string = "audio/mpeg"
TYPE_FONT_WOFF string = "font/woff"
TYPE_FONT_WOFF2 string = "font/woff2"
TYPE_IMAGE_JPG string = "image/jpg"
TYPE_IMAGE_PNG string = "image/png"
TYPE_TEXT_CSS string = "text/css"
TYPE_TEXT_HTML string = "text/html"
TYPE_TEXT_JS string = "text/javascript"
TYPE_TEXT_PLAIN string = "text/plain"
TYPE_TEXT_RAW string = "text/raw"
)
var validFiles map[string]string = map[string]string{
"/robots.txt": TYPE_TEXT_PLAIN,
"/apple-touch-icon.png": TYPE_IMAGE_PNG,
"/favicon.ico": TYPE_IMAGE_PNG,
"/favicon-16x16.png": TYPE_IMAGE_PNG,
"/favicon-32x32.png": TYPE_IMAGE_PNG,
"/js/bootstrap.bundle.min.js": TYPE_TEXT_JS,
"/js/bootstrap.bundle.min.js.map": TYPE_APPLICATION_JSON,
"/js/jquery.min.js": TYPE_TEXT_JS,
}
func isValidReq(file string) (string, error) {
for f, t := range validFiles {
if file == f {
return t, nil
}
}
return "", fmt.Errorf("Invalid file requested: %s", file)
}
func httpAccessLog(req *http.Request) {
config.Cfg.Log.Debug("http request", "method", req.Method, "remote-address", req.RemoteAddr, "request-uri", req.RequestURI)
}
func crossSiteOrigin(w http.ResponseWriter) {
w.Header().Add("Access-Control-Allow-Origin", "*")
w.Header().Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
}
func Start() {
path := http.NewServeMux()
connection := &http.Server{
Addr: config.Cfg.WebServerIP + ":" + strconv.FormatInt(int64(config.Cfg.WebServerPort), 10),
Handler: path,
ReadTimeout: time.Duration(config.Cfg.WebServerReadTimeout) * time.Second,
WriteTimeout: time.Duration(config.Cfg.WebServerWriteTimeout) * time.Second,
IdleTimeout: time.Duration(config.Cfg.WebServerIdleTimeout) * time.Second,
}
path.HandleFunc("/", webRoot)
if err := connection.ListenAndServe(); err != nil {
config.Cfg.Log.Error("unable to start webserver", "error", err)
}
}
func webRoot(w http.ResponseWriter, r *http.Request) {
httpAccessLog(r)
crossSiteOrigin(w)
if strings.ToLower(r.Method) != "get" {
config.Cfg.Log.Debug("http invalid method", "url", r.URL.Path, "expected", "GET", "received", r.Method)
tmpltError(w, http.StatusBadRequest, "Invalid http method.")
return
}
if r.URL.Path == "/" {
tmpltWebRoot(w, r)
} else {
cType, err := isValidReq(r.URL.Path)
if err != nil {
config.Cfg.Log.Debug("request not found", "url", r.URL.Path)
tmpltStatusNotFound(w, r.URL.Path)
return
}
w.Header().Add("Content-Type", cType)
o, err := assets.EmbedHTML.ReadFile("html" + r.URL.Path)
if err != nil {
log.Printf("[ERROR] Unable to read local embedded file data: %v\n", err)
tmpltError(w, http.StatusInternalServerError, "Server unable to retrieve file data.")
return
}
if regexp.MustCompile(`gzip`).Match([]byte(r.Header.Get("Accept-Encoding"))) {
w.Header().Add("Content-Encoding", "gzip")
gw := gzip.NewWriter(w)
defer gw.Close()
gw.Write(o)
} else {
w.Write(o)
}
}
}

View File

@@ -0,0 +1,102 @@
package webserver
import (
"bytes"
"strings"
"time"
"encoding/json"
"net/http"
"text/template"
"istheinternetonfire.app/assets"
"istheinternetonfire.app/internal/cisa"
"istheinternetonfire.app/internal/config"
)
type webErrStruct struct {
Error bool `json:"error" yaml:"error"`
ErrorMsg string `json:"error_message" yaml:"errorMessage"`
}
func tmpltError(w http.ResponseWriter, serverStatus int, message string) {
var (
output []byte
o = webErrStruct{
Error: true,
ErrorMsg: message,
}
err error
)
w.Header().Add("Content-Type", "application/json")
output, err = json.MarshalIndent(o, "", " ")
if err != nil {
config.Cfg.Log.Warn("marshal error", "error", err)
w.WriteHeader(serverStatus)
w.Write(output) //nolint:errcheck
}
}
func tmpltWebRoot(w http.ResponseWriter, r *http.Request) {
tmplt, err := template.New("index.tplt").Funcs(template.FuncMap{
"ToUpper": strings.ToUpper,
}).ParseFS(
assets.EmbedHTML,
"html/index.tplt",
"html/css/style.css",
)
if err != nil {
config.Cfg.Log.Debug("unable to parse html template", "error", err)
tmpltError(w, http.StatusInternalServerError, "Template Parse Error.")
return
}
var (
msgBuffer bytes.Buffer
cves []cisa.VulStruct
)
c := cisa.Read()
for _, i := range c.Vulnerabilities {
t, _ := time.Parse("2006-01-02", i.DateAdded)
if t.After(time.Now().Add(-time.Hour * 720)) {
cves = append(cves, i)
}
}
if err := tmplt.Execute(&msgBuffer, struct {
CVEs []cisa.VulStruct
}{
CVEs: cves[len(cves)-3:],
}); err != nil {
config.Cfg.Log.Debug("unable to execute html template", err)
tmpltError(w, http.StatusInternalServerError, "Template Parse Error.")
return
}
w.Write(msgBuffer.Bytes())
}
func tmpltStatusNotFound(w http.ResponseWriter, path string) {
tmplt, err := template.ParseFS(assets.EmbedHTML, "html/file-not-found.tplt")
if err != nil {
config.Cfg.Log.Debug("unable to parse html template", err)
tmpltError(w, http.StatusInternalServerError, "Template Parse Error.")
return
}
var msgBuffer bytes.Buffer
if err := tmplt.Execute(&msgBuffer, struct {
Title string
ErrorCode int
}{
Title: path,
ErrorCode: http.StatusNotFound,
}); err != nil {
config.Cfg.Log.Debug("unable to execute html template", err)
tmpltError(w, http.StatusInternalServerError, "Template Parse Error.")
return
}
w.Write(msgBuffer.Bytes())
}