Skip to content

Commit

Permalink
Added invalid serialization detector
Browse files Browse the repository at this point in the history
  • Loading branch information
jaredoconnell committed Feb 14, 2024
1 parent a481c35 commit 12d474c
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 0 deletions.
83 changes: 83 additions & 0 deletions schema/invalid_serialization_detector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package schema

import (
"fmt"
"reflect"
)

// NewInvalidSerializationDetectorSchema creates a new test schema type.
func NewInvalidSerializationDetectorSchema() *InvalidSerializationDetectorSchema {
return &InvalidSerializationDetectorSchema{}
}

// InvalidSerializationDetectorSchema is a testing type that detects double serialization or double unserialization,
// which helps prevent bugs.
// It is backed by a string, which is set to the last operation performed. If the last operation matches the
// current operation, then that indicates double serialization or double unserialization.
type InvalidSerializationDetectorSchema struct{}

func (d InvalidSerializationDetectorSchema) Unserialize(data any) (any, error) {
// The input is expected to always be a string.
// If the input is equal to "unserialized", then that means it's being unserialized a second time.
return d.detectInvalidValue("unserialized", data)
}

func (d InvalidSerializationDetectorSchema) detectInvalidValue(operation string, data any) (any, error) {
asString, isString := data.(string)
if !isString {
return nil, &ConstraintError{
Message: fmt.Sprintf(
"unsupported data type for InvalidSerializationDetectorSchema; expected string, got %T",
data,
),
}
}
if asString == operation {
return nil, &ConstraintError{
Message: fmt.Sprintf("InvalidSerializationDetectorSchema double %s", operation),
}
}
return operation, nil
}

func (d InvalidSerializationDetectorSchema) UnserializeType(data any) (string, error) {
unserialized, err := d.Unserialize(data)
if err != nil {
return "", err
}
return unserialized.(string), nil
}

func (d InvalidSerializationDetectorSchema) ValidateCompatibility(_ any) error {
// Not applicable to this data type
return nil
}

func (d InvalidSerializationDetectorSchema) Validate(data any) error {
_, err := d.Serialize(data)
return err
}

func (d InvalidSerializationDetectorSchema) ValidateType(data string) error {
return d.Validate(data)
}

func (d InvalidSerializationDetectorSchema) Serialize(data any) (any, error) {
// The input is expected to always be a string.
// If the input is equal to "serialized", then that means it's being serialized a second time.
return d.detectInvalidValue("serialized", data)
}

func (d InvalidSerializationDetectorSchema) SerializeType(data string) (any, error) {
return d.Serialize(data)
}

func (d InvalidSerializationDetectorSchema) ApplyScope(scope Scope) {}

func (d InvalidSerializationDetectorSchema) TypeID() TypeID {
return TypeIDString // This is a subset of a string schema.
}

func (d InvalidSerializationDetectorSchema) ReflectedType() reflect.Type {
return reflect.TypeOf("")
}
63 changes: 63 additions & 0 deletions schema/invalid_serialization_detector_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package schema_test

import (
"go.arcalot.io/assert"
"go.flow.arcalot.io/pluginsdk/schema"
"reflect"
"testing"
)

func TestInvalidSerializationDetectorSchema_ProperUsage(t *testing.T) {
schemaType := schema.NewInvalidSerializationDetectorSchema()
unserialized, err := schemaType.Unserialize("original")
assert.NoError(t, err)
assert.Equals[any](t, unserialized, "unserialized")
serialized, err := schemaType.Serialize(unserialized)
assert.NoError(t, err)
assert.Equals[any](t, serialized, "serialized")
unserialized, err = schemaType.Unserialize(serialized)
assert.NoError(t, err)
assert.Equals[any](t, unserialized, "unserialized")
}

func TestInvalidSerializationDetectorSchema_InvalidInput(t *testing.T) {
schemaType := schema.NewInvalidSerializationDetectorSchema()
_, err := schemaType.Unserialize(true)
assert.Error(t, err)
_, err = schemaType.Serialize(true)
assert.Error(t, err)
}

func TestInvalidSerializationDetectorSchema_DoubleSerialization(t *testing.T) {
schemaType := schema.NewInvalidSerializationDetectorSchema()
serialized, err := schemaType.Serialize("original")
assert.NoError(t, err)
_, err = schemaType.Serialize(serialized)
assert.Error(t, err)
assert.InstanceOf[string](t, serialized)
_, err = schemaType.SerializeType(serialized.(string))
assert.Error(t, err)
}

func TestInvalidSerializationDetectorSchema_DoubleUnserialization(t *testing.T) {
schemaType := schema.NewInvalidSerializationDetectorSchema()
unserialized, err := schemaType.Unserialize("original")
assert.NoError(t, err)
_, err = schemaType.Unserialize(unserialized)
assert.Error(t, err)
_, err = schemaType.UnserializeType(unserialized)
assert.Error(t, err)
}

func TestInvalidSerializationDetectorSchema_Other(t *testing.T) {
schemaType := schema.NewInvalidSerializationDetectorSchema()
// Test TypeID()
assert.Equals(t, schemaType.TypeID(), schema.TypeIDString)
// Test Validate and ValidateType
assert.NoError(t, schemaType.Validate("any string"))
assert.NoError(t, schemaType.ValidateType("any string"))
// Test ReflectedType
assert.Equals(t, schemaType.ReflectedType().Kind(), reflect.String)
// Cover ValidateCompatibility
assert.NoError(t, schemaType.ValidateCompatibility(nil))
}

0 comments on commit 12d474c

Please sign in to comment.