Skip to content

fix(backend): expand saved search query length to 2048 and enforce AST node complexity guard (#2604) - #2653

Merged
jcscottiii merged 1 commit into
mainfrom
fix-2604-saved-search-query-length
Aug 3, 2026
Merged

fix(backend): expand saved search query length to 2048 and enforce AST node complexity guard (#2604)#2653
jcscottiii merged 1 commit into
mainfrom
fix-2604-saved-search-query-length

Conversation

@jcscottiii

Copy link
Copy Markdown
Collaborator

Summary

This PR resolves Issue #2604 by expanding the SavedSearch.query character length limit from 256 to 2048 characters across OpenAPI schemas, Go HTTP backend validation handlers, and Lit frontend components.

To permit users to bookmark long feature ID lists (e.g., 33+ feature IDs) while protecting Cloud Spanner against parameter exhaustion limits (950 max parameters) and query amplification attacks, this change introduces upstream AST deduplication and structural complexity validation.


What Was Changed

  1. OpenAPI Schema Definitions (openapi/backend/openapi.yaml):

    • Updated SavedSearch.query maxLength: 2048 (was 256).
    • Updated SavedSearchUpdateRequest.query maxLength: 2048 and description maxLength: 1024.
    • Updated SavedSearchUpdateRequest.name maxLength: 32.
    • Regenerated Go and TypeScript OpenAPI types (make openapi).
  2. Upstream Go Backend Validation (backend/pkg/httpserver & lib/backendtypes):

    • Updated savedSearchQueryMaxLength = 2048.
    • Defined const MaxASTNodes = 50 in lib/backendtypes/types.go.
    • Implemented AST deduplication (searchtypes.Deduplicate) and node complexity validation (CountNodes(dedupNode) <= 50) in CreateSavedSearch and UpdateSavedSearch.
    • Mapped backendtypes.ErrQueryComplexityExceeded to HTTP 400 Bad Request.
  3. Spanner Adapter Expansion Safeguard (lib/gcpspanner/spanneradapters):

    • Enforced CountNodes(Deduplicate(expandedNode)) <= 50 in ValidateQueryReferences after recursive @saved-search resolution to prevent expansion amplification bypasses.
  4. Frontend Constraints & Error Formatting (frontend/src/static/js):

    • Set QueryMaxLength: 2048 and DescriptionMaxLength: 1024 in webstatus-saved-search-editor.ts.
    • Enhanced frontend/src/static/js/api/errors.ts to format fieldErrorMap strings directly for Toast notifications.
  5. Test Suite & Verification:

    • Added HTTP boundary test cases for 2048 length, 50 AST nodes, >50 AST nodes, and duplicate deduplication in create_saved_search_test.go and update_saved_search_test.go.
    • Added AST deduplication and nil safety unit tests in features_search_parse_test.go.
    • Added TestWorstCaseMaxComplexityQuerySpannerLimits in spanner_pipeline_analysis_test.go verifying that a worst-case 50-node query consumes only 27 / 950 parameters (2.84% of limit) and 5.7 KB / 1 MB SQL text (0.55% of limit).

Corrected Assumptions / Learnings

  • AST Deduplication Unwrapping: When parsing left-associative binary AND/OR trees from ANTLR, deduplicating children leaves single-child Keyword nodes (A). Unwrapping single-child Keyword nodes (if node.IsKeyword() && len(children) == 1 { return children[0] }) collapses binary tree chains like (((A AND A) AND A) AND A) down to A.
  • Expansion Complexity Validation: Upstream validation must run both on the unexpanded query string (in HTTP handlers) and after expandSavedSearches (in ValidateQueryReferences) to prevent nested @saved-search references from amplifying AST node complexity post-validation.

Fixes #2604

@jcscottiii
jcscottiii force-pushed the fix-2604-saved-search-query-length branch from a8fc771 to 2c55401 Compare July 28, 2026 18:36
…T node complexity guard (#2604)

Increases SavedSearch.query max length from 256 to 2048 characters in OpenAPI schemas and backend HTTP handlers (CreateSavedSearch and UpdateSavedSearch).

To protect Cloud Spanner against parameter exhaustion and query amplification attacks while permitting long feature ID lists (issue #2604), this change introduces:
1. Upstream AST Deduplication (searchtypes.Deduplicate) based on the Boolean Idempotent Law (A AND A = A, A OR A = A).
2. Structural AST Complexity Validation capping post-deduplication AST complexity to MaxASTNodes = 50.
3. Recursive Saved Search Expansion Safeguard in ValidateQueryReferences ensuring nested saved: search expansion cannot exceed 50 AST nodes.
4. Comprehensive unit, HTTP handler boundary, Spanner emulator, and programmatic worst-case regression test suites.

Fixes #2604
@jcscottiii
jcscottiii force-pushed the fix-2604-saved-search-query-length branch from 2c55401 to 30e6952 Compare July 28, 2026 21:19
@jcscottiii

Copy link
Copy Markdown
Collaborator Author

This can wait until next week's release @neilv-g

@jcscottiii
jcscottiii added this pull request to the merge queue Aug 3, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks Aug 3, 2026
@jcscottiii
jcscottiii added this pull request to the merge queue Aug 3, 2026
Merged via the queue into main with commit d865ba3 Aug 3, 2026
13 checks passed
@jcscottiii
jcscottiii deleted the fix-2604-saved-search-query-length branch August 3, 2026 17:17
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.

[BUG] Unable to bookmark a query longer than 256 chars

2 participants