Add OnBeforeFilterRemovedSourceRecords integration event in Email Impl (#8635)#8771
Conversation
microsoft#8635) Adds an OnBefore integration event to the FilterRemovedSourceRecords procedure in codeunit 8900 "Email Impl", published via codeunit 8901 Email. This lets partners override the default filtering (which relies on RecordRef.ReadPermission against an unfiltered RecordRef) so they can, for example, apply SecurityFiltering::Ignored before the permission check. Without this, users with a security filter on a source table get 'Did not find any attachments related to this email' when adding files from the source document. Fixes microsoft#8635 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| /// <param name="EmailRelatedRecord">The related source records to filter. Subscribers can apply their own filtering, for example to ignore security filtering when determining read permission.</param> | ||
| /// <param name="IsHandled">Out parameter to set if the event was handled. If set to true, the default filtering is skipped.</param> | ||
| [IntegrationEvent(false, false)] | ||
| internal procedure OnBeforeFilterRemovedSourceRecords(var EmailRelatedRecord: Record "Email Related Record"; var IsHandled: Boolean) |
There was a problem hiding this comment.
OnBeforeFilterRemovedSourceRecords declares 'var IsHandled: Boolean' and the caller does 'if IsHandled then exit', allowing any subscriber in the same app to skip the entire security-filtering loop.
The XML documentation makes the bypass intent explicit: "Subscribers can apply their own filtering, for example to ignore security filtering when determining read permission." This matches the article's anti-pattern exactly: a var Boolean guard parameter on an IntegrationEvent whose name signals a security decision, followed by an unconditional exit that drops the publisher's check. The recommended fix is to restructure so subscribers can only tighten the filter (e.g., an OnAfterFilterRemovedSourceRecords event fired after the default filtering, where subscribers can add additional SetRange calls or call Error to deny access), rather than providing a hook that bypasses the default security filter entirely.
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why
Copilot PR ReviewIteration 1 · Outcome: completed Knowledge source: https://github.com/microsoft/BCQuality@822cae1b2771ac25f665f73369f69093bd4fd630 Findings by domainFindings split into Knowledge-backed (cite a BCQuality article) and Agent (the agent's own judgement, no matching BCQuality rule).
Totals: 1 knowledge-backed · 0 agent findings. Orchestrator pre-filter (13 file(s) excluded)
Findings produced by the Copilot CLI agent against BCQuality at |
What & why
Adds an
OnBeforeFilterRemovedSourceRecordsintegration event to theFilterRemovedSourceRecordsprocedure in codeunit 8900"Email Impl", published via the public facade codeunit 8901Email.FilterRemovedSourceRecordsopens aRecordRefon each related source table with no filter applied and callsRecordRef.ReadPermission()to decide whether the related record is shown. As documented,ReadPermission()on an unfilteredRecordReftests full, unrestricted read permission on the entire table. When a user has a security filter on the source table (e.g.Purchase Header), that check returnsfalseeven though the user can read the specific record, so the "Add file from source document" action reports "Did not find any attachments related to this email".This new
OnBefore/IsHandledevent lets partners override the default filtering — for example applyingSourceRecordRef.SecurityFiltering(SecurityFilter::Ignored)before the permission check — without forking the System Application.Linked work
Fixes #8635
How I validated this
What I tested and the outcome
[IntegrationEvent(false, false)]publisher on codeunitEmail, raised at the start ofFilterRemovedSourceRecordswith anIsHandledguard. It mirrors the exact, already-shipping pattern used by the other events in the same#region Eventsof codeunitEmail(e.g.OnShowSource), which are subscribed to elsewhere viaCodeunit::Email.IsHandled— the original filtering loop runs unchanged.FilterRemovedSourceRecordscoverage remains valid.Risk & compatibility
Low. Additive integration event only; no signature changes to existing procedures or events, no data/upgrade impact. Default code path is unchanged when the event is not handled.