-
Notifications
You must be signed in to change notification settings - Fork 149
fix(cql): shape-check the definitions/subgraphs containers before reading them #853
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2794,8 +2794,18 @@ def _subgraph_defs_by_id(workflow: dict) -> dict[str, dict]: | |
| always wins. We still register ``name`` as a *fallback* key (only when it | ||
| doesn't shadow an id and isn't ambiguous across defs) to support older | ||
| name-typed templates that predate UUID ids. | ||
|
|
||
| Containers that are not a ``dict``/``list`` read as "no definitions", so a | ||
| corrupt file degrades to an empty index rather than raising (a truthy | ||
| non-dict ``definitions`` or non-list ``subgraphs``) or walking a string | ||
| per-character. | ||
| """ | ||
| defs = (workflow.get("definitions") or {}).get("subgraphs") or [] | ||
| definitions = workflow.get("definitions") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟢 Low — The sweep for this idiom appears to miss CLI-reachable siblings: Raised by 1 of 6 reviewers (claude-opus-5-thinking-max adversarial). |
||
| if not isinstance(definitions, dict): | ||
| return {} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟢 Low — The degradation is silent: an unreadable Raised by 1 of 6 reviewers (claude-opus-5-thinking-max edge-case). |
||
| defs = definitions.get("subgraphs") | ||
| if not isinstance(defs, list): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Medium — The new checks stop at the outer Raised by 3 of 6 reviewers (gpt-5.6-sol-max adversarial, gpt-5.6-sol-max edge-case, claude-opus-5-thinking-max edge-case). |
||
| return {} | ||
| by_id: dict[str, dict] = {} | ||
| name_counts: dict[str, int] = {} | ||
| name_first: dict[str, dict] = {} | ||
|
|
@@ -3493,11 +3503,14 @@ def _count_instances(workflow: dict, def_id: str) -> int: | |
| for n in workflow.get("nodes") or []: | ||
| if isinstance(n, dict) and str(n.get("type", "")) == def_id: | ||
| count += 1 | ||
| for sg in (workflow.get("definitions") or {}).get("subgraphs") or []: | ||
| if isinstance(sg, dict): | ||
| for n in sg.get("nodes") or []: | ||
| if isinstance(n, dict) and str(n.get("type", "")) == def_id: | ||
| count += 1 | ||
| definitions = workflow.get("definitions") | ||
| subgraphs = definitions.get("subgraphs") if isinstance(definitions, dict) else None | ||
| if isinstance(subgraphs, list): | ||
| for sg in subgraphs: | ||
| if isinstance(sg, dict): | ||
| for n in sg.get("nodes") or []: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Medium — Raised by 4 of 6 reviewers (claude-opus-5-thinking-max adversarial, gpt-5.6-sol-max adversarial, claude-opus-5-thinking-max edge-case, gpt-5.6-sol-max edge-case). |
||
| if isinstance(n, dict) and str(n.get("type", "")) == def_id: | ||
| count += 1 | ||
| return count | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Medium —
sg.get("nodes") or []appends a truthy non-list, which then reachesfor node in nodestwo lines below, so{"nodes": [], "definitions": {"subgraphs": [{"nodes": 5}]}}still raisesTypeError— the same defect this hunk fixes, one level deeper — on remote-fetched (cache-poisonable) template JSON thatrun_template_cmdfeeds to_detect_paid_nodes/_enforce_spend_gatewithout exception handling._iter_workflow_nodesin this file already guards withisinstance(sg_nodes, list); note the new parametrized test only variesdefinitions/subgraphs, never a per-subgraphnodes, which is why the gap survives.Raised by 4 of 6 reviewers (claude-opus-5-thinking-max adversarial, gpt-5.6-sol-max adversarial, claude-opus-5-thinking-max edge-case, gpt-5.6-sol-max edge-case).