Skip to content

fix: trying to fix ssl certification issue while downloading on some macs#1

Merged
ParticularlyPythonicBS merged 1 commit intomainfrom
fix/ssl_cert_issues
Feb 1, 2026
Merged

fix: trying to fix ssl certification issue while downloading on some macs#1
ParticularlyPythonicBS merged 1 commit intomainfrom
fix/ssl_cert_issues

Conversation

@ParticularlyPythonicBS
Copy link
Member

@ParticularlyPythonicBS ParticularlyPythonicBS commented Jan 30, 2026

Summary by CodeRabbit

  • New Features

    • Added POST API to download the tutorial database into local assets with atomic streaming and automatic assets-directory creation.
    • Introduced a secure TLS context for downloads, with an opt-out via environment variable.
  • Tests

    • Added tests for successful download, SSL-context behavior, and error handling.
  • Chores

    • Added certifi dependency and package initializer.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 30, 2026

Walkthrough

Adds a POST /api/download_tutorial endpoint that streams a remote tutorial SQLite into assets/tutorial_database.sqlite, a secure SSL context utility, streaming-based asset download in temoa_runner.py, tests for the endpoint, and a backend/__init__.py; also adds certifi to project dependencies.

Changes

Cohort / File(s) Summary
Backend API endpoint
backend/main.py
Adds POST /api/download_tutorial which streams a remote tutorial SQLite into assets/tutorial_database.sqlite, creates assets/ if missing, uses a secure SSL context, writes via a temp .part file and atomically replaces the target; returns success path or HTTP 500 on failure.
Endpoint tests
backend/tests/test_download.py
Adds tests for success and failure paths of /api/download_tutorial, mocking urllib.request.urlopen, shutil.copyfileobj, file I/O, and Path operations; asserts SSL context behavior varies with TEMOA_SKIP_CERT_VERIFY.
SSL utility
backend/utils.py
Adds create_secure_ssl_context() that uses certifi's CA bundle by default and optionally disables verification when TEMOA_SKIP_CERT_VERIFY="1".
Asset download changes
temoa_runner.py
Replaces urllib.request.urlretrieve with streaming download using create_secure_ssl_context(), urllib.request.urlopen(..., context=ctx), shutil.copyfileobj, and atomic rename/cleanup of partial files; also adds related imports and minor type/annotation updates.
Package init
backend/__init__.py
Adds package initializer placeholder file.
Project config
pyproject.toml
Adds certifi to project dependencies.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Backend
    participant RemoteStorage
    participant FileSystem

    Client->>Backend: POST /api/download_tutorial
    Backend->>Backend: create_secure_ssl_context()
    Backend->>RemoteStorage: urlopen(remote_url, context)
    RemoteStorage-->>Backend: stream response
    Backend->>FileSystem: ensure assets/ exists
    Backend->>FileSystem: write to temp file via shutil.copyfileobj(stream, temp)
    Backend->>FileSystem: temp.replace(assets/tutorial_database.sqlite)
    FileSystem-->>Backend: replace complete
    Backend-->>Client: 200 OK {"status":"ok","path":".../assets/tutorial_database.sqlite"}

    Note over Backend: On error -> log and return 500 {"detail":"Download failed: ..."}
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically identifies the main change: fixing SSL certification issues during downloads on macOS machines.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/ssl_cert_issues

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@backend/main.py`:
- Around line 168-170: The code creates an insecure SSL context by setting
ctx.check_hostname = False and ctx.verify_mode = ssl.CERT_NONE; replace this
with a shared utility (e.g., create_secure_ssl_context) that builds an SSL
context using certifi roots (ssl.create_default_context(cafile=certifi.where())
or ctx.load_verify_locations(certifi.where())) and does not disable hostname
verification; update backend/main.py to call that new create_secure_ssl_context
function instead of creating and mutating ctx locally and reuse it from
temoa_runner.py to remove duplication.
- Around line 177-179: Replace the current except block that does "except
Exception as e:" and calls logging.error(...) then raises HTTPException with a
plain detail; instead call logging.exception(...) to include the traceback and
raise the HTTPException using "raise HTTPException(... ) from e" to preserve the
exception chain (the block that uses logging.error(f\"Failed to download
tutorial: {e}\") and raises HTTPException should be updated). Ensure the
HTTPException detail string remains informative but the underlying exception is
chained and the traceback is logged.

In `@backend/tests/test_download.py`:
- Around line 30-34: The test currently asserts an intentionally insecure SSL
configuration by checking kwargs["context"] is an ssl.SSLContext with
ctx.check_hostname False and ctx.verify_mode == ssl.CERT_NONE; update the test
to either document this expectation or make it flexible: add a concise comment
in test_download.py explaining that the test expects an insecure SSLContext (why
and whether it's intentional), or change the assertions around ctx, e.g., allow
either disabled verification or a verified context by checking for presence of a
context and conditionally asserting verification state, referencing kwargs, ctx,
ssl.SSLContext, ctx.check_hostname and ctx.verify_mode to locate the code to
modify.
- Around line 37-41: The test's mock patches the global "urllib.request.urlopen"
which doesn't intercept the call used by the /api/download_tutorial endpoint;
update test_download_tutorial_failure to patch urllib.request.urlopen on the
actual module that defines the endpoint/handler (the module that contains the
download_tutorial function used by the route) — e.g. patch
"<module_where_download_tutorial_is_defined>.urllib.request.urlopen" — so the
exception is raised where the code calls urlopen, then keep the existing
client.post("/api/download_tutorial") and assertions.

In `@temoa_runner.py`:
- Around line 126-128: The code in temoa_runner.py creates an SSLContext (ctx =
ssl.create_default_context()) and then disables verification via
ctx.check_hostname = False and ctx.verify_mode = ssl.CERT_NONE; update this to
avoid bypassing certificate validation by either: A) use certifi by creating the
context with cafile=certifi.where() (add certifi to pyproject.toml and import
certifi) and remove the check_hostname/verify_mode overrides, or B) make the
insecure path explicit and opt-in via an environment variable (e.g.,
TEMOA_SKIP_CERT_VERIFY) so that by default validation remains enabled; if you
keep the opt-out, log a clear warning when TEMOA_SKIP_CERT_VERIFY is set and
replicate the same change in backend/main.py and any tests that currently expect
disabled verification so they either use certifi or set the env var explicitly
in test setup.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In `@backend/tests/test_download.py`:
- Around line 36-44: Replace the runtime-dependent assertions by parametrizing
the test to control TEMOA_SKIP_CERT_VERIFY explicitly: create a pytest test
(e.g., test_download_tutorial_ssl_context) parametrized over skip_verify values
("0","1"), use monkeypatch.setenv("TEMOA_SKIP_CERT_VERIFY", skip_verify) and
patch/Mock urllib.request.urlopen, shutil.copyfileobj and open so
client.post("/api/download_tutorial") runs deterministically, then capture the
urlopen call args to extract the SSL context (ctx) and assert ctx.check_hostname
and ctx.verify_mode according to skip_verify; this ensures deterministic
behavior and removes reliance on the external environment variable at runtime.
- Around line 11-13: The test test_download_tutorial_success is patching global
imports (urllib.request.urlopen, shutil.copyfileobj, builtins.open) so the mocks
may not intercept calls; update the patch targets to the module under test
(e.g., patch "backend.main.urllib.request.urlopen",
"backend.main.shutil.copyfileobj", and "backend.main.open" or use
patch("backend.main.open", new_callable=MagicMock)) so they match how functions
are imported/used in that module and mirror the correct approach used in
test_download_tutorial_failure.
- Around line 1-6: Add FastAPI to project dependencies so tests importing
TestClient and backend.main's app can import fastapi: update pyproject.toml to
include "fastapi" (and optionally "starlette"/"pydantic" versions if required)
either under [project.dependencies] if backend needs it at runtime or under
[project.optional-dependencies.dev] / [dependency-groups.dev] (depending on your
pyproject layout) so that imports like from fastapi.testclient import TestClient
and backend.main's app resolve during CI.

In `@backend/utils.py`:
- Around line 7-25: Duplicate create_secure_ssl_context() found in backend.utils
and temoa_runner.py; remove the copy in temoa_runner.py and import the single
implementation from backend.utils instead (i.e., replace the function definition
in temoa_runner.py with a from backend.utils import create_secure_ssl_context
and use that), or if temoa_runner.py must be standalone, keep the function but
add a clear comment stating it is intentionally duplicated and link to
backend.utils.create_secure_ssl_context for maintenance; update any references
to ensure they call create_secure_ssl_context() from the shared location.
- Around line 15-17: Replace the root logger call with a module-level named
logger: create a logger via logging.getLogger(__name__) in backend/utils.py
(module scope) and change the logging.warning(...) invocation that warns about
"SSL certificate verification is DISABLED via TEMOA_SKIP_CERT_VERIFY." to use
logger.warning(...) instead so filtering and configuration target this module
(ensure the logger is defined before the warning is emitted).

In `@temoa_runner.py`:
- Line 20: Replace the deprecated typing.List import and annotations with the
built-in list type: remove List from the import line ("from typing import List,
Optional") and update any annotations that use List[...] (e.g., the annotation
at/around line 98) to use list[...] instead; leave Optional as-is (or switch to
X | None if you prefer modern union syntax) so the code uses built-in generics
supported by Python 3.12+.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@backend/main.py`:
- Around line 159-177: The download_tutorial handler can hang (no timeout) and
may leave partial files; update download_tutorial to open the URL with a
reasonable timeout (e.g., timeout=10) and write to a temporary file (e.g.,
target_path.with_suffix(".tmp") or tempfile.NamedTemporaryFile) then atomically
replace target_path with Path.replace once write completes; ensure assets
directory exists (assets_path.mkdir(parents=True, exist_ok=True)); on any
exception or interruption remove the temp file in a finally block and
re-raise/return the HTTPException as currently done; keep
create_secure_ssl_context usage and include the temp-file cleanup logic tied to
the temp filename used.

