Skip to content

Conversation

@ArbenP
Copy link

@ArbenP ArbenP commented Nov 8, 2025

User description

This commit updates the project to meet modern Python and security best practices as of November 2025.

Dependency Updates

  • Update Python requirement from 3.10 to 3.11+
  • Update FastAPI from 0.104 to 0.115+
  • Update Pydantic from 2.5 to 2.9+
  • Update uvicorn from 0.24 to 0.32+
  • Update all other dependencies to latest stable versions
  • Remove unused asyncio-mqtt dependency

Security Improvements

  • Add path sanitization to prevent directory traversal attacks
  • Add validation to ensure paths stay within project root
  • Fix CORS default from "*" to localhost-only
  • Add comprehensive logging for security-sensitive operations

License Fixes

  • Fix license confusion between README (GPL-3.0) and package metadata
  • Update pyproject.toml and setup.py to correctly declare GPL-3.0-or-later

Code Quality

  • Remove dead mock code from claude_manager.py (25 lines)
  • Update Black target version from py310 to py311
  • Update mypy python_version from 3.10 to 3.11
  • Add Python 3.13 classifier

Testing Improvements

  • Add test coverage reporting with pytest-cov
  • Add 'make test' target with HTML coverage reports
  • Add 'make coverage' to view coverage reports
  • Add 'make test-no-cov' for quick testing without coverage

Configuration

  • Improve CORS configuration with explicit method list
  • Better environment variable parsing
  • Add security-focused defaults suitable for firewalled environments

All changes maintain backward compatibility while improving security, code quality, and developer experience.


PR Type

Enhancement, Bug fix


Description

  • Update Python requirement from 3.10 to 3.11+

  • Add path sanitization to prevent directory traversal attacks

  • Fix CORS default from "*" to localhost-only

  • Update all dependencies to November 2025 stable versions

  • Remove unused asyncio-mqtt dependency and dead mock code

  • Fix license declaration from MIT to GPL-3.0-or-later

  • Add test coverage reporting with pytest-cov integration

  • Update Black and mypy target versions to py311


Diagram Walkthrough

flowchart LR
  A["Python 3.10"] -->|upgrade| B["Python 3.11+"]
  C["Outdated Dependencies"] -->|update| D["Nov 2025 Versions"]
  E["CORS: *"] -->|restrict| F["Localhost only"]
  G["Path Traversal Risk"] -->|sanitize| H["Secure Path Handling"]
  I["MIT License"] -->|fix| J["GPL-3.0-or-later"]
  K["No Coverage"] -->|add| L["pytest-cov Reports"]
Loading

File Walkthrough

Relevant files
Security, bug fix
claude_manager.py
Add path sanitization and remove dead mock code                   

claude_code_api/core/claude_manager.py

  • Remove 25 lines of dead mock process code (_start_mock_process method)
  • Add sanitize_path_component() function to prevent directory traversal
    attacks
  • Enhance create_project_directory() with path sanitization and
    validation
  • Enhance cleanup_project_directory() with path validation and security
    checks
  • Add comprehensive logging for security-sensitive operations
+47/-33 
Security, configuration changes
config.py
Restrict CORS to localhost for security                                   

claude_code_api/core/config.py

  • Change CORS allowed_origins default from ["*"] to localhost-only
    patterns
  • Change CORS allowed_methods from ["*"] to explicit list: GET, POST,
    PUT, DELETE, OPTIONS, PATCH
  • Update CORS validator to default to localhost when parsing empty
    values
  • Add security-focused defaults suitable for firewalled environments
+12/-5   
Dependencies
setup.py
Update dependencies and Python version requirements           

setup.py

  • Update Python requirement from >=3.10 to >=3.11
  • Update FastAPI from >=0.104.0 to >=0.115.0
  • Update Pydantic from >=2.5.0 to >=2.9.0
  • Update uvicorn from >=0.24.0 to >=0.32.0
  • Update all other dependencies to latest stable versions
  • Remove unused asyncio-mqtt>=0.16.1 dependency
  • Add openai>=1.54.0 to dependencies
  • Update test and dev dependencies to latest versions
  • Fix license classifier from MIT to GPL-3.0-or-later
  • Add Python 3.13 classifier and remove 3.10
+26/-26 
Tests, configuration changes
Makefile
Add test coverage reporting targets                                           

