feat(todo): categories and tags for todos (#28)#89
feat(todo): categories and tags for todos (#28)#89demo5-labworksdev wants to merge 4 commits intomainfrom
Conversation
- Add tags and todo_tags tables with predefined categories: Work, Personal, Shopping, Health
- Multi-select tag checkboxes on the add form with color-coded badges
- Filter todos by tag via ?tag=<id> query param
- Session-based ownership: todos are scoped to the creating browser session
- CSRF protection on all mutating endpoints (POST-only toggle/delete)
- Role-based authorization via _ROLE_PERMISSIONS with session role validation
- owner_id format validated against ^[0-9a-f]{32}$ to prevent malformed values
- Secure session cookie attributes: HttpOnly, SameSite=Lax, Secure (env-controlled)
- 24 unit tests, 100% coverage
Closes #28
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
I've got 1 comment for you to consider
Reviewed with 🤟 by Zenable
|
|
||
| # Switch session to a different owner | ||
| with client.session_transaction() as sess: | ||
| sess["owner_id"] = "different-owner" |
There was a problem hiding this comment.
The owner_id value "different-owner" does not match the ^[0-9a-f]{32}$ format that the PR description states is validated before any DB operation. If the app validates owner_id format and rejects or ignores malformed values, this test may be passing for the wrong reason — the 403 could be returned due to format validation failure rather than ownership enforcement. The same issue exists in test_delete_forbidden_for_non_owner (line 274). Use a valid 32-char hex string (e.g., "b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5") to ensure the 403 is actually testing ownership authorization and not input validation.
Why did I show this?
Category: bug
Comment Quality: high
Influenced by requirements:
Tools used:
get_file_lines,{'file_path': 'todo_app/app.py', 'start_line': '1', 'end_line': '200'}find_files,{'pattern': '**/app.py'}
Pass current_owner_id to template and guard action buttons with
{% if todo.owner_id == current_owner_id %} so users never see
controls for todos they do not own.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d format
- Set todo['is_owner'] in index route (server-side) so template uses a boolean
flag rather than comparing raw owner IDs
- Validate the DB-stored owner_id matches ^[0-9a-f]{32}$ inside authorize_todo
as defence-in-depth against malformed records
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
Implements issue #28 — Categories / Tags for todos.
?tag=<id>query param with a filter bar on the index pageSecurity
_ROLE_PERMISSIONSmap; session role validated against known rolesowner_idformat validated against^[0-9a-f]{32}$before any DB operationHttpOnly,SameSite=Lax,Secure(controlled viaHTTPSenv var)Test plan
uv run pytest tests/test_app.py -v)🤖 Generated with Claude Code