Skip to content

fix: use bounded read for integration catalog HTTP responses - #3763

Open
Quratulain-bilal wants to merge 2 commits into
github:mainfrom
Quratulain-bilal:fix/unbounded-catalog-response
Open

fix: use bounded read for integration catalog HTTP responses#3763
Quratulain-bilal wants to merge 2 commits into
github:mainfrom
Quratulain-bilal:fix/unbounded-catalog-response

Conversation

@Quratulain-bilal

Copy link
Copy Markdown
Contributor

Summary

The integration catalog fetch in integrations/catalog.py used unbounded resp.read() to read HTTP responses into memory. A malicious or misconfigured catalog server could return an arbitrarily large response causing OOM (denial of service).

Changes

  • src/specify_cli/integrations/catalog.py: Replaced resp.read() with read_response_limited(resp, max_bytes=MAX_JSON_METADATA_BYTES) capped at 1 MiB, consistent with how other JSON fetch paths in the codebase (_version.py, _github_http.py, authentication/azure_devops.py) already enforce bounded reads.
  • tests/integrations/test_integration_catalog.py: Updated FakeResponse.read() mock to accept a positional n argument for streaming compatibility with read_response_limited().

Testing

All 117 tests in test_integration_catalog.py pass after the fix.

Security Impact

This is a Medium severity fix - it closes a potential memory exhaustion vector against the integration catalog fetch endpoint, aligning it with the bounded-read pattern already used everywhere else in the codebase.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds bounded integration-catalog response reads and an unrelated skill-frontmatter parsing fix.

Changes:

  • Limits catalog JSON responses to 1 MiB.
  • Updates HTTP response mocks for streaming reads.
  • Parses skill frontmatter using line-anchored delimiters.
Show a summary per file
File Description
src/specify_cli/integrations/catalog.py Adds bounded catalog reads.
tests/integrations/test_integration_catalog.py Updates response mocks.
src/specify_cli/integrations/base.py Fixes skill frontmatter parsing.
tests/integrations/test_skill_frontmatter_quoting.py Tests embedded delimiter handling.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 4/4 changed files
  • Comments generated: 3
  • Review effort level: Medium

Comment thread src/specify_cli/integrations/catalog.py
Comment thread tests/integrations/test_integration_catalog.py
Comment thread src/specify_cli/integrations/base.py

@mnriem mnriem left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are putting in a default there needs to be a way to override the default

SkillsIntegration.setup parsed each command template's frontmatter with
raw.split("---", 2). A bare substring split stops at the first `---`
*anywhere*, so a template whose description embeds `---` (e.g.
"Separate sections with --- markers") truncated the parsed frontmatter:
later keys were dropped, the description fell back to the generic default,
and the leftover frontmatter spilled into the skill body.

Scan for the closing `---` on its own line instead, for both the
description parse and the body strip. The frontmatter block is parsed
unstripped so trailing newlines in literal (|) block scalars still survive,
and the body slice keeps the newline after the marker so output stays
byte-for-byte identical to the old split for well-formed templates.

Adds regression tests covering the dashed-description truncation and the
frontmatter-spilled-into-body cases.
@Quratulain-bilal
Quratulain-bilal force-pushed the fix/unbounded-catalog-response branch from b4d94ba to ff96c2c Compare July 27, 2026 20:27
@Quratulain-bilal

Quratulain-bilal commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

All 3 issues addressed in the latest push

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Comments suppressed due to low confidence (1)

src/specify_cli/integrations/base.py:1634

  • This introduces a separate SKILL.md frontmatter behavior change that is absent from the PR title, summary, change list, and testing claims, all of which describe only bounded catalog HTTP reads. Please either move the frontmatter implementation/tests into a focused PR or update this PR's description and testing evidence so reviewers can assess both fixes.
            # Parse frontmatter for description. Locate the closing ``---`` on
            # its own line rather than with ``raw.split("---", 2)`` — a bare
            # substring split stops at the first ``---`` *anywhere*, including
            # one inside a value such as ``description: Separate sections
            # with ---``, which truncates the frontmatter and drops later keys.
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment on lines +1639 to +1644
fm_lines = raw.splitlines(keepends=True)
fm_close = next(
(
i
for i in range(1, len(fm_lines))
if fm_lines[i].rstrip() == "---"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer relevant: since the unrelated base.py changes were removed, there is no HermesIntegration.setup() path affected by this PR.

@mnriem

mnriem commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Please address Copilot feedback

The integration 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), consistent with how other JSON fetch paths in the codebase
(_version.py, _github_http.py, authentication/azure_devops.py) already
enforce bounded reads.

Pass error_type=IntegrationCatalogError so oversized catalogs are
caught by the existing per-entry recovery path in
_get_merged_integrations() rather than aborting the entire merge.

Add regression test verifying oversized responses are rejected as
IntegrationCatalogError and that healthy catalogs remain usable.
@Quratulain-bilal
Quratulain-bilal force-pushed the fix/unbounded-catalog-response branch from ff96c2c to 691fae3 Compare July 27, 2026 21:39
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.

3 participants