From 164c73d2bea0f1beaaa43c3ded63a4d5f0fdee6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E9=A3=9E=E8=99=B9?= Date: Wed, 25 Feb 2026 02:26:56 +0800 Subject: [PATCH 1/2] docs: add v0.11.0 release notes to CHANGELOG - Add comprehensive v0.11.0 changelog with all P0+P1 fixes - Document path traversal protection and ACK retry mechanism - Include test coverage statistics (93.8%+) - Link to PR #23, Issue #21, and release notes - Follow Keep a Changelog format --- CHANGELOG.md | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f795a7a..d356b158 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,95 @@ +## [v0.11.0] - 2026-02-25 + +### ๐Ÿ” Slack ๅฎ‰ๅ…จๅขžๅผบไธŽๅฏ้ ๆ€งๆๅ‡ + +This release addresses critical security and reliability gaps identified in the [Slack Gap Analysis Report](docs/chatapps/slack-gap-analysis.md). We've implemented comprehensive path traversal protection, Socket Mode ACK retry mechanism, and extensive documentation. + +### Added +- **Path Traversal Attack Protection**: + - New `expandPath()` function with `~` expansion to user home directory + - New `isSensitivePath()` function blocking access to system directories (`/etc`, `/var`, `/usr`, `/bin`, `/sbin`, `/root`, `/proc`, `/sys`, `/boot`, `/dev`) + - Automatic detection and blocking of path traversal attempts (e.g., `../etc/passwd`) + - Safe path cleaning with `filepath.Clean` for relative paths + +- **Socket Mode ACK Retry Mechanism**: + - New `sendACKWithRetry()` function with exponential backoff (1s โ†’ 2s โ†’ 4s) + - Maximum 3 retries (4 total attempts) for reliable message delivery + - Slack API compliant 3-second response requirement + - Comprehensive logging for debugging connection issues + +- **Comprehensive Unit Tests**: + - 26 test cases for `expandPath()` covering normal paths, edge cases, and security scenarios + - 10 test cases for `isSensitivePath()` covering all blocked directories + - 93.8% test coverage for path handling functions + - New test file `chatapps/setup_test.go` (+279 lines) + +- **Gap Analysis Report** ([Issue #21](https://github.com/hrygo/hotplex/issues/21)): + - Comprehensive 416-line comparison: HotPlex vs OpenClaw Slack implementations + - 30+ feature gaps identified across 6 categories (P0/P1/P2 priority) + - 3-phase implementation roadmap (14-20 weeks estimated) + - Technical debt risk identification + +- **Documentation Updates**: + - System prompt configuration guide with injection flow diagram + - Security features documentation (path checks, ACK retry, signature verification) + - Troubleshooting examples (Q5: System prompt not็”Ÿๆ•ˆ๏ผŒQ6: Path blocked) + - Example environment files (`.env.development`, `.env.production`) + +### Changed +- **Configuration Enhancements** (`chatapps/configs/slack.yaml`): + - Detailed path security documentation with examples + - ACK retry mechanism explanation + - System prompt injection flow description + - Complete troubleshooting section + +- **User Manual** (`docs/chatapps/chatapps-slack.md`): + - Added Chapter 7: System Prompt Configuration + - Added Chapter 8: Security Features + - Updated changelog with v0.10.0, v0.9.0, v0.8.0 + +- **Code Quality**: + - Project-wide lint cleanup + - Improved error handling in path expansion + - Enhanced logging for security events + +### Fixed +- **Duplicate Message Processing** ([PR #23](https://github.com/hrygo/hotplex/pull/23)): + - Removed duplicate `handleEventsAPI()` call in Socket Mode + - Added empty payload validation + - Fixed potential message duplication issue + +- **Security Vulnerabilities**: + - Blocked access to sensitive system directories + - Prevented path traversal attacks via `..` sequences + - Hardened path validation with multiple security layers + +### Technical Details +- **Files Changed**: 7 files +- **Lines Added**: +1,256 +- **Lines Removed**: -140 +- **Net Change**: +1,116 lines +- **Test Coverage**: 93.8%+ (41 test cases) + +### Verification +```bash +โœ… go test ./... - All tests pass +โœ… go build ./... - Build succeeds +โœ… golangci-lint run - 0 issues +โœ… Path security - Blocks /etc, /var, /root successfully +โœ… ACK retry - Handles connection failures +``` + +### Contributors +- [@hrygo](https://github.com/hrygo) + +### Related +- **PR**: [#23](https://github.com/hrygo/hotplex/pull/23) +- **Issue**: [#21](https://github.com/hrygo/hotplex/issues/21) +- **Release**: [v0.11.0](https://github.com/hrygo/hotplex/releases/tag/v0.11.0) +- **Gap Analysis**: [docs/chatapps/slack-gap-analysis.md](docs/chatapps/slack-gap-analysis.md) + +--- + ## [v0.10.0] - 2026-02-23 ### ๐Ÿš€ ChatApps-as-a-Service Milestone (v0.10.0) From 5369670a93e03231394738983b2683ecdfa0c709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E9=A3=9E=E8=99=B9?= Date: Wed, 25 Feb 2026 02:33:21 +0800 Subject: [PATCH 2/2] feat(slack): add reaction support - Add Reaction type to base.RichContent - Add AddReaction method to Slack adapter - Integrate reactions into defaultSender --- chatapps/base/types.go | 8 ++++ chatapps/slack/adapter.go | 65 +++++++++++++++++++++++++++++++++ docs-site/.vitepress/config.mts | 4 +- 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/chatapps/base/types.go b/chatapps/base/types.go index 37d3ae3d..b6552b89 100644 --- a/chatapps/base/types.go +++ b/chatapps/base/types.go @@ -23,6 +23,14 @@ type RichContent struct { Blocks []any Embeds []any Attachments []Attachment + Reactions []Reaction +} + +// Reaction represents a reaction to add to a message +type Reaction struct { + Name string // emoji name (e.g., "thumbsup", "+1") + Channel string + Timestamp string // message timestamp to react to } type Attachment struct { diff --git a/chatapps/slack/adapter.go b/chatapps/slack/adapter.go index 9424ecf6..4bafabe0 100644 --- a/chatapps/slack/adapter.go +++ b/chatapps/slack/adapter.go @@ -108,6 +108,16 @@ func (a *Adapter) defaultSender(ctx context.Context, sessionID string, msg *base } } + // Send reactions if present + if msg.RichContent != nil && len(msg.RichContent.Reactions) > 0 { + for _, reaction := range msg.RichContent.Reactions { + reaction.Channel = channelID + if err := a.AddReaction(ctx, reaction); err != nil { + a.Logger().Error("Failed to add reaction", "error", err, "reaction", reaction.Name) + } + } + } + // Send media/attachments if present if msg.RichContent != nil && len(msg.RichContent.Attachments) > 0 { for _, attachment := range msg.RichContent.Attachments { @@ -530,3 +540,58 @@ func (a *Adapter) sendToChannelOnce(ctx context.Context, channelID, text, thread a.Logger().Debug("Message sent successfully", "channel", channelID) return nil } + +// AddReaction adds a reaction to a message +func (a *Adapter) AddReaction(ctx context.Context, reaction base.Reaction) error { + if a.config.BotToken == "" { + return fmt.Errorf("slack bot token not configured") + } + + if reaction.Channel == "" || reaction.Timestamp == "" { + return fmt.Errorf("channel and timestamp are required for reaction") + } + + payload := map[string]any{ + "channel": reaction.Channel, + "name": reaction.Name, + "ts": reaction.Timestamp, + } + + body, err := json.Marshal(payload) + if err != nil { + return fmt.Errorf("marshal payload: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "POST", "https://slack.com/api/reactions.add", bytes.NewReader(body)) + if err != nil { + return fmt.Errorf("create request: %w", err) + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+a.config.BotToken) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return fmt.Errorf("send request: %w", err) + } + defer func() { _ = resp.Body.Close() }() + + if resp.StatusCode >= 400 { + respBody, _ := io.ReadAll(resp.Body) + return fmt.Errorf("reaction add failed: %d %s", resp.StatusCode, string(respBody)) + } + + var slackResp struct { + OK bool `json:"ok"` + Error string `json:"error,omitempty"` + } + if err := json.NewDecoder(resp.Body).Decode(&slackResp); err != nil { + return fmt.Errorf("parse response: %w", err) + } + + if !slackResp.OK { + return fmt.Errorf("slack API error: %s", slackResp.Error) + } + + a.Logger().Debug("Reaction added", "emoji", reaction.Name, "channel", reaction.Channel) + return nil +} diff --git a/docs-site/.vitepress/config.mts b/docs-site/.vitepress/config.mts index b3c0f0a8..f860cb7c 100644 --- a/docs-site/.vitepress/config.mts +++ b/docs-site/.vitepress/config.mts @@ -56,7 +56,9 @@ export default defineConfig({ { text: 'WebSocket Protocol', link: '/guide/websocket' }, { text: 'OpenCode HTTP/SSE', link: '/guide/opencode-http' }, { text: 'ChatApps Overview', link: '/guide/chatapps' }, - { text: 'โ””โ”€ DingTalk Deep Dive', link: '/guide/chatapps-dingtalk' } + { text: 'โ””โ”€ DingTalk Deep Dive', link: '/guide/chatapps-dingtalk' }, + { text: 'โ””โ”€ Slack Deep Dive', link: '/guide/chatapps-slack' }, + { text: 'โ””โ”€ Slack Gap Analysis', link: '/guide/slack-gap-analysis' } ] }, {