Skip to content
This repository was archived by the owner on Jun 16, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@
**Vulnerability:** Found an unused `_attempt_import` function in `src/codeweaver/server/mcp/server.py` that dynamically imports a module directly from unvalidated configuration (`import_module(mw.rsplit(".", 1)[0])`), leading to potential arbitrary code execution.
**Learning:** Functions that perform dynamic imports should not be left around in the codebase if they are unused, especially if they are designed to take unvalidated strings as input.
**Prevention:** Avoid dynamic imports based on configuration or inputs without strict whitelisting. Use tools like `semgrep` with python security rules to actively catch these patterns.

## 2025-02-25 - Arbitrary code execution in AST eval
**Vulnerability:** Found a lack of whitelisting for `ast.Call` nodes in type annotation resolution logic within `TypeValidator` (at `src/codeweaver/core/di/container.py`). This allowed any arbitrary function within the module's global namespace to be called during `eval()`, potentially leading to Arbitrary Code Execution (ACE).
**Learning:** Permitting generic `ast.Call` nodes inside restricted Abstract Syntax Tree parsing environments drastically increases the attack surface, allowing escape from restricted evaluation constraints.
**Prevention:** Strictly limit `ast.Call` nodes to specifically required functions (e.g., `Depends`, `depends`, `Field`, `PrivateAttr`, `Tag`, `Parameter`) when validating trees meant for dynamic type evaluation.
16 changes: 15 additions & 1 deletion src/codeweaver/core/di/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def __init__(self) -> None:
self._request_cache: dict[Any, Any] = {} # Keys can be types or callables
self._providers_loaded: bool = False # Track if auto-discovery has run

def _safe_eval_type(self, type_str: str, globalns: dict[str, Any]) -> Any | None:
def _safe_eval_type(self, type_str: str, globalns: dict[str, Any]) -> Any | None: # noqa: C901
"""Safely evaluate a type string using AST validation.

Parses the type string into an AST, validates that it contains only safe
Expand Down Expand Up @@ -136,6 +136,20 @@ def generic_visit(self, node: ast.AST) -> None:
if isinstance(node, ast.Attribute) and node.attr.startswith("__"):
raise TypeError(f"Forbidden dunder attribute: {node.attr}")

# Security: Restrict ast.Call nodes to known safe functions to prevent arbitrary code execution (ACE).
# Allowing arbitrary functions (like 'eval' or malicious callables) inside type annotations is dangerous.
if isinstance(node, ast.Call):
Comment on lines +139 to +141
if isinstance(node.func, ast.Name):
func_name = node.func.id
elif isinstance(node.func, ast.Attribute):
func_name = node.func.attr
else:
raise TypeError(f"Forbidden call function node type: {type(node.func).__name__}")

allowed_calls = {"Depends", "depends", "Field", "PrivateAttr", "Tag", "Parameter"}
if func_name not in allowed_calls:
Comment on lines +144 to +150

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.

🚨 issue (security): Relying only on the function name for ast.Attribute calls may allow unexpected call targets.

Because only node.func.attr is checked for ast.Attribute, any *.Depends / *.Field / etc. call is accepted, regardless of what object actually provides the attribute. A user could supply an object in globalns with a malicious .Depends that still passes this check. To strengthen ACE protection, also validate the base (node.func.value) or resolve the callable from globalns and ensure it matches the expected functions instead of trusting the attribute name alone.

raise TypeError(f"Forbidden function call in type string: {func_name}")
Comment on lines +139 to +151

super().generic_visit(node)

try:
Expand Down
Loading