Fixing up some directory traversal attacks
This commit is contained in:
parent
b11be2f32c
commit
7a42f90b3f
@ -9,13 +9,25 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
ConfigType string
|
ConfigType string
|
||||||
|
|
||||||
|
// ConfigEntry defines a single route configuration entry.
|
||||||
|
// Used to configure routes from a JSON config file.
|
||||||
ConfigEntry struct {
|
ConfigEntry struct {
|
||||||
ConfigType ConfigType
|
|
||||||
UrlPattern string
|
// ConfigType indicates the type of route - Text, Image etc.
|
||||||
FileLocation string
|
ConfigType ConfigType
|
||||||
|
|
||||||
|
// UrlPattern is the URL regex pattern to match for the route.
|
||||||
|
UrlPattern string
|
||||||
|
|
||||||
|
// FileLocation is the file path or directory to serve files from.
|
||||||
|
FileLocation string
|
||||||
|
|
||||||
|
// UseBuiltinCache indicates whether to use the built-in LRU cache.
|
||||||
UseBuiltinCache bool
|
UseBuiltinCache bool
|
||||||
}
|
}
|
||||||
|
|
||||||
Config struct {
|
Config struct {
|
||||||
ListeningAddress string
|
ListeningAddress string
|
||||||
Routes []ConfigEntry
|
Routes []ConfigEntry
|
||||||
|
|||||||
@ -25,12 +25,19 @@ type ErrorResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
// VinegarServlet is the main server struct that handles HTTP requests and routing.
|
||||||
|
// It contains the TCP port to listen on, the routes to match requests against,
|
||||||
|
// and a map of status code to error handling routes.
|
||||||
VinegarServlet struct {
|
VinegarServlet struct {
|
||||||
Port string
|
Port string
|
||||||
Routes []*VinegarRoute
|
Routes []*VinegarRoute
|
||||||
ErrorRoutes map[int]*TemplateRoute
|
ErrorRoutes map[int]*TemplateRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VinegarRoute defines a single route in the router.
|
||||||
|
// It contains a regex Pattern to match against the URL path,
|
||||||
|
// a Handler function to call when the route matches,
|
||||||
|
// and an optional Cache to enable caching for the route.
|
||||||
VinegarRoute struct {
|
VinegarRoute struct {
|
||||||
Pattern *regexp.Regexp
|
Pattern *regexp.Regexp
|
||||||
Handler VinegarHandlerFunction
|
Handler VinegarHandlerFunction
|
||||||
|
|||||||
@ -5,20 +5,67 @@ import (
|
|||||||
util "geniuscartel.xyz/vinegar/vinegarUtil"
|
util "geniuscartel.xyz/vinegar/vinegarUtil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
// FileRoute implements a static file serving route.
|
||||||
|
// It serves files from a given file path.
|
||||||
FileRoute struct {
|
FileRoute struct {
|
||||||
*VinegarRoute
|
|
||||||
srv *VinegarServlet
|
// VinegarRoute is the base route containing the URL pattern and handler.
|
||||||
|
VinegarRoute *VinegarRoute
|
||||||
|
|
||||||
|
// srv is the VinegarServlet instance that this route is attached to.
|
||||||
|
srv *VinegarServlet
|
||||||
|
|
||||||
|
// fileRoot is the base file path to serve files from.
|
||||||
fileRoot string
|
fileRoot string
|
||||||
|
|
||||||
|
// UseCache indicates whether to use caching for this route.
|
||||||
UseCache bool
|
UseCache bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//RouteConstructor
|
||||||
|
//
|
||||||
|
//Params:
|
||||||
|
//
|
||||||
|
//servlet - The VinegarServlet instance to add the route to
|
||||||
|
//
|
||||||
|
//urlPattern - The URL regex pattern for route to match
|
||||||
|
//
|
||||||
|
//pathlike - The file path to serve
|
||||||
|
//
|
||||||
|
//useCache - Whether to use caching for this route
|
||||||
|
//
|
||||||
|
// A RouteConstructor is a function that accepts a VinegarServlet, urlPattern, file path, and cache option. It uses
|
||||||
|
// these to construct and return a FileRoute.
|
||||||
|
// The return value is a FileRoute that will serve the files from the given path.
|
||||||
|
//
|
||||||
|
// This function signature allows encapsulating the creation of different types of FileRoutes. It is used to define
|
||||||
|
// constructor functions for each file type, like NewTextRoute or NewImageRoute.
|
||||||
RouteConstructor func(servlet *VinegarServlet, urlPattern string, pathlike string, useCache bool) *FileRoute
|
RouteConstructor func(servlet *VinegarServlet, urlPattern string, pathlike string, useCache bool) *FileRoute
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NewTextRoute creates a new FileRoute for serving text files.
|
||||||
|
//
|
||||||
|
// It handles text files as compressible content, gzipping them when the client sends the Accept-Encoding: gzip header.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
//
|
||||||
|
// servlet - The VinegarServlet instance to attach the route to.
|
||||||
|
//
|
||||||
|
// urlPattern - The URL regex pattern that triggers this route.
|
||||||
|
//
|
||||||
|
// pathlike - The file path on disk to serve files from.
|
||||||
|
//
|
||||||
|
// useCache - Whether to use caching for this route.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
//
|
||||||
|
// A FileRoute instance configured for serving text files, added to
|
||||||
|
// the provided VinegarServlet.
|
||||||
var NewTextRoute RouteConstructor = func(servlet *VinegarServlet, urlPattern string, pathlike string, useCache bool) *FileRoute {
|
var NewTextRoute RouteConstructor = func(servlet *VinegarServlet, urlPattern string, pathlike string, useCache bool) *FileRoute {
|
||||||
defaultPrune := strings.Replace(urlPattern, ".*", "", -1)
|
defaultPrune := strings.Replace(urlPattern, ".*", "", -1)
|
||||||
route := FileRoute{srv: servlet, fileRoot: pathlike, UseCache: useCache}
|
route := FileRoute{srv: servlet, fileRoot: pathlike, UseCache: useCache}
|
||||||
@ -61,6 +108,30 @@ var NewSingleFileRoute RouteConstructor = func(servlet *VinegarServlet, urlPatte
|
|||||||
return &route
|
return &route
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createSingleFileServletFunction creates a handler function for serving a single file.
|
||||||
|
//
|
||||||
|
// It looks up the file content from the route's cache and writes the appropriate
|
||||||
|
// headers and file content to the response.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
//
|
||||||
|
// route - The FileRoute instance this handler is attached to.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
//
|
||||||
|
// A VinegarHandlerFunction that serves the single file for the provided route.
|
||||||
|
//
|
||||||
|
// The handler function checks the route's cache for the file content.
|
||||||
|
// If caching is enabled, it first checks the cache directly.
|
||||||
|
// If caching is disabled, it calls GetFresh to avoid the cache.
|
||||||
|
//
|
||||||
|
// If the content is not found, it returns a 404 error.
|
||||||
|
//
|
||||||
|
// Otherwise, it sets the Content-Type header based on the cache's MIME type,
|
||||||
|
// and writes the file content to the response.
|
||||||
|
//
|
||||||
|
// If the client accepts gzip encoding, it compresses the content before writing.
|
||||||
|
|
||||||
func createSingleFileServletFunction(route *FileRoute) VinegarHandlerFunction {
|
func createSingleFileServletFunction(route *FileRoute) VinegarHandlerFunction {
|
||||||
|
|
||||||
var fun VinegarHandlerFunction = func(w http.ResponseWriter, req *http.Request) {
|
var fun VinegarHandlerFunction = func(w http.ResponseWriter, req *http.Request) {
|
||||||
@ -68,9 +139,9 @@ func createSingleFileServletFunction(route *FileRoute) VinegarHandlerFunction {
|
|||||||
var exists bool
|
var exists bool
|
||||||
|
|
||||||
if route.UseCache {
|
if route.UseCache {
|
||||||
cache, exists = route.Cache.Get("")
|
cache, exists = route.VinegarRoute.Cache.Get("")
|
||||||
} else {
|
} else {
|
||||||
cache, exists = route.Cache.GetFresh("")
|
cache, exists = route.VinegarRoute.Cache.GetFresh("")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
@ -98,17 +169,17 @@ func createSingleFileServletFunction(route *FileRoute) VinegarHandlerFunction {
|
|||||||
func createCompressibleFileServletFunction(route *FileRoute, basePattern string, pathlike string) VinegarHandlerFunction {
|
func createCompressibleFileServletFunction(route *FileRoute, basePattern string, pathlike string) VinegarHandlerFunction {
|
||||||
var fun VinegarHandlerFunction = func(w http.ResponseWriter, req *http.Request) {
|
var fun VinegarHandlerFunction = func(w http.ResponseWriter, req *http.Request) {
|
||||||
stub := strings.Replace(req.URL.Path, basePattern, "", 1)
|
stub := strings.Replace(req.URL.Path, basePattern, "", 1)
|
||||||
cachedContent, exists := route.Cache.Get(stub)
|
cachedContent, exists := route.VinegarRoute.Cache.Get(stub)
|
||||||
//i don't like this logic below. we need to streamline this a lot better. it's a twisty jungle right now
|
//i don't like this logic below. we need to streamline this a lot better. it's a twisty jungle right now
|
||||||
|
|
||||||
resourcePath := path.Join(pathlike, stub)
|
resourcePath := path.Join(pathlike, filepath.Clean(stub))
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
content, fileExists := util.GetDiskContent(resourcePath)
|
content, fileExists := util.GetDiskContent(resourcePath)
|
||||||
if fileExists {
|
if fileExists {
|
||||||
if route.UseCache {
|
if route.UseCache {
|
||||||
route.Cache.Put(stub, resourcePath)
|
route.VinegarRoute.Cache.Put(stub, resourcePath)
|
||||||
cachedContent, _ = route.Cache.Get(stub)
|
cachedContent, _ = route.VinegarRoute.Cache.Get(stub)
|
||||||
} else {
|
} else {
|
||||||
w.Header().Add(ContentTypeHeaderKey, util.GuessMimetype(stub))
|
w.Header().Add(ContentTypeHeaderKey, util.GuessMimetype(stub))
|
||||||
w.Write(*content)
|
w.Write(*content)
|
||||||
@ -138,12 +209,12 @@ func createCompressibleFileServletFunction(route *FileRoute, basePattern string,
|
|||||||
|
|
||||||
func createUncompressedFileServletFunction(route *FileRoute, basePattern string, pathlike string) VinegarHandlerFunction {
|
func createUncompressedFileServletFunction(route *FileRoute, basePattern string, pathlike string) VinegarHandlerFunction {
|
||||||
var fun VinegarHandlerFunction = func(w http.ResponseWriter, req *http.Request) {
|
var fun VinegarHandlerFunction = func(w http.ResponseWriter, req *http.Request) {
|
||||||
stub := strings.Replace(req.URL.Path, basePattern, "", 1)
|
stub := filepath.Clean(strings.Replace(req.URL.Path, basePattern, "", 1))
|
||||||
resourcePath := path.Join(pathlike, stub)
|
resourcePath := path.Join(pathlike, stub)
|
||||||
entry, exists := route.Cache.Get(stub)
|
entry, exists := route.VinegarRoute.Cache.Get(stub)
|
||||||
if !exists {
|
if !exists {
|
||||||
route.Cache.Put(stub, resourcePath)
|
route.VinegarRoute.Cache.Put(stub, resourcePath)
|
||||||
entry, exists = route.Cache.Get(stub)
|
entry, exists = route.VinegarRoute.Cache.Get(stub)
|
||||||
}
|
}
|
||||||
|
|
||||||
if exists {
|
if exists {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user