-
Notifications
You must be signed in to change notification settings - Fork 182
Bugfix: Handling Slack messages with file attachments #159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ec37d12
66cc498
353260f
07e9b2c
8968a06
b675c0f
da5dc64
7f4d712
4ff1278
a9da7ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -311,6 +311,9 @@ pub struct Attachment { | |
| pub mime_type: String, | ||
| pub url: String, | ||
| pub size_bytes: Option<u64>, | ||
| /// Optional auth header value for private URLs (e.g. Slack's `url_private`). | ||
| #[serde(default, skip_serializing_if = "Option::is_none")] | ||
| pub auth_header: Option<String>, | ||
|
Comment on lines
+314
to
+316
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Find uses of auth_header and related serialization patterns
rg -n "auth_header" -g'*.rs' -A 3 -B 3Repository: spacedriveapp/spacebot Length of output: 7152 🏁 Script executed: #!/bin/bash
# Find Attachment struct definition and related serialization
rg -n "struct Attachment|impl.*Attachment|#\[derive.*Serialize" -g'*.rs' -A 5Repository: spacedriveapp/spacebot Length of output: 50378 🏁 Script executed: #!/bin/bash
# Check for SpacebotHook and on_tool_result implementation
rg -n "SpacebotHook|on_tool_result|on_completion_response" -g'*.rs' -A 10Repository: spacedriveapp/spacebot Length of output: 13524 🏁 Script executed: #!/bin/bash
# Search for actual persistence/logging of messages or attachments
rg -n "save_conversation|persist_message|log_.*message|to_string|to_json|format!" -g'*.rs' | head -50Repository: spacedriveapp/spacebot Length of output: 4086 🏁 Script executed: #!/bin/bash
# Read the full on_tool_result implementation in SpacebotHook
sed -n '220,260p' src/hooks/spacebot.rsRepository: spacedriveapp/spacebot Length of output: 1749 🏁 Script executed: #!/bin/bash
# Search for where InboundMessage or Attachment are actually persisted or logged
rg -n "InboundMessage|Attachment" -g'*.rs' | rg -i "save|persist|log|serialize|json|store|write|db" | head -30Repository: spacedriveapp/spacebot Length of output: 48 🏁 Script executed: #!/bin/bash
# Check if conversation history includes attachments and whether it's saved to DB
rg -n "conversation.*save|history.*persist|TimelineItem|save_conversation" -g'*.rs' -A 3 | head -50Repository: spacedriveapp/spacebot Length of output: 3210 🏁 Script executed: #!/bin/bash
# Find the scan_for_leaks implementation to see what it detects
rg -n "fn scan_for_leaks|scan_for_leaks" -g'*.rs' -A 20 | head -80Repository: spacedriveapp/spacebot Length of output: 4462 🏁 Script executed: #!/bin/bash
# Check if InboundMessage is ever saved to conversation history or DB
rg -n "InboundMessage" -g'*.rs' | rg -v "use |pub struct|pub enum|fn.*InboundMessage|=>|Message::Text|Message::Media" | head -20Repository: spacedriveapp/spacebot Length of output: 1634 🏁 Script executed: #!/bin/bash
# Search for what gets saved in conversation history - look at Message enum
rg -n "TimelineItem::Message" -g'*.rs' -B 5 -A 5 | head -60Repository: spacedriveapp/spacebot Length of output: 1867 🏁 Script executed: #!/bin/bash
# Check if Attachment is ever returned in API responses or serialized in outbound messages
rg -n "Attachment|auth_header" -g'*.rs' | rg -E "OutboundResponse|api|response|serialize" | head -20Repository: spacedriveapp/spacebot Length of output: 462 🏁 Script executed: #!/bin/bash
# Check what happens with attachments in tool results - can they leak through tool outputs?
rg -n "attachment|media|Attachment" -g'*.rs' | rg -E "to_string|format|json|serde" | head -20Repository: spacedriveapp/spacebot Length of output: 462 🏁 Script executed: #!/bin/bash
# Verify if download_attachment_bytes_with_auth actually uses the Bearer token and whether the result is exposed
sed -n '2475,2530p' src/agent/channel.rsRepository: spacedriveapp/spacebot Length of output: 1802 🏁 Script executed: #!/bin/bash
# Check if Attachment is ever serialized in API responses or output
rg -n "Attachment" -g'*.rs' -A 3 -B 1 | grep -E "(to_json|serde_json|Serialize|api|response)" | head -20Repository: spacedriveapp/spacebot Length of output: 128 🏁 Script executed: #!/bin/bash
# Check if there's any path where download_attachment_bytes result could include the auth header or leak
rg -n "download_attachment_bytes|download_image_attachment" -g'*.rs' -A 10 | head -50Repository: spacedriveapp/spacebot Length of output: 3219 🏁 Script executed: #!/bin/bash
# Check if MessageContent::Media is ever logged or exposed in logs/APIs
rg -n "MessageContent::Media" -g'*.rs' -B 2 -A 5Repository: spacedriveapp/spacebot Length of output: 3965 🏁 Script executed: #!/bin/bash
# Final check: is InboundMessage ever logged or serialized in error/debug paths?
rg -n "tracing::|log::|println|debug|InboundMessage|Attachment" -g'*.rs' | grep -E "tracing::(debug|trace|warn|error).*InboundMessage|format.*InboundMessage" | head -10Repository: spacedriveapp/spacebot Length of output: 168 🏁 Script executed: #!/bin/bash
# Check if there's any serialization of InboundMessage to JSON for logs or APIs
rg -n "serde_json::to_|json!" -g'*.rs' -B 2 -A 2 | grep -E "(InboundMessage|Attachment|message)" | head -15Repository: spacedriveapp/spacebot Length of output: 1495 Add
🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| /// Outbound response to messaging platforms. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
Slack url_private redirect behavior cross-origin domains Authorization header💡 Result:
Slack
file.url_private/url_private_downloadURLs must be fetched with an OAuth token in anAuthorization: Bearer …header (token needsfiles:read).[1]The gotcha is that hitting
url_privateoften returns an HTTP 3xx redirect to a different host (commonly another Slack-controlled domain such asfiles.slack.com). Many HTTP stacks intentionally stripAuthorizationon cross-host redirects to prevent credential leaks—so the redirected request becomes unauthenticated and the download fails (often showing up as 404/redirect loops).requests: it stripsAuthorizationwhen the redirect hostname changes (should_strip_auth).[2]Authorization:headers are only sent to the original host; you must explicitly allow sending them to redirected hosts (e.g.,--location-trusted/CURLOPT_UNRESTRICTED_AUTH).[3]Practical fixes
url_privatewith redirects disabled, read theLocation, then make a second request to that URL adding theAuthorizationheader again only if the redirect target is a Slack domain you trust.--location-trusted)—again only if you’ve validated the redirect chain is to Slack-controlled hosts.[3]Sources: Slack file auth requirement[1];
requestsredirect auth-stripping logic[2]; curl redirect/auth behavior and--location-trustedguidance[3]; Fetch/redirect security discussion[4].[1] (docs.slack.dev)
[2] (requests.readthedocs.io)
[3] (curl.se)
[4] (github.com)
Citations:
🌐 Web query:
HTTP redirect Location relative URL RFC 7231 3986💡 Result:
Locationin HTTP redirects may be a relative reference.Location = URI-reference(i.e., a URI reference, not necessarily an absolute URI). If the value is a relative reference, the client computes the final target by resolving it against the effective request URI, using RFC 3986’s rules. [1]So: a redirect like
Location: /new-pathis valid, and the client resolves it relative to the request URL per RFC 3986. [1][2]Sources: [1] RFC 7231 (HTTP/1.1 Semantics), Section 7.1.2 Location; [2] RFC 3986 (URI Generic Syntax), Sections 4.2 and 5.2.
🏁 Script executed:
Repository: spacedriveapp/spacebot
Length of output: 92
🏁 Script executed:
Repository: spacedriveapp/spacebot
Length of output: 92
🏁 Script executed:
rg "download_attachment_bytes_with_auth" -A 40 src/agent/channel.rsRepository: spacedriveapp/spacebot
Length of output: 1963
🏁 Script executed:
Repository: spacedriveapp/spacebot
Length of output: 382
Fix critical Authorization header leak on Slack redirect chains and validate Location header parsing.
The code forwards the Authorization header to every redirect target without host validation. Slack's
url_privateURLs redirect to different hosts (e.g.,files.slack.com); standard HTTP clients intentionally strip Authorization on cross-host redirects to prevent credential leaks. The current implementation bypasses this protection and sends credentials to untrusted hosts.Additionally,
location.to_str().unwrap_or_default()silently discards UTF-8 decoding errors, and relativeLocationheader values (valid per RFC 7231/3986) are not resolved against the request URL.Required fixes:
reqwest::Url::parse()to enable proper relative URL resolution via.join().map_err()on header parsing instead ofunwrap_or_default()Failing to validate the redirect host will leak OAuth tokens to attacker-controlled domains if a Slack URL is compromised or during a network attack.
🤖 Prompt for AI Agents