Skip to content

Commit

Permalink
[bugfix] Don't return Internal Server Error when searching for URIs t…
Browse files Browse the repository at this point in the history
…hat don't return AP JSON (#2550)

* [bugfix] Don't return Internal Server Error when searching for URIs that don't return AP JSON

* don't pass map pointer
  • Loading branch information
tsmethurst committed Feb 14, 2024
1 parent 1188971 commit ad6f756
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 19 deletions.
54 changes: 38 additions & 16 deletions internal/ap/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (

"github.com/superseriousbusiness/activity/pub"
"github.com/superseriousbusiness/activity/streams"
"github.com/superseriousbusiness/activity/streams/vocab"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)

Expand Down Expand Up @@ -56,6 +57,35 @@ func putMap(m map[string]any) {
mapPool.Put(m)
}

// bytesToType tries to parse the given bytes slice
// as a JSON ActivityPub type, failing if the input
// bytes are not parseable as JSON, or do not parse
// to an ActivityPub that we can understand.
//
// The given map pointer will also be populated with
// the parsed JSON, to allow further processing.
func bytesToType(
ctx context.Context,
b []byte,
raw map[string]any,
) (vocab.Type, error) {
// Unmarshal the raw JSON bytes into a "raw" map.
// This will fail if the input is not parseable
// as JSON; eg., a remote has returned HTML as a
// fallback response to an ActivityPub JSON request.
if err := json.Unmarshal(b, &raw); err != nil {
return nil, gtserror.NewfAt(3, "error unmarshalling bytes into json: %w", err)
}

// Resolve an ActivityStreams type.
t, err := streams.ToType(ctx, raw)
if err != nil {
return nil, gtserror.NewfAt(3, "error resolving json into ap vocab type: %w", err)
}

return t, nil
}

// ResolveActivity is a util function for pulling a pub.Activity type out of an incoming request body,
// returning the resolved activity type, error and whether to accept activity (false = transient i.e. ignore).
func ResolveIncomingActivity(r *http.Request) (pub.Activity, bool, gtserror.WithCode) {
Expand Down Expand Up @@ -121,15 +151,11 @@ func ResolveStatusable(ctx context.Context, b []byte) (Statusable, error) {
// destination.
raw := getMap()

// Unmarshal the raw JSON data in a "raw" JSON map.
if err := json.Unmarshal(b, &raw); err != nil {
return nil, gtserror.Newf("error unmarshalling bytes into json: %w", err)
}

// Resolve an ActivityStreams type from JSON.
t, err := streams.ToType(ctx, raw)
// Convert raw bytes to an AP type.
// This will also populate the map.
t, err := bytesToType(ctx, b, raw)
if err != nil {
return nil, gtserror.Newf("error resolving json into ap vocab type: %w", err)
return nil, gtserror.SetWrongType(err)
}

// Attempt to cast as Statusable.
Expand Down Expand Up @@ -166,15 +192,11 @@ func ResolveAccountable(ctx context.Context, b []byte) (Accountable, error) {
// destination.
raw := getMap()

// Unmarshal the raw JSON data in a "raw" JSON map.
if err := json.Unmarshal(b, &raw); err != nil {
return nil, gtserror.Newf("error unmarshalling bytes into json: %w", err)
}

// Resolve an ActivityStreams type from JSON.
t, err := streams.ToType(ctx, raw)
// Convert raw bytes to an AP type.
// This will also populate the map.
t, err := bytesToType(ctx, b, raw)
if err != nil {
return nil, gtserror.Newf("error resolving json into ap vocab type: %w", err)
return nil, gtserror.SetWrongType(err)
}

// Attempt to cast as Statusable.
Expand Down
23 changes: 23 additions & 0 deletions internal/ap/resolve_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,29 @@ func (suite *ResolveTestSuite) TestResolveDocumentAsAccountable() {
suite.Nil(accountable)
}

func (suite *ResolveTestSuite) TestResolveHTMLAsAccountable() {
b := []byte(`<!DOCTYPE html>
<title>.</title>`)

accountable, err := ap.ResolveAccountable(context.Background(), b)
suite.True(gtserror.IsWrongType(err))
suite.EqualError(err, "ResolveAccountable: error unmarshalling bytes into json: invalid character '<' looking for beginning of value")
suite.Nil(accountable)
}

func (suite *ResolveTestSuite) TestResolveNonAPJSONAsAccountable() {
b := []byte(`{
"@context": "definitely a legit context muy lord",
"type": "definitely an account muy lord",
"pee pee":"poo poo"
}`)

accountable, err := ap.ResolveAccountable(context.Background(), b)
suite.True(gtserror.IsWrongType(err))
suite.EqualError(err, "ResolveAccountable: error resolving json into ap vocab type: activity stream did not match any known types")
suite.Nil(accountable)
}

func TestResolveTestSuite(t *testing.T) {
suite.Run(t, &ResolveTestSuite{})
}
12 changes: 9 additions & 3 deletions internal/gtserror/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,15 @@ func SetUnretrievable(err error) error {
return errors.WithValue(err, unrtrvableKey, struct{}{})
}

// IsWrongType checks error for a stored "wrong type" flag. Wrong type
// indicates that an ActivityPub URI returned a type we weren't expecting:
// Statusable instead of Accountable, or vice versa, for example.
// IsWrongType checks error for a stored "wrong type" flag.
// Wrong type indicates that an ActivityPub URI returned a
// type we weren't expecting. For example:
//
// - HTML instead of JSON.
// - Normal JSON instead of ActivityPub JSON.
// - Statusable instead of Accountable.
// - Accountable instead of Statusable.
// - etc.
func IsWrongType(err error) bool {
_, ok := errors.Value(err, wrongTypeKey).(struct{})
return ok
Expand Down

0 comments on commit ad6f756

Please sign in to comment.