tsk-f4nt53 [OPEN] S2A follow-ups: doc-gate, CSRF dep, agent_chat mem - #2202
tsk-f4nt53 [OPEN] S2A follow-ups: doc-gate, CSRF dep, agent_chat mem#2202jaylfc wants to merge 1 commit into
Conversation
📝 WalkthroughWalkthroughThe share destinations route now applies CSRF verification, documents its device bearer contract, and resolves agent members by slug when direct registry lookup fails. ChangesShare destinations
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Summary by QodoFix device share agent resolution and CSRF wiring
AI Description
Diagram
High-Level Assessment
Files changed (4)
|
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/agent-coordination.md`:
- Around line 236-243: Update the GET /api/share/destinations documentation to
describe access as paired-device bearer authentication only, matching the
handler’s require_device behavior; remove the unsupported “agents” access
wording unless an agent credential path is actually implemented and documented.
In `@tinyagentos/agent_registry_store.py`:
- Around line 542-559: Update get_by_slug to resolve only the exact normalized
slug rather than using the broad canonical_id LIKE prefix query; persist and
query the slug field if available, or parse canonical_id and enforce the slug
boundary before selecting a record. Preserve the existing status filtering,
oldest-record ordering, and None behavior for unmatched slugs.
In `@tinyagentos/routes/share.py`:
- Around line 53-54: Add regression tests in test_routes_share_destinations.py
covering destination discovery when a channel member is stored as an agent slug,
including a similarly prefixed slug that must not resolve to another agent.
Exercise the slug fallback in the share destination flow around
registry.get_by_slug and assert both the correct resolution and non-matching
behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: e6703bf0-2a6f-487a-9b71-4e4a3f636f27
📒 Files selected for processing (4)
docs/agent-coordination.mdtinyagentos/agent_registry_store.pytinyagentos/routes/__init__.pytinyagentos/routes/share.py
| `GET /api/share/destinations` lets a paired device discover the ingest endpoints it | ||
| is allowed to write to. It is reachable by agents and devices, not by the browser | ||
| session. | ||
|
|
||
| **Auth model.** Device bearer only: the caller sends | ||
| `Authorization: Bearer <scoped_token>` (the device's scoped token, issued at | ||
| `POST /api/devices/register`). This route is **not** gated by the user session | ||
| cookie (`taos_session`) and is listed in `EXEMPT_PATHS` in |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
Make the caller contract consistent.
This section says the endpoint is reachable by “agents and devices,” but then defines it as device bearer only; the handler also calls require_device. Document this as paired-device access only, or explicitly document the agent credential path if one is supported.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/agent-coordination.md` around lines 236 - 243, Update the GET
/api/share/destinations documentation to describe access as paired-device bearer
authentication only, matching the handler’s require_device behavior; remove the
unsupported “agents” access wording unless an agent credential path is actually
implemented and documented.
| async def get_by_slug(self, slug: str, *, status: str = "active") -> Optional[dict]: | ||
| """Return the oldest entry whose canonical_id starts with *slug*, or ``None``. | ||
|
|
||
| DM channels created by the deploy route store the agent slug (not the | ||
| canonical_id) as the channel member, so callers that need to resolve a | ||
| channel member back to an agent record must look up by slug. Pass | ||
| ``status=None`` to match any status. | ||
| """ | ||
| if self._db is None: | ||
| raise RuntimeError("AgentRegistryStore not initialised") | ||
| if status is None: | ||
| cursor = await self._db.execute( | ||
| "SELECT * FROM agent_registry WHERE canonical_id LIKE ? ORDER BY id LIMIT 1", | ||
| (f"{slug}-%",), | ||
| ) | ||
| else: | ||
| cursor = await self._db.execute( | ||
| "SELECT * FROM agent_registry WHERE canonical_id LIKE ? AND status = ? ORDER BY id LIMIT 1", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Make get_by_slug an exact slug lookup.
LIKE f"{slug}-%" also matches longer slugs with the same prefix—for example, slug foo can resolve an agent whose canonical ID was created from foo-bar. ORDER BY id LIMIT 1 may then return the wrong active agent to the share route. Persist/query the normalized slug directly, or parse and compare the slug boundary exactly.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tinyagentos/agent_registry_store.py` around lines 542 - 559, Update
get_by_slug to resolve only the exact normalized slug rather than using the
broad canonical_id LIKE prefix query; persist and query the slug field if
available, or parse canonical_id and enforce the slug boundary before selecting
a record. Preserve the existing status filtering, oldest-record ordering, and
None behavior for unmatched slugs.
| if agent is None: | ||
| agent = await registry.get_by_slug(member) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Add regression coverage for slug fallback.
The new lookup path changes destination discovery, but no test file is updated. Add coverage in tests/test_routes_share_destinations.py for a channel member stored as a slug, plus a similarly prefixed slug that must not resolve to the wrong agent.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tinyagentos/routes/share.py` around lines 53 - 54, Add regression tests in
test_routes_share_destinations.py covering destination discovery when a channel
member is stored as an agent slug, including a similarly prefixed slug that must
not resolve to another agent. Exercise the slug fallback in the share
destination flow around registry.get_by_slug and assert both the correct
resolution and non-matching behavior.
Code Review by Qodo
1. Device writes reject token
|
| if agent is None: | ||
| agent = await registry.get_by_slug(member) |
There was a problem hiding this comment.
1. Slug fallback lacks regression test 📜 Skill insight ▣ Testability
The new get_by_slug() fallback fixes agent-chat destinations whose channel members contain slugs, but the PR adds no regression test for that case. The existing route test uses a canonical ID and would pass without this fix.
Agent Prompt
## Issue description
Add a regression test proving that an agent DM channel storing an agent slug is returned by `/api/share/destinations` with the correct active agent label.
## Issue Context
The existing test stores the full `canonical_id` as the channel member, so it does not exercise the newly added `get_by_slug()` fallback. The new test must fail when the fallback is removed and pass with this PR.
## Fix Focus Areas
- tests/test_routes_share_destinations.py[79-117]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| if agent is None: | ||
| agent = await registry.get_by_slug(member) |
There was a problem hiding this comment.
2. Deployed dms stay hidden 🐞 Bug ≡ Correctness
The fallback still resolves slug-based channel members exclusively through AgentRegistryStore, but the normal agent deployment flow creates a config agent and slug-based DM without creating a registry row. Both lookups therefore return None and standard deployed agents remain absent from share destinations.
Agent Prompt
## Issue description
Slug-based DM members created by the standard deployment flow are omitted because share destination resolution requires an AgentRegistryStore row that deployment does not create.
## Issue Context
The deploy route persists the agent in application config and creates its DM using the config slug. Either resolve these members from the authoritative config-agent collection, or register deployed agents and persist an unambiguous registry association. Add a regression test exercising the normal deployed-agent channel shape rather than manually registering a canonical ID.
## Fix Focus Areas
- tinyagentos/routes/share.py[50-61]
- tinyagentos/routes/agents.py[530-612]
- tinyagentos/routes/agents.py[696-704]
- tests/test_routes_share_destinations.py[79-116]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| "SELECT * FROM agent_registry WHERE canonical_id LIKE ? ORDER BY id LIMIT 1", | ||
| (f"{slug}-%",), |
There was a problem hiding this comment.
3. Slug prefix selects wrong agent 🐞 Bug ≡ Correctness
get_by_slug treats every canonical ID beginning with <slug>- as the requested slug, so looking up alpha can return an older active alpha-beta record. The destination can consequently pass the active check using another agent and display that agent's label.
Agent Prompt
## Issue description
The LIKE `<slug>-%` query cannot distinguish a requested slug from longer hyphenated slugs sharing its prefix.
## Issue Context
Canonical IDs use `<slug>-<YYYYMMDD>-<HHMMSS>`, but slugs themselves may contain hyphens. Store/query an explicit slug column or otherwise enforce the exact timestamp boundary, and add regression coverage for slugs such as `alpha` and `alpha-beta`.
## Fix Focus Areas
- tinyagentos/agent_registry_store.py[542-563]
- tinyagentos/agent_registry_store.py[363-371]
- tests/test_routes_share_destinations.py[79-116]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| - **library** -- `POST /api/library/ingest` (file upload or URL reference into the | ||
| shared library). Always present for any paired device. | ||
| - **project_files** -- `POST /api/projects/{slug}/files/upload` (multipart upload | ||
| into the project's file tree). One entry per project owned by the device's | ||
| `user_id`; the `id` is the project slug. | ||
| - **agent_chat** -- `POST /api/chat/messages` (send a message into the agent's DM | ||
| channel; the request body carries `channel_id`). One entry per active agent that |
There was a problem hiding this comment.
4. Device writes reject token 🐞 Bug ≡ Correctness
The new documentation presents the listed routes as endpoints a paired device is allowed to write to, but the scoped device token is authenticated only on /api/share/destinations. Library and chat writes require session authentication, while project-file uploads validate a registry JWT, so the documented device workflow fails authorization at every destination.
Agent Prompt
## Issue description
Paired devices can discover destinations with their scoped token but cannot use that credential on any advertised write endpoint.
## Issue Context
Add narrowly allowlisted device-token passthrough and route-level authorization for each supported write operation, including project ownership and chat-channel membership checks. Alternatively, document and implement a credential-exchange flow if destination writes intentionally require another credential; add end-to-end tests for every destination kind.
## Fix Focus Areas
- tinyagentos/auth_middleware.py[263-324]
- tinyagentos/auth_middleware.py[384-405]
- tinyagentos/routes/library.py[99-114]
- tinyagentos/routes/project_files.py[40-92]
- tinyagentos/routes/chat.py[298-306]
- tinyagentos/device_auth.py[25-38]
- tests/test_routes_share_destinations.py[14-116]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| ).fetchone() | ||
| return _row_to_dict(row) if row else None | ||
|
|
||
| async def get_by_slug(self, slug: str, *, status: str = "active") -> Optional[dict]: |
There was a problem hiding this comment.
SUGGESTION: Type hint mismatch — status: str should be Optional[str] to match the docstring's status=None behavior
The docstring documents Pass status=None to match any status, but the type hint only accepts str. Type checkers will flag callers that pass None.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| `POST /api/devices/register`). This route is **not** gated by the user session | ||
| cookie (`taos_session`) and is listed in `EXEMPT_PATHS` in | ||
| `tinyagentos/auth_middleware.py`. CSRF is still registered on the router | ||
| (`dependencies=_csrf`) so that future POST routes on this router inherit the |
There was a problem hiding this comment.
SUGGESTION: Documentation overstates CSRF coverage for future POST routes
verify_csrf exempts bearer-authenticated requests, so any POST route added to this router would remain CSRF-exempt (the dependency is effectively a no-op for bearer-authenticated routes).
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
|
nemotron-ultra-orB review VERDICT: Several correctness and security concerns need addressing before merge.
Automated first-pass review by the nemotron-ultra-orB lane. The lead still reviews before merge. |
Code Review SummaryStatus: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (4 files)
Fix these issues in Kilo Cloud Reviewed by step-3.7-flash · Input: 130.8K · Output: 29K · Cached: 1.5M |
|
nemotron-ultra-kilo review VERDICT: Potential authorization bypass in share.py fallback logic; SQL prefix match in get_by_slug may return wrong agent; missing tests.
Automated first-pass review by the nemotron-ultra-kilo lane. The lead still reviews before merge. |
|
Reviewed at code level with behavioural probes, not just the diff. BLOCKED. @hognek fix-forward on the existing branch. CI is green and every bot review is anchored on head 0c34e2e, so this one is blocked on substance, not process. What holds up:
Blocking items, each verified against the running code:
Bot adjudication so you do not chase noise: CodeRabbit's docs mismatch and slug-collision findings are real (items 4 and 2). qodo 1 through 4 are all real (items 1, 3, 4). Kilo's Re-request review after pushing and I will re-verify the four items with the same probes. |
|
nemotron-super review VERDICT: No blocking issues found. Automated first-pass review by the nemotron-super lane. The lead still reviews before merge. |
|
Superseded by #2225, now merged: same fallback plus the GLOB-bounded slug tail (with collision-suffix support), the bare-slug regression test and prefix-collision negative test this branch lacked, the CSRF dep, corrected docs, a human-username fail-closed guard, and metachar input gating. All review findings folded. |
…2225) * tsk-rll2pj [OPEN] Fix-forward PR 2202 (S2A follow-ups): slug fallbac * complete supersede of #2202: CSRF dep on share router + corrected share-destinations docs * fold review findings: skip human usernames in destination resolution, gate get_by_slug input to slug shape (kills GLOB metachar surface) * fail closed when the username check errors (kilo fold)
Autonomous build of board card tsk-f4nt53.
Files:
docs/agent-coordination.md | 45 +++++++++++++++++++++++++++++++++++++
tinyagentos/agent_registry_store.py | 23 +++++++++++++++++++
tinyagentos/routes/init.py | 2 +-
tinyagentos/routes/share.py | 2 ++
4 files changed, 71 insertions(+), 1 deletion(-)
Summary by CodeRabbit
New Features
Security