Skip to content

security: fix four vulnerabilities (GHSA-779p-m5rp-r4h4)#14734

Merged
guill merged 4 commits into
masterfrom
security/ghsa-779p-fixes
Jul 3, 2026
Merged

security: fix four vulnerabilities (GHSA-779p-m5rp-r4h4)#14734
guill merged 4 commits into
masterfrom
security/ghsa-779p-fixes

Conversation

@mattmillerai

@mattmillerai mattmillerai commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Coordinated-disclosure fixes for GHSA-779p-m5rp-r4h4 (reported via CISA VINCE).

CVEs fixed

CVE Issue Fix
CVE-2026-56670 Stored XSS via SVG upload on /view Force download of SVG/XML responses
CVE-2026-56671 Path traversal in /experiment/models/preview Contain reads within the model folder
CVE-2026-56672 Stored XSS via /userdata/{file} content-type sniffing Stop inline rendering of uploaded content
CVE-2026-56673 Path traversal in LoadImage via /prompt Harden get_annotated_filepath

Not included

  • CVE-2026-56674 (Origin: null CSRF): assessed as already mitigated by the pre-existing Sec-Fetch-Site: cross-site check on current browsers. The null-origin change was reverted to avoid breaking legitimate sandboxed-iframe embeds. This CVE is being disputed with CISA rather than fixed.

Notes

  • Adds regression tests under tests-unit/security_test/ (four findings).
  • Please squash-merge (default-branch linear-history rule).

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR addresses GHSA-779p-m5rp-r4h4, fixing content-type sniffing/stored-XSS, path traversal, and CSRF issues. It adds DANGEROUS_CONTENT_TYPES/is_dangerous_content_type and is_within_directory helpers in folder_paths.py, applies them to asset download, userdata, model preview, and image view endpoints to force safe content-types/attachment disposition and block directory/symlink traversal. It also extracts loopback-aware origin CSRF logic into a new utils/origin_check.py module used by server.py, and adds extensive unit tests covering each fix.

Changes

