Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions conversations/web_search_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ func (c *Conversations) unmarshalWebSearchContext(webSearchContextJSON string, p
return nil
}

// Unmarshaling the literal JSON "null" succeeds and leaves params nil, which
// would make the unconditional writes below panic ("assignment to entry in
// nil map"). A user can set this prop to any value, so guard against it.
if params == nil {
params = make(map[string]interface{})
}

// Reconstruct proper types for web search context values
if raw, ok := params[mmtools.WebSearchContextKey]; ok {
// Re-marshal and unmarshal to get proper types
Expand Down
75 changes: 75 additions & 0 deletions conversations/web_search_context_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright (c) 2023-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

package conversations

import (
"testing"

"github.com/mattermost/mattermost-plugin-agents/v2/mmapi/mocks"
"github.com/mattermost/mattermost-plugin-agents/v2/mmtools"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)

func TestUnmarshalWebSearchContext(t *testing.T) {
cases := []struct {
name string
json string
wantNil bool
wantResetKeys bool
}{
{
// A user (or buggy client) can set the web_search_context post prop
// to the literal JSON "null". json.Unmarshal succeeds and leaves the
// target map nil, so the unconditional writes that follow used to
// panic with "assignment to entry in nil map" and crash the plugin.
name: "literal null does not panic",
json: "null",
wantResetKeys: true,
},
{
name: "empty object resets tracking",
json: "{}",
wantResetKeys: true,
},
{
name: "valid context is parsed and tracking reset",
json: `{"mm_web_search_allowed_urls":["https://example.com"]}`,
wantResetKeys: true,
},
{
name: "invalid json returns nil",
json: "{not-json",
wantNil: true,
},
{
name: "non-object json returns nil",
json: "123",
wantNil: true,
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
mmClient := mocks.NewMockClient(t)
mmClient.EXPECT().LogDebug(mock.Anything, mock.Anything).Maybe()
mmClient.EXPECT().LogError(mock.Anything, mock.Anything).Maybe()

c := &Conversations{mmClient: mmClient}

params := c.unmarshalWebSearchContext(tc.json, "post-id")

if tc.wantNil {
require.Nil(t, params)
return
}

require.NotNil(t, params)
if tc.wantResetKeys {
require.Equal(t, 0, params[mmtools.WebSearchCountKey])
require.Equal(t, []string{}, params[mmtools.WebSearchExecutedQueriesKey])
}
})
}
}
Loading