Makefile

  • Update test target to include coverage reporting with --cov flags
  • Add test-no-cov target for quick testing without coverage
  • Add coverage target to open HTML coverage reports
  • Update help text to document new test targets
+20/-1   
Dependencies, configuration changes
pyproject.toml
Update dependencies and tool configurations                           

pyproject.toml

  • Update Python requirement from >=3.10 to >=3.11
  • Update FastAPI from >=0.104.0 to >=0.115.0
  • Update Pydantic from >=2.5.0 to >=2.9.0
  • Update uvicorn from >=0.24.0 to >=0.32.0
  • Update all other dependencies to latest stable versions
  • Remove unused asyncio-mqtt>=0.16.1 dependency
  • Update OpenAI from >=1.0.0 to >=1.54.0
  • Update Black target version from py310 to py311
  • Update mypy python_version from 3.10 to 3.11
  • Fix license from MIT to GPL-3.0-or-later
  • Add Python 3.13 classifier and remove 3.10
+29/-30 

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Added path validation and sanitization for file operations.
    • CORS default origins restricted to localhost variants instead of all origins.
  • Chores

    • License changed to GPL-3.0-or-later.
    • Minimum Python version requirement updated to 3.11.
    • Core dependencies updated to latest compatible versions.

This commit updates the project to meet modern Python and security
best practices as of November 2025.

## Dependency Updates
- Update Python requirement from 3.10 to 3.11+
- Update FastAPI from 0.104 to 0.115+
- Update Pydantic from 2.5 to 2.9+
- Update uvicorn from 0.24 to 0.32+
- Update all other dependencies to latest stable versions
- Remove unused asyncio-mqtt dependency

## Security Improvements
- Add path sanitization to prevent directory traversal attacks
- Add validation to ensure paths stay within project root
- Fix CORS default from "*" to localhost-only
- Add comprehensive logging for security-sensitive operations

## License Fixes
- Fix license confusion between README (GPL-3.0) and package metadata
- Update pyproject.toml and setup.py to correctly declare GPL-3.0-or-later

## Code Quality
- Remove dead mock code from claude_manager.py (25 lines)
- Update Black target version from py310 to py311
- Update mypy python_version from 3.10 to 3.11
- Add Python 3.13 classifier

## Testing Improvements
- Add test coverage reporting with pytest-cov
- Add 'make test' target with HTML coverage reports
- Add 'make coverage' to view coverage reports
- Add 'make test-no-cov' for quick testing without coverage

## Configuration
- Improve CORS configuration with explicit method list
- Better environment variable parsing
- Add security-focused defaults suitable for firewalled environments

All changes maintain backward compatibility while improving security,
code quality, and developer experience.
@coderabbitai
Copy link

coderabbitai bot commented Nov 8, 2025

Walkthrough

The PR introduces test coverage reporting in the build system, adds path sanitization and binary validation for enhanced security in process management, restricts CORS defaults to localhost variants, changes the project license to GPL-3.0-or-later, and updates Python minimum version requirement from 3.10 to 3.11 with corresponding dependency version bumps across build and test tools.

Changes

Cohort / File(s) Summary
Build & Test Configuration
Makefile, pyproject.toml, setup.py
Makefile: added test-no-cov and coverage targets; updated test target to generate coverage reports. pyproject.toml & setup.py: synchronized license to GPL-3.0-or-later, raised Python requirement to >=3.11, removed Python 3.10 classifier, added Python 3.13 classifier, and updated all core dependencies (fastapi, uvicorn, pydantic, httpx, aiofiles, structlog, sqlalchemy, pytest, mypy, etc.) to newer versions.
Security Hardening
claude_code_api/core/claude_manager.py
Removed internal mock process method. Added sanitize_path_component() to prevent path traversal attacks. Added validate_claude_binary() to verify binary availability. Enhanced create_project_directory() and cleanup_project_directory() to validate paths remain within project_root before operations, with sanitization and logging.
CORS Configuration
claude_code_api/core/config.py
Changed allowed_origins default from ["*"] to localhost variants (["http://localhost:*", "http://127.0.0.1:*", "http://[::1]:*"]). Changed allowed_methods default from ["*"] to explicit list (["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"]). Updated parse_cors_lists() validator to handle comma-separated input and default to ["http://localhost:*"] when empty.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Attention areas:
    • Path sanitization logic in claude_manager.py: verify regex/validation rules are sufficiently restrictive and don't cause false positives
    • Boundary enforcement in cleanup_project_directory(): ensure path validation prevents unintended directory deletions
    • CORS defaults migration: confirm the new localhost-only approach doesn't break existing deployments or development workflows
    • Dependency versions in pyproject.toml and setup.py: verify no breaking changes in fastapi, pydantic, sqlalchemy, or pytest ecosystem upgrades between versions

