Repository navigation
fix(cron): agent tools can no longer turn on a job's auto-approve - #24352
Conversation
Sensitive Change Review
|
UX Review (Opus 5.5) — ⏭️ skippedRevision |
First Principles Review (Opus 5.5) — ✅ PASSPremise-level review of First-Principles-Verdict: PASS Nothing blocks. Every item is a security fix with a linked issue (#24345), and the agent turning on its own unattended approval falls inside the documented boundary that the agent is untrusted about its own governance ceiling. Notes
What this change shipsInventory (6 items) — 6 justifiedIntent: stop an agent's cron tools (and internal-secret callers) from switching a cron job to unattended auto-approve; the owner does it from the Schedule page. FIX.
[FIRST-PRINCIPLES-REVIEWED] d8f7bee Description read[DESCRIPTION-READ] 7cf88b3021ede08409c2b644968b99c92153da79ebdf0175fd3480717ab71e1d That sha256 names the PR description this verdict read. Recompute it with |
Design Review (Opus 5.5) — ✅ PASSDesign-level review of Design-Verdict: PASS No design-level concerns. Turning auto-approve on now happens only through the owner-gated routes. The take-away readers I checked still work: the CLI calls Notes
[DESIGN-REVIEWED] d8f7bee Description read[DESCRIPTION-READ] b1309cd741249e59c14b462ba464dd2c78aa4806c15643c73f7791ed00138832 That sha256 names everything this verdict read: the PR description, plus the 1 evidence file(s) the workflow collected from it and handed this review. It covers that evidence because the media strip replaces every attachment URL with the same placeholder, so an edit swapping one attachment for another would leave the description's captured text identical while what this review judged changed. Recompute it with |
GPT 6.1 Review — ✅ no blocking findingsGPT 6.1 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
Opus 5.5 Review — ✅ no blocking findingsReviewed Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
d8d0d98 to
3bf46dc
Compare
3bf46dc to
270e16b
Compare
270e16b to
d8450ee
Compare
8b2b111 to
f7ee4f4
Compare
f7ee4f4 to
40a0e0c
Compare
|
|
AI-review override not recorded: |
40a0e0c to
e57670c
Compare
e57670c to
279a00e
Compare
A cron job with approval_mode="auto" approves every tool call of every later fire with nobody watching. The Schedule page is where the owner turns that on, but the agent tools and internal-secret callers could set it too: - cron_add / cron_update accepted approval_mode="auto". They now refuse it with a message pointing at the Schedule page; "" (turn it off) is still accepted. The gateway's /api/crons/tools route runs the same dispatch, so it is refused there as well. - cron_update kept an owner-set auto job on auto while changing its message, agent, model, schedule, channel or thread. Such an edit now writes approval_mode="" in the same update, so the owner re-enables auto for the changed job; a rename keeps it. Tests that called these routes on a bare app now carry the owner identity (dashboard_owner_helpers.as_owner). Out of scope: the job store file itself, tracked separately. Closes #24345
279a00e to
d8f7bee
Compare
Problem / Motivation
Goal: Agent tools and internal-secret callers can no longer turn on a cron job's auto-approve; the owner turns it on from the Schedule page.
A job with
approval_mode="auto"approves every tool call of every later run with nobody watching. Today the cron tools (cron_add/cron_update) accept it, and the dashboard cron write routes skip the owner check for a request that carries no app claim.Why it matters
An auto job keeps running unattended on a schedule. Turning that on should be the owner's choice, made where the owner can see it.
Not a goal
crons.json) and who can write it directly. Out of scope here and tracked separately.approval_mode.What changed (motivation → approach → change)
cron_addandcron_updaterefuseapproval_mode="auto"and point to the Schedule page.""still works, so a tool can turn auto off. The gateway's/api/crons/toolsroute runs the same dispatch, so the refusal holds there too. The tool descriptions (mcp_cron_tool_list.py) say so up front.cron_updatethat changesmessage,agent,model, schedule,channelorthread_tsnow also writesapproval_mode=""in the same update, so the owner re-enables auto for the changed job; a rename keeps it. Otherwise an owner-approved auto job could be changed to do something else and stay auto.request.get("app") == "", which skipped the owner check for a request with no app claim. They now usenot request.get("app"), the shape the file and workflow routes use. App tokens keep their manifest scope.Docs: the cron user guide (
src/kiro_crew/docs/cron-and-scheduling.md) and the cron spec say who turns auto on. Test fixtures that called these routes on a bare app now carry the owner identity (dashboard_owner_helpers.as_owner).Backwards compatibility
Breaking: an agent can no longer create or switch a job to
approval_mode="auto"throughcron_add/cron_update; it creates the job and the user turns auto on in the Schedule page. Acron_updatethat changes what an auto job does now turns auto off. Internal-secret callers of the five cron write routes now get403 owner_only.Writer sweep on origin/main @ 2356a1b:
website/src/components/JobForm.tsx/ApprovalModePicker.tsx-- Schedule page, owner cookie -- unchanged./api/cronsfrom the owner's browser (dashboard/handlers/change_cards.py) -- owner cookie, unchanged (test_change_card_one_shot.py).apps/cron_sdk.py-- App SDK -- unchanged.cli_commands_cron.py--kirocrew cron add --approval-mode auto-- unchanged./api/crons: only/run(cron_trigger.py),/history(mcp_cron_list_render.py) and/tools(mcp_cron.py); none of the five gated routes.Reader: src/kiro_crew/mcp_cron.py:_call_tool_inner -- chat --
cron_add/cron_updatewithout"auto"work as before (test_cron_add_with_default_approval_still_works,test_cron_update_can_still_turn_auto_off)Reader: src/kiro_crew/dashboard/handlers/cron_jobs.py:api_crons_create -- crew page -- the owner still creates an auto job (
test_the_owner_still_creates_an_auto_job_from_the_schedule_page,test_r7_owner_gate_positive_controls.py)Tests
test/test_cron_auto_approval_owner_only.py: internal-secret callers getowner_onlyon create/update/delete/batch delete/enable and nothing is written; the owner still creates an auto job;cron_add/cron_updaterefuse auto and write nothing;""still works; auto can be turned off.test_cron_auto_approval_owner_only.py(edits): each behaviour field turns auto off; a rename keeps it; an edit to a normal job says nothing about auto; the owner's PATCH keeps auto.test_cron_approval_mode.py,test_validation_user_actions.py,test_mcp_cron.py: the old "tool sets auto" expectations now expect the refusal.Proof: PROVEN test/test_cron_approval_mode.py::TestCronApprovalModeValidation::test_valid_auto, test/test_cron_auto_approval_owner_only.py::test_internal_secret_caller_is_refused_by_the_owner_gate[create], test/test_cron_auto_approval_owner_only.py::test_internal_secret_caller_is_refused_by_the_owner_gate[update], test/test_cron_auto_approval_owner_only.py::test_internal_secret_caller_is_refused_by_the_owner_gate[delete], test/test_cron_auto_approval_owner_only.py::test_internal_secret_caller_is_refused_by_the_owner_gate[batch] (head e57670c)
Manual verification
N/A — unit coverage drives each writer through the real handler or tool dispatch.
Related Issues
Closes #24345
Sibling sweep
Swept: git grep -n 'request.get("app") == ""' -- src/kiro_crew/dashboard/handlers/cron*.py
Sibling: src/kiro_crew/dashboard/handlers/cron_jobs.py -- changed here
Sibling: src/kiro_crew/dashboard/handlers/cron_runs.py -- not affected -- run/cancel/ack routes have an internal-secret caller and never write approval_mode
Swept: git grep -n 'approval_mode' -- src/kiro_crew/mcp_cron.py
Sibling: src/kiro_crew/mcp_cron.py -- changed here
Pattern harvest
Rule candidate: review-prompt
Pattern: an owner gate written as
request.get("app") == ""skips callers that publish no app claim; usenot request.get("app").Checklist
feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert: ...)