feat(plugin): gate pages on companion features
This commit is contained in:
@@ -361,7 +361,7 @@ func ValidatePluginCreateInputs(fields []domain.PluginCreateField, inputs map[st
|
||||
|
||||
func validateGameClientBridgeManifest(field string, bridge domain.GameClientBridgeManifest, permissions []string, pages []domain.GamePluginPage, runtimeProfiles domain.GamePluginRuntimeProfiles) []string {
|
||||
companionPresent := bridge.Companion != (domain.GameClientBridgeCompanionDeclaration{})
|
||||
if len(bridge.Commands) == 0 && len(bridge.Snapshots) == 0 && len(bridge.QueryTemplates) == 0 && len(bridge.Pages) == 0 && bridge.Retention.KeepForSeconds == 0 && bridge.Retention.MaxRecords == 0 && !companionPresent {
|
||||
if len(bridge.Commands) == 0 && len(bridge.Snapshots) == 0 && len(bridge.QueryTemplates) == 0 && len(bridge.Pages) == 0 && len(bridge.Features) == 0 && bridge.Retention.KeepForSeconds == 0 && bridge.Retention.MaxRecords == 0 && !companionPresent {
|
||||
return nil
|
||||
}
|
||||
var violations []string
|
||||
@@ -515,6 +515,50 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
|
||||
for _, page := range pages {
|
||||
pageDeclarations[page.Key] = page
|
||||
}
|
||||
features := map[string]domain.GameClientBridgeFeatureDeclaration{}
|
||||
for index, feature := range bridge.Features {
|
||||
prefix := fmt.Sprintf("%s.features[%d]", field, index)
|
||||
if !clientManagerIdentifierPattern.MatchString(feature.Key) || unsafeGameClientBridgeCommandType(feature.Key) {
|
||||
violations = append(violations, prefix+".key is invalid or unsafe")
|
||||
}
|
||||
if _, exists := features[feature.Key]; exists {
|
||||
violations = append(violations, prefix+".key is duplicated")
|
||||
}
|
||||
features[feature.Key] = feature
|
||||
if strings.TrimSpace(feature.Title) == "" || len([]rune(feature.Title)) > 80 {
|
||||
violations = append(violations, prefix+".title is invalid")
|
||||
}
|
||||
if !containsString(permissions, feature.Permission) {
|
||||
violations = append(violations, prefix+".permission must be declared by the plugin")
|
||||
}
|
||||
if len(feature.RequiredHandlers) == 0 && len(feature.RequiredEventProducers) == 0 {
|
||||
violations = append(violations, prefix+" must require a handler or event producer")
|
||||
}
|
||||
for _, handler := range feature.RequiredHandlers {
|
||||
if !clientManagerIdentifierPattern.MatchString(handler) {
|
||||
violations = append(violations, prefix+".requiredHandlers contains an invalid handler")
|
||||
}
|
||||
}
|
||||
for _, producer := range feature.RequiredEventProducers {
|
||||
if !clientManagerIdentifierPattern.MatchString(producer) {
|
||||
violations = append(violations, prefix+".requiredEventProducers contains an invalid producer")
|
||||
}
|
||||
}
|
||||
violations = append(violations, duplicateViolations(prefix+".requiredHandlers", feature.RequiredHandlers)...)
|
||||
violations = append(violations, duplicateViolations(prefix+".requiredEventProducers", feature.RequiredEventProducers)...)
|
||||
}
|
||||
for _, page := range pages {
|
||||
for _, featureKey := range page.FeatureKeys {
|
||||
feature, exists := features[featureKey]
|
||||
if !exists {
|
||||
violations = append(violations, field+" page "+page.Key+" references undeclared feature "+featureKey)
|
||||
continue
|
||||
}
|
||||
if !containsString(page.Permissions, feature.Permission) {
|
||||
violations = append(violations, field+" page "+page.Key+" must declare feature permission "+feature.Permission)
|
||||
}
|
||||
}
|
||||
}
|
||||
for index, page := range bridge.Pages {
|
||||
prefix := fmt.Sprintf("%s.pages[%d]", field, index)
|
||||
pageDeclaration, pageExists := pageDeclarations[page.PageKey]
|
||||
@@ -551,6 +595,16 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
|
||||
violations = append(violations, prefix+" must declare remote.access.request for query templates")
|
||||
}
|
||||
}
|
||||
for _, featureKey := range page.FeatureKeys {
|
||||
feature, exists := features[featureKey]
|
||||
if !exists {
|
||||
violations = append(violations, prefix+" references undeclared feature "+featureKey)
|
||||
continue
|
||||
}
|
||||
if !containsString(pageDeclaration.Permissions, feature.Permission) {
|
||||
violations = append(violations, prefix+" must declare feature permission "+feature.Permission)
|
||||
}
|
||||
}
|
||||
}
|
||||
return violations
|
||||
}
|
||||
@@ -1401,7 +1455,9 @@ func validatePluginPages(pages []domain.GamePluginPage) []string {
|
||||
violations = appendRequired(violations, prefix+".bundle.key", page.Bundle.Key)
|
||||
violations = appendRequired(violations, prefix+".bundle.version", page.Bundle.Version)
|
||||
violations = appendRequired(violations, prefix+".bundle.integritySha256", page.Bundle.IntegritySHA256)
|
||||
if !validDistributionLogicalKey(page.Bundle.Key) || !validPluginPageBundleVersion(page.Bundle.Version) || !validPluginPageBundleIntegrity(page.Bundle.IntegritySHA256) { violations = append(violations, prefix+".bundle is invalid") }
|
||||
if !validDistributionLogicalKey(page.Bundle.Key) || !validPluginPageBundleVersion(page.Bundle.Version) || !validPluginPageBundleIntegrity(page.Bundle.IntegritySHA256) {
|
||||
violations = append(violations, prefix+".bundle is invalid")
|
||||
}
|
||||
}
|
||||
if page.Key != "" {
|
||||
if _, exists := seenKeys[page.Key]; exists {
|
||||
@@ -1416,19 +1472,37 @@ func validatePluginPages(pages []domain.GamePluginPage) []string {
|
||||
}
|
||||
violations = append(violations, duplicateViolations(prefix+".permissions", page.Permissions)...)
|
||||
violations = append(violations, validateBridgeActions(prefix+".bridgeActions", page.BridgeActions)...)
|
||||
for _, key := range page.FeatureKeys {
|
||||
if !clientManagerIdentifierPattern.MatchString(key) {
|
||||
violations = append(violations, prefix+".featureKeys contains an invalid feature key")
|
||||
}
|
||||
}
|
||||
violations = append(violations, duplicateViolations(prefix+".featureKeys", page.FeatureKeys)...)
|
||||
}
|
||||
return violations
|
||||
}
|
||||
|
||||
func validPluginPageBundleVersion(value string) bool {
|
||||
if len(value) == 0 || len(value) > 80 { return false }
|
||||
for _, item := range value { if !(item >= 'a' && item <= 'z' || item >= 'A' && item <= 'Z' || item >= '0' && item <= '9' || item == '.' || item == '_' || item == '-') { return false } }
|
||||
if len(value) == 0 || len(value) > 80 {
|
||||
return false
|
||||
}
|
||||
for _, item := range value {
|
||||
if !(item >= 'a' && item <= 'z' || item >= 'A' && item <= 'Z' || item >= '0' && item <= '9' || item == '.' || item == '_' || item == '-') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func validPluginPageBundleIntegrity(value string) bool {
|
||||
if len(value) != len("sha256:")+64 || !strings.HasPrefix(value, "sha256:") { return false }
|
||||
for _, item := range value[len("sha256:"):] { if !(item >= 'a' && item <= 'f' || item >= '0' && item <= '9') { return false } }
|
||||
if len(value) != len("sha256:")+64 || !strings.HasPrefix(value, "sha256:") {
|
||||
return false
|
||||
}
|
||||
for _, item := range value[len("sha256:"):] {
|
||||
if !(item >= 'a' && item <= 'f' || item >= '0' && item <= '9') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user