In `@backend/utils.py`:
- Around line 1-27: The project is importing certifi in backend/utils.py (see
create_secure_ssl_context and the certifi import) but certifi is not declared as
a runtime dependency; add "certifi" to the [project] dependencies in
pyproject.toml so installations include the CA bundle at runtime, then run a
dependency install to verify import resolution and ensure
create_secure_ssl_context continues to use certifi.where() for the secure
default path; keep the TEMOA_SKIP_CERT_VERIFY logic unchanged.

In `@temoa_runner.py`:
- Around line 149-162: The download loop can leave partial files and block
indefinitely; update the logic in the for loop that uses
create_secure_ssl_context(), urllib.request.urlopen and shutil.copyfileobj so
downloads use urlopen(..., timeout=...) and write to a temporary file (e.g.,
target.with_suffix(".part") or similar) and only atomically rename the temp file
to target on successful completion; on any exception remove the temp file (or
ensure it does not mask future retries) and re-raise/log the error so future
runs will retry the download instead of skipping due to target.exists().

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@backend/main.py`:
- Around line 179-180: The code currently calls logging.exception("Failed to
download tutorial") using the root logger; add a module-level named logger
(logger = logging.getLogger(__name__)) at the top of backend/main.py and replace
the root logging call with logger.exception(...) so the endpoint uses the named
logger consistently with backend/utils.py; update any other direct uses of
logging in this module (e.g., around the HTTPException raise) to use logger
instead.

Comment on lines +179 to +180
logging.exception("Failed to download tutorial")
raise HTTPException(status_code=500, detail=f"Download failed: {str(e)}") from e
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider using a named logger for consistency with backend/utils.py.

The endpoint uses logging.exception() on the root logger. For consistency with backend/utils.py which uses a named logger, consider creating a module-level logger.

♻️ Proposed fix

Add at module level:

logger = logging.getLogger(__name__)

Then replace:

-        logging.exception("Failed to download tutorial")
+        logger.exception("Failed to download tutorial")
🧰 Tools
🪛 Ruff (0.14.14)

[warning] 179-179: exception() call on root logger

(LOG015)


[warning] 180-180: Use explicit conversion flag

Replace with conversion flag

(RUF010)

🤖 Prompt for AI Agents
In `@backend/main.py` around lines 179 - 180, The code currently calls
logging.exception("Failed to download tutorial") using the root logger; add a
module-level named logger (logger = logging.getLogger(__name__)) at the top of
backend/main.py and replace the root logging call with logger.exception(...) so
the endpoint uses the named logger consistently with backend/utils.py; update
any other direct uses of logging in this module (e.g., around the HTTPException
raise) to use logger instead.

@ParticularlyPythonicBS
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 31, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ParticularlyPythonicBS ParticularlyPythonicBS merged commit 7c6b7ce into main Feb 1, 2026
3 checks passed
@ParticularlyPythonicBS ParticularlyPythonicBS deleted the fix/ssl_cert_issues branch February 1, 2026 00:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant