1
0
Fork 0
mirror of https://github.com/Luzifer/share.git synced 2024-12-20 10:31:16 +00:00

Enable full-path templating for uploads, use safe filenames

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2021-10-19 23:28:40 +02:00
parent d08a7a7dac
commit 7572dcdff6
Signed by: luzifer
GPG key ID: 0066F03ED215AD7D
5 changed files with 81 additions and 15 deletions

3
go.mod
View file

@ -6,6 +6,8 @@ require (
github.com/Luzifer/rconfig/v2 v2.3.0 github.com/Luzifer/rconfig/v2 v2.3.0
github.com/aws/aws-sdk-go v1.40.30 github.com/aws/aws-sdk-go v1.40.30
github.com/cheggaaa/pb v1.0.29 github.com/cheggaaa/pb v1.0.29
github.com/gofrs/uuid v4.1.0+incompatible
github.com/gosimple/slug v1.11.0
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/rivo/uniseg v0.2.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect
github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus v1.8.1
@ -13,6 +15,7 @@ require (
require ( require (
github.com/Luzifer/rconfig v1.2.0 // indirect github.com/Luzifer/rconfig v1.2.0 // indirect
github.com/gosimple/unidecode v1.0.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/onsi/ginkgo v1.16.4 // indirect github.com/onsi/ginkgo v1.16.4 // indirect

6
go.sum
View file

@ -15,6 +15,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gofrs/uuid v4.1.0+incompatible h1:sIa2eCvUTwgjbqXrPLfNwUf9S3i3mpH1O1atV+iL/Wk=
github.com/gofrs/uuid v4.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@ -28,6 +30,10 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/gosimple/slug v1.11.0 h1:QkFeOkXIEDvvtIt++P7cUuO4G9PZVQEgLuYbYZzawMA=
github.com/gosimple/slug v1.11.0/go.mod h1:MICb3w495l9KNdZm+Xn5b6T2Hn831f9DMxiJ1r+bAjw=
github.com/gosimple/unidecode v1.0.0 h1:kPdvM+qy0tnk4/BrnkrbdJ82xe88xn7c9hcaipDz4dQ=
github.com/gosimple/unidecode v1.0.0/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=

28
main.go
View file

@ -17,12 +17,13 @@ import (
var ( var (
cfg = struct { cfg = struct {
BaseURL string `flag:"base-url" env:"BASE_URL" default:"" description:"URL to prepend before filename"` BaseURL string `flag:"base-url" default:"" description:"URL to prepend before filename"`
BasePath string `flag:"base-path" env:"BASE_PATH" default:"file/{{ printf \"%.6s\" .Hash }}" description:"Path to upload the file to"` BasePath string `flag:"base-path" default:"" description:"DEPRECATED: Path to upload the file to"`
Bootstrap bool `flag:"bootstrap" default:"false" description:"Upload frontend files into bucket"` Bootstrap bool `flag:"bootstrap" default:"false" description:"Upload frontend files into bucket"`
Bucket string `flag:"bucket" env:"BUCKET" default:"" description:"S3 bucket to upload files to" validate:"nonzero"` Bucket string `flag:"bucket" default:"" description:"S3 bucket to upload files to" validate:"nonzero"`
ContentType string `flag:"content-type,c" default:"" description:"Force content-type to be set to this value"` ContentType string `flag:"content-type,c" vardefault:"file_template" description:"Force content-type to be set to this value"`
Listen string `flag:"listen" env:"LISTEN" default:"" description:"Enable HTTP server if set to IP/Port (e.g. ':3000')"` FileTemplate string `flag:"file-template" default:"" description:"Full name template of the uploaded file"`
Listen string `flag:"listen" default:"" description:"Enable HTTP server if set to IP/Port (e.g. ':3000')"`
Progress bool `flag:"progress" default:"false" description:"Show progress bar while uploading"` Progress bool `flag:"progress" default:"false" description:"Show progress bar while uploading"`
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"` VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
}{} }{}
@ -33,8 +34,12 @@ var (
version = "dev" version = "dev"
) )
func init() { func initApp() {
rconfig.AutoEnv(true) rconfig.AutoEnv(true)
rconfig.SetVariableDefaults(map[string]string{
"file_template": `file/{{ printf "%.6s" .Hash }}/{{ .SafeFileName }}`,
})
if err := rconfig.ParseAndValidate(&cfg); err != nil { if err := rconfig.ParseAndValidate(&cfg); err != nil {
log.Fatalf("Unable to parse commandline options: %s", err) log.Fatalf("Unable to parse commandline options: %s", err)
} }
@ -43,9 +48,16 @@ func init() {
fmt.Printf("share %s\n", version) fmt.Printf("share %s\n", version)
os.Exit(0) os.Exit(0)
} }
if cfg.BasePath != "" {
cfg.FileTemplate = strings.Join([]string{strings.TrimRight(cfg.BasePath, "/"), `{{ .SafeFileName }}`}, "/")
log.WithField("file-template", cfg.FileTemplate).Warn("Using deprecated base-path parameter! Using update file-template...")
}
} }
func main() { func main() {
initApp()
switch { switch {
case cfg.Bootstrap: case cfg.Bootstrap:
@ -81,10 +93,12 @@ func doCLIUpload() error {
inFileName := rconfig.Args()[1] inFileName := rconfig.Args()[1]
if inFileName == "-" { if inFileName == "-" {
inFileName = "stdin"
if cfg.ContentType == "" { if cfg.ContentType == "" {
// If we don't have an explicitly set content-type assume stdin contains text // If we don't have an explicitly set content-type assume stdin contains text
inFileName = "stdin"
cfg.ContentType = "text/plain" cfg.ContentType = "text/plain"
} else if ext, err := mimeResolver.ExtensionsByType(cfg.ContentType); err == nil {
inFileName = strings.Join([]string{"stdin", ext}, "")
} }
// Stdin is not seekable, so we need to buffer it // Stdin is not seekable, so we need to buffer it

37
mime.go Normal file
View file

@ -0,0 +1,37 @@
package main
import (
"mime"
"strings"
"github.com/pkg/errors"
)
type mimeDB map[string]string
// mimeResolver contains some well-known mime-types and falls back
// to mime package to resolve the extension if no internal override
// is known for the given mime-type
var mimeResolver = mimeDB{
"text/plain": ".txt", // Detected as .asc when using mime package
}
func (m mimeDB) ExtensionsByType(t string) (string, error) {
if v, ok := m[t]; ok {
return v, nil
}
exts, err := mime.ExtensionsByType(t)
if err != nil {
return "", err
}
for _, ext := range exts {
if !strings.HasPrefix(ext, ".") {
continue
}
return ext, nil
}
return "", errors.New("no extension found")
}

View file

@ -17,6 +17,8 @@ import (
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3"
"github.com/cheggaaa/pb" "github.com/cheggaaa/pb"
"github.com/gofrs/uuid"
"github.com/gosimple/slug"
"github.com/pkg/errors" "github.com/pkg/errors"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@ -101,18 +103,22 @@ func executeUpload(inFileName string, inFileHandle io.ReadSeeker, useCalculatedF
} }
func calculateUploadFilename(inFile string, inFileHandle io.ReadSeeker) (string, error) { func calculateUploadFilename(inFile string, inFileHandle io.ReadSeeker) (string, error) {
upFile := path.Join(
cfg.BasePath,
strings.Replace(path.Base(inFile), " ", "_", -1),
)
fileHash, err := hashFile(inFileHandle) fileHash, err := hashFile(inFileHandle)
if err != nil { if err != nil {
return "", err return "", err
} }
return executeTemplate(upFile, map[string]interface{}{ safeFileName := strings.Join([]string{
"Hash": fileHash, slug.Make(strings.TrimSuffix(path.Base(inFile), path.Ext(inFile))),
path.Ext(inFile),
}, "")
return executeTemplate(cfg.FileTemplate, map[string]interface{}{
"Ext": path.Ext(inFile),
"FileName": path.Base(inFile),
"Hash": fileHash,
"SafeFileName": safeFileName,
"UUID": uuid.Must(uuid.NewV4()).String(),
}) })
} }
@ -130,7 +136,7 @@ func hashFile(inFileHandle io.ReadSeeker) (string, error) {
} }
func executeTemplate(tplStr string, vars map[string]interface{}) (string, error) { func executeTemplate(tplStr string, vars map[string]interface{}) (string, error) {
tpl, err := template.New("basepath").Parse(tplStr) tpl, err := template.New("filename").Parse(tplStr)
if err != nil { if err != nil {
return "", err return "", err
} }