mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-02-04 20:05:05 +00:00
[bugfix] harden checks for remotes masquerading as local, and return correct local account redirects early (#3706)
This commit is contained in:
parent
d16e4fa34d
commit
1ab960bf15
|
@ -639,7 +639,16 @@ func (d *Dereferencer) enrichAccount(
|
||||||
return nil, nil, gtserror.Newf("db error getting account after redirects: %w", err)
|
return nil, nil, gtserror.Newf("db error getting account after redirects: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if alreadyAcc != nil {
|
switch {
|
||||||
|
case alreadyAcc == nil:
|
||||||
|
// nothing to do
|
||||||
|
|
||||||
|
case alreadyAcc.IsLocal():
|
||||||
|
// Request eventually redirected to a
|
||||||
|
// local account. Return it as-is here.
|
||||||
|
return alreadyAcc, nil, nil
|
||||||
|
|
||||||
|
default:
|
||||||
// We had this account stored
|
// We had this account stored
|
||||||
// under discovered final URI.
|
// under discovered final URI.
|
||||||
//
|
//
|
||||||
|
@ -718,12 +727,6 @@ func (d *Dereferencer) enrichAccount(
|
||||||
latestAcc.Username = cmp.Or(latestAcc.Username, accUsername)
|
latestAcc.Username = cmp.Or(latestAcc.Username, accUsername)
|
||||||
}
|
}
|
||||||
|
|
||||||
if latestAcc.Domain == "" {
|
|
||||||
// Ensure we have a domain set by this point,
|
|
||||||
// otherwise it gets stored as a local user!
|
|
||||||
return nil, nil, gtserror.Newf("empty domain for %s", uri)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the final parsed account URI matches
|
// Ensure the final parsed account URI matches
|
||||||
// the input URI we fetched (or received) it as.
|
// the input URI we fetched (or received) it as.
|
||||||
if matches, err := util.URIMatches(
|
if matches, err := util.URIMatches(
|
||||||
|
@ -740,10 +743,16 @@ func (d *Dereferencer) enrichAccount(
|
||||||
} else if !matches {
|
} else if !matches {
|
||||||
return nil, nil, gtserror.Newf(
|
return nil, nil, gtserror.Newf(
|
||||||
"account uri %s does not match %s",
|
"account uri %s does not match %s",
|
||||||
latestAcc.URI, uri.String(),
|
latestAcc.URI, uri,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure this isn't a local account,
|
||||||
|
// or a remote masquerading as such!
|
||||||
|
if latestAcc.IsLocal() {
|
||||||
|
return nil, nil, gtserror.Newf("cannot dereference local account %s", uri)
|
||||||
|
}
|
||||||
|
|
||||||
// Get current time.
|
// Get current time.
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,15 @@
|
||||||
package dereferencing_test
|
package dereferencing_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -33,6 +37,7 @@
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/federation/dereferencing"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
@ -214,6 +219,111 @@ func (suite *AccountTestSuite) TestDereferenceLocalAccountWithUnknownUserURI() {
|
||||||
suite.Nil(fetchedAccount)
|
suite.Nil(fetchedAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *AccountTestSuite) TestDereferenceLocalAccountByRedirect() {
|
||||||
|
ctx, cncl := context.WithCancel(context.Background())
|
||||||
|
defer cncl()
|
||||||
|
|
||||||
|
fetchingAccount := suite.testAccounts["local_account_1"]
|
||||||
|
targetAccount := suite.testAccounts["local_account_2"]
|
||||||
|
|
||||||
|
// Convert the target account to ActivityStreams model for dereference.
|
||||||
|
targetAccountable, err := suite.converter.AccountToAS(ctx, targetAccount)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.NotNil(targetAccountable)
|
||||||
|
|
||||||
|
// Serialize to "raw" JSON map for response.
|
||||||
|
rawJSON, err := ap.Serialize(targetAccountable)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
// Finally serialize to actual bytes.
|
||||||
|
json, err := json.Marshal(rawJSON)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
// Replace test HTTP client with one that always returns the target account AS model.
|
||||||
|
suite.client = testrig.NewMockHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
Status: http.StatusText(http.StatusOK),
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
ContentLength: int64(len(json)),
|
||||||
|
Header: http.Header{"Content-Type": {"application/activity+json"}},
|
||||||
|
Body: io.NopCloser(bytes.NewReader(json)),
|
||||||
|
Request: &http.Request{URL: testrig.URLMustParse(targetAccount.URI)},
|
||||||
|
}, nil
|
||||||
|
}, "")
|
||||||
|
|
||||||
|
// Update dereferencer to use new test HTTP client.
|
||||||
|
suite.dereferencer = dereferencing.NewDereferencer(
|
||||||
|
&suite.state,
|
||||||
|
suite.converter,
|
||||||
|
testrig.NewTestTransportController(&suite.state, suite.client),
|
||||||
|
suite.visFilter,
|
||||||
|
suite.intFilter,
|
||||||
|
suite.media,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Use any old input test URI, this doesn't actually matter what it is.
|
||||||
|
uri := testrig.URLMustParse("https://this-will-be-redirected.butts/")
|
||||||
|
|
||||||
|
// Try dereference the test URI, since it correctly redirects to us it should return our account.
|
||||||
|
account, accountable, err := suite.dereferencer.GetAccountByURI(ctx, fetchingAccount.Username, uri)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Nil(accountable)
|
||||||
|
suite.NotNil(account)
|
||||||
|
suite.Equal(targetAccount.ID, account.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *AccountTestSuite) TestDereferenceMasqueradingLocalAccount() {
|
||||||
|
ctx, cncl := context.WithCancel(context.Background())
|
||||||
|
defer cncl()
|
||||||
|
|
||||||
|
fetchingAccount := suite.testAccounts["local_account_1"]
|
||||||
|
targetAccount := suite.testAccounts["local_account_2"]
|
||||||
|
|
||||||
|
// Convert the target account to ActivityStreams model for dereference.
|
||||||
|
targetAccountable, err := suite.converter.AccountToAS(ctx, targetAccount)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.NotNil(targetAccountable)
|
||||||
|
|
||||||
|
// Serialize to "raw" JSON map for response.
|
||||||
|
rawJSON, err := ap.Serialize(targetAccountable)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
// Finally serialize to actual bytes.
|
||||||
|
json, err := json.Marshal(rawJSON)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
// Use any old input test URI, this doesn't actually matter what it is.
|
||||||
|
uri := testrig.URLMustParse("https://this-will-be-redirected.butts/")
|
||||||
|
|
||||||
|
// Replace test HTTP client with one that returns OUR account, but at their URI endpoint.
|
||||||
|
suite.client = testrig.NewMockHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
Status: http.StatusText(http.StatusOK),
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
ContentLength: int64(len(json)),
|
||||||
|
Header: http.Header{"Content-Type": {"application/activity+json"}},
|
||||||
|
Body: io.NopCloser(bytes.NewReader(json)),
|
||||||
|
Request: &http.Request{URL: uri},
|
||||||
|
}, nil
|
||||||
|
}, "")
|
||||||
|
|
||||||
|
// Update dereferencer to use new test HTTP client.
|
||||||
|
suite.dereferencer = dereferencing.NewDereferencer(
|
||||||
|
&suite.state,
|
||||||
|
suite.converter,
|
||||||
|
testrig.NewTestTransportController(&suite.state, suite.client),
|
||||||
|
suite.visFilter,
|
||||||
|
suite.intFilter,
|
||||||
|
suite.media,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Try dereference the test URI, since it correctly redirects to us it should return our account.
|
||||||
|
account, accountable, err := suite.dereferencer.GetAccountByURI(ctx, fetchingAccount.Username, uri)
|
||||||
|
suite.NotNil(err)
|
||||||
|
suite.Nil(account)
|
||||||
|
suite.Nil(accountable)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *AccountTestSuite) TestDereferenceRemoteAccountWithNonMatchingURI() {
|
func (suite *AccountTestSuite) TestDereferenceRemoteAccountWithNonMatchingURI() {
|
||||||
fetchingAccount := suite.testAccounts["local_account_1"]
|
fetchingAccount := suite.testAccounts["local_account_1"]
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/filter/interaction"
|
"github.com/superseriousbusiness/gotosocial/internal/filter/interaction"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
|
@ -34,10 +35,14 @@
|
||||||
|
|
||||||
type DereferencerStandardTestSuite struct {
|
type DereferencerStandardTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
db db.DB
|
db db.DB
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
state state.State
|
state state.State
|
||||||
client *testrig.MockHTTPClient
|
client *testrig.MockHTTPClient
|
||||||
|
converter *typeutils.Converter
|
||||||
|
visFilter *visibility.Filter
|
||||||
|
intFilter *interaction.Filter
|
||||||
|
media *media.Manager
|
||||||
|
|
||||||
testRemoteStatuses map[string]vocab.ActivityStreamsNote
|
testRemoteStatuses map[string]vocab.ActivityStreamsNote
|
||||||
testRemotePeople map[string]vocab.ActivityStreamsPerson
|
testRemotePeople map[string]vocab.ActivityStreamsPerson
|
||||||
|
@ -67,12 +72,15 @@ func (suite *DereferencerStandardTestSuite) SetupTest() {
|
||||||
|
|
||||||
suite.db = testrig.NewTestDB(&suite.state)
|
suite.db = testrig.NewTestDB(&suite.state)
|
||||||
|
|
||||||
converter := typeutils.NewConverter(&suite.state)
|
suite.converter = typeutils.NewConverter(&suite.state)
|
||||||
|
suite.visFilter = visibility.NewFilter(&suite.state)
|
||||||
|
suite.intFilter = interaction.NewFilter(&suite.state)
|
||||||
|
suite.media = testrig.NewTestMediaManager(&suite.state)
|
||||||
|
|
||||||
testrig.StartTimelines(
|
testrig.StartTimelines(
|
||||||
&suite.state,
|
&suite.state,
|
||||||
visibility.NewFilter(&suite.state),
|
suite.visFilter,
|
||||||
converter,
|
suite.converter,
|
||||||
)
|
)
|
||||||
|
|
||||||
suite.client = testrig.NewMockHTTPClient(nil, "../../../testrig/media")
|
suite.client = testrig.NewMockHTTPClient(nil, "../../../testrig/media")
|
||||||
|
@ -81,19 +89,16 @@ func (suite *DereferencerStandardTestSuite) SetupTest() {
|
||||||
suite.state.AdminActions = admin.New(suite.state.DB, &suite.state.Workers)
|
suite.state.AdminActions = admin.New(suite.state.DB, &suite.state.Workers)
|
||||||
suite.state.Storage = suite.storage
|
suite.state.Storage = suite.storage
|
||||||
|
|
||||||
visFilter := visibility.NewFilter(&suite.state)
|
|
||||||
intFilter := interaction.NewFilter(&suite.state)
|
|
||||||
media := testrig.NewTestMediaManager(&suite.state)
|
|
||||||
suite.dereferencer = dereferencing.NewDereferencer(
|
suite.dereferencer = dereferencing.NewDereferencer(
|
||||||
&suite.state,
|
&suite.state,
|
||||||
converter,
|
suite.converter,
|
||||||
testrig.NewTestTransportController(
|
testrig.NewTestTransportController(
|
||||||
&suite.state,
|
&suite.state,
|
||||||
suite.client,
|
suite.client,
|
||||||
),
|
),
|
||||||
visFilter,
|
suite.visFilter,
|
||||||
intFilter,
|
suite.intFilter,
|
||||||
media,
|
suite.media,
|
||||||
)
|
)
|
||||||
testrig.StandardDBSetup(suite.db, nil)
|
testrig.StandardDBSetup(suite.db, nil)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue