Tighten opaque plugin content boundaries
This commit is contained in:
@@ -33,6 +33,9 @@ var (
|
||||
gameClientBridgeCollectionPattern = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9._-]{0,119}$`)
|
||||
gameClientBridgeFieldPattern = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9._-]{0,79}$`)
|
||||
gameClientBridgeCaptureNamePattern = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_]{0,79}$`)
|
||||
gameClientBridgeAcronymBoundary = regexp.MustCompile(`([A-Z]+)([A-Z][a-z])`)
|
||||
gameClientBridgeCamelBoundary = regexp.MustCompile(`([a-z0-9])([A-Z])`)
|
||||
gameClientBridgeNonWord = regexp.MustCompile(`[^A-Za-z0-9]+`)
|
||||
runtimeIdentifierPattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._:-]{0,119}$`)
|
||||
)
|
||||
|
||||
@@ -467,8 +470,7 @@ func ValidatePluginCreateInputs(fields []domain.PluginCreateField, inputs map[st
|
||||
}
|
||||
|
||||
func validateGameClientBridgeManifest(field string, bridge domain.GameClientBridgeManifest, permissions []string, runCapabilities []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.LifecycleProjections) == 0 && len(bridge.DataPacks) == 0 && len(bridge.Pages) == 0 && len(bridge.Features) == 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.LifecycleProjections) == 0 && len(bridge.DataPacks) == 0 && len(bridge.Pages) == 0 && len(bridge.Features) == 0 && bridge.Retention.KeepForSeconds == 0 && bridge.Retention.MaxRecords == 0 {
|
||||
return nil
|
||||
}
|
||||
var violations []string
|
||||
@@ -478,9 +480,6 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
|
||||
if bridge.Retention.MaxRecords <= 0 || bridge.Retention.MaxRecords > 100000 {
|
||||
violations = append(violations, field+".maxCommands is invalid")
|
||||
}
|
||||
if companionPresent {
|
||||
violations = append(violations, field+".companion is no longer supported")
|
||||
}
|
||||
transports := map[string]domain.RuntimeTransportProfile{}
|
||||
for _, transport := range runtimeProfiles.TransportProfiles {
|
||||
transports[transport.Key] = transport
|
||||
@@ -880,27 +879,8 @@ func validateGameClientBridgeBulkActivityTarget(prefix string, target domain.Gam
|
||||
return violations
|
||||
}
|
||||
|
||||
func validCompanionProofEnvironment(value string) bool {
|
||||
if len(value) < 3 || len(value) > 64 || value[0] < 'A' || value[0] > 'Z' {
|
||||
return false
|
||||
}
|
||||
for _, character := range value[1:] {
|
||||
if character != '_' && (character < 'A' || character > 'Z') && (character < '0' || character > '9') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
reserved := map[string]struct{}{
|
||||
"COMSPEC": {}, "DYLD_INSERT_LIBRARIES": {}, "DYLD_LIBRARY_PATH": {}, "HOME": {}, "LD_LIBRARY_PATH": {}, "LD_PRELOAD": {},
|
||||
"PATH": {}, "PATHEXT": {}, "SHELL": {}, "SYSTEMROOT": {}, "TEMP": {}, "TMP": {}, "USERPROFILE": {}, "WINDIR": {},
|
||||
}
|
||||
if _, exists := reserved[value]; exists {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func unsafeGameClientBridgeCommandType(value string) bool {
|
||||
tokens := gameClientBridgePayloadKeyTokens(value)
|
||||
tokens := gameClientBridgeCommandTypeTokens(value)
|
||||
tokenSet := make(map[string]struct{}, len(tokens))
|
||||
for _, token := range tokens {
|
||||
tokenSet[token] = struct{}{}
|
||||
@@ -920,6 +900,12 @@ func unsafeGameClientBridgeCommandType(value string) bool {
|
||||
return has("shell", "powershell", "script", "terminal", "execute", "exec", "eval") || has("command", "cmd", "process", "system", "os", "executor") && has("run")
|
||||
}
|
||||
|
||||
func gameClientBridgeCommandTypeTokens(value string) []string {
|
||||
withAcronymBoundaries := gameClientBridgeAcronymBoundary.ReplaceAllString(value, `${1} ${2}`)
|
||||
withCamelBoundaries := gameClientBridgeCamelBoundary.ReplaceAllString(withAcronymBoundaries, `${1} ${2}`)
|
||||
return strings.Fields(strings.ToLower(gameClientBridgeNonWord.ReplaceAllString(withCamelBoundaries, " ")))
|
||||
}
|
||||
|
||||
func ValidatePluginBridgeAuthorizeRequest(request domain.PluginBridgeAuthorizeRequest) error {
|
||||
var violations []string
|
||||
violations = appendRequired(violations, "pluginId", request.PluginID)
|
||||
@@ -972,15 +958,6 @@ func ValidatePluginBridgeExecuteRequest(request domain.PluginBridgeExecuteReques
|
||||
if len([]rune(value)) > valueLimit {
|
||||
violations = append(violations, "payload value is too long")
|
||||
}
|
||||
for _, reason := range unsafePluginStringReasons(key) {
|
||||
violations = append(violations, "payload key: "+reason)
|
||||
}
|
||||
for _, reason := range unsafePluginStringReasons(value) {
|
||||
violations = append(violations, "payload."+key+": "+reason)
|
||||
}
|
||||
if containsUnsafeRuntimeSecret(value) || strings.Contains(strings.ToLower(value), "unix://") || strings.Contains(strings.ToLower(value), "tcp://") {
|
||||
violations = append(violations, "payload contains unsafe content")
|
||||
}
|
||||
}
|
||||
if payloadSize > maxPluginBridgePayloadSize {
|
||||
violations = append(violations, "payload is too large")
|
||||
|
||||
Reference in New Issue
Block a user