Harden Windows package paths (T-605)

This commit is contained in:
ila
2026-07-16 23:56:19 +08:00
parent f7a803d944
commit 6fd19d0f43
19 changed files with 664 additions and 108 deletions
+7 -15
View File
@@ -9,12 +9,12 @@ import (
"fmt"
"io"
"net/url"
"path"
"regexp"
"strings"
"time"
"softbox.local/core/domain"
"softbox.local/core/internal/safepath"
)
var (
@@ -163,8 +163,12 @@ func validateApp(app App) error {
if len(app.Architectures) == 0 {
return invalidField("architectures", "must not be empty")
}
if !validSafeRelativePath(app.EntryEXE) {
return invalidField("entry_exe", "must be a safe relative path")
if err := safepath.ValidateRelative(app.EntryEXE); err != nil {
return invalidField(
"entry_exe",
"must be a safe Windows relative path: %v",
err,
)
}
if len(app.Packages) == 0 {
return invalidField("packages", "must not be empty")
@@ -261,18 +265,6 @@ func validateHTTPSURL(value string) error {
return nil
}
func validSafeRelativePath(value string) bool {
if value == "" || strings.Contains(value, `\`) || strings.Contains(value, ":") {
return false
}
cleaned := path.Clean(value)
return cleaned == value &&
cleaned != "." &&
!strings.HasPrefix(cleaned, "/") &&
cleaned != ".." &&
!strings.HasPrefix(cleaned, "../")
}
func invalidField(field, format string, values ...any) error {
return fmt.Errorf(
"%w: %s: %s",
+39
View File
@@ -70,6 +70,45 @@ func TestParserRejectsDuplicateAppIDAndPackageMismatch(t *testing.T) {
}
}
func TestParserRejectsUnsafeWindowsEntrypoints(t *testing.T) {
for _, entrypoint := range []string{
".. /escape.exe",
"bin./App.exe",
"App.exe.",
"App.exe ",
" App.exe",
"NUL",
"con.txt",
"bad?.exe",
`bin\App.exe`,
} {
t.Run(entrypoint, func(t *testing.T) {
manifest := validManifestForTest()
manifest.Apps[0].EntryEXE = entrypoint
_, err := parseSignedManifestForTest(t, manifest, ChannelModern)
if !errors.Is(err, ErrInvalidManifest) {
t.Fatalf("Parse() error = %v, want %v", err, ErrInvalidManifest)
}
})
}
}
func TestParserAcceptsUnicodeNestedEntrypoint(t *testing.T) {
manifest := validManifestForTest()
manifest.Apps[0].EntryEXE = "工具/解析器.exe"
parsed, err := parseSignedManifestForTest(t, manifest, ChannelModern)
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
if parsed.Apps[0].EntryEXE != manifest.Apps[0].EntryEXE {
t.Fatalf(
"EntryEXE = %q, want %q",
parsed.Apps[0].EntryEXE,
manifest.Apps[0].EntryEXE,
)
}
}
func signedFixture(t *testing.T, name string) (Verifier, []byte) {
t.Helper()
publicKey, privateKey := catalogTestKey()