Fix local run freshness and plugin asset handling

This commit is contained in:
npc0-hue
2026-08-22 19:45:00 +08:00
parent bb5e48b29c
commit ebd616c549
23 changed files with 312 additions and 58 deletions
+35 -1
View File
@@ -1,10 +1,13 @@
package validator
import (
"bytes"
"encoding/base64"
"fmt"
"regexp"
"strconv"
"strings"
"unicode/utf8"
"browser.local/platform/domain"
)
@@ -260,6 +263,9 @@ func validatePluginAssetFileDeclarations(prefix string, files []domain.PluginAss
if file.Content != "" {
violations = append(violations, field+".content must be supplied only in registration assetFiles")
}
if file.Encoding != "" {
violations = append(violations, field+".encoding must be supplied only in registration assetFiles")
}
if file.Mode != 0 && file.Mode != 0o600 && file.Mode != 0o700 {
violations = append(violations, field+".mode is unsafe")
}
@@ -282,7 +288,10 @@ func validatePluginAssetFiles(prefix string, files []domain.PluginAssetFile) []s
violations = append(violations, field+".path is duplicated")
}
seen[file.Path] = struct{}{}
if len([]byte(file.Content)) > 64*1024 || strings.ContainsRune(file.Content, '\x00') || containsUnsafeRuntimeSecret(file.Content) {
content, err := pluginAssetContentBytes(file)
if err != nil {
violations = append(violations, field+err.Error())
} else if len(content) > 64*1024 || pluginAssetContentUnsafe(file, content) {
violations = append(violations, field+".content is unsafe")
}
if file.Mode != 0 && (file.Mode < 0o400 || file.Mode > 0o700 || file.Mode&0o022 != 0) {
@@ -292,6 +301,31 @@ func validatePluginAssetFiles(prefix string, files []domain.PluginAssetFile) []s
return violations
}
func pluginAssetContentBytes(file domain.PluginAssetFile) ([]byte, error) {
switch strings.TrimSpace(file.Encoding) {
case "":
return []byte(file.Content), nil
case "base64":
content, err := base64.StdEncoding.DecodeString(strings.TrimSpace(file.Content))
if err != nil {
return nil, fmt.Errorf(".content is not valid base64")
}
return content, nil
default:
return nil, fmt.Errorf(".encoding is unsupported")
}
}
func pluginAssetContentUnsafe(file domain.PluginAssetFile, content []byte) bool {
if strings.TrimSpace(file.Encoding) == "base64" {
if bytes.ContainsRune(content, '\x00') || !utf8.Valid(content) {
return false
}
return containsUnsafeRuntimeSecret(string(content))
}
return bytes.ContainsRune(content, '\x00') || containsUnsafeRuntimeSecret(string(content))
}
func validateRegistrationAssetCoverage(declared []domain.PluginAssetFile, payload []domain.PluginAssetFile) []string {
if len(declared) == 0 {
return nil
+9
View File
@@ -1,6 +1,7 @@
package validator
import (
"encoding/base64"
"strings"
"testing"
@@ -79,10 +80,12 @@ func TestValidateGamePluginManifestRegistrationValidatesAssetFileCoverage(t *tes
registration.Manifest.AssetFiles = []domain.PluginAssetFile{
{Path: "actions/install.json", Mode: 0o600},
{Path: "bin/install-server", Mode: 0o700},
{Path: "assets/map.bin", Mode: 0o600},
}
registration.AssetFiles = []domain.PluginAssetFile{
{Path: "actions/install.json", Content: "{}", Mode: 0o600},
{Path: "bin/install-server", Content: "#!/usr/bin/env sh\n", Mode: 0o700},
{Path: "assets/map.bin", Content: base64.StdEncoding.EncodeToString([]byte{0xff, 0x00, 0x7f}), Encoding: "base64", Mode: 0o600},
}
if err := ValidateGamePluginManifestRegistration(registration); err != nil {
t.Fatalf("expected declared asset files with matching content to validate, got %v", err)
@@ -105,6 +108,12 @@ func TestValidateGamePluginManifestRegistrationValidatesAssetFileCoverage(t *tes
if err := ValidateGamePluginManifestRegistration(inlineContent); err == nil || !strings.Contains(err.Error(), "content must be supplied only") {
t.Fatalf("expected manifest inline content rejection, got %v", err)
}
unsafeEncodedText := domain.CopyGamePluginManifestRegistration(registration)
unsafeEncodedText.AssetFiles[2].Content = base64.StdEncoding.EncodeToString([]byte("password=secret"))
if err := ValidateGamePluginManifestRegistration(unsafeEncodedText); err == nil || !strings.Contains(err.Error(), "content is unsafe") {
t.Fatalf("expected unsafe base64 text rejection, got %v", err)
}
}
func TestValidateGamePluginManifestRegistrationValidatesRuntimeProfiles(t *testing.T) {