Skip to content

[pull] master from mattermost:master#715

Merged
pull[bot] merged 3 commits into
code:masterfrom
mattermost:master
May 19, 2026
Merged

[pull] master from mattermost:master#715
pull[bot] merged 3 commits into
code:masterfrom
mattermost:master

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented May 19, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

wiggin77 and others added 3 commits May 19, 2026 10:12
…36592)

* MM-68838: ping restored plugin remote immediately on re-register

  RegisterPluginForSharedChannels' restore branch updated the row but did
  not call PingNow, leaving the restored remote offline until the next
  pingLoop tick (up to PingFreq, default 1 minute). The new-connection
  branch already calls PingNow; the restore branch now mirrors it so
  sync attempts immediately after a plugin restart no longer fail with
  "offline remote cluster".

* MM-68838: gob-encode error returns in apiRPCServer.ReceiveSharedChannelAttachmentSyncMsg

  The apiRPCServer wrapper for ReceiveSharedChannelAttachmentSyncMsg
  assigned the hook's error return directly to the gob-encoded response
  struct. When the framework's App.ReceiveSharedChannelAttachmentSyncMsg
  returned an error wrapped with %w (*fmt.wrapError, an unexported type),
  gob refused to encode it and the RPC server broke the connection with
  "type not registered for interface: fmt.wrapError".

  Every subsequent plugin/server RPC call then returned the zero-value
  response struct, causing plugins that dereferenced the nil returns to
  crash.

  Apply the existing encodableError() helper so the returned error
  becomes a gob-safe ErrorString, matching every other apiRPCServer
  method in this file.
Authenticate DinD pulls at runtime using Cursor dashboard secrets so agents avoid anonymous Docker Hub rate limits.

Co-authored-by: Cursor <cursoragent@cursor.com>
* MM-68501 - implement GetMaskedVisualAST and wire API handler

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* add missing test and fix style issues

* fix styles

* implement coderabbit feedback

* MM-68501 - PR review: split masking file, model-level access mode, reject contradictory config

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68501 - apply shared_only filter to non-option field values (binary masking)

* MM-68501 - consolidate masking flag check and log corrupt text value during masking

* MM-68503 - add CEL utilities, write-path validation, and merge helpers

Combined set of helpers consumed by BE-5's save path:

CEL construction / serialization
  - extractStringValues, buildCELFromConditions, conditionToCEL,
    celStringLiteral, celValueLiteral. Used to rebuild a CEL string from a
    VisualExpression, including for GetMaskedExpression on the read-side
    of policy GET / search responses.

Merge-on-save helpers
  - getHiddenValues (per-condition, with pre-fetched fields map for N+1
    avoidance) — finds which stored values are not visible to the caller.
  - mergeConditionValues — re-injects the hidden values into a submitted
    condition without duplicates.
  - Together, these let BE-5 preserve attribute values the caller cannot
    see while still letting them edit the visible parts of a policy.

Write-path value-hold validation
  - validatePolicyExpressionValues, invalidValueError, validateConditionValues.
  - Generic "Invalid value." error on every rejection — no signal about
    whether the value exists or is merely not held (prevents enumeration).
  - Rejects the masked-token sentinel "--------" if submitted as a literal.

These all live in access_control_masking.go alongside the masking primitives
that BE-2 introduced. i18n entries added for the two new error IDs
(app.pap.save_policy.invalid_value, app.pap.validate_expression_values.app_error).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68503 - handle the masked-token sentinel in validation and merge

When the GET /policies endpoint returns a policy via MaskPolicyExpressions,
the raw expression contains the masked-token sentinel "--------" in place
of hidden values. If the frontend round-trips that expression unchanged
back to the server (e.g., the admin only modified channel assignment, not
the rules), the sentinel reaches the save path.

The previous code in validateConditionValues rejected the sentinel as
"Invalid value." This blocks the legitimate round-trip case.

Fix:
  - validateConditionValues: treat the sentinel as a placeholder and skip
    it during visibility / source-only / unknown-mode checks. Other values
    are still validated normally.
  - mergeConditionValues: strip the sentinel from submitted values before
    appending hidden values, so it never propagates to the stored result.
    Both array and single-value forms (string == "--------") are handled.

