diff --git a/go.mod b/go.mod index 6015e22..fb50320 100644 --- a/go.mod +++ b/go.mod @@ -120,7 +120,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.14.0 // indirect golang.org/x/crypto v0.32.0 // indirect - golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c // indirect + golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 // indirect golang.org/x/image v0.24.0 // indirect golang.org/x/mod v0.23.0 // indirect golang.org/x/sync v0.11.0 // indirect @@ -128,7 +128,7 @@ require ( golang.org/x/term v0.29.0 // indirect golang.org/x/text v0.22.0 // indirect golang.org/x/tools v0.29.0 // indirect - google.golang.org/protobuf v1.36.4 // indirect + google.golang.org/protobuf v1.36.5 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index fc1a143..858b08a 100644 --- a/go.sum +++ b/go.sum @@ -401,8 +401,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc= -golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= +golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 h1:qNgPs5exUA+G0C96DrPwNrvLSj7GT/9D+3WMWUcUg34= +golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ= @@ -534,8 +534,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= -google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/server/ws/.testdata/application.yaml b/server/ws/.testdata/application.yaml index fcdaa04..091305d 100644 --- a/server/ws/.testdata/application.yaml +++ b/server/ws/.testdata/application.yaml @@ -2,6 +2,9 @@ validation: max-wrapped-event-expiration: 720h + max-content-sizes: + 30023: 65535 + 30175: 65535 server: ws: diff --git a/subzero_ion_connect.yaml b/subzero_ion_connect.yaml index 5d4c8eb..53e9a87 100644 --- a/subzero_ion_connect.yaml +++ b/subzero_ion_connect.yaml @@ -2,6 +2,9 @@ validation: max-wrapped-event-expiration: 720h + max-content-sizes: + 30023: 65535 + 30175: 65535 server: relay-url: &self "wss://example.com" diff --git a/validation/.testdata/application.yaml b/validation/.testdata/application.yaml index 84573e8..1ca76c6 100644 --- a/validation/.testdata/application.yaml +++ b/validation/.testdata/application.yaml @@ -2,6 +2,9 @@ validation: max-wrapped-event-expiration: 720h + max-content-sizes: + 30023: 65535 + 30175: 65535 database: query: diff --git a/validation/config.go b/validation/config.go index f5316db..5e096b7 100644 --- a/validation/config.go +++ b/validation/config.go @@ -11,6 +11,7 @@ import ( type ( config struct { MaxWrappedEventExpiration time.Duration `yaml:"max-wrapped-event-expiration"` + MaxContentSizes map[int]int `yaml:"max-content-sizes"` // Kind -> size (bytes). } ) @@ -21,3 +22,12 @@ var ( func init() { globalConfig = cfg.MustGet[config]() } + +func (c *config) MaxContentSizeOf(kind int) int { + if c != nil { + if size, ok := c.MaxContentSizes[kind]; ok { + return size + } + } + return 0 +} diff --git a/validation/config_test.go b/validation/config_test.go new file mode 100644 index 0000000..0182a74 --- /dev/null +++ b/validation/config_test.go @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: ice License 1.0 + +package validation + +import ( + "testing" + + "github.com/nbd-wtf/go-nostr" + "github.com/stretchr/testify/require" +) + +func TestGlobalMaxPostSizeOf(t *testing.T) { + t.Parallel() + + require.Equal(t, 0, globalConfig.MaxContentSizeOf(0)) + require.Equal(t, 0xffff, globalConfig.MaxContentSizeOf(nostr.KindArticle)) +} diff --git a/validation/validate.go b/validation/validate.go index da8b3c0..0e718b3 100644 --- a/validation/validate.go +++ b/validation/validate.go @@ -407,6 +407,9 @@ func Validate(ctx context.Context, e *model.Event) error { if err := validateEventTags(e); err != nil { return errors.Wrapf(err, "event: %+v", e) } + if actualSize, maxSize := len(e.Content), globalConfig.MaxContentSizeOf(e.Kind); maxSize > 0 && actualSize > maxSize { + return errors.Wrapf(ErrWrongEventParams, "content is too long %d, max is %d", actualSize, maxSize) + } switch e.Kind { case nostr.KindProfileMetadata: return validateKindProfileMetadataEvent(e) @@ -496,9 +499,9 @@ func Validate(ctx context.Context, e *model.Event) error { return validateKindProfileBadgesEvent(e) case nostr.KindBadgeDefinition: return validateKindBadgeDefinitionEvent(e) - case nostr.KindArticle, nostr.KindDraftArticle: - if e.Content == "" { - return errors.Wrap(ErrWrongEventParams, "nip-23: this kind should have text markdown content") + case nostr.KindArticle, nostr.KindDraftArticle, model.CustomIONKindEditableTextNote: + if len(e.Content) < 1 { + return errors.Wrap(ErrWrongEventParams, "content is empty or too short") } if err := validatePostCommunityEvent(ctx, e); err != nil { return err @@ -515,7 +518,7 @@ func Validate(ctx context.Context, e *model.Event) error { case model.CustomIONKindCommunityBanUser: return validateCustomIONKindCommunityBanUserEvent(ctx, e) default: - if e.Kind >= 6000 && e.Kind <= 6999 { + if e.IsJobResponse() { return validateKindJobResult(e) } }