-
-
Notifications
You must be signed in to change notification settings - Fork 33
feat(agents): canvas_read/canvas_write scopes (#1800 slice 1) #1824
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
Changes from all commits
d5ff3c1
9874328
297fd18
81a84c4
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 | ||||
|---|---|---|---|---|---|---|
|
|
@@ -54,6 +54,12 @@ | |||||
| # agent's OWN project only (bound by the token's project_id claim). Does NOT | ||||||
| # grant task create, member management, or project lifecycle. | ||||||
| "project_tasks", | ||||||
| # Canvas access: read and write on a specific project's canvas. Like | ||||||
| # project_tasks, a project_id is required so the token is bound to the | ||||||
| # operator-validated project rather than whatever the unauthenticated agent | ||||||
| # named in the request. | ||||||
| "canvas_read", | ||||||
| "canvas_write", | ||||||
| }) | ||||||
|
|
||||||
|
|
||||||
|
|
@@ -298,17 +304,26 @@ async def _do_approve(request: Request, request_id: str, body: ApproveBody, user | |||||
| ), | ||||||
| ) | ||||||
|
|
||||||
| # project_tasks binds the token to a specific project and adds a membership | ||||||
| # row, so require the human to pick that project explicitly in the consent | ||||||
| # card. Never fall back to the agent-supplied project_id for a task grant: | ||||||
| # POST /api/agents/auth-requests is unauthenticated, so the request could | ||||||
| # name any existing project the operator never validated. Other scopes keep | ||||||
| # the fallback so global tokens still work. Checked before any registration | ||||||
| # so a rejected approval never leaves an orphaned agent. | ||||||
| if "project_tasks" in body.granted_scopes and body.project_id is None: | ||||||
| _CANVAS_SCOPES = {"canvas_read", "canvas_write"} | ||||||
| _PROJECT_SCOPES = {"project_tasks"} | _CANVAS_SCOPES | ||||||
|
|
||||||
| # project_tasks and the canvas scopes bind the token to a specific project | ||||||
| # and add a membership row, so require the human to pick that project | ||||||
|
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. WARNING: Comment overstates current behavior — canvas scopes do NOT add a membership row. This comment claims "project_tasks and the canvas scopes ... add a membership row", but the actual membership + a2a channel join at line 381 only triggers for |
||||||
| # explicitly in the consent card. Never fall back to the agent-supplied | ||||||
| # project_id for these grants: POST /api/agents/auth-requests is | ||||||
| # unauthenticated, so the request could name any existing project the | ||||||
| # operator never validated. Other scopes keep the fallback so global | ||||||
| # tokens still work. Checked before any registration so a rejected approval | ||||||
| # never leaves an orphaned agent. | ||||||
| needs_project = bool(set(body.granted_scopes) & _PROJECT_SCOPES) | ||||||
| # Reject None, "", and whitespace-only: a blank project_id is not a real | ||||||
| # binding, and a downstream truthy check would treat it as unbound, so an | ||||||
| # empty string must fail closed exactly like a missing one. | ||||||
| if needs_project and not (body.project_id and body.project_id.strip()): | ||||||
|
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. SUGGESTION: Guard strips for emptiness but the bound project_id is stored unstripped This rejects whitespace-only ids (e.g. Reply with |
||||||
| missing = sorted(set(body.granted_scopes) & _PROJECT_SCOPES) | ||||||
| raise HTTPException( | ||||||
| status_code=400, | ||||||
| detail="project_id is required when granting project_tasks", | ||||||
| detail=f"project_id is required when granting {missing}", | ||||||
|
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. SUGGESTION: The 400 detail renders a raw Python list repr (e.g.
Suggested change
|
||||||
| ) | ||||||
|
Comment on lines
+307
to
327
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. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
rg -n 'project_tasks.*granted_scopes|granted_scopes.*project_tasks' tinyagentos/routes/agent_auth_requests.py -B5 -A15Repository: jaylfc/taOS Length of output: 1233 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the relevant section of the approval flow.
sed -n '300,410p' tinyagentos/routes/agent_auth_requests.py
printf '\n---\n'
# Find canvas scope handling elsewhere in the repo.
rg -n '"canvas_read"|"canvas_write"|canvas_scopes|canvas' tinyagentos -g '!**/__pycache__/**'Repository: jaylfc/taOS Length of output: 30822 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the project canvas routes for how canvas_read/canvas_write are enforced.
sed -n '1,260p' tinyagentos/routes/project_canvas.py
printf '\n---\n'
# Inspect project canvas store permission checks around can_edit_canvas and member rows.
sed -n '1,320p' tinyagentos/projects/canvas/store.py
printf '\n---\n'
# Find where project membership is used as the authoritative signal for canvas access.
rg -n 'can_edit_canvas|project_members|add_member\(|a2a channel|membership' tinyagentos/routes tinyagentos/projects -g '!**/__pycache__/**'Repository: jaylfc/taOS Length of output: 22308 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the agent-facing canvas tools to see whether they depend on project membership.
sed -n '1,260p' tinyagentos/projects/canvas/mcp_tools.py
printf '\n---\n'
# Inspect project store schema and add_member behavior for the member row fields.
sed -n '1,280p' tinyagentos/projects/project_store.pyRepository: jaylfc/taOS Length of output: 17998 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect project a2a membership resolution and channel sync behavior.
sed -n '1,260p' tinyagentos/projects/a2a.py
printf '\n---\n'
# Show the scope definitions at the top of agent_auth_requests.py.
sed -n '1,90p' tinyagentos/routes/agent_auth_requests.pyRepository: jaylfc/taOS Length of output: 12101 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Find any other place where canvas scopes or project_tasks map to member permissions.
rg -n 'canvas_read|canvas_write|can_edit_canvas|project_tasks' tinyagentos -g '!**/__pycache__/**'
printf '\n---\n'
# Inspect any approval or grant logic around project member permissions.
rg -n 'add_member\(|can_edit_canvas =|set_canvas_permission|ensure_a2a_channel' tinyagentos/routes tinyagentos/projects -g '!**/__pycache__/**'Repository: jaylfc/taOS Length of output: 7827 Canvas scopes should join the project membership sync 🤖 Prompt for AI Agents |
||||||
|
|
||||||
| registry = _get_registry_store(request) | ||||||
|
|
||||||
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.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Incomplete label text.
"Grant project access for" reads as a truncated sentence with no object. Consider "Project" or "Grant project access for this request".
✏️ Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents