fix: use bounded read for workflow catalog HTTP responses - #3766
Quratulain-bilal wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Bounds workflow and step catalog HTTP responses to 1 MiB, mitigating memory-exhaustion risks.
Changes:
- Uses
read_response_limitedfor both catalog fetch paths. - Applies
MAX_JSON_METADATA_BYTESas the response limit.
Show a summary per file
| File | Description |
|---|---|
src/specify_cli/workflows/catalog.py |
Adds bounded reads for workflow and step catalogs. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comments suppressed due to low confidence (1)
src/specify_cli/workflows/catalog.py:1219
- Please cover the step-catalog fetch path with an oversized streamed response and assert
StepCatalogError. StepCatalog has separate fetch/cache handling, and its current fetch tests also exit before reading the body, so they do not verify that this call site actually enforces the new limit.
data = json.loads(
read_response_limited(resp, max_bytes=MAX_JSON_METADATA_BYTES).decode("utf-8")
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Medium
mnriem
left a comment
There was a problem hiding this comment.
Please address Copilot feedback
The workflow catalog fetch used unbounded resp.read() to read HTTP responses into memory. A malicious or misconfigured catalog server could return an arbitrarily large response causing OOM. Replace with read_response_limited() capped at MAX_JSON_METADATA_BYTES (1 MiB) at both call sites, consistent with how other JSON fetch paths in the codebase enforce bounded reads.
291d343 to
5c2087c
Compare
|
Please resolve conflicts |
There was a problem hiding this comment.
Review details
Comments suppressed due to low confidence (6)
tests/test_workflows.py:6843
- This patch target does not exist: the workflow fetch passes
MAX_JSON_CATALOG_BYTEStoread_response_limited. As written, the test fails during setup rather than validating recovery after an oversized response.
"specify_cli.workflows.catalog.MAX_JSON_METADATA_BYTES", 512
tests/test_workflows.py:7675
workflows.catalogexposesMAX_JSON_CATALOG_BYTES, notMAX_JSON_METADATA_BYTES, so this monkeypatch fails withAttributeErrorbefore exercisingStepCatalog._fetch_single_catalog. Patch the constant actually used by that fetch.
"specify_cli.workflows.catalog.MAX_JSON_METADATA_BYTES", 512
tests/test_workflows.py:7727
- This test patches a nonexistent module attribute and therefore cannot verify that a healthy step catalog still works after rejection. The production bounded read uses
MAX_JSON_CATALOG_BYTES.
"specify_cli.workflows.catalog.MAX_JSON_METADATA_BYTES", 512
tests/test_workflows.py:7666
- The step-catalog fetch is bounded by
MAX_JSON_CATALOG_BYTES, not the metadata limit. Update the docstring so it documents the actual regression contract.
MAX_JSON_METADATA_BYTES instead of reading unbounded into memory."""
tests/test_workflows.py:6791
workflows.catalogimportsMAX_JSON_CATALOG_BYTES, notMAX_JSON_METADATA_BYTES, somonkeypatch.setattrraisesAttributeErrorand this regression test never reaches the fetch. Patch the catalog limit used by the production call instead.
This issue also appears in the following locations of the same file:
- line 6843
- line 7675
- line 7727
"specify_cli.workflows.catalog.MAX_JSON_METADATA_BYTES", 512
tests/test_workflows.py:6782
- The implementation now uses the catalog-specific 8 MiB ceiling, so this docstring names the wrong constant and incorrectly describes the behavior under test.
This issue also appears on line 7666 of the same file.
MAX_JSON_METADATA_BYTES instead of reading unbounded into memory."""
- Files reviewed: 1/1 changed files
- Comments generated: 0 new
- Review effort level: Medium
|
Please address test & lint errors |
|
This pull request has been automatically marked as stale because it has not had any activity for 60 days. It will be closed in 30 days if no further activity occurs. |
…w-catalog-response
The unbounded-read fix this PR opened with landed upstream when src/specify_cli/workflows/catalog.py became the workflows/catalog/ package: both fetch sites now call read_response_limited() with MAX_JSON_CATALOG_BYTES, and tests/test_workflows.py already covers the rejection path at test_fetch_rejects_oversized_catalog_response. Drop the two rejection tests that duplicated that coverage and patched MAX_JSON_METADATA_BYTES, which no longer exists, so they failed with AttributeError. Keep the scenario main does not cover: an oversized catalog is rejected without poisoning the next healthy fetch. Patch MAX_JSON_CATALOG_BYTES on the package that _max_json_catalog_bytes() reads at call time. Assisted-by: opencode (model: mimo-v2.6-flash-free, autonomous)
|
Addressed in Why this PR lost its original payload. The unbounded What this branch now carries.
Verification. Disclosure. This change was prepared with AI coding assistance.
Assisted-by: opencode (model: mimo-v2.6-flash-free, autonomous) |
Summary
The workflow catalog fetch (
WorkflowCatalog._fetch_single_catalogandStepCatalog._fetch_single_catalog) used unboundedresp.read()to read HTTP responses into memory. A malicious or misconfigured catalog server could return an arbitrarily large response causing OOM.Changes
src/specify_cli/workflows/catalog.py(lines 541, 1214): Replacedresp.read()withread_response_limited(resp, max_bytes=MAX_JSON_METADATA_BYTES)capped at 1 MiB at both call sites, consistent with how other JSON fetch paths in the codebase enforce bounded reads.Testing
All 176 tests in
tests/workflows/pass after the fix.Security Impact
This is a Medium severity fix - it closes a potential memory exhaustion vector against the workflow catalog fetch endpoint.