mirror of
https://codeberg.org/scip/ephemerup.git
synced 2025-12-16 20:20:58 +01:00
added basic input validation/cleanup + tests
This commit is contained in:
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -105,3 +106,27 @@ func IsExpired(start time.Time, duration string) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
Untaint user input, that is: remove all non supported chars.
|
||||
|
||||
wanted is a regexp matching chars we shall leave. Everything else
|
||||
will be removed. Eg:
|
||||
|
||||
untainted := Untaint(input, `[^a-zA-Z0-9\-]`)
|
||||
|
||||
Returns a new string and an error if the input string has been
|
||||
modified. It's the callers choice to decide what to do about
|
||||
it. You may ignore the error and use the untainted string or bail
|
||||
out.
|
||||
*/
|
||||
func Untaint(input string, wanted string) (string, error) {
|
||||
re := regexp.MustCompile(wanted)
|
||||
untainted := re.ReplaceAllString(input, "")
|
||||
|
||||
if len(untainted) != len(input) {
|
||||
return untainted, errors.New("Invalid input string!")
|
||||
}
|
||||
|
||||
return untainted, nil
|
||||
}
|
||||
|
||||
@@ -51,3 +51,28 @@ func TestIsExpired(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUntaint(t *testing.T) {
|
||||
var tests = []struct {
|
||||
want string
|
||||
input string
|
||||
expect string
|
||||
wanterr bool
|
||||
}{
|
||||
{`[^a-zA-Z0-9\-]`, "ab23-bb43-beef", "ab23-bb43-beef", false},
|
||||
{`[^a-zA-Z0-9\-]`, "`cat passwd`+ab23-bb43-beef", "catpasswdab23-bb43-beef", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
testname := fmt.Sprintf("untaint-%s-%s", tt.want, tt.expect)
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
untainted, err := Untaint(tt.input, tt.want)
|
||||
if untainted != tt.expect {
|
||||
t.Errorf("got %s, want %s", untainted, tt.expect)
|
||||
}
|
||||
if err != nil && !tt.wanterr {
|
||||
t.Errorf("got error: %s", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,10 +56,8 @@ func FilePut(c *fiber.Ctx, cfg *cfg.Config, db *Db) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
//entry := &Upload{Id: id, Uploaded: time.Now()}
|
||||
entry := &Upload{Id: id, Uploaded: Timestamp{Time: time.Now()}}
|
||||
|
||||
// init upload obj
|
||||
entry := &Upload{Id: id, Uploaded: Timestamp{Time: time.Now()}}
|
||||
|
||||
// retrieve files, if any
|
||||
files := form.File["upload[]"]
|
||||
@@ -83,7 +81,11 @@ func FilePut(c *fiber.Ctx, cfg *cfg.Config, db *Db) (string, error) {
|
||||
if len(formdata.Expire) == 0 {
|
||||
entry.Expire = "asap"
|
||||
} else {
|
||||
entry.Expire = formdata.Expire // FIXME: validate
|
||||
ex, err := Untaint(formdata.Expire, `[dhms0-9]`) // duration or asap allowed
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
entry.Expire = ex
|
||||
}
|
||||
|
||||
if len(entry.Members) == 1 {
|
||||
@@ -131,10 +133,14 @@ func FilePut(c *fiber.Ctx, cfg *cfg.Config, db *Db) (string, error) {
|
||||
}
|
||||
|
||||
func FileGet(c *fiber.Ctx, cfg *cfg.Config, db *Db) error {
|
||||
// deliver a file and delete it after a (configurable?) delay
|
||||
// deliver a file and delete it if expire is set to asap
|
||||
|
||||
id := c.Params("id")
|
||||
file := c.Params("file")
|
||||
// we ignore c.Params("file"), cause it may be malign. Also we've
|
||||
// got it in the db anyway
|
||||
id, err := Untaint(c.Params("id"), `[^a-zA-Z0-9\-]`)
|
||||
if err != nil {
|
||||
return fiber.NewError(403, "Invalid id provided!")
|
||||
}
|
||||
|
||||
upload, err := db.Lookup(id)
|
||||
if err != nil {
|
||||
@@ -142,10 +148,7 @@ func FileGet(c *fiber.Ctx, cfg *cfg.Config, db *Db) error {
|
||||
return fiber.NewError(404, "No download with that id could be found!")
|
||||
}
|
||||
|
||||
if len(file) == 0 {
|
||||
// actual file name is optional
|
||||
file = upload.File
|
||||
}
|
||||
file := upload.File
|
||||
|
||||
filename := filepath.Join(cfg.StorageDir, id, file)
|
||||
|
||||
@@ -158,10 +161,10 @@ func FileGet(c *fiber.Ctx, cfg *cfg.Config, db *Db) error {
|
||||
err = c.Download(filename, file)
|
||||
|
||||
go func() {
|
||||
// check if we need to delete the file now
|
||||
// check if we need to delete the file now and do it in the background
|
||||
if upload.Expire == "asap" {
|
||||
cleanup(filepath.Join(cfg.StorageDir, id))
|
||||
go db.Delete(id)
|
||||
db.Delete(id)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -175,7 +178,10 @@ type Id struct {
|
||||
func FileDelete(c *fiber.Ctx, cfg *cfg.Config, db *Db) error {
|
||||
// delete file, id dir and db entry
|
||||
|
||||
id := c.Params("id")
|
||||
id, err := Untaint(c.Params("id"), `[^a-zA-Z0-9\-]`)
|
||||
if err != nil {
|
||||
return fiber.NewError(403, "Invalid id provided!")
|
||||
}
|
||||
|
||||
// try: path, body(json), query param
|
||||
if len(id) == 0 {
|
||||
@@ -193,7 +199,7 @@ func FileDelete(c *fiber.Ctx, cfg *cfg.Config, db *Db) error {
|
||||
|
||||
cleanup(filepath.Join(cfg.StorageDir, id))
|
||||
|
||||
err := db.Delete(id)
|
||||
err = db.Delete(id)
|
||||
if err != nil {
|
||||
// non existent db entry with that id, or other db error, see logs
|
||||
return fiber.NewError(404, "No upload with that id could be found!")
|
||||
|
||||
Reference in New Issue
Block a user