fix(platform): support Windows storage and native filesystem boundaries
This commit is contained in:
1 parent
3726423338
commit
e239fff253
24 files changed
+355
-51
No files matched your search
@@ -78,6 +78,8 @@ func TestPortableNotesExcludeApplicationData(t *testing.T) {
|
||||
}
|
||||
// As in the real process, export temporary files live inside the application data directory.
|
||||
t.Setenv("TMPDIR", filepath.Join(data, "temp"))
|
||||
t.Setenv("TMP", filepath.Join(data, "temp"))
|
||||
t.Setenv("TEMP", filepath.Join(data, "temp"))
|
||||
res = f.do(f.admin, "POST", "/api/v1/maintenance/notes/export", "", nil)
|
||||
defer res.Body.Close()
|
||||
assertOK(t, res)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"rawmark/internal/fileutil"
|
||||
"rawmark/internal/i18n"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -65,11 +66,11 @@ func saveUpload(dir string, meta maintenanceUpload) error {
|
||||
return os.WriteFile(filepath.Join(dir, "upload.json"), data, 0600)
|
||||
}
|
||||
func enoughSpace(dir string, size int64) error {
|
||||
var stat syscall.Statfs_t
|
||||
if err := syscall.Statfs(dir, &stat); err != nil {
|
||||
available, err := fileutil.AvailableBytes(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if size < 0 || uint64(size)+(32<<20) > stat.Bavail*uint64(stat.Bsize) {
|
||||
if size < 0 || uint64(size)+(32<<20) > available {
|
||||
return i18n.New("not_enough_disk_space_w_a39b20", syscall.ENOSPC)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"rawmark/internal/fileutil"
|
||||
"rawmark/internal/i18n"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -42,7 +43,7 @@ type pluginInfo struct {
|
||||
}
|
||||
|
||||
func validPluginID(id string) bool {
|
||||
return pluginIDPattern.MatchString(id) && !strings.HasPrefix(id, "rawmark-")
|
||||
return pluginIDPattern.MatchString(id) && fileutil.IsLocalPath(id) && !strings.HasPrefix(id, "rawmark-")
|
||||
}
|
||||
|
||||
func parsePluginManifest(data []byte) (pluginManifest, error) {
|
||||
@@ -181,7 +182,7 @@ func unpackPlugin(data []byte, destination string) (pluginInfo, error) {
|
||||
var total int64
|
||||
for _, file := range archive.File {
|
||||
name := strings.TrimSuffix(file.Name, "/")
|
||||
if !fs.ValidPath(name) || strings.ContainsAny(name, "\\:\x00") || strings.HasPrefix(name, ".") || name != path.Clean(name) || seen[name] || (!file.FileInfo().IsDir() && !file.Mode().IsRegular()) {
|
||||
if !fs.ValidPath(name) || !fileutil.IsLocalPath(name) || strings.ContainsAny(name, "\\:\x00") || strings.HasPrefix(name, ".") || name != path.Clean(name) || seen[name] || (!file.FileInfo().IsDir() && !file.Mode().IsRegular()) {
|
||||
return info, i18n.New("plugin_contains_invalid_paths_duplicate_6a1e05")
|
||||
}
|
||||
seen[name] = true
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWindowsPluginPaths(t *testing.T) {
|
||||
if validPluginID("con") {
|
||||
t.Fatal("reserved Windows plugin ID accepted")
|
||||
}
|
||||
for _, name := range []string{"main.js.", "dir /module.js", "NUL.js", "module.js:stream"} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
var data bytes.Buffer
|
||||
archive := zip.NewWriter(&data)
|
||||
for _, entry := range []struct{ name, content string }{
|
||||
{"manifest.json", `{"id":"portable","name":"Portable","version":"1","apiVersion":1}`},
|
||||
{"main.js", "export default {}"},
|
||||
{name, "must not overwrite"},
|
||||
} {
|
||||
file, err := archive.Create(entry.name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := file.Write([]byte(entry.content)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := archive.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := unpackPlugin(data.Bytes(), t.TempDir()); err == nil {
|
||||
t.Fatal("unsafe Windows archive path accepted")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user