Skip to content

fix(workflows): harden catalog.py against mis-shaped registry & non-string fields#3375

Open
jawwad-ali wants to merge 1 commit into
github:mainfrom
jawwad-ali:fix/workflow-catalog-malformed-input
Open

fix(workflows): harden catalog.py against mis-shaped registry & non-string fields#3375
jawwad-ali wants to merge 1 commit into
github:mainfrom
jawwad-ali:fix/workflow-catalog-malformed-input

Conversation

@jawwad-ali

Copy link
Copy Markdown
Contributor

Description

Two robustness gaps in workflows/catalog.py where the Workflow* classes diverge from their Step* siblings, which already guard both cases:

1. WorkflowRegistry._load returns json.load() verbatim. A JSON-valid but mis-shaped registry file crashes every method that indexes data["workflows"]:

  • workflow-registry.json = []is_installed/get/list/remove/add raise TypeError: list indices must be integers;
  • = {"schema_version": "1.0"} (no workflows key) → KeyError: 'workflows'.

StepRegistry._load already validates the shape (dict root + dict steps) and resets to default. Reproduced on main @ 92b7cf7.

2. WorkflowCatalog.search joins name/description/id without coercion. A catalog entry with a null or non-string field raises TypeError: sequence item 0: expected str instance, NoneType found. StepCatalog.search already coerces with str(... or "") — and has a regression test for exactly this; WorkflowCatalog.search was the outlier.

Fix

Mirror the siblings: validate the registry shape in _load (+ widen the except tuple to OSError/UnicodeError), and coerce the search fields with str(wf_data.get(...) or "").

Testing

  • test_load_tolerates_misshaped_registry (parametrized [] / no-workflows): registry recovers to the default shape and all methods run. Fails before (TypeError/KeyError).
  • test_search_with_non_string_fields (WorkflowCatalog, mirrors the existing StepCatalog test): null/int fields no longer raise. Fails before (TypeError).
  • Both verified fail-before via source-stash; TestWorkflowRegistry + the new catalog test pass; uvx ruff check clean.

AI Disclosure

  • I did use AI assistance (describe below)

Found and fixed with Claude Code (Claude Fable 5) under my direction. AI diffed the Workflow* classes against their Step* siblings; I reproduced both crashes, verified fail-before/pass-after, and reviewed the diff.

…tring fields

Two robustness gaps where WorkflowRegistry/WorkflowCatalog diverged from
their StepRegistry/StepCatalog siblings, which already guard both:

- WorkflowRegistry._load returned json.load() verbatim, so a JSON-valid
  but mis-shaped registry (a list root, or a dict lacking a 'workflows'
  mapping) made is_installed/get/list/remove/add crash with
  TypeError/KeyError. Mirror StepRegistry._load: validate the shape and
  reset to default, and widen the except tuple to OSError/UnicodeError.
- WorkflowCatalog.search joined name/description/id without coercion, so a
  null or non-string field raised TypeError. Coerce with str(... or '')
  exactly as StepCatalog.search does.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jawwad-ali jawwad-ali requested a review from mnriem as a code owner July 6, 2026 20:21
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