From db029c51d215c59e82b5a860edaa1cc53f4809d7 Mon Sep 17 00:00:00 2001 From: Paul Lizer Date: Tue, 6 Oct 2026 11:30:26 -0400 Subject: [PATCH] Validate group collaboration invitees before reading conversation history Group conversation conversion now checks every invitee against current group membership before preparing Microsoft 365 history publication or reading the transcript. The group document collaboration fixture exports the current workflow alert safety constants. Refs #1651. --- application/single_app/config.py | 2 +- .../single_app/functions_collaboration.py | 5 ++ ...LABORATION_INVITEE_VALIDATION_ORDER_FIX.md | 66 +++++++++++++++++++ docs/explanation/release_notes.md | 9 +++ ..._group_collaboration_source_storage_fix.py | 5 +- .../test_group_document_collaboration.py | 12 +++- 6 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 docs/explanation/fixes/GROUP_COLLABORATION_INVITEE_VALIDATION_ORDER_FIX.md diff --git a/application/single_app/config.py b/application/single_app/config.py index 6cc00241d..7778bc66a 100644 --- a/application/single_app/config.py +++ b/application/single_app/config.py @@ -101,7 +101,7 @@ EXECUTOR_TYPE = 'thread' EXECUTOR_MAX_WORKERS = 30 SESSION_TYPE = 'filesystem' -VERSION = "0.261.256" +VERSION = "0.261.258" IS_DEVELOPMENT = is_development_env_enabled() # Opt-out for deployments where App Service Easy Auth is active but the platform diff --git a/application/single_app/functions_collaboration.py b/application/single_app/functions_collaboration.py index ae39bdca1..f1f244ed8 100644 --- a/application/single_app/functions_collaboration.py +++ b/application/single_app/functions_collaboration.py @@ -1256,6 +1256,11 @@ def ensure_group_collaboration_for_legacy_conversation(source_conversation_id, o if not allowed: raise PermissionError(reason) + invited_participants = ( + _normalize_group_conversation_participants(group_doc, invited_participants) + if invited_participants else [] + ) + collaboration_conversation_doc = None linked_collaboration_id = str(source_conversation_doc.get('collaboration_conversation_id') or '').strip() if linked_collaboration_id: diff --git a/docs/explanation/fixes/GROUP_COLLABORATION_INVITEE_VALIDATION_ORDER_FIX.md b/docs/explanation/fixes/GROUP_COLLABORATION_INVITEE_VALIDATION_ORDER_FIX.md new file mode 100644 index 000000000..3e80cdce4 --- /dev/null +++ b/docs/explanation/fixes/GROUP_COLLABORATION_INVITEE_VALIDATION_ORDER_FIX.md @@ -0,0 +1,66 @@ +# Group Collaboration Invitee Validation Order Fix (v0.261.258) + +## Overview + +Group conversation conversion now proves that every requested invitee is a current member of the source group before reading the conversation's message history. This keeps authorization checks ahead of dependent data access while preserving the existing conversion and invitation behavior. + +Fixed in version: **0.261.258** + +Related issue: [#1651](https://github.com/microsoft/simplechat/issues/1651) + +Dependencies: group membership normalization, group role authorization, and Microsoft 365 history publication. + +The application version was updated in `application/single_app/config.py` from `0.261.256` to `0.261.258`. Version `0.261.257` was already claimed by an open change against the React V2 branch. + +## Issue Description + +An owner converting an eligible legacy group conversation could include an identity that was not a current member of the source group. The request was rejected and no records were changed, but Microsoft 365 publication preparation queried the source message history before participant normalization raised the rejection. + +The group document collaboration suite also replaced the workflow-alert safety module with a test stub that no longer matched the production module's exported constants. That mismatch stopped the suite during fixture setup and hid its sharing assertions. + +## Root Cause + +`ensure_group_collaboration_for_legacy_conversation` passed the raw invitee list into history publication before `create_group_collaboration_conversation_record` normalized those invitees against the current group document. The correct validation existed, but it ran after a dependent transcript read. + +Separately, `functions_notifications` gained an import path through `functions_workflow_alerts`, whose safety contract requires three public diagnostic constants. The isolated test stub exported only the sanitizer function. + +## Technical Details + +### Files Modified + +- `application/single_app/functions_collaboration.py` +- `application/single_app/config.py` +- `functional_tests/test_group_collaboration_source_storage_fix.py` +- `functional_tests/test_group_document_collaboration.py` +- `docs/explanation/release_notes.md` + +### Code Changes + +The conversion path now normalizes requested invitees immediately after it verifies the owner, source conversation, group, current owner role, and active group status. Only the validated participant summaries can reach an existing collaborative conversation, Microsoft 365 publication preparation, conversation record creation, or transcript copying. + +The document collaboration fixture now provides the exact workflow-alert diagnostic constants exported by production while retaining its assertion that document sharing must not enter workflow-alert rendering. + +No route, response status, or request payload contract changed. + +## Testing And Validation + +The focused regression command exercises both invalid-invitee storage modes, the V1 route contract, and the document-sharing fixture import path. It completed with 3 tests and 38 subtests passing. + +The complete group source-storage suite completed with 20 tests and 106 subtests passing. The targeted document-sharing fixture case passed, editor diagnostics reported no errors, and the broken-access-control guardrail passed for the changed production module. Documentation validation completed with 7 of 7 application-surface checks and 6 of 6 site-quality checks passing. + +## Impact Analysis + +### Before + +- An out-of-group invitee was rejected without mutation, but only after the source transcript was queried. +- The document collaboration suite stopped during fixture setup because its safety stub was incomplete. + +### After + +- Every invitee is authorized against current group membership before transcript or publication evidence access. +- Rejected conversion requests do not read either possible source message container. +- The document collaboration suite reaches its route, policy, Search, notification, and storage assertions. + +## Known Limitations + +The fix does not change who may convert a conversation, which groups may be used, or which participant roles are assigned. It only moves the existing invitee normalization to the earliest point after the source group and caller have been authorized. \ No newline at end of file diff --git a/docs/explanation/release_notes.md b/docs/explanation/release_notes.md index 1d018486b..ce8cd7bdc 100644 --- a/docs/explanation/release_notes.md +++ b/docs/explanation/release_notes.md @@ -2,6 +2,15 @@ For feature-focused and fix-focused drill-downs by version, see [Features by Version](https://github.com/microsoft/simplechat/tree/main/docs/explanation/features) and [Fixes by Version](https://github.com/microsoft/simplechat/tree/main/docs/explanation/fixes). +### **(v0.261.258)** + +#### Bug Fixes + +* **Rejected Group Conversation Invites Stop Before History Reads** + * Converting an existing group conversation now verifies every invited participant against the group's current membership before preparing Microsoft 365 publication evidence or reading the conversation transcript. An identity outside the group is still rejected without changing any records, but the rejection now happens before message history is queried. + * The document collaboration regression fixture now exposes the current workflow-alert safety contract, so its sharing and repair checks reach their assertions instead of stopping during module import. + * (Ref: #1651, `ensure_group_collaboration_for_legacy_conversation`, `test_group_collaboration_source_storage_fix.py`, `test_group_document_collaboration.py`, [Group Collaboration Invitee Validation Order Fix](fixes/GROUP_COLLABORATION_INVITEE_VALIDATION_ORDER_FIX.md)) + ### **(v0.261.256)** #### Bug Fixes diff --git a/functional_tests/test_group_collaboration_source_storage_fix.py b/functional_tests/test_group_collaboration_source_storage_fix.py index 8b2872664..a8d85952c 100644 --- a/functional_tests/test_group_collaboration_source_storage_fix.py +++ b/functional_tests/test_group_collaboration_source_storage_fix.py @@ -2,10 +2,11 @@ #!/usr/bin/env python3 """ Functional regression for group conversation source storage. -Version: 0.261.214 +Version: 0.261.258 Implemented in: 0.261.024 Ported to the React branch in: 0.261.106 -Related issue: microsoft/simplechat#1472 +Invitee validation ordering fixed in: 0.261.258 +Related issues: microsoft/simplechat#1472, microsoft/simplechat#1651 Exercise production conversion and route helpers against isolated Cosmos stores. Group context must not imply group-container storage or weaken authorization. diff --git a/functional_tests/test_group_document_collaboration.py b/functional_tests/test_group_document_collaboration.py index b432468c0..bac5cd3a9 100644 --- a/functional_tests/test_group_document_collaboration.py +++ b/functional_tests/test_group_document_collaboration.py @@ -1,8 +1,9 @@ # test_group_document_collaboration.py """ Functional tests for immutable group document sharing and repair. -Version: 0.261.131 +Version: 0.261.258 Implemented in: 0.261.131 +Workflow alert safety fixture updated in: 0.261.258 Real Flask routes, policy, conditional source writes, Search write guards, access index projections and notification persistence run against local service seams. @@ -179,7 +180,14 @@ def directory_query(query, parameters=None, **kwargs): get_user_public_workspaces=Mock(), )) patch.setitem(sys.modules, "functions_workflow_alert_safety", module_stub( - "functions_workflow_alert_safety", sanitize_workflow_alert_record=Mock( + "functions_workflow_alert_safety", + WORKFLOW_ALERT_EVALUATION_ERROR_CODE="workflow_alert_evaluation_failed", + WORKFLOW_ALERT_EVALUATION_ERROR_MESSAGE=( + "Alert conditions could not be evaluated. " + "Review the workflow model configuration or contact an administrator." + ), + WORKFLOW_ALERT_EVALUATOR_UNAVAILABLE_MESSAGE="No model evaluator was available for this run.", + sanitize_workflow_alert_record=Mock( side_effect=AssertionError("Sharing must not enter workflow alert rendering."), ), ))