Poem

🐰 Hoppy hops through security's door,
Coverage measured, paths now pure,
GPL's green, and localhost's shore,
Python leaps to 3.11—sure!
Dependencies dance, the future's secure.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The PR title 'Modernize codebase to November 2025 standards' is too vague and generic. While it relates to the overall modernization effort, it lacks specificity about the main changes (security improvements, dependency updates, license fixes, etc.). Consider a more specific title that highlights the primary changes, such as 'Add path sanitization and restrict CORS defaults' or 'Update Python 3.11+ and modernize dependencies for November 2025'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@gemini-code-assist
Copy link

Summary of Changes

Hello @ArbenP, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly modernizes the codebase by upgrading Python and key dependencies, enhancing security measures through path validation and stricter CORS defaults, and improving code quality by removing dead code and updating development tool configurations. It also introduces robust test coverage reporting, ensuring the project adheres to contemporary best practices and provides a more secure and maintainable foundation.

Highlights

  • Dependency Updates: The project's Python requirement has been updated from 3.10 to 3.11+, and core dependencies like FastAPI, Pydantic, and Uvicorn have been upgraded to their latest stable versions. The unused asyncio-mqtt dependency has also been removed.
  • Security Enhancements: New path sanitization and validation logic have been implemented to prevent directory traversal attacks and ensure all file operations remain within the project root. Additionally, default CORS settings have been tightened from a permissive '*' to localhost-only, and comprehensive logging has been added for security-sensitive operations.
  • License Alignment: The project's license has been standardized to GPL-3.0-or-later across both the pyproject.toml and setup.py files, resolving previous inconsistencies with the README.
  • Code Quality & Modernization: Dead mock code has been removed from claude_manager.py. The Black formatter and Mypy type checker configurations have been updated to target Python 3.11, and a Python 3.13 classifier has been added to project metadata.
  • Testing Infrastructure: Test coverage reporting has been integrated using pytest-cov, with new Makefile targets (test, test-no-cov, coverage) to facilitate running tests with coverage, without coverage, and viewing HTML coverage reports.
  • Configuration Improvements: CORS configuration now includes an explicit list of allowed HTTP methods, and environment variable parsing for CORS origins has been refined to provide more secure defaults suitable for firewalled environments.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@qodo-merge-for-open-source
Copy link

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Path traversal risk

Description: The regex pattern [^\w\-.] allows dots in path components, which combined with the
lstrip('.') on line 319, could still permit relative path traversal using sequences like
.. if the sanitization is bypassed or if multiple dots remain after the first is stripped.

claude_manager.py [313-324]

Referred Code
def sanitize_path_component(component: str) -> str:
    """Sanitize a path component to prevent directory traversal attacks."""
    import re
    # Remove any path separators and special characters
    sanitized = re.sub(r'[^\w\-.]', '_', component)
    # Remove leading dots to prevent hidden files
    sanitized = sanitized.lstrip('.')
    # Limit length
    sanitized = sanitized[:255]
    if not sanitized:
        raise ValueError("Invalid path component: results in empty string after sanitization")
    return sanitized
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Empty sanitized path handling: The sanitize_path_component function raises a generic ValueError when sanitization results
in an empty string, but doesn't provide context about the original input value for
debugging purposes.

Referred Code
def sanitize_path_component(component: str) -> str:
    """Sanitize a path component to prevent directory traversal attacks."""
    import re
    # Remove any path separators and special characters
    sanitized = re.sub(r'[^\w\-.]', '_', component)
    # Remove leading dots to prevent hidden files
    sanitized = sanitized.lstrip('.')
    # Limit length
    sanitized = sanitized[:255]
    if not sanitized:
        raise ValueError("Invalid path component: results in empty string after sanitization")
    return sanitized
Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Path exposure in error: The ValueError at line 340 and 362 exposes the full file path in the error message, which
could reveal internal directory structure to users if not properly caught and sanitized at
a higher level.