Cohort / File(s) Summary
folder_paths.py Added DANGEROUS_CONTENT_TYPES, is_dangerous_content_type(), is_within_directory(); hardened get_annotated_filepath() and exists_annotated_filepath()
app/assets/api/routes.py Replaced hardcoded MIME list with is_dangerous_content_type() check in download_asset_content
app/user_manager.py Enforced safe content-type, nosniff, and attachment headers on /userdata/{file}
server.py Reworked view_image disposition/header logic; delegated origin CSRF checks to new module
app/model_manager.py Hardened model preview route with input validation and directory containment checks
utils/origin_check.py New module with is_loopback() and is_cross_origin_forbidden()
tests-unit/** Added regression tests for SVG XSS, userdata XSS, preview traversal, annotated traversal, dangerous content types, and origin CSRF; updated existing folder_paths test assertions

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Endpoint
  participant FolderPaths
  Client->>Endpoint: request file/asset with content-type
  Endpoint->>FolderPaths: is_dangerous_content_type(content_type)
  FolderPaths-->>Endpoint: True/False
  alt dangerous
    Endpoint-->>Client: octet-stream, attachment, nosniff
  else safe
    Endpoint-->>Client: original content-type
  end
Loading

Related PRs: None specified.

Suggested labels: security, needs-review

Suggested reviewers: None specified.

Poem
A rabbit hops through code so deep,
Patching holes where XSS could creep,
Traversal blocked, and origins checked,
Loopback CSRF now correctly wrecked,
Hop, hop, hooray—the burrow's secure and neat!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title matches the PR's security-focused vulnerability fixes, though it understates the number of issues.
Description check ✅ Passed The description is about the same GHSA security fixes and regression tests, even though one CVE note is inconsistent.

Comment @coderabbitai help to get the list of available commands.

@mattmillerai mattmillerai added the Core Core team dependency label Jul 3, 2026
@mattmillerai mattmillerai added the cursor-review Trigger multi-model Cursor code review label Jul 3, 2026
- CVE-2026-56670: force download of SVG/XML responses on /view to prevent stored XSS
- CVE-2026-56671: contain /experiment/models/preview reads within the model folder
- CVE-2026-56672: stop inline rendering of uploaded /userdata/{file} content
- CVE-2026-56673: prevent path traversal in get_annotated_filepath (LoadImage /prompt input)
- CVE-2026-56674: reject opaque/null Origin to close the CSRF middleware bypass

Adds regression tests under tests-unit/security_test/ covering all five.
@mattmillerai mattmillerai force-pushed the security/ghsa-779p-fixes branch from 926c169 to ae4fcaa Compare July 3, 2026 02:10
guill
guill previously approved these changes Jul 3, 2026

@github-actions github-actions Bot 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.

🔍 Cursor Review — Consolidated panel

Triggered by @mattmillerai.

Found 8 finding(s).

Severity Count
🟠 High 1
🟡 Medium 4
🟢 Low 2
⚪ Nit 1

Panel: 8/8 reviewers contributed findings.

Comment thread utils/origin_check.py Outdated
Comment thread utils/origin_check.py Outdated
Comment thread folder_paths.py
Comment thread utils/origin_check.py Outdated
Comment thread server.py Outdated
Comment thread utils/origin_check.py Outdated
Comment thread app/model_manager.py
Comment thread utils/origin_check.py Outdated
- Fix Windows CI failure in test_get_annotated_filepath: compare against
  os.path.abspath(...) to match the intentional abspath normalization added
  by the traversal hardening (abspath prepends the drive letter on Windows).
- origin_check: narrow the bare `except:` in is_loopback() to ValueError so
  genuine interrupts aren't swallowed (review nit).
- origin_check: guard .port access in is_cross_origin_forbidden() so a
  malformed/out-of-range port (e.g. Origin: http://127.0.0.1:99999) fails
  closed with a 403 instead of surfacing an uncaught 500 in the middleware.
- server /view: escape backslash/quote in the Content-Disposition filename
  (RFC 6266 quoted-string) so a filename containing a double quote can't
  malform the response header.
@mattmillerai mattmillerai force-pushed the security/ghsa-779p-fixes branch from 8a275f1 to e4eb7f2 Compare July 3, 2026 02:58

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests-unit/security_test/test_ghsa_779p_05_dangerous_content_types.py (1)

8-14: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Docstring describes a stale/bypassable mechanism.

This describes the /view closure as checking content_type in folder_paths.DANGEROUS_CONTENT_TYPES (raw set membership), but the actual handler (per server.py's view_image) calls folder_paths.is_dangerous_content_type(content_type), which normalizes casing/params/XML dialects — that's the whole point of the hardening this test file documents. Worth correcting so future readers don't think the raw membership check (the bypassable one) is what's actually gating /view.

✏️ Suggested docstring fix
-The /view forced-download decision lives in a closure inside
-server.PromptServer.add_routes (server.py ~line 624), which checks
-`content_type in folder_paths.DANGEROUS_CONTENT_TYPES` and, on a match, rewrites
-the response to application/octet-stream with a Content-Disposition: attachment
-header. server.py cannot be imported in a unit test (importing it spins up the
+The /view forced-download decision lives in a closure inside
+server.PromptServer.add_routes (server.py ~line 624), which checks
+`folder_paths.is_dangerous_content_type(content_type)` and, on a match, rewrites
+the response to application/octet-stream with a Content-Disposition: attachment
+header. server.py cannot be imported in a unit test (importing it spins up the
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests-unit/security_test/test_ghsa_779p_05_dangerous_content_types.py` around
lines 8 - 14, Update the test docstring/comments in the security test to
describe the actual /view gating logic, not the stale raw membership check.
Reference the view handler path via server.PromptServer.add_routes and the
view_image behavior, and make it clear that the closure uses
folder_paths.is_dangerous_content_type(...) rather than direct content_type in
folder_paths.DANGEROUS_CONTENT_TYPES membership. Keep the explanation aligned
with the hardened, normalized check so readers understand the test is pinning
the underlying dangerous-content data, not the bypassable raw set lookup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests-unit/security_test/test_ghsa_779p_03_annotated_traversal.py`:
- Around line 72-95: The symlink-escape test is not safe to run unconditionally
on Windows because os.symlink may require elevated privileges or Developer Mode.
Update test_is_within_directory_symlink_escape to add the same
platform/permission guard used by the other symlink tests, so the symlink setup
and assertion are skipped on Windows CI when symlinks are unavailable.

---

Nitpick comments:
In `@tests-unit/security_test/test_ghsa_779p_05_dangerous_content_types.py`:
- Around line 8-14: Update the test docstring/comments in the security test to
describe the actual /view gating logic, not the stale raw membership check.
Reference the view handler path via server.PromptServer.add_routes and the
view_image behavior, and make it clear that the closure uses
folder_paths.is_dangerous_content_type(...) rather than direct content_type in
folder_paths.DANGEROUS_CONTENT_TYPES membership. Keep the explanation aligned
with the hardened, normalized check so readers understand the test is pinning
the underlying dangerous-content data, not the bypassable raw set lookup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f3d7af24-b907-4bbc-ab95-78b13db88f98

📥 Commits

Reviewing files that changed from the base of the PR and between 35c1470 and 8a275f1a10a32254d1c2ae8ecd4f3980f77f3bf9.

📒 Files selected for processing (14)
  • app/assets/api/routes.py
  • app/model_manager.py
  • app/user_manager.py
  • folder_paths.py
  • server.py
  • tests-unit/assets_test/test_downloads.py
  • tests-unit/comfy_test/folder_path_test.py
  • tests-unit/security_test/__init__.py
  • tests-unit/security_test/test_ghsa_779p_01_origin_csrf.py
  • tests-unit/security_test/test_ghsa_779p_02_preview_traversal.py
  • tests-unit/security_test/test_ghsa_779p_03_annotated_traversal.py
  • tests-unit/security_test/test_ghsa_779p_04_userdata_xss.py
  • tests-unit/security_test/test_ghsa_779p_05_dangerous_content_types.py
  • utils/origin_check.py

Comment thread tests-unit/security_test/test_ghsa_779p_03_annotated_traversal.py
- test #3: guard the symlink-escape test with a try/except skip so it no
  longer errors on Windows CI where os.symlink needs elevated privileges /
  Developer Mode (mirrors the guard in the sibling test #2).
- test #5: refresh the stale module docstring to describe the actual /view
  gating (view_image closure calling folder_paths.is_dangerous_content_type,
  the normalising check) instead of the bypassable raw set-membership test.
@mattmillerai mattmillerai force-pushed the security/ghsa-779p-fixes branch from 29cd1a0 to 5611d1c Compare July 3, 2026 03:14
Per maintainer review, the reported CSRF is already mitigated by the pre-existing
Sec-Fetch-Site: cross-site check for current browsers, and the null-origin
rejection risked breaking legitimate sandboxed-iframe embeds. Restores
origin_only_middleware and is_loopback in server.py to their prior state
(the Sec-Fetch-Site check is retained) and removes utils/origin_check.py and its
regression test. The other four GHSA-779p fixes are unaffected.

@guill guill left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approved assuming CI passes

@mattmillerai mattmillerai changed the title security: fix five vulnerabilities (GHSA-779p-m5rp-r4h4) security: fix four vulnerabilities (GHSA-779p-m5rp-r4h4) Jul 3, 2026
@guill guill merged commit 96e0e35 into master Jul 3, 2026
16 checks passed
@comfyanonymous comfyanonymous deleted the security/ghsa-779p-fixes branch July 8, 2026 04:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Core Core team dependency cursor-review Trigger multi-model Cursor code review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants