aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-10-29 03:40:09 +0900
committerOphestra <cat@gensokyo.uk>2025-10-29 03:40:09 +0900
commit274686d10d3386a41f8fb6bc3363269a734afe0e (patch)
tree59ce81d5617cf5e1f67b819dab49b83b71d8ff93
parent65342d588ff061d2130e6379d86a26be90c908ae (diff)
internal/validate: relocate from app
These are free of the dispatcher from internal/app. This change relocates them into their own package. Signed-off-by: Ophestra <cat@gensokyo.uk>
-rw-r--r--internal/app/outcome.go2
-rw-r--r--internal/app/path.go15
-rw-r--r--internal/app/spaccount.go3
-rw-r--r--internal/app/spcontainer.go3
-rw-r--r--internal/app/sysconf.go8
-rw-r--r--internal/app/username_test.go28
-rw-r--r--internal/validate/sysconf.go8
-rw-r--r--internal/validate/sysconf_test.go (renamed from internal/app/sysconf_test.go)10
-rw-r--r--internal/validate/username.go (renamed from internal/app/username.go)8
-rw-r--r--internal/validate/username_test.go30
-rw-r--r--internal/validate/validate.go20
-rw-r--r--internal/validate/validate_test.go (renamed from internal/app/path_test.go)10
12 files changed, 80 insertions, 65 deletions
diff --git a/internal/app/outcome.go b/internal/app/outcome.go
index f462dfd5..237095af 100644
--- a/internal/app/outcome.go
+++ b/internal/app/outcome.go
@@ -43,7 +43,7 @@ type outcomeState struct {
Identity int
// Copied from Identity.
identity *stringPair[int]
- // Returned by [Hsu.MustIDMsg].
+ // Returned by [Hsu.MustID].
UserID int
// Target init namespace uid resolved from UserID and identity.
uid *stringPair[int]
diff --git a/internal/app/path.go b/internal/app/path.go
deleted file mode 100644
index 493dfe39..00000000
--- a/internal/app/path.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package app
-
-import (
- "path/filepath"
- "strings"
-)
-
-func deepContainsH(basepath, targpath string) (bool, error) {
- const upper = ".." + string(filepath.Separator)
-
- rel, err := filepath.Rel(basepath, targpath)
- return err == nil &&
- rel != ".." &&
- !strings.HasPrefix(rel, upper), err
-}
diff --git a/internal/app/spaccount.go b/internal/app/spaccount.go
index 7da54366..c9f08fe9 100644
--- a/internal/app/spaccount.go
+++ b/internal/app/spaccount.go
@@ -6,6 +6,7 @@ import (
"syscall"
"hakurei.app/container/fhs"
+ "hakurei.app/internal/validate"
)
func init() { gob.Register(spAccountOp{}) }
@@ -21,7 +22,7 @@ func (s spAccountOp) toSystem(state *outcomeStateSys) error {
}
// default is applied in toContainer
- if state.Container.Username != "" && !isValidUsername(state.Container.Username) {
+ if state.Container.Username != "" && !validate.IsValidUsername(state.Container.Username) {
return newWithMessage(fmt.Sprintf("invalid user name %q", state.Container.Username))
}
return nil
diff --git a/internal/app/spcontainer.go b/internal/app/spcontainer.go
index 65071f7c..d411cb9f 100644
--- a/internal/app/spcontainer.go
+++ b/internal/app/spcontainer.go
@@ -16,6 +16,7 @@ import (
"hakurei.app/container/fhs"
"hakurei.app/container/seccomp"
"hakurei.app/hst"
+ "hakurei.app/internal/validate"
"hakurei.app/message"
"hakurei.app/system"
"hakurei.app/system/acl"
@@ -243,7 +244,7 @@ func (s *spFilesystemOp) toSystem(state *outcomeStateSys) error {
continue
}
- if ok, err := deepContainsH(p[0], hidePaths[i]); err != nil {
+ if ok, err := validate.DeepContainsH(p[0], hidePaths[i]); err != nil {
return &hst.AppError{Step: "determine path hiding outcome", Err: err}
} else if ok {
hidePathMatch[i] = true
diff --git a/internal/app/sysconf.go b/internal/app/sysconf.go
deleted file mode 100644
index 4c5714a9..00000000
--- a/internal/app/sysconf.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package app
-
-//#include <unistd.h>
-import "C"
-
-const _SC_LOGIN_NAME_MAX = C._SC_LOGIN_NAME_MAX
-
-func sysconf(name C.int) int { return int(C.sysconf(name)) }
diff --git a/internal/app/username_test.go b/internal/app/username_test.go
deleted file mode 100644
index 1b2e0459..00000000
--- a/internal/app/username_test.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package app
-
-import (
- "strings"
- "testing"
-)
-
-func TestIsValidUsername(t *testing.T) {
- t.Parallel()
-
- t.Run("long", func(t *testing.T) {
- if isValidUsername(strings.Repeat("a", sysconf(_SC_LOGIN_NAME_MAX))) {
- t.Errorf("isValidUsername unexpected true")
- }
- })
-
- t.Run("regexp", func(t *testing.T) {
- if isValidUsername("0") {
- t.Errorf("isValidUsername unexpected true")
- }
- })
-
- t.Run("valid", func(t *testing.T) {
- if !isValidUsername("alice") {
- t.Errorf("isValidUsername unexpected false")
- }
- })
-}
diff --git a/internal/validate/sysconf.go b/internal/validate/sysconf.go
new file mode 100644
index 00000000..0960e9dd
--- /dev/null
+++ b/internal/validate/sysconf.go
@@ -0,0 +1,8 @@
+package validate
+
+//#include <unistd.h>
+import "C"
+
+const SC_LOGIN_NAME_MAX = C._SC_LOGIN_NAME_MAX
+
+func Sysconf(name C.int) int { return int(C.sysconf(name)) }
diff --git a/internal/app/sysconf_test.go b/internal/validate/sysconf_test.go
index fb0c7f8a..95502d34 100644
--- a/internal/app/sysconf_test.go
+++ b/internal/validate/sysconf_test.go
@@ -1,6 +1,10 @@
-package app
+package validate_test
-import "testing"
+import (
+ "testing"
+
+ "hakurei.app/internal/validate"
+)
const (
_POSIX_LOGIN_NAME_MAX = 9
@@ -10,7 +14,7 @@ func TestSysconf(t *testing.T) {
t.Parallel()
t.Run("LOGIN_NAME_MAX", func(t *testing.T) {
- if got := sysconf(_SC_LOGIN_NAME_MAX); got < _POSIX_LOGIN_NAME_MAX {
+ if got := validate.Sysconf(validate.SC_LOGIN_NAME_MAX); got < _POSIX_LOGIN_NAME_MAX {
t.Errorf("sysconf(_SC_LOGIN_NAME_MAX): %d < _POSIX_LOGIN_NAME_MAX", got)
}
})
diff --git a/internal/app/username.go b/internal/validate/username.go
index 566daf5f..2249cd27 100644
--- a/internal/app/username.go
+++ b/internal/validate/username.go
@@ -1,12 +1,12 @@
-package app
+package validate
import "regexp"
// nameRegex is the default NAME_REGEX value from adduser.
var nameRegex = regexp.MustCompilePOSIX(`^[a-zA-Z][a-zA-Z0-9_-]*\$?$`)
-// isValidUsername returns whether the argument is a valid username
-func isValidUsername(username string) bool {
- return len(username) < sysconf(_SC_LOGIN_NAME_MAX) &&
+// IsValidUsername returns whether the argument is a valid username.
+func IsValidUsername(username string) bool {
+ return len(username) < Sysconf(SC_LOGIN_NAME_MAX) &&
nameRegex.MatchString(username)
}
diff --git a/internal/validate/username_test.go b/internal/validate/username_test.go
new file mode 100644
index 00000000..1970fa4a
--- /dev/null
+++ b/internal/validate/username_test.go
@@ -0,0 +1,30 @@
+package validate_test
+
+import (
+ "strings"
+ "testing"
+
+ "hakurei.app/internal/validate"
+)
+
+func TestIsValidUsername(t *testing.T) {
+ t.Parallel()
+
+ t.Run("long", func(t *testing.T) {
+ if validate.IsValidUsername(strings.Repeat("a", validate.Sysconf(validate.SC_LOGIN_NAME_MAX))) {
+ t.Errorf("IsValidUsername unexpected true")
+ }
+ })
+
+ t.Run("regexp", func(t *testing.T) {
+ if validate.IsValidUsername("0") {
+ t.Errorf("IsValidUsername unexpected true")
+ }
+ })
+
+ t.Run("valid", func(t *testing.T) {
+ if !validate.IsValidUsername("alice") {
+ t.Errorf("IsValidUsername unexpected false")
+ }
+ })
+}
diff --git a/internal/validate/validate.go b/internal/validate/validate.go
new file mode 100644
index 00000000..a4e82753
--- /dev/null
+++ b/internal/validate/validate.go
@@ -0,0 +1,20 @@
+// Package validate provides functions for validating string values of various types.
+package validate
+
+import (
+ "path/filepath"
+ "strings"
+)
+
+// DeepContainsH returns whether basepath is equivalent to or is the parent of targpath.
+//
+// This is used for path hiding warning behaviour, the purpose of which is to improve
+// user experience and is *not* a security feature and must not be treated as such.
+func DeepContainsH(basepath, targpath string) (bool, error) {
+ const upper = ".." + string(filepath.Separator)
+
+ rel, err := filepath.Rel(basepath, targpath)
+ return err == nil &&
+ rel != ".." &&
+ !strings.HasPrefix(rel, upper), err
+}
diff --git a/internal/app/path_test.go b/internal/validate/validate_test.go
index 1f2d8fa6..338e5693 100644
--- a/internal/app/path_test.go
+++ b/internal/validate/validate_test.go
@@ -1,7 +1,9 @@
-package app
+package validate_test
import (
"testing"
+
+ "hakurei.app/internal/validate"
)
func TestDeepContainsH(t *testing.T) {
@@ -78,10 +80,10 @@ func TestDeepContainsH(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
- if got, err := deepContainsH(tc.basepath, tc.targpath); (err != nil) != tc.wantErr {
- t.Errorf("deepContainsH() error = %v, wantErr %v", err, tc.wantErr)
+ if got, err := validate.DeepContainsH(tc.basepath, tc.targpath); (err != nil) != tc.wantErr {
+ t.Errorf("DeepContainsH: error = %v, wantErr %v", err, tc.wantErr)
} else if got != tc.want {
- t.Errorf("deepContainsH() = %v, want %v", got, tc.want)
+ t.Errorf("DeepContainsH: = %v, want %v", got, tc.want)
}
})
}