Referred Code
raise ValueError(f"Invalid project path: {project_id} resolves outside project root")
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-merge-for-open-source
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Security
Fix directory traversal vulnerability

Add an explicit check in sanitize_path_component to reject . and .. as inputs,
fixing a potential directory traversal vulnerability.

claude_code_api/core/claude_manager.py [313-324]

 def sanitize_path_component(component: str) -> str:
     """Sanitize a path component to prevent directory traversal attacks."""
     import re
+    # Disallow '.' and '..' as components
+    if component == "." or component == "..":
+        raise ValueError(f"Invalid path component: '{component}' is not allowed.")
+
     # Remove any path separators and special characters
     sanitized = re.sub(r'[^\w\-.]', '_', component)
     # Remove leading dots to prevent hidden files
     sanitized = sanitized.lstrip('.')
     # Limit length
     sanitized = sanitized[:255]
     if not sanitized:
         raise ValueError("Invalid path component: results in empty string after sanitization")
     return sanitized
  • Apply / Chat
Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a security flaw in the new sanitize_path_component function, where it fails to block .., potentially enabling directory traversal. Although another check mitigates this, fixing the sanitization function itself is crucial for robust, layered security.

High
High-level
Consolidate packaging metadata into pyproject.toml

Consolidate all static packaging metadata from the redundant setup.py into
pyproject.toml to align with modern Python standards (PEP 621). This simplifies
maintenance by creating a single source of truth, allowing for the removal of
setup.py.

Examples:

pyproject.toml [5-43]
[project]
name = "claude-code-api"
version = "1.0.0"
description = "OpenAI-compatible API gateway for Claude Code with streaming support"
readme = "README.md"
license = {text = "GPL-3.0-or-later"}
authors = [
    {name = "Claude Code API Team"}
]
keywords = ["claude", "api", "openai", "streaming", "ai"]

 ... (clipped 29 lines)
