Skip to content

Commit

Permalink
Used limited reader in user invite (#25943)
Browse files Browse the repository at this point in the history
* Used limited reader in user invite

* Added tests
  • Loading branch information
harshilsharma63 committed Jan 23, 2024
1 parent 09b7012 commit 62064e3
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 18 deletions.
8 changes: 2 additions & 6 deletions server/channels/api4/team.go
Expand Up @@ -1375,16 +1375,12 @@ func inviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request) {
return
}

bf, err := io.ReadAll(r.Body)
memberInvite := &model.MemberInvite{}
err := model.StructFromJSONLimited(r.Body, *c.App.Config().ServiceSettings.MaximumPayloadSizeBytes, memberInvite)
if err != nil {
c.Err = model.NewAppError("Api4.inviteUsersToTeams", "api.team.invite_members_to_team_and_channels.invalid_body.app_error", nil, "", http.StatusBadRequest).Wrap(err)
return
}
memberInvite := &model.MemberInvite{}
if err := json.Unmarshal(bf, memberInvite); err != nil {
c.Err = model.NewAppError("Api4.inviteUsersToTeams", "api.team.invite_members_to_team_and_channels.invalid_body_parsing.app_error", nil, "", http.StatusBadRequest).Wrap(err)
return
}

emailList := memberInvite.Emails

Expand Down
10 changes: 2 additions & 8 deletions server/channels/api4/team_local.go
Expand Up @@ -6,7 +6,6 @@ package api4
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"

Expand Down Expand Up @@ -79,15 +78,10 @@ func localInviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request)
return
}

bf, err := io.ReadAll(r.Body)
if err != nil {
c.Err = model.NewAppError("Api4.inviteUsersToTeams", "api.team.invite_members_to_team_and_channels.invalid_body.app_error", nil, "", http.StatusBadRequest).Wrap(err)
return
}
memberInvite := &model.MemberInvite{}
err = json.Unmarshal(bf, memberInvite)
err := model.StructFromJSONLimited(r.Body, *c.App.Config().ServiceSettings.MaximumPayloadSizeBytes, memberInvite)
if err != nil {
c.Err = model.NewAppError("Api4.inviteUsersToTeams", "api.team.invite_members_to_team_and_channels.invalid_body_parsing.app_error", nil, "", http.StatusBadRequest).Wrap(err)
c.Err = model.NewAppError("Api4.localInviteUsersToTeam", "api.team.invite_members_to_team_and_channels.invalid_body.app_error", nil, "", http.StatusBadRequest).Wrap(err)
return
}

