@@ -31,6 +31,7 @@ function makeSlackCtx(allowFrom: string[]): SlackMonitorContext {
3131function makeAuthorizeCtx ( params ?: {
3232 allowFrom ?: string [ ] ;
3333 channelsConfig ?: Record < string , { users ?: string [ ] } > ;
34+ dmPolicy ?: SlackMonitorContext [ "dmPolicy" ] ;
3435 resolveUserName ?: ( userId : string ) => Promise < { name ?: string } > ;
3536 resolveChannelName ?: (
3637 channelId : string ,
@@ -39,7 +40,7 @@ function makeAuthorizeCtx(params?: {
3940 return {
4041 allowFrom : params ?. allowFrom ?? [ ] ,
4142 accountId : "main" ,
42- dmPolicy : "open" ,
43+ dmPolicy : params ?. dmPolicy ?? "open" ,
4344 dmEnabled : true ,
4445 allowNameMatching : false ,
4546 channelsConfig : params ?. channelsConfig ?? { } ,
@@ -119,6 +120,7 @@ describe("authorizeSlackSystemEventSender", () => {
119120 } ) ;
120121
121122 beforeEach ( ( ) => {
123+ readChannelIngressStoreAllowFromForDmPolicyMock . mockReset ( ) ;
122124 clearSlackAllowFromCacheForTest ( ) ;
123125 delete process . env . OPENCLAW_SLACK_CHANNEL_MEMBERS_CACHE_TTL_MS ;
124126 } ) ;
@@ -286,6 +288,44 @@ describe("authorizeSlackSystemEventSender", () => {
286288 } ) ;
287289 } ) ;
288290
291+ it ( "does not use pairing-store allowFrom for MPIM system events" , async ( ) => {
292+ readChannelIngressStoreAllowFromForDmPolicyMock . mockResolvedValue ( [ "U_ATTACKER" ] ) ;
293+ const result = await authorizeSlackSystemEventSender ( {
294+ ctx : makeAuthorizeCtx ( {
295+ allowFrom : [ ] ,
296+ dmPolicy : "pairing" ,
297+ resolveChannelName : async ( ) => ( { name : "group-dm" , type : "mpim" } ) ,
298+ } ) ,
299+ senderId : "U_ATTACKER" ,
300+ channelId : "G_MPIM" ,
301+ } ) ;
302+
303+ expect ( result ) . toEqual ( {
304+ allowed : false ,
305+ reason : "sender-not-allowlisted" ,
306+ channelType : "mpim" ,
307+ channelName : "group-dm" ,
308+ } ) ;
309+ expect ( readChannelIngressStoreAllowFromForDmPolicyMock ) . not . toHaveBeenCalled ( ) ;
310+ } ) ;
311+
312+ it ( "allows MPIM system-event senders in the configured allowFrom" , async ( ) => {
313+ const result = await authorizeSlackSystemEventSender ( {
314+ ctx : makeAuthorizeCtx ( {
315+ allowFrom : [ "U_OWNER" ] ,
316+ resolveChannelName : async ( ) => ( { name : "group-dm" , type : "mpim" } ) ,
317+ } ) ,
318+ senderId : "U_OWNER" ,
319+ channelId : "G_MPIM" ,
320+ } ) ;
321+
322+ expect ( result ) . toEqual ( {
323+ allowed : true ,
324+ channelType : "mpim" ,
325+ channelName : "group-dm" ,
326+ } ) ;
327+ } ) ;
328+
289329 it ( "keeps channel users as the non-interactive gate even when global allowFrom is configured" , async ( ) => {
290330 const result = await authorizeSlackSystemEventSender ( {
291331 ctx : makeAuthorizeCtx ( {
@@ -479,6 +519,36 @@ describe("resolveSlackCommandIngress", () => {
479519 expect ( result . commandAccess . authorized ) . toBe ( false ) ;
480520 expect ( result . commandAccess . shouldBlockControlCommand ) . toBe ( false ) ;
481521 } ) ;
522+
523+ it ( "blocks MPIM senders outside the configured allowFrom" , async ( ) => {
524+ const result = await resolveSlackCommandIngress ( {
525+ ctx : makeAuthorizeCtx ( ) ,
526+ senderId : "U_ATTACKER" ,
527+ channelType : "mpim" ,
528+ channelId : "G_MPIM" ,
529+ ownerAllowFromLower : [ "u_owner" ] ,
530+ allowTextCommands : false ,
531+ hasControlCommand : false ,
532+ } ) ;
533+
534+ expect ( result . senderAccess . decision ) . toBe ( "block" ) ;
535+ expect ( result . senderAccess . gate ?. allowed ) . toBe ( false ) ;
536+ } ) ;
537+
538+ it ( "allows MPIM senders in the configured allowFrom" , async ( ) => {
539+ const result = await resolveSlackCommandIngress ( {
540+ ctx : makeAuthorizeCtx ( ) ,
541+ senderId : "U_OWNER" ,
542+ channelType : "mpim" ,
543+ channelId : "G_MPIM" ,
544+ ownerAllowFromLower : [ "u_owner" ] ,
545+ allowTextCommands : false ,
546+ hasControlCommand : false ,
547+ } ) ;
548+
549+ expect ( result . senderAccess . decision ) . toBe ( "allow" ) ;
550+ expect ( result . senderAccess . gate ?. allowed ) . toBe ( true ) ;
551+ } ) ;
482552} ) ;
483553
484554describe ( "authorizeSlackSystemEventSender interactiveEvent" , ( ) => {
0 commit comments