setup.py [7-66]
setup(
    name="claude-code-api",
    version="1.0.0",
    description="OpenAI-compatible API gateway for Claude Code with streaming support",
    long_description=open("README.md").read() if os.path.exists("README.md") else "",
    long_description_content_type="text/markdown",
    author="Claude Code API Team",
    url="https://github.com/claude-code-api/claude-code-api",
    packages=find_packages(),
    python_requires=">=3.11",

 ... (clipped 50 lines)

Solution Walkthrough:

Before:

// pyproject.toml
[project]
name = "claude-code-api"
version = "1.0.0"
license = {text = "GPL-3.0-or-later"}
requires-python = ">=3.11"
dependencies = [
    "fastapi>=0.115.0",
    ...
]

// setup.py
setup(
    name="claude-code-api",
    version="1.0.0",
    python_requires=">=3.11",
    install_requires=[
        "fastapi>=0.115.0",
        ...
    ],
    classifiers=[
        "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
        ...
    ],
)

After:

// pyproject.toml
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "claude-code-api"
version = "1.0.0"
readme = "README.md"
license = {text = "GPL-3.0-or-later"}
requires-python = ">=3.11"
dependencies = [
    "fastapi>=0.115.0",
    ...
]
# All metadata is now sourced from here.

// setup.py
// (This file is deleted)
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies redundant packaging metadata across setup.py and pyproject.toml, and its removal aligns perfectly with the PR's goal of modernizing the project, thus improving maintainability.

Medium
Possible issue
Correctly handle empty CORS configuration

Modify the parse_cors_lists validator to correctly handle an empty string from
an environment variable, ensuring it results in an empty list rather than
falling back to a default value.

claude_code_api/core/config.py [106-112]

 @field_validator('allowed_origins', 'allowed_methods', 'allowed_headers', mode='before')
 def parse_cors_lists(cls, v):
     if isinstance(v, str):
         # Support comma-separated values or "*" for all
-        parsed = [x.strip() for x in v.split(',') if x.strip()]
-        return parsed if parsed else ["http://localhost:*"]
-    return v if v else ["http://localhost:*"]
+        return [x.strip() for x in v.split(',') if x.strip()]
+    if v is None:
+        return ["http://localhost:*"]
+    return v
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that an empty string for a CORS setting incorrectly falls back to a default value instead of an empty list. The proposed fix correctly handles this edge case, improving the configuration's robustness and respecting user intent.

Low
  • More

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request is a significant step forward in modernizing the codebase, with valuable dependency updates and security enhancements. The move to stricter CORS policies and the introduction of path sanitization are excellent improvements. My review focuses on further hardening these new security features. I've identified two critical vulnerabilities related to symlink handling where os.path.realpath should be used instead of os.path.abspath. Additionally, I've found a high-severity bug in the new CORS configuration validator and provided suggestions to improve the path sanitization logic and the portability of a new Makefile target. Addressing these points will make the application significantly more secure and robust.

Comment on lines +335 to +337
# Verify the resulting path is still within project_root (defense in depth)
project_path = os.path.abspath(project_path)
root_path = os.path.abspath(settings.project_root)

Choose a reason for hiding this comment

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

critical

There is a potential security vulnerability here. os.path.abspath does not resolve symbolic links, which could allow a specially crafted project_id (containing a symlink) to bypass the directory traversal check. The subsequent os.makedirs could then write files outside the intended project root. You should use os.path.realpath to resolve all symlinks in the path before validation.

Suggested change
# Verify the resulting path is still within project_root (defense in depth)
project_path = os.path.abspath(project_path)
root_path = os.path.abspath(settings.project_root)
# Verify the resulting path is still within project_root (defense in depth)
project_path = os.path.realpath(project_path)
root_path = os.path.realpath(settings.project_root)

Comment on lines +352 to +354
# Verify the path is within project_root before deletion
abs_project_path = os.path.abspath(project_path)
root_path = os.path.abspath(settings.project_root)

Choose a reason for hiding this comment

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

critical

Similar to the create_project_directory function, using os.path.abspath here presents a security risk. It does not resolve symlinks, which could lead to a Time-of-check to time-of-use (TOCTOU) vulnerability. An attacker could replace a directory with a symlink after validation but before deletion, causing shutil.rmtree to delete files outside the project root. Please use os.path.realpath to ensure the canonical, symlink-resolved path is checked and used for deletion.

Suggested change
# Verify the path is within project_root before deletion
abs_project_path = os.path.abspath(project_path)
root_path = os.path.abspath(settings.project_root)
# Verify the path is within project_root before deletion
abs_project_path = os.path.realpath(project_path)
root_path = os.path.realpath(settings.project_root)

Comment on lines +313 to +324
def sanitize_path_component(component: str) -> str:
"""Sanitize a path component to prevent directory traversal attacks."""
import re
# Remove any path separators and special characters
sanitized = re.sub(r'[^\w\-.]', '_', component)
# Remove leading dots to prevent hidden files
sanitized = sanitized.lstrip('.')
# Limit length
sanitized = sanitized[:255]
if not sanitized:
raise ValueError("Invalid path component: results in empty string after sanitization")
return sanitized

Choose a reason for hiding this comment

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

high

This sanitization function is a good security measure. However, it could be made more robust and aligned with best practices:

  1. The import re should be at the top of the file (as per PEP 8), along with import shutil used in cleanup_project_directory.
  2. The function doesn't explicitly reject . or .. as path components. While the downstream create_project_directory function's abspath check provides defense-in-depth, a sanitizer's primary role is to reject or clean invalid input. Allowing . or .. to pass through is risky.

Here is a suggested improvement that addresses the second point. Please also move the import to the top of the file.

Suggested change
def sanitize_path_component(component: str) -> str:
"""Sanitize a path component to prevent directory traversal attacks."""
import re
# Remove any path separators and special characters
sanitized = re.sub(r'[^\w\-.]', '_', component)
# Remove leading dots to prevent hidden files
sanitized = sanitized.lstrip('.')
# Limit length
sanitized = sanitized[:255]
if not sanitized:
raise ValueError("Invalid path component: results in empty string after sanitization")
return sanitized
def sanitize_path_component(component: str) -> str:
"""Sanitize a path component to prevent directory traversal attacks."""
import re
# Prohibit special path components that could be used for traversal.
if component in (".", ".."):
raise ValueError(f"Invalid path component: '{component}' is not allowed.")
# Remove any path separators and special characters
sanitized = re.sub(r'[^\w\-.]', '_', component)
# Remove leading dots to prevent hidden files
sanitized = sanitized.lstrip('.')
# Limit length
sanitized = sanitized[:255]
if not sanitized:
raise ValueError("Invalid path component: results in empty string after sanitization")
return sanitized

Comment on lines 106 to +112
@field_validator('allowed_origins', 'allowed_methods', 'allowed_headers', mode='before')
def parse_cors_lists(cls, v):
if isinstance(v, str):
return [x.strip() for x in v.split(',') if x.strip()]
return v or ["*"]
# Support comma-separated values or "*" for all
parsed = [x.strip() for x in v.split(',') if x.strip()]
return parsed if parsed else ["http://localhost:*"]
return v if v else ["http://localhost:*"]

Choose a reason for hiding this comment

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

high

This shared validator for allowed_origins, allowed_methods, and allowed_headers has two issues:

  1. It incorrectly applies the same fallback logic to all three fields. If ALLOWED_METHODS is set to an empty string, it will be assigned ['http://localhost:*'], which is incorrect.
  2. For allowed_origins, when no value is provided, it falls back to ['http://localhost:*'], which is inconsistent with the field's default value that contains three patterns (for localhost, 127.0.0.1, and ::1).

These fields should have separate validators to handle their distinct logic and defaults correctly.

Suggested change
@field_validator('allowed_origins', 'allowed_methods', 'allowed_headers', mode='before')
def parse_cors_lists(cls, v):
if isinstance(v, str):
return [x.strip() for x in v.split(',') if x.strip()]
return v or ["*"]
# Support comma-separated values or "*" for all
parsed = [x.strip() for x in v.split(',') if x.strip()]
return parsed if parsed else ["http://localhost:*"]
return v if v else ["http://localhost:*"]
@field_validator('allowed_origins', mode='before')
def parse_allowed_origins(cls, v):
default = [
"http://localhost:*",
"http://127.0.0.1:*",
"http://[::1]:*",
]
if isinstance(v, str):
parsed = [x.strip() for x in v.split(',') if x.strip()]
return parsed if parsed else default
return v if v else default
@field_validator('allowed_methods', 'allowed_headers', mode='before')
def parse_other_cors(cls, v):
if isinstance(v, str):
parsed = [x.strip() for x in v.split(',') if x.strip()]
# Return None if parsing an empty string, so Pydantic uses the field's default.
return parsed if parsed else None
return v

Comment on lines +17 to +29
coverage:
@if [ -f htmlcov/index.html ]; then \
echo "Opening coverage report..."; \
if command -v xdg-open > /dev/null 2>&1; then \
xdg-open htmlcov/index.html; \
elif command -v open > /dev/null 2>&1; then \
open htmlcov/index.html; \
else \
echo "Coverage report available at: htmlcov/index.html"; \
fi \
else \
echo "No coverage report found. Run 'make test' first."; \
fi

Choose a reason for hiding this comment

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

medium

The shell logic to find a command to open the browser is clever, but it's not fully portable (e.g., it might not work on all Linux distributions or in environments like WSL without xdg-open). A more portable and simpler approach is to use Python's built-in webbrowser module, which handles the cross-platform differences internally.

coverage:
	@if [ -f htmlcov/index.html ]; then \
		echo "Opening coverage report in browser..."; \
		python -m webbrowser -t "htmlcov/index.html"; \
	else \
		echo "No coverage report found. Run 'make test' first."; \
	fi

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: 0

🧹 Nitpick comments (2)
claude_code_api/core/claude_manager.py (2)

313-324: Good path sanitization, but move import to module level.

The sanitization logic effectively prevents directory traversal attacks by removing path separators and limiting to safe characters. The length limit and empty string validation are appropriate.

Consider moving the import re to the top of the file with other imports:

 import asyncio
 import json
 import os
+import re
 import subprocess

Then remove line 315:

 def sanitize_path_component(component: str) -> str:
     """Sanitize a path component to prevent directory traversal attacks."""
-    import re
     # Remove any path separators and special characters

347-368: Strong security validation before deletion, but move import.

The path validation before deletion is critical and well-implemented, preventing deletion of files outside the project root. The logging provides good security audit trails.

Move the import shutil to the module level with other imports:

 import asyncio
 import json
 import os
 import re
+import shutil
 import subprocess

Then remove line 350:

 def cleanup_project_directory(project_path: str):
     """Clean up project directory with path validation."""
     try:
-        import shutil
-
         # Verify the path is within project_root before deletion
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd618dc and 70addcb.

📒 Files selected for processing (5)
  • Makefile (2 hunks)
  • claude_code_api/core/claude_manager.py (2 hunks)
  • claude_code_api/core/config.py (1 hunks)
  • pyproject.toml (3 hunks)
  • setup.py (2 hunks)
🧰 Additional context used
🪛 checkmake (0.2.2)
Makefile

[warning] 17-17: Target body for "coverage" exceeds allowed length of 5 (12).

(maxbodylength)

🪛 Ruff (0.14.3)
claude_code_api/core/claude_manager.py

323-323: Avoid specifying long messages outside the exception class

(TRY003)


340-340: Avoid specifying long messages outside the exception class

(TRY003)


362-362: Abstract raise to an inner function

(TRY301)


362-362: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (12)
pyproject.toml (4)

10-10: License change properly reflected in both fields.

The change from MIT to GPL-3.0-or-later is consistently applied in both the license text field and the classifier. This is a significant licensing change that affects how the code can be used and distributed.

Also applies to: 18-18


107-107: LGTM! Tooling configuration properly updated.

Black and mypy configurations have been correctly updated to target Python 3.11, aligning with the new minimum Python version requirement.

Also applies to: 130-130


28-43: I'll now verify the changelogs for potential breaking changes in the key dependency updates.

No breaking changes detected in the specified dependency versions.

All 13 dependency versions have been verified on PyPI. FastAPI 0.115.0 contains no major nor breaking changes. The other dependencies (uvicorn 0.32.0, Pydantic 2.9.0, etc.) are incremental updates with no documented critical breaking changes. The version bumps are safe to deploy.


22-22: Dependency compatibility verified for Python 3.11-3.13.

All dependencies specified in pyproject.toml support the updated Python version range:

  • Pydantic 2.9.0 meets the 2.8+ requirement for Python 3.13 support
  • Uvicorn 0.32.0 includes official Python 3.13 support
  • FastAPI, uvicorn, and pytest versions are compatible with 3.11-3.13

No compatibility issues detected.

Makefile (3)

8-10: LGTM! Good coverage reporting setup.

The test target now includes comprehensive coverage reporting with both HTML and terminal output, which aligns well with the addition of pytest-cov in the dependencies.


11-12: LGTM! Well-implemented coverage workflow.

The new test-no-cov and coverage targets provide good developer ergonomics:

  • test-no-cov allows faster test iterations without coverage overhead
  • coverage target handles cross-platform HTML report opening with appropriate fallbacks

The shell script complexity is justified for proper cross-platform support.

Also applies to: 17-29


64-67: LGTM! Clear documentation of new test targets.

The help text accurately describes the new test and coverage targets, making it easy for developers to understand when to use each option.

claude_code_api/core/claude_manager.py (2)

327-344: Excellent defense-in-depth path validation.

The function properly implements multiple layers of security:

  1. Input sanitization via sanitize_path_component
  2. Path resolution with os.path.abspath to handle symbolic links and relative paths
  3. Explicit validation that the result stays within project_root using the os.sep suffix to prevent prefix matching vulnerabilities
  4. Security-focused logging

371-382: LGTM! Simple and robust binary validation.

The validation function properly uses subprocess with a timeout and returns a clear boolean result. The broad exception handling is appropriate since this is a validation check that should gracefully return False on any failure.

setup.py (1)

16-16: LGTM! Consistent with pyproject.toml updates.

All changes in setup.py properly mirror the updates made in pyproject.toml:

  • Python version requirement: 3.11+
  • License: GPL-3.0-or-later
  • Python 3.13 classifier added
  • Dependency versions match exactly

This consistency between both packaging files is important for proper package installation.

Also applies to: 18-32, 36-47, 58-58, 62-62

claude_code_api/core/config.py (2)

97-104: Excellent security improvement to CORS defaults.

Restricting CORS origins from "*" to localhost-only variants significantly improves the default security posture. The configuration:

  • Covers all localhost representations (localhost, 127.0.0.1, [::1])
  • Supports any port with the :* wildcard for development flexibility
  • Provides explicit HTTP methods instead of allowing all
  • Can be overridden via environment variables for production needs

This is a breaking change for deployments relying on the "*" default, but the security benefit justifies it.


106-112: Good parsing logic with secure defaults.

The validator properly handles comma-separated environment variables and defaults to a secure localhost-only configuration when empty values are provided. This applies to all CORS configuration fields (origins, methods, headers), ensuring consistent behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants