Add Microsoft Teams channel#1366
Conversation
PR Summary by QodoAdd Microsoft Teams channel integration plugin Description
Diagram
High-Level Assessment
Files changed (22)
|
Code Review by Qodo
1. ConversationReference stored by reference
|
| public Task SaveAsync(string userId, ConversationReference reference) | ||
| { | ||
| _references[userId] = reference; | ||
| return Task.CompletedTask; | ||
| } | ||
|
|
||
| public Task<ConversationReference?> GetAsync(string userId) | ||
| { | ||
| _references.TryGetValue(userId, out var reference); | ||
| return Task.FromResult(reference); | ||
| } | ||
|
|
||
| public Task<IReadOnlyCollection<ConversationReference>> GetAllAsync() | ||
| => Task.FromResult((IReadOnlyCollection<ConversationReference>)_references.Values.ToList()); |
There was a problem hiding this comment.
1. conversationreference stored by reference 📘 Rule violation ⛨ Security
InMemoryConversationReferenceStore stores and returns mutable ConversationReference instances by reference, allowing callers/other code paths to mutate cached state. This can cause cross-request state leakage and incorrect proactive message routing.
Agent Prompt
## Issue description
`InMemoryConversationReferenceStore` stores the caller-provided `ConversationReference` directly and returns the same instance from `GetAsync`/`GetAllAsync`. Since `ConversationReference` is mutable, downstream mutation can leak state across requests/components.
## Issue Context
The compliance requirement mandates defensive copies when storing caller-provided mutable objects or returning cached/shared mutable objects.
## Fix Focus Areas
- src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/InMemoryConversationReferenceStore.cs[10-23]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| var agentService = _services.GetRequiredService<IAgentService>(); | ||
| var agent = await agentService.GetAgent(agentId); | ||
|
|
||
| var welcomeTemplate = agent?.Templates?.FirstOrDefault(x => x.Name == ".welcome"); | ||
| if (welcomeTemplate == null) | ||
| { |
There was a problem hiding this comment.
2. .welcome compared case-sensitively 📘 Rule violation ≡ Correctness
The code matches template name using x.Name == ".welcome", which is case-sensitive and may break identifier-like matching across locales/inputs. This can cause the welcome flow to be skipped unexpectedly.
Agent Prompt
## Issue description
Template name lookup uses case-sensitive equality (`==`) for an identifier-like string.
## Issue Context
Compliance requires ordinal, case-insensitive comparisons for identifier-like strings to avoid mismatches.
## Fix Focus Areas
- src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsActivityBot.cs[108-113]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| public async Task<IActionResult> NotifyAsync([FromBody] TeamsNotifyRequest request, CancellationToken cancellationToken) | ||
| { | ||
| if (string.IsNullOrEmpty(request.UserId)) | ||
| { | ||
| return BadRequest("userId is required."); | ||
| } |
There was a problem hiding this comment.
3. notifyasync missing null body check 📘 Rule violation ☼ Reliability
NotifyAsync dereferences request.UserId without guarding against a null request body, which can produce a 500 error instead of a predictable 400 response. This violates fail-fast boundary validation expectations.
Agent Prompt
## Issue description
The controller action assumes `[FromBody]` binding always provides a non-null `request`, but ASP.NET Core can pass null for missing/invalid bodies. The code then dereferences `request.UserId`.
## Issue Context
System boundary inputs (request bodies/routes) must be validated for null/empty and return a predictable error (e.g., `BadRequest`) rather than throwing.
## Fix Focus Areas
- src/Plugins/BotSharp.Plugin.MicrosoftTeams/Controllers/TeamsMessageController.cs[50-55]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| private readonly ConcurrentDictionary<string, ConversationReference> _references = new(); | ||
|
|
||
| public Task SaveAsync(string userId, ConversationReference reference) | ||
| { | ||
| _references[userId] = reference; | ||
| return Task.CompletedTask; |
There was a problem hiding this comment.
4. Proactive message misrouting 🐞 Bug ⛨ Security
InMemoryConversationReferenceStore stores only one ConversationReference per userId and overwrites it on every inbound turn, so a user chatting in multiple Teams scopes (personal vs channel/group) will have their last scope win. As a result, /teams/notify can send a proactive message into the wrong conversation (including a public channel) for that userId.
Agent Prompt
### Issue description
`IConversationReferenceStore` currently keys references only by `userId`, and `TeamsActivityBot` overwrites that value every turn. This can cause proactive messages to be delivered to the wrong Teams conversation (e.g., a team/channel thread instead of a 1:1 chat) when the same user interacts with the bot in multiple scopes.
### Issue Context
- `TeamsNotifyRequest.UserId` is an AAD object id.
- `TeamsActivityBot.GetUserId` prefers `Activity.From.AadObjectId`, which is stable across scopes, so all of that user's conversations collapse into a single key.
- Proactive send later does a single lookup by `userId` and uses that reference.
### Fix Focus Areas
- src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/InMemoryConversationReferenceStore.cs[6-23]
- src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsActivityBot.cs[34-43]
- src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsActivityBot.cs[138-142]
- src/Plugins/BotSharp.Plugin.MicrosoftTeams/Models/TeamsNotifyRequest.cs[3-19]
- src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsNotificationService.cs[30-64]
### Implementation direction
- Store conversation references under a composite key (at least `userId + conversationId`, optionally also tenant/team/channel scope).
- Extend `TeamsNotifyRequest` (and controller validation) to accept either `conversationId`/`scope` so callers can choose the correct destination, or implement deterministic selection logic (e.g., prefer personal-scope references and never use channel-scope for user-targeted notifications).
- Update `IConversationReferenceStore` to support saving and retrieving multiple references per user and selecting by scope/conversation.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
No description provided.