Skip to content

fix: harden input validation#67

Merged
jpicklyk merged 9 commits intomainfrom
fix/harden-input-validation
Mar 8, 2026
Merged

fix: harden input validation#67
jpicklyk merged 9 commits intomainfrom
fix/harden-input-validation

Conversation

@jpicklyk
Copy link
Owner

@jpicklyk jpicklyk commented Mar 8, 2026

Summary

  • requireUUID() helper — Added to BaseToolDefinition to eliminate !! non-null assertions on extractUUID(required=true) calls. Replaced 4 call sites across QueryNotesTool, QueryDependenciesTool, and GetNextStatusTool.
  • Offset bounds check — Added .coerceAtLeast(0) in SQLiteWorkItemRepository.findByFilters() for defense-in-depth (tool layer already coerces, but repository should not assume validated input).
  • Time range validation — Added early rejection of inverted time ranges (createdAfter > createdBefore, etc.) in QueryItemsTool.executeSearch() to prevent silent empty results.
  • Visited-set cycle detection — Replaced repeat(MAX_DEPTH + 1) with a visited-set loop in ItemHierarchyValidator.validateAndComputeDepth() to correctly detect pre-existing cycles in the hierarchy.

Test plan

  • All 1240+ existing tests pass (./gradlew :current:clean :current:test)
  • No behavioral changes for valid inputs — all fixes are defensive guards

🤖 Generated with Claude Code

jpicklyk and others added 9 commits March 8, 2026 08:49
RELATES_TO dependencies are purely informational and cannot create
blocking deadlocks. The DFS cycle detection was incorrectly traversing
RELATES_TO edges, causing false positive cycle rejections — e.g.,
creating B RELATES_TO A when A BLOCKS B existed would be rejected.

Three changes:
- DFS filter: `!= IS_BLOCKED_BY` → `== BLOCKS` (excludes RELATES_TO)
- insertDependencyInTransaction: skip cycle check for RELATES_TO
- createBatch: skip cycle check for RELATES_TO

Adds 5 TDD tests covering the fix and regression guards.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract create, update, and delete operations from the 702-line god class
into CreateItemHandler, UpdateItemHandler, and DeleteItemHandler. Consolidate
duplicated hierarchy validation (cycle detection, depth computation) into
shared ItemHierarchyValidator service. Extract JSON field helpers into
ItemFieldExtractors. ManageItemsTool is now a ~170-line dispatcher.

MCP interface unchanged — all existing tests pass without modification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add DatabaseManager.suspendedTransaction() extension function that wraps
newSuspendedTransaction + try/catch into a single call. Apply across all
33 suspend repository methods in SQLiteNoteRepository (7),
SQLiteRoleTransitionRepository (5), and SQLiteWorkItemRepository (21).

Net reduction of ~80 lines of boilerplate. Consolidates the deprecated
newSuspendedTransaction call to a single site for future Exposed migration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract WorkItem, Note, and Dependency serialization into extension
functions in EntityJsonSerializers.kt. Replace 17+ inline
role.name.lowercase() / priority.name.lowercase() calls across 10 tool
files with Role.toJsonString() / Priority.toJsonString() helpers.

Removes duplicate private serializer functions from QueryItemsTool and
QueryNotesTool, reducing ~40 lines of duplicated code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add extractItemBoolean to ItemFieldExtractors for handler-context
boolean extraction. Replace inline jsonObject["key"]?.jsonPrimitive
?.booleanOrNull patterns with optionalBoolean() across 5 tool files.
Replace inline offset extraction with optionalInt() in QueryItemsTool.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tory

Extract buildFilteredQuery() to eliminate exact duplicate filter
construction between findByFilters() and countByFilters() — 20 lines
of identical condition-building code consolidated into one method.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…s, cycle detection

- Add requireUUID() helper to BaseToolDefinition to eliminate !! non-null assertions
- Replace extractUUID(required=true)!! with requireUUID() in 4 call sites
- Add .coerceAtLeast(0) offset bounds check in SQLiteWorkItemRepository.findByFilters()
- Add time range validation (createdAfter < createdBefore, etc.) in QueryItemsTool.executeSearch()
- Replace repeat()-based ancestor walk with visited-set cycle detection in ItemHierarchyValidator

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keep refactored extension methods, transaction wrapper, and toWorkItemOrNull
naming from the decompose-manage-items refactoring that landed on main.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keep hardening fixes (time range validation, visited-set cycle detection)
that are the purpose of this PR.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jpicklyk jpicklyk merged commit 770e939 into main Mar 8, 2026
2 checks passed
@jpicklyk jpicklyk deleted the fix/harden-input-validation branch March 8, 2026 15:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant