Skip to content

fix(security): close CWE-939 urlopen sinks in Cursor memory CLIs - #225

Merged
claude[bot] merged 3 commits into
mainfrom
feat/cursor-memory-refuse-file-url
Aug 23, 2026
Merged

fix(security): close CWE-939 urlopen sinks in Cursor memory CLIs#225
claude[bot] merged 3 commits into
mainfrom
feat/cursor-memory-refuse-file-url

Conversation

@cryptoxdog

Copy link
Copy Markdown
Collaborator

Summary

  • Stop using urllib.request.urlopen in cursor_memory_client.py and cursor_neo4j_query.py (CWE-939 / file://).
  • Shared socket helper refuses file://, userinfo, and unsigned remote HTTP. Documented C1 http://46.62.243.82 and loopback still work.
  • No requests dependency.

Test plan

  • pytest tests/unit/test_cursor_memory_http.py --noconftest
  • ruff check on the four changed files
  • Confirm Semgrep no longer reports CWE-939 on these clients

Made with Cursor

Memory and Neo4j clients now use a socket HTTP helper that refuses file:// and unsigned remote http, keeping the documented C1 and loopback paths.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown

⚠️ Large PR Warning
Reviewable lines changed: 469
Warning threshold: 300 lines
Consider splitting for easier review

📋 Best Practices for Large Changes

  1. Refactoring + Features: Separate into 2 PRs
  2. Multiple Features: One PR per feature
  3. Database + Code: Separate migration from logic
  4. Generated Code: Exclude it from reviewable-size accounting

This PR passes the blocking limit but is larger than recommended.

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown

L9 Audit Harness Report

  • Generated: 2026-08-23T13:06:57.303864+00:00
  • Repo root: /home/runner/work/Cognitive.Engine.Graphs/Cognitive.Engine.Graphs
  • Overall result: ✅ PASSED
  • Exit code: 0

Step Results

Step Status Exit Code Notes
Architecture Audit ✅ Passed 0
Spec Coverage ✅ Passed 0
Contract Wiring ✅ Passed 0

Architecture Audit Findings

Severity Count
🔴 CRITICAL 0
🟠 HIGH 0
🟡 MEDIUM 25
🔵 LOW 0

See artifacts/audit_report.md for full details.

Spec Coverage

  • ✅ Implemented: 37
  • ⚠️ Partial: 9
  • ❌ Missing: 0
  • Total features: 46
Category Implemented Partial Missing Total
gates 10 0 0 10
scoring 7 0 0 7
v1.1_node 2 0 0 2
v1.1_edge 2 0 0 2
v1.1_action 0 2 0 2
v1.1_scoring 1 1 0 2
action_handler 0 6 0 6
gds_algorithm 5 0 0 5
research_pattern 10 0 0 10

See artifacts/coverage_report.md for full details.

Next Steps

All checks passed. Safe to merge.

Comment thread agents/cursor/_safe_http.py Fixed

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: abd7670d34

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread agents/cursor/_safe_http.py Outdated
Comment thread agents/cursor/_safe_http.py Outdated
Comment thread agents/cursor/cursor_memory_client.py
CI on this branch was red on ruff-format, and CodeQL raised a new high
alert against the socket client the PR introduces. Root causes, all in
code this PR touches:

- The shared SSL contexts came from a bare ssl.create_default_context(),
  which still negotiates TLSv1 and TLSv1.1. Both contexts now come from
  secure_ssl_context(), pinned to TLSv1_2 as the minimum.
  Closes CodeQL "Use of insecure SSL/TLS version" (py/insecure-protocol).
- require_http_url embedded the first 120 characters of the rejected URL
  in every error message, so a userinfo-bearing endpoint leaked its
  credentials into any log that recorded the error. Messages now carry
  redact_url() output: scheme and authority only.
- The Host header was built as f"{host}:{port}", which produced the
  invalid authority "::1:8000" for an IPv6 loopback URL, and it dropped
  an explicit port whenever it was 80 or 443 regardless of scheme, so
  https://host:80 was sent as "Host: host". format_authority() brackets
  IPv6 literals, and the port is now omitted only when it is the default
  for the selected scheme.
- ruff format over the two files CI flagged.

Tests cover each fix: minimum TLS version on all three live contexts,
userinfo never surfacing in an error, redact_url dropping path/query and
bracketing IPv6, format_authority across the four host/port shapes, and
a real loopback exchange asserting the Host header keeps its port.

Local: ruff check + ruff format --check clean on 0.15.5 and 0.15.8,
mypy engine/ clean (139 files), pytest -m unit 741 passed / 3 pre-existing
skips.

Remediation-Cycle: Cognitive.Engine.Graphs#225/cycle-1

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015dio7eWN7aBnLnszqmF2JS
Comment thread tests/unit/test_cursor_memory_http.py Fixed
CodeQL raised py/incomplete-url-substring-sanitization (high) against the
new userinfo test on the previous head: `assert "example.com" in message`
reads as a host check that a crafted URL could satisfy at an arbitrary
position.

The assertion is stronger as an equality anyway — redact_url() output is
fully determined, so the test now pins the whole message. No suppression
and no lost coverage: the credential-absence assertions are unchanged.

Remediation-Cycle: Cognitive.Engine.Graphs#225/cycle-2

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015dio7eWN7aBnLnszqmF2JS
@sonarqubecloud

Copy link
Copy Markdown

@claude
claude Bot merged commit e155f87 into main Aug 23, 2026
50 checks passed
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