[bugfix] Don't return Internal Server Error when searching for URIs that 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
This commit is contained in:
tobi 2024-01-22 15:38:45 +01:00 committed by GitHub
parent b3ba1516a7
commit d9729e7d28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 70 additions and 19 deletions

View file

@ -27,6 +27,7 @@ import (
"github.com/superseriousbusiness/activity/pub" "github.com/superseriousbusiness/activity/pub"
"github.com/superseriousbusiness/activity/streams" "github.com/superseriousbusiness/activity/streams"
"github.com/superseriousbusiness/activity/streams/vocab"
"github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtserror"
) )
@ -56,6 +57,35 @@ func putMap(m map[string]any) {
mapPool.Put(m) 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, // 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). // 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) { func ResolveIncomingActivity(r *http.Request) (pub.Activity, bool, gtserror.WithCode) {
@ -121,15 +151,11 @@ func ResolveStatusable(ctx context.Context, b []byte) (Statusable, error) {
// destination. // destination.
raw := getMap() raw := getMap()
// Unmarshal the raw JSON data in a "raw" JSON map. // Convert raw bytes to an AP type.
if err := json.Unmarshal(b, &raw); err != nil { // This will also populate the map.
return nil, gtserror.Newf("error unmarshalling bytes into json: %w", err) t, err := bytesToType(ctx, b, raw)
}
// Resolve an ActivityStreams type from JSON.
t, err := streams.ToType(ctx, raw)
if err != nil { 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. // Attempt to cast as Statusable.
@ -166,15 +192,11 @@ func ResolveAccountable(ctx context.Context, b []byte) (Accountable, error) {
// destination. // destination.
raw := getMap() raw := getMap()
// Unmarshal the raw JSON data in a "raw" JSON map. // Convert raw bytes to an AP type.
if err := json.Unmarshal(b, &raw); err != nil { // This will also populate the map.
return nil, gtserror.Newf("error unmarshalling bytes into json: %w", err) t, err := bytesToType(ctx, b, raw)
}
// Resolve an ActivityStreams type from JSON.
t, err := streams.ToType(ctx, raw)
if err != nil { 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. // Attempt to cast as Statusable.

View file

@ -47,6 +47,29 @@ func (suite *ResolveTestSuite) TestResolveDocumentAsAccountable() {
suite.Nil(accountable) 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) { func TestResolveTestSuite(t *testing.T) {
suite.Run(t, &ResolveTestSuite{}) suite.Run(t, &ResolveTestSuite{})
} }

View file

@ -55,9 +55,15 @@ func SetUnretrievable(err error) error {
return errors.WithValue(err, unrtrvableKey, struct{}{}) return errors.WithValue(err, unrtrvableKey, struct{}{})
} }
// IsWrongType checks error for a stored "wrong type" flag. Wrong type // IsWrongType checks error for a stored "wrong type" flag.
// indicates that an ActivityPub URI returned a type we weren't expecting: // Wrong type indicates that an ActivityPub URI returned a
// Statusable instead of Accountable, or vice versa, for example. // 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 { func IsWrongType(err error) bool {
_, ok := errors.Value(err, wrongTypeKey).(struct{}) _, ok := errors.Value(err, wrongTypeKey).(struct{})
return ok return ok