mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 19:02:24 -05:00 
			
		
		
		
	[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:
		
					parent
					
						
							
								b3ba1516a7
							
						
					
				
			
			
				commit
				
					
						d9729e7d28
					
				
			
		
					 3 changed files with 70 additions and 19 deletions
				
			
		|  | @ -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. | ||||||
|  |  | ||||||
|  | @ -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{}) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue