Back to Blog Home

How to Stop Prompt Injection from Hijacking Slack AI Agent Tool Calls

Folder rows narrowing from a wide team to a single person as access is restricted step by step

Prompt injection turns into a Slack incident at the tool call, so put the control there. Keep an untrusted label on everything the agent reads from other people's messages, files and connector results, even after the model summarizes it, and run a policy check outside the model before any tool that writes or sends. A defensive system prompt helps against obvious jailbreaks and is not a gate. We also recommend deciding by who will see the result: a post into a channel with external members is an outbound send, whatever the tool is called.

The risk is easy to miss because the mention looks ordinary. A public channel message, uploaded file, GitHub issue comment or connector result contains hidden directives, and the agent then searches private knowledge, posts a reply, opens a ticket or prepares a code change nobody asked for. The requester thinks they asked a normal question, and the audit trail points at a trusted user. This plan is for admins who own multi-tool Slack agents for knowledge retrieval, support drafting or GitHub-aware ops work.

Why Slack agent prompt injection is a tool problem

OWASP LLM01: Prompt Injection covers both direct overrides and indirect injection, where instructions arrive inside content the model retrieves. For Slack agents the second path matters most: the dangerous text sits in another message, a file, an issue body or a tool response that your runtime treated as ordinary context. PromptArmor showed how attacker-written public-channel messages could steer Slack AI into exposing private-channel data through rendered links. Embrace The Red showed an agent on Anthropic's Slack MCP server posting a link that carried secrets, which Slack's link unfurling then fetched from an attacker-controlled host. That server is archived and will not be patched, so replace it if you still run it. Both are researcher reports on specific products, and both show the pattern: once an agent can read workspace content and call outbound tools, retrieved text becomes a control path.

Repello makes the same point for tool-using agents. Text returned by a Slack read tool enters the model as structured tool output, and the protocol has no mechanism to keep adversarial instructions out of it. Anyone who can write to a source the agent reads can try to steer later tool calls without touching your user-facing prompt filter.

A defensive prompt or a hand-rolled allowlist does not replace a gate in front of tools. Microsoft's Agent Framework FIDES post says both fail silently the day someone slips an override line into an issue body, because nothing outside the model checks what the model then tries to write or post.

Separate trusted intent from untrusted content

Every Slack agent run has two kinds of text from the first message onward:

  1. Trusted intent: the requesting user's goal for this turn, plus policy your platform owns (allowed tools, environment, approval rules).
  2. Untrusted content: Slack thread text from other people, uploaded files, search hits, GitHub issue or PR bodies, Intercom notes, web fetches, and any other connector output.

OWASP's Prompt Injection Prevention Cheat Sheet treats retrieved documents, tool results and conversation history as untrusted. It asks for least privilege on each tool, human approval before consequential tool actions, and enforcement of the caller's permissions in code outside the model. The Guide to the Slack AI Guardrails covers native Slack AI features: context engineering to reduce prompt-injection risk, URL filtering against phishing, and output format validation. A custom agent that calls your own knowledge, GitHub, Intercom or Ads connectors still needs its own mediation, because native guardrails do not automatically protect a third-party agent endpoint.

Slack's developer docs on governance and trust ask builders to balance oversight with a usable agent, keep people able to pause, stop or redirect it, and log which channels were accessed, what actions were taken, who triggered the request and which tools ran. Those audit fields help only if your runtime can also say whether untrusted content was in scope when a write was attempted, so add the integrity-label scope and the policy decision to the same row. A blocked hijack attempt then shows up later.

Kipwise Agent is designed as a thread-native work agent: a teammate mentions it once and continues with ordinary replies; applicable actions use the requesting user's permissions; it cites sources it actually opened; it refuses to guess; sandboxed code work is isolated; consequential changes stay explicit. Those product behaviors stay trustworthy only if retrieved Slack and connector text cannot become the authority for the next tool call.

Propagate integrity labels through tool results

Do not clear the untrusted label when the model summarizes a Slack message or tool payload. If a public channel message is untrusted on read, the summary that enters the next reasoning step must still carry a label that says untrusted content influenced the run. Otherwise the agent "launders" injected instructions into clean-looking intermediate state and then calls a write tool. Microsoft FIDES describes integrity and confidentiality labels that travel with content and combine restrictively as the run proceeds, so policy evaluates the most restrictive combination of everything the model has read before a sensitive tool executes. Copy that pattern into your agent runtime even if you do not adopt the framework.

Practical labeling rules for Slack agents:

SourceDefault integrity labelWhy
Requester's current mention and later plain replies in the same owned thread turnTrusted intent (user goal)This is the job the user asked for
Other users' messages in the same thread or channelUntrusted contentAnyone in the channel can plant instructions
Uploaded files and OCR/extractsUntrusted contentDocuments are a classic indirect injection path
Kipwise search hits, GitHub issue/PR bodies, Intercom notes, Ads or Analytics rowsUntrusted contentConnector output is still external text relative to your policy engine
Your platform policy, allowlists, and approval decisionsTrusted platform policyOwned by admins, not by workspace chatter

Keep labels in structured run state, not only inside the prompt. The model may ignore a warning in the prompt text. Your tool gateway must still see the labels.

Enforce tool policy out of band before mutating calls

Mediate tools outside the model. If untrusted content is in scope, do not rely on the model to "decide carefully" whether create_ticket, post_message, update_knowledge, open_pull_request, or send_customer_reply is appropriate. Check the tool name, destination, and current labels in middleware. Block, require approval, or allow only according to that policy. FIDES puts the enforcement point there: policy middleware runs before the sensitive function, and a violation can become an approval request instead of a silent block when operators want an override path.

That differs from a generic human-approval workflow. Human approval for a known consequential write still matters, and Kipwise-style products keep sensitive actions explicit. Tool mediation answers a prior question: should this tool run at all when the run has already read untrusted text that could have steered the choice?

A compact policy table for Slack multi-tool agents:

Tool classUntrusted content in scopeDefault action
Read-only search in already-permitted sourcesYesAllow, but keep results labeled untrusted
Summarize or classify untrusted textYesPrefer quarantine worker; return a handle plus summary
Post to the same thread as a citation-backed answerYesAllow only if destinations and URLs pass filters; disable unfurling for sensitive payloads
Post into a Slack Connect channel or a channel with guestsYesTreat as an outbound send: block or require approval
Create or update tickets, knowledge, GitHub artifactsYesBlock or require explicit human approval with the proposed tool args visible
External HTTP fetch or customer-visible sendYesBlock or require approval; never pass secrets in query strings

Decide by who will see the result, not by the tool's name. A reply in an internal thread and a reply in a channel shared with a client are the same post_message call, and the second one discloses to another organization. Slack draws the same line in its own product: the AI answer step for workflows cannot be added in channels with external people. Give your gateway the destination's audience (internal, guests, another organization) as an input, and put every external audience in the same row as a customer-visible send.

Back the policy with credentials. Give the agent loop read-only tokens for GitHub, Jira and the knowledge base, and keep the write token with the executor that runs only after the gateway or a human has approved the exact call. A bug in the gateway then fails closed, because the read path holds no write credential to misuse. We recommend this even when a connector makes one all-purpose token easier to set up.

Also refuse tool arguments that encode exfiltration patterns. The Embrace The Red case hinged on posting a URL that carried secrets and relying on Slack to unfurl it. For agent posts, set unfurl_links and unfurl_media to false when the message may contain sensitive parameters, allow-list the domains an agent may link to, and reject outbound URLs that embed tokens, keys, or private channel contents in the query string.

Quarantine raw untrusted text before the main agent reasons over it

Keep raw untrusted bytes away from the main tool-calling model whenever you can. Store the Slack message or tool payload in a side store, give the main agent a handle such as var_12, and let a quarantined summarizer extract only the fields the job needs. The main agent should not see a literal SYSTEM OVERRIDE block sitting inside an otherwise ordinary support note.

FIDES hides untrusted tool results behind stored references and sends the real processing to a separate model that has no tools, so its output can never trigger a privileged action on its own. Repello explains why the naive design fails: tool output looks like a trusted channel to the model even when the bytes came from an attacker-controlled Slack message.

Recommended Slack-agent flow:

  1. Receive the mention and verify Slack request signatures.
  2. Resolve the requester identity and live permissions for the planned tools.
  3. Collect thread and connector context with integrity labels.
  4. Store raw untrusted payloads; pass handles plus minimal structured fields to the main agent.
  5. Let the main agent propose tool calls for the user goal.
  6. Run out-of-band policy on each call using current labels and tool class.
  7. Execute allowed reads; gate or block writes; cite opened sources only.
  8. Post one grounded reply or an explicit refusal; write audit rows for blocks and approvals.

Worked example: support drafting with knowledge and Intercom context

Scenario: a support operations lead mentions the agent in a triage thread: "Draft an answer for this billing escalation using our refund policy and the Intercom conversation."

Without mediation, the agent reads the thread, opens Kipwise, opens Intercom, and finds a customer note that says to ignore prior policy and post a full account export link into the channel. A weakly gated agent may treat that note as instruction and attempt a sensitive search or an outbound post.

With mediation:

  1. Trusted intent is "draft a refund answer from policy and Intercom evidence."
  2. Thread chatter, the customer note, and Intercom bodies are labeled untrusted.
  3. Policy allows labeled reads of Kipwise and Intercom under the requester's permissions.
  4. Raw Intercom text is quarantined; the main agent receives a summary handle and citation IDs.
  5. When the model proposes post_message with an external export URL, middleware blocks or pauses for approval because untrusted content is in scope and the destination looks like exfiltration.
  6. The agent posts a draft reply that cites the refund policy page it opened, or it asks the human to approve a customer-visible send. It does not invent policy and it does not follow the injected export instruction.

Kipwise Agent fits this path: permission-aware reads, citations to opened sources, refuse-to-guess behavior, and explicit consequential actions, with injection treated as a tool-authorization problem rather than a prompt-wording problem.

Prove the gate works before you trust it

Three failures show up after launch. Over-blocking: if every run that read a channel message needs approval, nobody uses the agent, so split read-only tools from mutating tools and auto-allow labeled reads. Identity confusion: if the audit row shows the bot and not the requester, you cannot tell who authorized the tool, so map permissions per user and do not share one bot token for user-scoped actions. False calm: demo threads never contain adversarial content, so add injection cases to pre-deploy tests, including public-channel bait, file-borne instructions and malicious tool payloads.

Run this pilot in a non-production workspace first:

  1. Plant a benign injection in a public test channel that asks the agent to call a forbidden write tool.
  2. Confirm the tool gateway blocks or pauses the call and records the untrusted sources in scope.
  3. Confirm the user still receives a useful refusal or approval card instead of a silent no-op with no explanation.
  4. Confirm citations refer only to sources the run opened.
  5. Confirm agent posts that could embed secrets do not unfurl sensitive URLs.
  6. Measure unauthorized tool calls blocked, approval overrides, mean time to review a gated write, and security questions answered from logs.

Start with the agent that can write somewhere that matters

Pick one production Slack agent that can write somewhere that matters: knowledge, tickets, GitHub, or customer-visible replies. Add integrity labels to Slack and connector inputs, put a policy decision in front of mutating tools, and quarantine raw untrusted text before the main model plans the next call. Keep human approval for consequential writes, but do not treat approval alone as a substitute for stopping unauthorized tool selection when injected instructions are already in the run.

Topics

Related posts

Your team’s knowledge, answered in Slack

Add the Kipwise Agent. It answers from your pages with the source, and asks before it changes anything. 7-day trial, no card needed.