mirror of
https://github.com/nezhahq/nezha.git
synced 2026-11-10 21:21:06 +08:00
test: cover embedded and local frontend path confinement
Co-authored-by: naiba/CloudCode <hi+cloudcode@nai.ba>
This commit is contained in:
1 parent
6c9726412d
commit
15fa673c74
1 file changed
+72
@@ -0,0 +1,72 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/nezhahq/nezha/model"
|
||||
"github.com/nezhahq/nezha/service/singleton"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFrontendPathConfinementForEmbeddedAndLocalOverrides(t *testing.T) {
|
||||
for _, mode := range []string{"embedded", "local-override"} {
|
||||
t.Run(mode, func(t *testing.T) {
|
||||
t.Chdir(t.TempDir())
|
||||
original := singleton.Conf
|
||||
singleton.Conf = &singleton.ConfigClass{Config: &model.Config{ConfigDashboard: model.ConfigDashboard{UserTemplate: "user-dist", AdminTemplate: "admin-dist"}}}
|
||||
t.Cleanup(func() { singleton.Conf = original })
|
||||
files := fstest.MapFS{
|
||||
"user-dist/index.html": &fstest.MapFile{Data: []byte("embedded user index")},
|
||||
"user-dist/assets/application.js": &fstest.MapFile{Data: []byte("embedded user asset")},
|
||||
"admin-dist/index.html": &fstest.MapFile{Data: []byte("embedded admin index")},
|
||||
"admin-dist/assets/application.js": &fstest.MapFile{Data: []byte("embedded admin asset")},
|
||||
"data/config.yaml": &fstest.MapFile{Data: []byte("outside-secret-canary")},
|
||||
"other-dist/private.txt": &fstest.MapFile{Data: []byte("other-theme-canary")},
|
||||
}
|
||||
// Distinct contents prove local files are served instead of silently falling back to embedded files.
|
||||
expectedSource := "embedded "
|
||||
if mode == "local-override" {
|
||||
expectedSource = "local "
|
||||
for name, file := range files {
|
||||
require.NoError(t, os.MkdirAll(filepath.Dir(name), 0750))
|
||||
require.NoError(t, os.WriteFile(name, []byte(strings.ReplaceAll(string(file.Data), "embedded ", "local ")), 0600))
|
||||
}
|
||||
outside, err := filepath.Abs("data/config.yaml")
|
||||
require.NoError(t, err)
|
||||
for _, directory := range []string{"user-dist", "admin-dist"} {
|
||||
require.NoError(t, os.Symlink(outside, filepath.Join(directory, "outside-link.txt")))
|
||||
}
|
||||
}
|
||||
gin.SetMode(gin.TestMode)
|
||||
router := gin.New()
|
||||
router.NoRoute(fallbackToFrontend(files))
|
||||
for _, role := range []struct{ prefix, name string }{{"/", "user"}, {"/dashboard/", "admin"}} {
|
||||
valid := performFrontendFallbackRequest(t, router, role.prefix+"assets/application.js")
|
||||
require.Equal(t, http.StatusOK, valid.Code)
|
||||
require.Equal(t, expectedSource+role.name+" asset", valid.Body.String())
|
||||
attacks := []string{"../data/config.yaml", "../../data/config.yaml", "%2e%2e/data/config.yaml", "%2e%2e%2fdata/config.yaml", "%252e%252e/data/config.yaml", "..%5cdata%5cconfig.yaml", "../other-dist/private.txt", "../admin-dist/assets/application.js", "../user-dist/assets/application.js", "/etc/passwd", "C:/Windows/win.ini"}
|
||||
if mode == "local-override" {
|
||||
outside, err := filepath.Abs("data/config.yaml")
|
||||
require.NoError(t, err)
|
||||
attacks = append(attacks, "outside-link.txt", filepath.ToSlash(outside))
|
||||
}
|
||||
for _, attack := range attacks {
|
||||
response := performFrontendFallbackRequest(t, router, role.prefix+attack)
|
||||
require.Equal(t, http.StatusNotFound, response.Code, role.prefix+attack)
|
||||
require.Equal(t, expectedSource+role.name+" index", response.Body.String(), role.prefix+attack)
|
||||
}
|
||||
}
|
||||
for _, attack := range []string{"/dashboard../data/config.yaml", "/dashboard%2e%2e/data/config.yaml", "/dashboard%2e%2e%2fdata/config.yaml"} {
|
||||
response := performFrontendFallbackRequest(t, router, attack)
|
||||
require.Equal(t, http.StatusNotFound, response.Code, attack)
|
||||
require.Equal(t, expectedSource+"user index", response.Body.String(), attack)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user