Expand Down
4 changes: 0 additions & 4 deletions server/i18n/en.json
Expand Up @@ -3262,10 +3262,6 @@
"id": "api.team.invite_members_to_team_and_channels.invalid_body.app_error",
"translation": "Invalid request body."
},
{
"id": "api.team.invite_members_to_team_and_channels.invalid_body_parsing.app_error",
"translation": "Error while parsing the body data."
},
{
"id": "api.team.is_team_creation_allowed.disabled.app_error",
"translation": "Team creation has been disabled. Please ask your System Administrator for details."
Expand Down
10 changes: 10 additions & 0 deletions server/public/model/utils.go
Expand Up @@ -553,6 +553,16 @@ func StringInterfaceFromJSON(data io.Reader) map[string]any {
return objmap
}

func StructFromJSONLimited[V any](data io.Reader, maxBytes int64, obj *V) error {
lr := io.LimitReader(data, maxBytes)
err := json.NewDecoder(lr).Decode(&obj)
if err != nil || obj == nil {
return err
}

return nil
}

// ToJSON serializes an arbitrary data type to JSON, discarding the error.
func ToJSON(v any) []byte {
b, _ := json.Marshal(v)
Expand Down
161 changes: 161 additions & 0 deletions server/public/model/utils_test.go
Expand Up @@ -1207,3 +1207,164 @@ func TestRemoveDuplicateStrings(t *testing.T) {
require.Equalf(t, actual, tc.Result, "case: %v\tshould returned: %#v", tc, tc.Result)
}
}

func TestStructFromJSONLimited(t *testing.T) {
t.Run("successfully parses basic struct", func(t *testing.T) {
type TestStruct struct {
StringField string
IntField int
FloatField float32
BoolField bool
}

testStruct := TestStruct{
StringField: "string",
IntField: 2,
FloatField: 3.1415,
BoolField: true,
}
testStructBytes, err := json.Marshal(testStruct)
require.NoError(t, err)

b := &TestStruct{}
err = StructFromJSONLimited(bytes.NewReader(testStructBytes), 1000, b)
require.NoError(t, err)

require.Equal(t, b.StringField, "string")
require.Equal(t, b.IntField, 2)
require.Equal(t, b.FloatField, float32(3.1415))
require.Equal(t, b.BoolField, true)
})

t.Run("error too big", func(t *testing.T) {
type TestStruct struct {
StringField string
IntField int
FloatField float32
BoolField bool
}

testStruct := TestStruct{
StringField: "string",
IntField: 2,
FloatField: 3.1415,
BoolField: true,
}
testStructBytes, err := json.Marshal(testStruct)
require.NoError(t, err)

b := &TestStruct{}
err = StructFromJSONLimited(bytes.NewReader(testStructBytes), 10, b)
require.Error(t, err)
require.ErrorIs(t, err, io.ErrUnexpectedEOF)
})

t.Run("successfully parses nested struct", func(t *testing.T) {
type TestStruct struct {
StringField string
IntField int
FloatField float32
BoolField bool
}

type NestedStruct struct {
FieldA TestStruct
FieldB TestStruct
FieldC []int
}

testStructA := TestStruct{
StringField: "string A",
IntField: 2,
FloatField: 3.1415,
BoolField: true,
}

testStructB := TestStruct{
StringField: "string B",
IntField: 3,
FloatField: 100,
BoolField: false,
}

nestedStruct := NestedStruct{
FieldA: testStructA,
FieldB: testStructB,
FieldC: []int{5, 9, 1, 5, 7},
}

nestedStructBytes, err := json.Marshal(nestedStruct)
require.NoError(t, err)

b := &NestedStruct{}
err = StructFromJSONLimited(bytes.NewReader(nestedStructBytes), 1000, b)
require.NoError(t, err)

require.Equal(t, b.FieldA.StringField, "string A")
require.Equal(t, b.FieldA.IntField, 2)
require.Equal(t, b.FieldA.FloatField, float32(3.1415))
require.Equal(t, b.FieldA.BoolField, true)

require.Equal(t, b.FieldB.StringField, "string B")
require.Equal(t, b.FieldB.IntField, 3)
require.Equal(t, b.FieldB.FloatField, float32(100))
require.Equal(t, b.FieldB.BoolField, false)

require.Equal(t, b.FieldC, []int{5, 9, 1, 5, 7})
})

t.Run("errors on too big nested struct", func(t *testing.T) {
type TestStruct struct {
StringField string
IntField int
FloatField float32
BoolField bool
}

type NestedStruct struct {
FieldA TestStruct
FieldB TestStruct
FieldC []int
}

testStructA := TestStruct{
StringField: "string A",
IntField: 2,
FloatField: 3.1415,
BoolField: true,
}

testStructB := TestStruct{
StringField: "string B",
IntField: 3,
FloatField: 100,
BoolField: false,
}

nestedStruct := NestedStruct{
FieldA: testStructA,
FieldB: testStructB,
FieldC: []int{5, 9, 1, 5, 7},
}

nestedStructBytes, err := json.Marshal(nestedStruct)
require.NoError(t, err)

b := &NestedStruct{}
err = StructFromJSONLimited(bytes.NewReader(nestedStructBytes), 50, b)
require.Error(t, err)
require.ErrorIs(t, err, io.ErrUnexpectedEOF)
})

t.Run("handles empty structs", func(t *testing.T) {
type TestStruct struct{}

testStruct := TestStruct{}
testStructBytes, err := json.Marshal(testStruct)
require.NoError(t, err)

b := &TestStruct{}
err = StructFromJSONLimited(bytes.NewReader(testStructBytes), 1000, b)
require.NoError(t, err)
})
}

0 comments on commit 62064e3

Please sign in to comment.