TestMaskedTokenRejection (which asserted the old rejection behavior) is
replaced by TestMaskedTokenConstant which only verifies the sentinel
string itself.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68504 - integrate save-path masking: 403 block on delete, merge-on-save, response masking

Save path (CreateOrUpdateAccessControlPolicy):
  * validatePolicyExpressionValues runs on the submitted expression before
    merge so re-injected hidden values are never validated against the
    caller's holdings.
  * mergeStoredPolicyExpressions re-injects hidden values from the stored
    policy and blocks (HTTP 403) any attempt to remove a condition that
    contained values the caller cannot see — closes the row-deletion gap
    in classified environments.
  * mergeExpressionWithMaskedValues unwraps single-element arrays for scalar
    operators after restoring the stored operator (avoids "attr == [val]"
    invalid CEL when the frontend submits "attr in []" as the masked-row
    placeholder for an originally-scalar condition).
  * checkSelfInclusion is bypassed for system admins (they may legitimately
    write conditions for values they do not hold); masking and value-hold
    validation still apply to system admins.

Delete path (DeleteAccessControlPolicy):
  * Same masked-values 403 block — a caller with masked values cannot delete
    the policy at all (UI Delete button is also disabled in FE-3).

Response masking:
  * createAccessControlPolicy and setAccessControlPolicyActiveStatus run
    MaskPolicyExpressions on the response so even a save reply doesn't
    leak the values the caller does not hold.

GetMaskedExpression, maskConditionValuesWithToken, replaceHiddenValuesWithToken,
MaskPolicyExpressions live alongside the rest of the masking helpers in
access_control_masking.go.

team_access_control.go: corrects ValidateChannelEligibilityForAccessControl
call site (drops the spurious receiver and rctx; it's a package-level helper
that only takes channel).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68503 - address PR review: batch field fetches, propagate errors, fail-closed write path

* MM-68503 - restore team-admin api4 tests accidentally dropped during BE-5 rebuild

* MM-68503 - address review and CodeRabbit feedback on save-path masking

* add tests for delete masking, self-inclusion, GET mask

* add assertions to strengten tests

* MM-68505 - add has_masked_values type and MaskedChip component

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* MM-68506 - add masking support to TableEditor and team settings modal

TableEditor (table_editor.tsx, table_editor.scss):
  - hasMaskedValues plumbed through rows; lock operator/attribute selectors on
    masked rows.
  - Row remove (trash) button disabled on masked rows; disabled-state CSS so
    the icon doesn't show the destructive hover colour or a pointer cursor.
  - Test Rules button disabled when any row has masked values, with tooltip.
  - onMaskedStateChange callback to notify the parent for cross-component
    states (CEL editor read-only, Save disabled, banners).

Value selectors (single_value_selector_menu.tsx, multi_value_selector_menu.tsx,
selector_menus.scss, value_selector_menu.tsx):
  - Append MaskedChip after visible chips on multi-value rows.
  - Render MaskedChip as the sole value on single-value rows where the caller
    holds no visible value.

Policy details (policy_details.tsx, .scss, .test.tsx):
  - Track hasMaskedRows state; receive from TableEditor via onMaskedStateChange.
  - Show masked-values warning banner above the editor when present.
  - Same banner on the Delete confirmation modal so admins understand why
    deletion is consequential.

Team settings modal (team_policy_editor.tsx, .scss):
  - Same masked-values plumbing; delete button uses the disabled state when
    a policy has masked values, regardless of whether channels are assigned.
  - Pre-save check no longer treats "in []" as an incomplete rule — that
    placeholder comes from fully-masked rows that merge-on-save will fill in.

i18n entries added for the new strings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68506 - fix hook order in SingleValueSelector when masked state changes

The early return for `hasMaskedValues && !value` sat between useState
and useCallback declarations, so when a parent re-render flipped the
masked state (e.g. after deleting a sibling rule) React saw a different
hook count and crashed with "Rendered fewer hooks than expected".

Move the read-only short-circuit after all hook declarations so the hook
order stays stable across renders.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68507 - CEL editor read-only when masked + system console wiring

