feat(kanban): attachment toolset + CLI with SSRF-guarded URL fetch (#36019 salvage) - #65698
Merged
Conversation
tonydwb
reviewed
Jul 16, 2026
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Comment
Overview
- Kanban attachment toolset + CLI with SSRF-guarded URL fetch
- 1172 additions, 41 deletions
Assessment
- SSRF protection on URL fetch is good security practice
- CLI tool for attachments adds useful functionality
- Multiple
print()statements to stderr for error reporting are acceptable for CLI tools
Suggestion
- Ensure the SSRF guard validates both the host and path components of URLs to prevent
http://169.254.169.254/style attacks
Reviewed by Hermes Agent
The kanban board has had full attachment storage and a dashboard HTTP API (upload/list/download/delete) since #35338, but there was no agent toolset tool and no `hermes kanban` CLI verb for attachments. Agents and scripts that don't go through the dashboard server (or can't touch the DB directly) had no way to create or read real attachments — only links in comments. Close that gap by mirroring the existing comment surface: - `kanban_db.store_attachment_bytes()` — one shared write path (validate name, enforce the 25 MB cap, write the blob under the per-task dir with collision-free naming, insert the metadata row, clean up an orphan blob if the insert fails). `_MAX_ATTACHMENT_BYTES`, `_safe_attachment_name`, and a new `_collision_free_path` move here so the dashboard, the tool, and the CLI all share one implementation and can't drift. - Tools (`tools/kanban_tools.py`): `kanban_attach` (inline base64), `kanban_attach_url` (server-side http/https fetch with the same cap), `kanban_attachments` (list). Write tools respect worker task-ownership; list is read-only. Registered in the `kanban` toolset. - CLI (`hermes_cli/kanban.py`): `attach <id> <path>`, `attachments <id>`, `attach-rm <attachment_id>`. - Dashboard `upload_task_attachment` now imports the shared helpers and uses `_collision_free_path` — behavior identical (still streams to disk with the cap, still 413 on overflow). - Docs (AGENTS.md, kanban-worker skill) and toolset membership updated. Tests: tool round-trip + oversize + bad base64 + ownership; attach_url against a local HTTP fixture incl. oversize-mid-stream and non-http scheme rejection; CLI attach/attachments/attach-rm; shared-helper unit tests; dashboard parity preserved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…BYTES The salvaged attachment-toolset commit predated main centralizing the 25 MB cap as kanban_db.KANBAN_ATTACHMENT_MAX_BYTES and re-introduced a private _MAX_ATTACHMENT_BYTES alias. Drop the duplicate: kanban_db's store_attachment_bytes(), the dashboard upload endpoint, and the kanban_attach_url tool all reference the one shared constant now, and the tests monkeypatch that same name.
_download_url_with_cap called urlopen() after only a scheme check, so a model-controlled URL could reach loopback services, RFC1918/CGNAT hosts, or cloud metadata endpoints (169.254.169.254), and a public host could 302 to any of those unvalidated. Route the fetch through the repo's canonical SSRF guard instead: validate every hop with tools.url_safety.is_safe_url() and follow redirects manually (httpx, follow_redirects=False, 5-hop limit) so each Location target is re-checked before it is fetched — the same pattern as tools/skills_hub._guarded_http_get. The streaming size cap is unchanged. Local-fixture tests opt in via HERMES_ALLOW_PRIVATE_URLS (the guard's documented escape hatch); new tests pin rejection of loopback, cloud-metadata, and private-range URLs, a mocked public→loopback redirect, and a mocked public happy path.
teknium1
force-pushed
the
salvage/36019-kanban-attachments
branch
from
July 16, 2026 14:28
82b62dd to
8e3372e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Kanban attachments are now reachable from agent tools and the CLI, matching the dashboard surface added in #35338:
kanban_attach(base64),kanban_attach_url(server-side fetch),kanban_attachments(list) + CLI verbsattach/attachments/attach-rm, all built on a sharedstore_attachment_bytes()inkanban_db.py.Salvage of #36019 by @otsune with both review blockers fixed on top (authorship preserved):
kanban_attach_url's fetch now routes throughtools/url_safety.py's canonical guard — loopback, private ranges, cloud-metadata endpoints, and redirects-to-private are rejected (tests for 127.0.0.1, 169.254.169.254, 10.0.0.1, public→loopback redirect, plus mocked happy path)._MAX_ATTACHMENT_BYTESin favor of main's centralizedKANBAN_ATTACHMENT_MAX_BYTES; kept main'splugin_api.pyhelpers and adapted the tools to them.Toolset footprint follows the accepted kanban pattern: all three tools gated by
check_fn=_check_kanban_mode(zero schema footprint outside dispatcher-spawned kanban tasks), write tools enforce_enforce_worker_task_ownership. The PR's kanban-worker SKILL.md hunk was re-targeted toKANBAN_GUIDANCE(main folded that skill in via #50473), staying under the guidance size bound.Changes
11 files, +1172/−41 — kanban_db shared helper, tools, CLI verbs, dashboard refactor onto shared path, guidance, tests, AUTHOR_MAP.
Validation
scripts/run_tests.sh)Infographic