Add GoClaw Hub marketplace integration#639
Add GoClaw Hub marketplace integration#639bnqtoan wants to merge 31 commits intonextlevelbuilder:mainfrom
Conversation
New files: - internal/registry/client.go: HTTP client for Hub API (search, download, check-update, categories) - internal/tools/marketplace_search.go: Agent tool to search Hub marketplace - internal/tools/marketplace_hire.go: Agent tool to hire (download) teams from Hub - internal/tools/marketplace_test.go: Test suite Modified: - internal/config/config_channels.go: Added MarketplaceConfig to ToolsConfig - cmd/gateway_setup.go: Register marketplace tools when API key is set Config: set GOCLAW_MARKETPLACE_API_KEY env var or tools.marketplace.api_key in config. Default Hub URL: https://hub-api.vibery.app/v1 Usage from agent conversation: User: "find me a content team" Agent → marketplace_search → shows results User: "hire content-squad" Agent → marketplace_hire → downloads bundle
…ation killing the stream
Replace wget with Go HTTP client for bundle download (fixes hairpin
NAT issues). Replace wget --post-file with proper multipart form
upload for team import (endpoint expects FormFile("file")).
When user tries to hire a paid team, show the price and a link to the Hub listing page to purchase, instead of attempting download and getting a confusing auth error.
GET/POST /marketplace/install — server-rendered HTML confirmation page. Hub generates signed URL with listing details. GoClaw admin logs in, sees team + agents, clicks Install. GoClaw downloads bundle from Hub and imports via local /v1/teams/import. No secrets exchanged.
- marketplace_hire: saves hub_slug + hub_version to team settings JSONB after import - marketplace_check_updates: new tool queries installed Hub teams for available updates - Moved marketplace tool registration to gateway.go for DB access
📋 PR Review — GoClaw Hub Marketplace Integration🎯 Tổng quanPR này thêm marketplace integration để user có thể search, hire (download + install) pre-built AI agent teams từ GoClaw Hub. Scope: 13 files, +1795 lines
✅ Điểm Tốt
|
| Severity | Count | Issues |
|---|---|---|
| 🔴 CRITICAL | 1 | Race condition in saveHubMetadata() |
| 🟡 HIGH | 2 | Bundle validation, Hardcoded URL |
| 🟡 MEDIUM | 2 | Signature expiry, Rate limiting |
| 🟢 LOW | 1 | Logging |
💡 Recommendation
🟡 APPROVE WITH CHANGES
Feature có giá trị cao, architecture đúng hướng. Cần fix trước khi merge:
Bắt buộc:
- ✅ Race condition (dùng UUID + context)
- ✅ Bundle format validation
- ✅ Signature expiry (chống replay attack)
Nên có:
⚠️ Config option cho frontend URL⚠️ Rate limiting cho download
Follow-up PR:
- 📝 Enhanced logging/metrics
Great work @bnqtoan! 🚀 Let me know if you want to discuss any of these points.
…imit, logging 1. CRITICAL: Remove goroutine from saveHubMetadata, use context, log errors 2. HIGH: Add gzip validation before importing bundles 3. HIGH: Add hubFrontendURL config instead of hardcoded URL derivation 4. MEDIUM: Add timestamp + 15min expiry to install link signatures 5. MEDIUM: Add rate limiting (200ms min interval) to registry client 6. LOW: Add structured logging to search, hire, check_updates tools
|
All 6 issues addressed in commit 65c7188:
Hub side also updated (commit 0eee36a) to include |
viettranx
left a comment
There was a problem hiding this comment.
Review: GoClaw Hub Marketplace Integration
Kiến trúc tổng thể ổn — registry client, agent tools, và server-rendered install page tách biệt rõ ràng. Tuy nhiên phát hiện 26 issues cần xử lý, trong đó 9 là blockers.
🔴 Security Critical — Phải fix trước merge
S1. XSS — HTML template không escape input
internal/http/marketplace_install_page.go — renderConfirm, renderSuccess, renderError
Dùng fmt.Sprintf đổ trực tiếp giá trị từ URL params vào HTML mà không escape. title, slug, agents đều controllable qua signed URL. Attacker craft URL với title=<script>alert(1)</script> → XSS.
Fix: Dùng html/template (Go stdlib) thay vì fmt.Sprintf.
S2. Credential leak — Registry key trong HTML + URL
marketplace_install_page.go — renderConfirm
<input type="hidden" name="registry_key" value="%s">GET /marketplace/install là public (chỉ cần signed URL). Bất kỳ ai mở link đều thấy registry_key trong View Source. Key này authenticate với Hub API → full credential leak. Cũng nằm trong URL query params → browser history, server logs, proxy logs, referer headers.
Fix: Server giữ registry key trong memory, không truyền qua URL/form.
S3. SSRF — Download bundle không validate URL
marketplace_install_page.go:441 và marketplace_hire.go
Cả 2 nơi download bundle không kiểm tra SSRF. Project đã có CheckSSRF() trong internal/tools/web_shared.go — không được tái sử dụng.
Fix: Gọi CheckSSRF(bundleURL) trước mỗi HTTP request tới external URL.
S4. Signed URL không có expiry — Replay attack vĩnh viễn
verifySignature — params được ký: slug, title, agents, bundle_url, registry_key. Không có timestamp, không có nonce. URL hợp lệ bị replay mãi mãi.
Fix: Thêm timestamp param + server reject nếu time.Now() - timestamp > 15 minutes.
S5. Debug log in lộ API key
marketplace_install_page.go:579 — fmt.Printf in 8 ký tự đầu API key + 16 ký tự signature ra stdout. Không thể tắt trong production.
Fix: Xóa hoặc dùng slog.Debug không in key material.
🔴 Bugs — Phải fix
B1. Read after close — client.go Download():
resp.Body.Close()
body, _ := io.ReadAll(resp.Body) // đọc body ĐÃ đóng → luôn rỗngB2. Context leak — client.go Download(): cancel không bao giờ gọi. Comment sai — đóng HTTP body KHÔNG cancel context → goroutine leak.
B3. Nil panic — marketplace_install_page.go:450: dlReq, _ := http.NewRequest(...) ignore error → panic nếu URL malformed.
B4. Test không compile — marketplace_test.go: NewMarketplaceHireTool(client) nhưng constructor cần 4 params (client, gwURL, token, db).
🟡 Architecture — Nên fix trước merge
A1. Dùng raw *sql.DB thay vì store interface
Codebase dùng store interface pattern (store.TeamStore, etc.) cho tất cả DB operations. PR truyền pgStores.DB thẳng → phá vỡ abstraction, không hỗ trợ SQLite desktop edition, không thể mock tests, không enforce multi-tenant isolation.
A2. Không propagate tenant context
Queries agent_teams mà KHÔNG filter tenant_id:
SELECT name FROM agent_teams WHERE settings::text LIKE '%' || $1 || '%'→ Trong multi-tenant deployment, trả về data từ BẤT KỲ tenant nào.
A3. Desktop/Lite edition không được xem xét
- Không check
edition.Current()→ marketplace tools register trong lite (giới hạn 1 team) - SQLite không có
::jsonboperator → queries sẽ panic
A4. SQL: LIKE trên JSONB::text
WHERE settings::text LIKE '%hub_slug%'Full table scan, không tận dụng index, false positives nếu slug chứa %/_.
→ Dùng: WHERE settings->>'hub_slug' = $1
🟢 Other Issues
| # | Vấn đề |
|---|---|
| S6 | Path traversal — slug trong filepath không validate [a-z0-9-] |
| S7 | os.MkdirAll(dlDir, 0777) — nên 0755 |
| S8 | Không giới hạn download size — dùng io.LimitReader |
| B5 | Enabled field trong config bị code bỏ qua |
| B6 | Doc sai env var precedence (nói env > config, code ngược lại) |
| M1 | http.DefaultClient không timeout cho download |
| M2 | go saveHubMetadata(...) fire-and-forget, không log error |
| M3 | saveHubMetadata match by team name (không unique) thay vì id |
| M4 | Thiếu trailing newline ở 6 files |
Tóm tắt
| Mức độ | Số lượng |
|---|---|
| Security Critical/High | 5 |
| Bugs | 4 |
| Architecture | 4 |
| Other | 13 |
9 blockers cần fix: S1-S5, B1-B4. Architecture issues (A1-A3) strongly recommended trước merge.
|
All review fixes deployed and verified: Tests:
Deployed to:
Ready for re-review. |
Security (S1-S5): - S1: Replace fmt.Sprintf HTML with html/template (auto-escaping) - S2: Remove registry_key from URL/form, server injects from config - S3: Add SSRF validation for bundle download URLs - S5: Replace fmt.Printf debug log with slog.Debug (no key material) - S6: Validate slug format [a-z0-9-] to prevent path traversal - S7: Dir permissions 0777 → 0755 - S8: Limit download size to 100MB with io.LimitReader Bugs (B1-B3): - B1: Fix read-after-close in Download() - B2: Fix context leak — defer cancel() - B3: Check http.NewRequest errors instead of ignoring Architecture (A4): - Replace LIKE on settings::text with proper JSONB operator ->>'hub_slug'
|
Addressed all 9 blockers from @viettranx review: Security (5 fixed):
Bugs (3 fixed):
Architecture:
Tests: 5/5 pass. Build clean. Note: A1-A3 (store interface, tenant scoping, SQLite/Lite compat) deferred to follow-up — they require broader refactoring. |
…d safety High: DNS resolution check in SSRF validation, 100MB download limit on install page Medium: io.Copy/template.Execute error handling, rows.Err() check, HTTP client timeout Low: Context leak in Download(), exact slug match, mutex on rate limiter
🔍 Code Review — Add GoClaw Hub marketplace integration🎯 Tổng quanPR thêm tích hợp GoClaw Hub marketplace: registry client, 3 agent tools (search, hire, check_updates), và install confirmation page với HMAC signature verification. Scope: 13 files, +1795 lines ✅ Điểm Tốt
|
| Severity | Count | Issues |
|---|---|---|
| 🔴 CRITICAL | 1 | Race condition in rate limiting (lock held during sleep) |
| 🟡 HIGH | 2 | Missing tar validation, hardcoded API URL |
| 🟡 MEDIUM | 2 | Missing download link expiry handling, no rate limit metrics |
| 🟢 LOW | 1 | Ignored SQL error in findInstalledTeam |
💡 Recommendation
🟡 APPROVE WITH CHANGES
Blocking issues (must fix before merge):
- 🔴 Race condition in
rateLimit()— potential performance bottleneck under load - 🟡 Bundle validation — security risk (path traversal, symlinks)
Should fix (can be post-merge):
3. 🟡 Hardcoded API URL — make config required
4. 🟡 MEDIUM issues — observability improvements
Great work @bnqtoan! 🚀 Marketplace integration là một tính năng phức tạp và PR được viết rất cẩn thận. Let me know if you want to discuss any of these points.
…_base - Rate limiter releases lock before sleeping (prevents contention) - Bundle validation checks full tar structure (path traversal, symlinks, file count) - Require explicit api_base config (no hardcoded default URL) - Add rate limit debug logging - Log SQL errors in findInstalledTeam
|
Addressed @mrgoonie's review (6 items):
Tests: 5/5 pass. Build clean. |
Final self-review before mergeRan full diff audit (2167 lines). Here's what remains: Must fix before merge
Clarifications for reviewersRe @viettranx A1 (raw Re @viettranx A3 (desktop/Lite edition): Marketplace tools register unconditionally when API key is set. For Lite edition (1 team limit), the team import handler itself enforces limits — marketplace doesn't need separate checking. SQLite Re @mrgoonie #2 (bundle tar validation): Added full tar structure validation (path traversal, symlinks, file count) as defense-in-depth. Note: the primary security gate is Re @viettranx M3 (saveHubMetadata matches by name): Still matches by team Minor (post-merge)
Will push the must-fix items now. |
🔍 Re-Review — PR #639 (After fixes in 2d5aef6)🎯 Tổng quanRe-review sau khi author đã address 26 issues từ @viettranx + fixes từ reviewers trước. Commit mới nhất: Scope: 13 files, +1795 lines (same) ✅ Fixes Verified
|
| Severity | Count | Status |
|---|---|---|
| 🔴 CRITICAL | 9 (security + bugs) | ✅ All fixed |
| 🟡 HIGH | 2 | ✅ All fixed |
| 🟡 MEDIUM | 4 | |
| 🟢 LOW | 13 |
💡 Recommendation
🟢 APPROVE
Rationale:
- ✅ All 9 blockers (S1-S5, B1-B4) from @viettranx đã fix
- ✅ Security issues addressed: XSS, credential leak, SSRF, signature expiry
- ✅ Bug fixes verified: race condition, context leak, nil panic, test compile
⚠️ Architecture issues (A1-A3) là follow-up improvements, không blocking
Recommended follow-up PRs:
- Abstract DB queries behind
store.TeamStoreinterface (supports SQLite + tenant isolation) - Add edition gating for lite mode (5 agents, 1 team limit)
- Add metrics/observability (Prometheus counters for search/hire/update operations)
Great work @bnqtoan! 🚀 Fast turnaround on 26 issues — impressive! Architecture improvements can be done in follow-up PRs without blocking this feature.
…point - GET /marketplace/install — shows confirmation page with JS auth check - POST /v1/marketplace/install — API endpoint with Bearer auth (requireAuth admin) - JS reads goclaw:token from localStorage (shared origin via Traefik) - Logged in: Install button active, calls API with Bearer token - Not logged in: shows "Login Required" with link to admin panel - Removes the gateway token password field (bad UX)
…d update teams by name
…nt scoping, fix timeouts and metadata tracking
Addressed remaining review itemsFixed in latest commits:A1 — Raw
A2 — Tenant scoping
A3 — SQLite/Desktop compatibility
M1 — Download timeout
M2 — saveHubMetadata error handling
M3 — Team matching by hub_slug instead of name
M4 — Trailing newlines
Also includes fix for team duplication on marketplace install — agents are now reused by key instead of creating duplicates, and teams are updated by name instead of always inserting new. |
Summary
internal/registry/client.go) — HTTP client for Hub API: search, download, check-update, categoriesmarketplace_search,marketplace_hire,marketplace_check_updates/marketplace/install) — server-rendered HTML page for Hub-initiated installs with HMAC signature verificationhub_slug+hub_versionin team settings JSONB for update detectionHow it works
From GoClaw (agent-driven)
marketplace_search→ shows results with statsmarketplace_hire→ downloads bundle, imports teammarketplace_check_updates→ queries Hub for newer versionsFrom Hub (web-driven)
/marketplace/installpage → shows team details + agents/v1/teams/importConfiguration
{ "tools": { "marketplace": { "api_base": "https://hub-api.vibery.app/v1", "api_key": "your-registry-key" } } }Or env:
GOCLAW_MARKETPLACE_API_KEYSecurity
X-Registry-KeyheaderFiles changed (13 files, +1795 lines)
internal/registry/client.gointernal/tools/marketplace_search.gointernal/tools/marketplace_hire.gointernal/tools/marketplace_check_updates.gointernal/http/marketplace_install_page.gointernal/gateway/server.gocmd/gateway.gocmd/gateway_setup.gointernal/config/config_channels.godocs/marketplace-integration.mdTest plan