Fix local run freshness and plugin asset handling
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user