功能修改
This commit is contained in:
@@ -11,6 +11,11 @@ import (
|
||||
"browser.local/platform/domain"
|
||||
)
|
||||
|
||||
var (
|
||||
runtimeLogEventSchemaRefPattern = regexp.MustCompile(`^[A-Za-z0-9_./-]+\.json$`)
|
||||
runtimeLogEventTypePattern = regexp.MustCompile(`^[a-z0-9][a-z0-9._-]{0,119}$`)
|
||||
)
|
||||
|
||||
func ValidateGamePluginRuntimeProfiles(profiles domain.GamePluginRuntimeProfiles) error {
|
||||
profiles = domain.CopyGamePluginRuntimeProfiles(profiles)
|
||||
var violations []string
|
||||
@@ -21,6 +26,9 @@ func ValidateGamePluginRuntimeProfiles(profiles domain.GamePluginRuntimeProfiles
|
||||
dependencyKeys := map[string]struct{}{}
|
||||
installPlanKeys := map[string]struct{}{}
|
||||
logSourceKeys := map[string]struct{}{}
|
||||
logSourceRetentions := map[string]int{}
|
||||
logEventKeys := map[string]struct{}{}
|
||||
logEventTypes := map[string]struct{}{}
|
||||
|
||||
for i, probe := range profiles.Discovery {
|
||||
prefix := fmt.Sprintf("runtimeProfiles.discovery[%d]", i)
|
||||
@@ -138,6 +146,9 @@ func ValidateGamePluginRuntimeProfiles(profiles domain.GamePluginRuntimeProfiles
|
||||
prefix := fmt.Sprintf("runtimeProfiles.logSources[%d]", i)
|
||||
violations = append(violations, validateProfileKey(prefix+".key", source.Key)...)
|
||||
violations = append(violations, recordRuntimeProfileKey(logSourceKeys, prefix+".key", source.Key)...)
|
||||
if source.Key != "" {
|
||||
logSourceRetentions[source.Key] = source.RetentionDays
|
||||
}
|
||||
if !oneOf(source.Kind, "process.stdout", "process.stderr", "file.tail", "ftp.poll", "sql.query", "client-manager") {
|
||||
violations = append(violations, prefix+".kind is invalid")
|
||||
}
|
||||
@@ -152,6 +163,44 @@ func ValidateGamePluginRuntimeProfiles(profiles domain.GamePluginRuntimeProfiles
|
||||
violations = append(violations, prefix+".retentionDays is invalid")
|
||||
}
|
||||
}
|
||||
if len(profiles.LogEvents) > 128 {
|
||||
violations = append(violations, "runtimeProfiles.logEvents must not exceed 128")
|
||||
}
|
||||
for i, event := range profiles.LogEvents {
|
||||
prefix := fmt.Sprintf("runtimeProfiles.logEvents[%d]", i)
|
||||
violations = append(violations, validateProfileKey(prefix+".key", event.Key)...)
|
||||
violations = append(violations, recordRuntimeProfileKey(logEventKeys, prefix+".key", event.Key)...)
|
||||
if strings.TrimSpace(event.Title) == "" || len([]rune(event.Title)) > 80 {
|
||||
violations = append(violations, prefix+".title is invalid")
|
||||
}
|
||||
violations = append(violations, validateSafeRuntimeValue(prefix+".title", event.Title)...)
|
||||
violations = append(violations, validateProfileKey(prefix+".sourceKey", event.SourceKey)...)
|
||||
if _, exists := logSourceKeys[event.SourceKey]; !exists {
|
||||
violations = append(violations, prefix+".sourceKey must reference a declared runtime log source")
|
||||
}
|
||||
if !runtimeLogEventTypePattern.MatchString(event.EventType) {
|
||||
violations = append(violations, prefix+".eventType is invalid")
|
||||
}
|
||||
violations = append(violations, recordRuntimeProfileKey(logEventTypes, prefix+".eventType", event.EventType)...)
|
||||
if hasUnsafeRuntimeLogEventSemantics(event.EventType) {
|
||||
violations = append(violations, prefix+".eventType contains unsafe operation semantics")
|
||||
}
|
||||
if !validPluginPermission(event.Permission) {
|
||||
violations = append(violations, prefix+".permission is not allowed")
|
||||
}
|
||||
if len(event.SchemaRef) > 240 || !runtimeLogEventSchemaRefPattern.MatchString(event.SchemaRef) || !safeRelativeJSONRef(event.SchemaRef) {
|
||||
violations = append(violations, prefix+".schemaRef must be a bounded safe relative JSON reference")
|
||||
}
|
||||
if event.RetentionDays < 1 || event.RetentionDays > 365 {
|
||||
violations = append(violations, prefix+".retentionDays is invalid")
|
||||
}
|
||||
if sourceRetention, exists := logSourceRetentions[event.SourceKey]; exists && sourceRetention > 0 && event.RetentionDays > sourceRetention {
|
||||
violations = append(violations, prefix+".retentionDays must not exceed the source retention")
|
||||
}
|
||||
if !oneOf(string(event.Severity), string(domain.RuntimeLogEventSeverityInfo), string(domain.RuntimeLogEventSeverityNotice), string(domain.RuntimeLogEventSeverityWarning), string(domain.RuntimeLogEventSeverityCritical)) {
|
||||
violations = append(violations, prefix+".severity is invalid")
|
||||
}
|
||||
}
|
||||
for i, transport := range profiles.TransportProfiles {
|
||||
prefix := fmt.Sprintf("runtimeProfiles.transportProfiles[%d]", i)
|
||||
violations = append(violations, validateProfileKey(prefix+".key", transport.Key)...)
|
||||
@@ -346,6 +395,42 @@ func validateSafeRuntimeValue(field, value string) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func hasUnsafeRuntimeLogEventSemantics(eventType string) bool {
|
||||
lowered := strings.ToLower(strings.TrimSpace(eventType))
|
||||
tokens := strings.FieldsFunc(lowered, func(char rune) bool {
|
||||
return char == '.' || char == '_' || char == '-' || char == '/'
|
||||
})
|
||||
unsafeTokens := map[string]struct{}{
|
||||
"apikey": {}, "credential": {}, "credentials": {}, "eval": {}, "exec": {},
|
||||
"execute": {}, "password": {}, "powershell": {}, "script": {}, "secret": {},
|
||||
"shell": {}, "socket": {}, "terminal": {}, "token": {},
|
||||
}
|
||||
for _, token := range tokens {
|
||||
if _, unsafe := unsafeTokens[token]; unsafe {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for index := 0; index+1 < len(tokens); index++ {
|
||||
pair := tokens[index] + "." + tokens[index+1]
|
||||
switch pair {
|
||||
case "absolute.path", "access.key", "api.key", "component.key", "database.query", "direct.socket", "file.path", "host.path", "private.key", "raw.path", "run.direct", "run.socket", "unix.socket":
|
||||
return true
|
||||
}
|
||||
}
|
||||
tokenSet := make(map[string]struct{}, len(tokens))
|
||||
for _, token := range tokens {
|
||||
tokenSet[token] = struct{}{}
|
||||
}
|
||||
if _, hasSQL := tokenSet["sql"]; hasSQL {
|
||||
for _, token := range []string{"query", "statement", "raw"} {
|
||||
if _, unsafe := tokenSet[token]; unsafe {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func recordRuntimeProfileKey(seen map[string]struct{}, field, key string) []string {
|
||||
if key == "" {
|
||||
return nil
|
||||
@@ -382,6 +467,20 @@ func validateRuntimeProfileCapabilityDeclarations(profiles domain.GamePluginRunt
|
||||
return violations
|
||||
}
|
||||
|
||||
func validateRuntimeLogEventPermissionDeclarations(field string, profiles domain.GamePluginRuntimeProfiles, declared []string) []string {
|
||||
declaredSet := make(map[string]struct{}, len(declared))
|
||||
for _, permission := range declared {
|
||||
declaredSet[permission] = struct{}{}
|
||||
}
|
||||
var violations []string
|
||||
for i, event := range profiles.LogEvents {
|
||||
if _, exists := declaredSet[event.Permission]; !exists {
|
||||
violations = append(violations, fmt.Sprintf("%s[%d].permission must be declared by the plugin", field, i))
|
||||
}
|
||||
}
|
||||
return violations
|
||||
}
|
||||
|
||||
func validateLifecycleActionsOptional(actions domain.PluginLifecycleActions) []string {
|
||||
var violations []string
|
||||
for field, value := range map[string]string{"install": actions.Install, "start": actions.Start, "stop": actions.Stop, "restart": actions.Restart, "status": actions.Status} {
|
||||
|
||||
Reference in New Issue
Block a user