π‘οΈ Sentinel: [CRITICAL] Prevent Arbitrary Code Execution in type evaluations#372
π‘οΈ Sentinel: [CRITICAL] Prevent Arbitrary Code Execution in type evaluations#372bashandbone wants to merge 1 commit into
Conversation
Restricts `ast.Call` nodes to a strict whitelist of explicitly required functions (`Depends`, `Field`, `Parameter`, `PrivateAttr`, `Tag`, `cast`, `depends`, `length`, `uuid7`) during dynamic type evaluations in `Container._safe_eval_type`. This prevents an Arbitrary Code Execution (ACE) vulnerability where potentially unvalidated type strings could call unrestricted functions from the module's global namespace. Added security finding to Sentinel's journal. Co-authored-by: bashandbone <89049923+bashandbone@users.noreply.github.com>
|
π Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a π emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
Reviewer's GuideIntroduces stricter AST validation for type string evaluation by whitelisting allowed function calls in Flow diagram for safe type string evaluation with whitelisted ast.Callflowchart TD
A[type_str input] --> B[ast.parse]
B --> C[TypeValidator.visit]
C --> D{ast.Call node?}
D -- no --> F[eval compiled AST]
D -- yes --> E[visit_Call]
E --> G{func_name in allowed_functions?}
G -- no --> H[raise TypeError]
G -- yes --> I[generic_visit]
I --> F[eval compiled AST]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
π€ Hi @bashandbone, I've received your request, and I'm working on it now! You can track my progress in the logs for more details. |
|
π€ I'm sorry @bashandbone, but I was unable to process your request. Please see the logs for more details. |
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- Consider hoisting the
allowed_functionsfrozenset to a module- or class-level constant so it isnβt recreated on everyvisit_Calland can be more easily shared or updated in one place. - In
visit_Call, you currently accept anyast.Attributeby checking onlynode.func.attr; if you care about tightening security further, you may want to validate the attributeβs base (e.g., ensure itβs from an expected module or name) rather than only whitelisting the attribute name. - When
func_namecannot be resolved (e.g., non-Name/Attributecallables), consider raising with a clearer message like'Unknown or dynamic callable in type string'instead of interpolatingNone, which will make debugging and log scanning easier.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider hoisting the `allowed_functions` frozenset to a module- or class-level constant so it isnβt recreated on every `visit_Call` and can be more easily shared or updated in one place.
- In `visit_Call`, you currently accept any `ast.Attribute` by checking only `node.func.attr`; if you care about tightening security further, you may want to validate the attributeβs base (e.g., ensure itβs from an expected module or name) rather than only whitelisting the attribute name.
- When `func_name` cannot be resolved (e.g., non-`Name`/`Attribute` callables), consider raising with a clearer message like `'Unknown or dynamic callable in type string'` instead of interpolating `None`, which will make debugging and log scanning easier.
## Individual Comments
### Comment 1
<location path="src/codeweaver/core/di/container.py" line_range="156-161" />
<code_context>
+ "uuid7",
+ })
+ func_name = None
+ if isinstance(node.func, ast.Name):
+ func_name = node.func.id
+ elif isinstance(node.func, ast.Attribute):
+ func_name = node.func.attr
+
+ if func_name not in allowed_functions:
+ raise TypeError(f"Forbidden function call in type string: {func_name}")
+
</code_context>
<issue_to_address>
**π¨ issue (security):** Tighten handling of attribute calls to avoid unexpected qualified names slipping through
Currently any `ast.Attribute` is accepted if its `.attr` is whitelisted, regardless of the base expression, so `dangerous.depends(...)` is treated like `depends(...)`. To avoid unintended qualified calls, consider either rejecting attribute calls entirely or only allowing them when the base is an explicitly approved name/module. This narrows the allowed surface for type string evaluation.
</issue_to_address>Help me be more useful! Please click π or π on each comment and I'll use the feedback to improve your reviews.
| if isinstance(node.func, ast.Name): | ||
| func_name = node.func.id | ||
| elif isinstance(node.func, ast.Attribute): | ||
| func_name = node.func.attr | ||
|
|
||
| if func_name not in allowed_functions: |
There was a problem hiding this comment.
π¨ issue (security): Tighten handling of attribute calls to avoid unexpected qualified names slipping through
Currently any ast.Attribute is accepted if its .attr is whitelisted, regardless of the base expression, so dangerous.depends(...) is treated like depends(...). To avoid unintended qualified calls, consider either rejecting attribute calls entirely or only allowing them when the base is an explicitly approved name/module. This narrows the allowed surface for type string evaluation.
There was a problem hiding this comment.
Pull request overview
This PR hardens DI type-string evaluation by adding validation for function calls in _safe_eval_type, aiming to prevent arbitrary callable execution during annotation resolution. It also records the security finding in the Sentinel log.
Changes:
- Adds an
ast.Callvisitor that restricts callable names to a whitelist beforeeval(). - Documents the ACE risk and mitigation in
.jules/sentinel.md. - Adds a complexity lint suppression for
_safe_eval_type.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/codeweaver/core/di/container.py |
Adds call whitelist validation during safe type evaluation. |
.jules/sentinel.md |
Logs the dynamic type evaluation security finding and prevention guidance. |
π‘ Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| elif isinstance(node.func, ast.Attribute): | ||
| func_name = node.func.attr | ||
|
|
||
| if func_name not in allowed_functions: |
|
|
||
| super().generic_visit(node) | ||
|
|
||
| def visit_Call(self, node: ast.Call) -> None: |
Pull Request ReviewSummaryThis PR addresses a critical security vulnerability (CVE-level) in the DI container's type evaluation system by implementing strict whitelisting for Vulnerability AnalysisThe vulnerability was real and serious:
Code Quality Reviewβ Strengths
|
π¨ Severity: CRITICAL
π‘ Vulnerability:
The
Container._safe_eval_typefunction evaluates type strings by constructing an Abstract Syntax Tree (AST) usingast.parseand then ensuring it is "safe" before calling Python'seval()on it. Previously, it allowed all genericast.Callnodes to be present in the tree. Because the AST was ultimately compiled and passed toeval()with the module'sglobalns, this flaw inherently allowed evaluating strings to invoke any callable within theglobalnsdictionary or Python's__builtins__, resulting in an Arbitrary Code Execution (ACE) vulnerability.π― Impact:
If an attacker could manipulate or inject arbitrary string type representations that
_safe_eval_typeprocesses (potentially through type inference APIs, dependency injection configurations, or dynamic parsing mechanisms), they could inject maliciousast.Callnodes. Since it fell through toeval(), it could be used to execute unintended underlying logic with the permissions of the application process.π§ Fix:
Implemented a strict node validation layer by overriding the
visit_Callmethod in theTypeValidatorclass. Now,ast.Callnodes are strongly restricted. If anast.Callappears in the type string tree, it validates that the function call strictly matches a specific, hardcoded whitelist of expected dependency injection and type system functions (e.g.,Depends,Parameter,Field,PrivateAttr,Tag). Any unexpected callable throws aTypeError, aborting the evaluation beforeeval()can be reached. An inline comment explaining the security concern was added alongside the fix.β Verification:
ast.Callevaluations.core/di/viapytest.mise //:check..jules/sentinel.md.PR created automatically by Jules for task 6024749506626654658 started by @bashandbone
Summary by Sourcery
Harden type string evaluation in the DI container to prevent arbitrary code execution and document the associated Sentinel security finding.
Bug Fixes:
_safe_eval_typeby validatingast.Callnodes against a strict whitelist of permitted functions before evaluation.Enhancements:
_safe_eval_typewith a complexity linter override and add inline documentation explaining the security rationale for restrictingast.Callnodes.Documentation:
.jules/sentinel.mdfor future security tracking.