CEL editor (editor.tsx, editor.scss):
  - hasMaskedRows prop: when true, Monaco is set to read-only and a banner
    explains why ("This expression contains restricted values. Switch to
    Simple mode to edit the values you have access to, or delete the entire
    rule.").
  - Test Rules button disabled in CEL mode when hasMaskedRows is true.

Policy details (policy_details.tsx, .scss):
  - hasMaskedRows state plumbed to CELEditor, TableEditor, and the Save /
    Delete buttons.
  - Save button disabled while masked rows are present (kept after the
    save-allowed-with-masked-values change in BE-5? — no, here we keep Save
    enabled so admins can add/modify rules; only row removal of masked rows
    is blocked).
  - Delete Policy button disabled when hasMaskedRows; a SectionNotice above
    the Delete card explains why ("This policy contains restricted values
    - Deletion not allowed").
  - New save error messages: invalid_value and self_exclusion are surfaced
    from the server's generic responses.

Policies list (policies.tsx): minor wiring change for the new state plumbing.

Table editor (table_editor.tsx): cross-component coordination — emits
onMaskedStateChange and respects the disabled-for-masked-row policy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68508 - E2E suite for attribute-value masking

Covers the full read+write masking flow against a real server:
  - Masked chip rendering, operator/attribute lock, Test Rules disabled.
  - System admin subject to masking like any other caller (no role bypass).
  - Save with masked values: hidden values preserved by merge-on-save.
  - Trash button disabled on masked rows; server returns 403 on direct
    API attempt to remove a masked condition.
  - Delete Policy button disabled + server 403 when policy has masked values
    (both system console and team settings modal paths).
  - Self-inclusion failure only fires when the caller holds full visibility.
  - CEL editor read-only with banner when masked rows present.
  - Direct API validation: non-held values and the masked-token sentinel
    rejected with a generic "Invalid value." error.
  - Feature-flag-off path: no masking, all values visible.
  - Text-field shared_only masking (binary) with `in` and `==` operators.

A pluggable DB-setup helper marks specific CPA fields as shared_only for
the duration of a test (with per-test cleanup) since the API blocks setting
access_mode=shared_only without a source_plugin_id.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68506 - fix lint, jest mock factory, and unreachable delete-modal test

* MM-68506 - localize masked-condition-deleted save error

* MM-68506 - fix masked-policy delete warning detection and localize masked_rule_deleted

* fix linter issues

* MM-68506 - surface delete error, lock value selector on masked rows, drop dead remove-modal

* fix linter, add translations, adjust specs

* import wittoltip from shared

* fix linter and use the correct button variant

* MM-68506 - drop dangling rationale comment in access_control_field_test

* fix linter, translation and e2e tests

* use pg ts types and dependencies for e2e types mocks

* adjust switch mode persistance restriction

* fix team settings style buttons

* fail-closed guard for advanced expressions in  merge-on-save, plus  helper unit tests, and FF/test-helper cleanups

* MM-68505 - add has_masked_values type and MaskedChip component

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* MM-68506 - add masking support to TableEditor and team settings modal

TableEditor (table_editor.tsx, table_editor.scss):
  - hasMaskedValues plumbed through rows; lock operator/attribute selectors on
    masked rows.
  - Row remove (trash) button disabled on masked rows; disabled-state CSS so
    the icon doesn't show the destructive hover colour or a pointer cursor.
  - Test Rules button disabled when any row has masked values, with tooltip.
  - onMaskedStateChange callback to notify the parent for cross-component
    states (CEL editor read-only, Save disabled, banners).

Value selectors (single_value_selector_menu.tsx, multi_value_selector_menu.tsx,
selector_menus.scss, value_selector_menu.tsx):
  - Append MaskedChip after visible chips on multi-value rows.
  - Render MaskedChip as the sole value on single-value rows where the caller
    holds no visible value.

Policy details (policy_details.tsx, .scss, .test.tsx):
  - Track hasMaskedRows state; receive from TableEditor via onMaskedStateChange.
  - Show masked-values warning banner above the editor when present.
  - Same banner on the Delete confirmation modal so admins understand why
    deletion is consequential.

Team settings modal (team_policy_editor.tsx, .scss):
  - Same masked-values plumbing; delete button uses the disabled state when
    a policy has masked values, regardless of whether channels are assigned.
  - Pre-save check no longer treats "in []" as an incomplete rule — that
    placeholder comes from fully-masked rows that merge-on-save will fill in.

i18n entries added for the new strings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68506 - fix hook order in SingleValueSelector when masked state changes

The early return for `hasMaskedValues && !value` sat between useState
and useCallback declarations, so when a parent re-render flipped the
masked state (e.g. after deleting a sibling rule) React saw a different
hook count and crashed with "Rendered fewer hooks than expected".

Move the read-only short-circuit after all hook declarations so the hook
order stays stable across renders.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68507 - CEL editor read-only when masked + system console wiring

CEL editor (editor.tsx, editor.scss):
  - hasMaskedRows prop: when true, Monaco is set to read-only and a banner
    explains why ("This expression contains restricted values. Switch to
    Simple mode to edit the values you have access to, or delete the entire
    rule.").
  - Test Rules button disabled in CEL mode when hasMaskedRows is true.

Policy details (policy_details.tsx, .scss):
  - hasMaskedRows state plumbed to CELEditor, TableEditor, and the Save /
    Delete buttons.
  - Save button disabled while masked rows are present (kept after the
    save-allowed-with-masked-values change in BE-5? — no, here we keep Save
    enabled so admins can add/modify rules; only row removal of masked rows
    is blocked).
  - Delete Policy button disabled when hasMaskedRows; a SectionNotice above
    the Delete card explains why ("This policy contains restricted values
    - Deletion not allowed").
  - New save error messages: invalid_value and self_exclusion are surfaced
    from the server's generic responses.

Policies list (policies.tsx): minor wiring change for the new state plumbing.

Table editor (table_editor.tsx): cross-component coordination — emits
onMaskedStateChange and respects the disabled-for-masked-row policy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68508 - E2E suite for attribute-value masking

Covers the full read+write masking flow against a real server:
  - Masked chip rendering, operator/attribute lock, Test Rules disabled.
  - System admin subject to masking like any other caller (no role bypass).
  - Save with masked values: hidden values preserved by merge-on-save.
  - Trash button disabled on masked rows; server returns 403 on direct
    API attempt to remove a masked condition.
  - Delete Policy button disabled + server 403 when policy has masked values
    (both system console and team settings modal paths).
  - Self-inclusion failure only fires when the caller holds full visibility.
  - CEL editor read-only with banner when masked rows present.
  - Direct API validation: non-held values and the masked-token sentinel
    rejected with a generic "Invalid value." error.
  - Feature-flag-off path: no masking, all values visible.
  - Text-field shared_only masking (binary) with `in` and `==` operators.

A pluggable DB-setup helper marks specific CPA fields as shared_only for
the duration of a test (with per-test cleanup) since the API blocks setting
access_mode=shared_only without a source_plugin_id.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68506 - fix lint, jest mock factory, and unreachable delete-modal test

* MM-68506 - localize masked-condition-deleted save error

* MM-68506 - fix masked-policy delete warning detection and localize masked_rule_deleted

* fix linter issues

* MM-68506 - surface delete error, lock value selector on masked rows, drop dead remove-modal

* fix linter, add translations, adjust specs

* import wittoltip from shared

* fix linter and use the correct button variant

* MM-68506 - drop dangling rationale comment in access_control_field_test

* fix linter, translation and e2e tests

* use pg ts types and dependencies for e2e types mocks

* adjust switch mode persistance restriction

* fix team settings style buttons

* fail-closed guard for advanced expressions in  merge-on-save, plus  helper unit tests, and FF/test-helper cleanups

* Refactor access control methods to use GetPropertyGroup for CPA group ID retrieval

* fix styles

* disable delete on masked policies in list view and remove dead modal warnings

* fix unit tests

* preserve hasAnyOf operator display for fully-masked multiselect conditions

* address PR feedback: lock Actions on masked save, filter source/shared_only from /attributes, add unit tests and e2e tests

* fix e2e tests

* comment out e2e to isolate issue

* completely remove the files to pass linter

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
@pull pull Bot locked and limited conversation to collaborators May 19, 2026
@pull pull Bot added the ⤵️ pull label May 19, 2026
@pull pull Bot merged commit 345a0b7 into code:master May 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants