Slack: Deny Read/Search/Summarize of Sensitive Channels
Blocks read, search, and summarize operations that target a configurable set of "sensitive" Slack channels.
- Direction
- ingress
- Rego package
slack.ingress.deny_sensitive_channel_read- App
- slack
- Bundle
- slack
- Published
- Minimum gateway
- 1.0.0b24
- Schema version
- 1.0.0
- Checksum
sha256:be0ecf31a6f6908191cd28f537364984fc5771a0c39a9990d141094155b8e47b
slackaccess-controldata-protectioningress
What this policy does
Direction: ingress (tool_pre_invoke)
Default: allow, with a targeted deny on sensitive-channel reads
Package: slack.ingress.deny_sensitive_channel_read
What it does
Blocks read, search, and summarize operations that target a configurable set of "sensitive" Slack channels. Every other Slack tool — and every channel not in the set — passes through untouched. A blocked call returns a clear denial reason instead of returning channel contents.
The set of sensitive channel IDs is configured once at the top of the Rego
(sensitive_channel_ids).
Why ingress
The risk is reading content out of a protected channel, and that intent is fully visible in the request (the tool name plus the channel argument). Denying at ingress stops the read before it reaches Slack, so no protected message, thread, or summary is ever returned to the caller.
How it matches
A call is denied only when all three conditions hold:
- Slack-server scoping. The first hyphen-separated segment of the tool name
starts with
slack, so the policy works regardless of how the Slack MCP server is named on a given gateway (slack-...,slack-prod-...,slack-mcp-...). - Restricted-tool detection. The (lowercased) tool name ends with one of
the known suffixes across three operation families — channel history/replies
reads, search, and summarize — with
-,_, and concatenated naming variants covered. - Sensitive-channel detection. The channel is matched by ID against the
configured set, both as a direct argument (
channel,channel_id,channelId) and as a substring of search-query arguments (query,q), so channel-mention syntax inside a query (e.g.in:<#C…>or<#C…|team-name>) is also caught.
Matching is on channel IDs, not names, because these tools receive resolved
channel IDs at call time — a name-based approach does not fire. ID comparisons
are case-sensitive, mirroring Slack's own behavior. Confirm the exact tool and
argument names your gateway emits with the dump-input debug technique, and
extend restricted_tool_suffixes or the channel-arg lookups if your Slack MCP
server differs.
Examples
Denied (reading a sensitive channel's history)
{
"input": {
"action": "tool_pre_invoke",
"resource": { "name": "slack-mcp-conversations-history", "type": "tool" },
"payload": {
"name": "slack-mcp-conversations-history",
"args": { "channel": "SLACK_CHANNEL_ID", "limit": 50 }
}
}
}
allow = false,
reason = "Reading, searching, or summarizing this Slack channel is not permitted via this gateway. ...".
Allowed (reading a non-sensitive channel)
{
"input": {
"action": "tool_pre_invoke",
"resource": { "name": "slack-mcp-conversations-history", "type": "tool" },
"payload": {
"name": "slack-mcp-conversations-history",
"args": { "channel": "C0PUBLIC001", "limit": 50 }
}
}
}
allow = true, no reason.
Configuration
Edit the sensitive_channel_ids set at the top of the policy. The shipped value
(SLACK_CHANNEL_ID) is a placeholder — replace it with your real Slack
channel IDs (uppercase, case-sensitive, e.g. C0B5LHR8DQV).
Composition
Single-purpose and default allow := true, so it composes cleanly with other
Slack ingress policies (e.g. block-secrets,
deny-channel-creation) on the same
pipeline.
Known limitations
- ID-based, not name-based. The policy keys off channel IDs; it does not resolve channel names to IDs. Populate the ID set with the real IDs of the channels you need to protect.
- Suffix list, not an exhaustive catalog. Only the tool shapes listed in
restricted_tool_suffixesare matched. Add the suffix for any additional read/search/summarize tool your Slack MCP server exposes. - No identity-based exemptions. All callers are treated the same. To allow a
specific break-glass user, gate a separate
allow ifbranch oninput.subject.claims.
Policy source (Rego)
package slack.ingress.deny_sensitive_channel_read
# Default-allow: only deny restricted operations on the configured channel IDs.
default allow := true
# -----------------------------------------------------------------------------
# CONFIG: Sensitive Slack channel IDs. Edit to add or remove. Slack channel
# IDs are uppercase alphanumeric and case-sensitive (e.g. "C0B5LHR8DQV").
# -----------------------------------------------------------------------------
sensitive_channel_ids := {
"SLACK_CHANNEL_ID", #placeholder value, replace with your Slack channel_id
}
# -----------------------------------------------------------------------------
# Slack-server detection: any tool whose first hyphen-separated segment starts
# with "slack" (slack-, slack3-, slack-prod-, etc.).
# -----------------------------------------------------------------------------
is_slack_tool if {
name := lower(input.resource.name)
server_name := split(name, "-")[0]
startswith(server_name, "slack")
}
# -----------------------------------------------------------------------------
# Restricted-tool detection — covers read (channel history / replies), search,
# and summarize operation families. Suffix matching tolerates different MCP
# naming conventions across implementations.
# -----------------------------------------------------------------------------
restricted_tool_suffixes := {
# ---- Read: channel history / messages ----
"-conversations-history",
"-conversations_history",
"-conversationshistory",
"-channel-history",
"-channel_history",
"-channelhistory",
"-get-history",
"-get_history",
"-gethistory",
"-get-messages",
"-get_messages",
"-getmessages",
"-read-channel",
"-read_channel",
"-readchannel",
# ---- Read: replies / threads ----
"-conversations-replies",
"-conversations_replies",
"-conversationsreplies",
"-get-replies",
"-get_replies",
"-getreplies",
"-thread-history",
"-thread_history",
"-threadhistory",
# ---- Search ----
"-search-messages",
"-search_messages",
"-searchmessages",
"-search-all",
"-search_all",
"-searchall",
"-search-files",
"-search_files",
"-searchfiles",
# ---- Summarize ----
"-summarize-channel",
"-summarize_channel",
"-summarizechannel",
"-channel-summary",
"-channel_summary",
"-channelsummary",
"-summarize-conversation",
"-summarize_conversation",
"-summarizeconversation",
"-summarize",
"-summary",
}
is_restricted_tool if {
is_slack_tool
name := lower(input.resource.name)
some suffix in restricted_tool_suffixes
endswith(name, suffix)
}
# -----------------------------------------------------------------------------
# Sensitive-channel-ID detection — match the channel arg against the
# configured ID set, OR find an ID inside a search query.
#
# Slack channel IDs are uppercase letter+digit strings (e.g. C0B5LHR8DQV).
# Comparisons are exact (case-sensitive) since the Slack API itself preserves
# case.
# -----------------------------------------------------------------------------
# Direct channel arg shapes
channel_id_is_sensitive if {
v := object.get(input.payload.args, "channel", "")
sensitive_channel_ids[v]
}
channel_id_is_sensitive if {
v := object.get(input.payload.args, "channel_id", "")
sensitive_channel_ids[v]
}
channel_id_is_sensitive if {
v := object.get(input.payload.args, "channelId", "")
sensitive_channel_ids[v]
}
# Search-query substring check — catches Slack channel-mention syntax inside
# queries (e.g. 'in:<#C0B5LHR8DQV>' or '<#C0B5LHR8DQV|fin-team>').
channel_id_is_sensitive if {
q := object.get(input.payload.args, "query", "")
is_string(q)
some id in sensitive_channel_ids
contains(q, id)
}
channel_id_is_sensitive if {
q := object.get(input.payload.args, "q", "")
is_string(q)
some id in sensitive_channel_ids
contains(q, id)
}
# -----------------------------------------------------------------------------
# Deny rule
# -----------------------------------------------------------------------------
allow := false if {
is_restricted_tool
channel_id_is_sensitive
}
reason := "Reading, searching, or summarizing this Slack channel is not permitted via this gateway. Contact your InfoSec team if this needs to change." if not allow Canonical source: policy.md on GitHub · raw · raw on this site (.md)
Used in these guides
Related policies
Block Secrets in Slack Messages
Blocks Slack send-message tool calls whose message body looks like it contains a secret — API keys, passwords, tokens, or PEM-formatted private keys.
Slack: Deny Channel Creation
Blocks Slack channel-creation tool calls at ingress. Every other Slack tool — and every non-Slack tool — passes through untouched.
Slack: Deny Sending Direct Messages
Blocks Slack message-write calls whose destination resolves to a direct conversation — a 1:1 DM, a message posted to a user ID (which Slack auto-opens as a…