package companion import ( "context" "fmt" "sort" "strings" ) // AuthorizedConfigPort is supplied by a version-bound Companion integration. // It exposes logical configuration values only: never a host path, connection // string, credential, arbitrary command, or direct database handle. type AuthorizedConfigPort interface { ReadConfig(context.Context) (map[string]string, error) ApplyConfigPatch(ctx context.Context, revision string, fields []ConfigFieldPatch) (map[string]string, error) } type ConfigFieldPatch struct { Key string Value string } type VersionedAdapter struct { ServerVersion string Config AuthorizedConfigPort DiagnosticsState map[string]string } func (adapter VersionedAdapter) ReadConfiguration(ctx context.Context) (map[string]any, error) { if !supportedAdapterVersion(adapter.ServerVersion) || adapter.Config == nil { return nil, fmt.Errorf("configuration adapter is unsupported") } fields, err := adapter.Config.ReadConfig(ctx) if err != nil { return nil, err } return map[string]any{"version": adapter.ServerVersion, "fields": redactConfigValues(fields)}, nil } func (adapter VersionedAdapter) PatchConfiguration(ctx context.Context, payload map[string]any) (map[string]any, error) { if !supportedAdapterVersion(adapter.ServerVersion) || adapter.Config == nil { return nil, fmt.Errorf("configuration adapter is unsupported") } revision, _ := payload["revision"].(string) raw, _ := payload["fields"].([]any) fields := make([]ConfigFieldPatch, 0, len(raw)) for _, value := range raw { item, ok := value.(map[string]any) if !ok { return nil, fmt.Errorf("configuration patch payload is invalid") } key, keyOK := item["key"].(string) fieldValue, valueOK := item["value"].(string) if !keyOK || !valueOK || !supportedConfigKey(key) { return nil, fmt.Errorf("configuration patch field is unsupported") } fields = append(fields, ConfigFieldPatch{Key: key, Value: fieldValue}) } applied, err := adapter.Config.ApplyConfigPatch(ctx, revision, fields) if err != nil { return nil, err } return map[string]any{"version": adapter.ServerVersion, "appliedFields": redactConfigValues(applied)}, nil } func (adapter VersionedAdapter) Diagnostics(context.Context) (map[string]any, error) { state := map[string]any{"version": adapter.ServerVersion, "adapter": "version-bound", "configuration": supportedAdapterVersion(adapter.ServerVersion)} for key, value := range adapter.DiagnosticsState { if safeDiagnosticField(key, value) { state[key] = value } } return state, nil } func (VersionedAdapter) PatchGameState(context.Context, map[string]any) (map[string]any, error) { return nil, fmt.Errorf("state adapter is unsupported") } func (VersionedAdapter) DeliverReward(context.Context, map[string]any) (map[string]any, error) { return nil, fmt.Errorf("reward adapter is unsupported") } func (VersionedAdapter) NotifyPlayer(context.Context, map[string]any) (map[string]any, error) { return nil, fmt.Errorf("notification adapter is unsupported") } func supportedAdapterVersion(version string) bool { return version == "0.9.700.90357" } func supportedConfigKey(key string) bool { return map[string]bool{"ServerName": true, "GamePort": true, "QueryPort": true, "MaxPlayers": true, "WelcomeMessage": true}[key] } func redactConfigValues(values map[string]string) map[string]string { result := map[string]string{} keys := make([]string, 0, len(values)) for key := range values { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { if supportedConfigKey(key) && safeDiagnosticField(key, values[key]) { result[key] = values[key] } } return result } func safeDiagnosticField(key, value string) bool { lowered := strings.ToLower(key + "=" + value) return !strings.Contains(lowered, "path") && !strings.Contains(lowered, "credential") && !strings.Contains(lowered, "password") && !strings.Contains(lowered, "bearer ") && !strings.Contains(lowered, "rcon") && !strings.Contains(lowered, "sql") && !strings.Contains(lowered, "://") }