Skip to content

Safe eval#8936

Open
ericspod wants to merge 10 commits into
Project-MONAI:devfrom
ericspod:safe_eval
Open

Safe eval#8936
ericspod wants to merge 10 commits into
Project-MONAI:devfrom
ericspod:safe_eval

Conversation

@ericspod

Copy link
Copy Markdown
Member

Addresses GHSA-h89g-r5pc-wxfm.

Description

This introduces a safe_eval function to evaluate known safe expressions which do not contain member access, calls, indexing, or other expressions which could be used for code injection. Use of eval is replaced where appropriate.

Types of changes

  • Non-breaking change (fix or new feature that would not break existing functionality).
  • Breaking change (fix or new feature that would cause existing functionality to change).
  • New tests added to cover the changes.
  • Integration tests passed locally by running ./runtests.sh -f -u --net --coverage.
  • Quick tests passed locally by running ./runtests.sh --quick --unittests --disttests.
  • In-line docstrings updated.
  • Documentation updated, tested make html command in the docs/ folder.

ericspod added 2 commits June 20, 2026 00:54
Signed-off-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com>
Signed-off-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com>
@ericspod ericspod requested review from KumoLiu and Nic-Ma as code owners June 20, 2026 05:05
@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Introduces monai/utils/safeeval.py with SAFE_TYPES (an AST node whitelist) and safe_eval, which parses an expression string, walks the AST to reject any node types not in the whitelist, and only then calls eval. Both are re-exported from monai.utils. Raw eval calls in _get_fake_spatial_shape (bundle scripts) and Ordering._order_template (ordering) are replaced with safe_eval and getattr respectively. A new tests/utils/test_safe_eval.py covers valid expressions, forbidden constructs, and allowed_types enforcement. A test docstring in test_alias.py is expanded.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive Title 'Safe eval' is vague and generic, lacking specificity about the actual change (introducing safe_eval function or addressing security vulnerability). Revise title to be more specific, e.g., 'Introduce safe_eval function to prevent code injection' or 'Replace eval with safe_eval for security'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed Description is complete with issue reference, clear explanation of changes, and checklist items properly filled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@monai/bundle/scripts.py`:
- Line 165: Add direct unit tests for the `_get_fake_spatial_shape` function in
an appropriate test file. Create test cases that validate both successful
handling of valid arithmetic expressions (such as "32 * n" and "2**p") and
rejection of unsafe expressions (such as "foo()" and "x.bar"). These tests
should directly test the `_get_fake_spatial_shape` function itself rather than
relying solely on integration tests that call it indirectly through
`verify_net_in_out`, ensuring the function meets the direct test coverage
requirement for modified definitions.

In `@monai/utils/safe_eval.py`:
- Around line 40-41: Rename the parameters `globals` and `locals` in the
function signature to avoid shadowing Python builtins (for example,
`globals_dict` and `locals_dict`), and update any usage of these parameters
throughout the function to use the new names. Additionally, fix the docstring
inconsistency by changing the documented parameter name from `allows_types` to
`allowed_types` to match the actual parameter name in the function signature.

In `@tests/utils/test_safe_eval.py`:
- Around line 33-36: The test method test_good_exprs uses parameter names
globals and locals which shadow Python builtins, violating PEP8 guidelines and
triggering Ruff A002. Rename these parameters to non-shadowing alternatives such
as globals_dict and locals_dict in both the method signature and the safe_eval
function call to improve code clarity and comply with style guidelines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3126c742-d09d-4287-a158-a500535cffb8

📥 Commits

Reviewing files that changed from the base of the PR and between 15f5073 and dd97330.

📒 Files selected for processing (5)
  • monai/bundle/scripts.py
  • monai/utils/__init__.py
  • monai/utils/ordering.py
  • monai/utils/safe_eval.py
  • tests/utils/test_safe_eval.py

Comment thread monai/bundle/scripts.py
Comment thread monai/utils/safe_eval.py Outdated
Comment thread tests/utils/test_safe_eval.py Outdated
Signed-off-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

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.

♻️ Duplicate comments (1)
monai/utils/safe_eval.py (1)

53-64: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Docstring parameter names still reference old globals/locals.

Lines 55-56 document globals: and locals: but the parameters are now globals_vars and locals_vars. Line 63 also uses the old names.

Proposed fix
     Args:
         expr: expression to evaluate, this will be stripped before parsing to avoid indentation complaints
-        globals: global variable mapping
-        locals: local variable mapping
+        globals_vars: global variable mapping
+        locals_vars: local variable mapping
         allowed_types: sequence of allowed AST types which can be found in `expr` when parsed
 
     Raises:
         ValueError: raised when any node in the AST parsed from `expr` has a type not in `allowed_types`
 
     Returns:
-        The evaluated expression value, using `eval` with `globals` and `locals`
+        The evaluated expression value, using `eval` with `globals_vars` and `locals_vars`
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@monai/utils/safe_eval.py` around lines 53 - 64, The docstring in the function
documents parameter names `globals` and `locals` on lines 55-56, but the actual
function parameters have been renamed to `globals_vars` and `locals_vars`.
Update the docstring parameter documentation to use the new parameter names
`globals_vars:` and `locals_vars:` instead of `globals:` and `locals:`, and also
update any other references to these parameter names within the docstring
(including line 63) to use the corrected names.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@monai/utils/safe_eval.py`:
- Around line 53-64: The docstring in the function documents parameter names
`globals` and `locals` on lines 55-56, but the actual function parameters have
been renamed to `globals_vars` and `locals_vars`. Update the docstring parameter
documentation to use the new parameter names `globals_vars:` and `locals_vars:`
instead of `globals:` and `locals:`, and also update any other references to
these parameter names within the docstring (including line 63) to use the
corrected names.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 25cc6d98-e150-4bc3-839e-0b3e5813d590

📥 Commits

Reviewing files that changed from the base of the PR and between dd97330 and f329f15.

📒 Files selected for processing (4)
  • monai/bundle/scripts.py
  • monai/utils/ordering.py
  • monai/utils/safe_eval.py
  • tests/utils/test_safe_eval.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/utils/test_safe_eval.py
  • monai/utils/ordering.py
  • monai/bundle/scripts.py

ericspod and others added 2 commits June 20, 2026 13:31
Signed-off-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
monai/utils/safeeval.py (1)

21-21: 💤 Low value

Incorrect type annotation for SAFE_TYPES.

The annotation Sequence[ast.AST] describes a sequence of AST node instances, but the value is a tuple of AST node types (classes). Should be Sequence[type[ast.AST]].

Suggested fix
-SAFE_TYPES: Sequence[ast.AST] = (
+SAFE_TYPES: Sequence[type[ast.AST]] = (
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@monai/utils/safeeval.py` at line 21, The type annotation for SAFE_TYPES is
incorrect because it declares Sequence[ast.AST] which represents a sequence of
AST node instances, but the actual value is a tuple of AST node classes/types.
Change the type annotation for SAFE_TYPES from Sequence[ast.AST] to
Sequence[type[ast.AST]] to correctly represent that it contains AST node types
rather than instances.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@monai/utils/safeeval.py`:
- Line 73: The eval function call on this line converts globals_vars to a
dictionary using dict(globals_vars), but when globals_vars has its default value
of None, this raises a TypeError because None is not iterable. Fix this by
either checking if globals_vars is None before converting it to a dict and
providing an empty dict as fallback, or change the default parameter value of
globals_vars from None to an empty dictionary instead.

---

Nitpick comments:
In `@monai/utils/safeeval.py`:
- Line 21: The type annotation for SAFE_TYPES is incorrect because it declares
Sequence[ast.AST] which represents a sequence of AST node instances, but the
actual value is a tuple of AST node classes/types. Change the type annotation
for SAFE_TYPES from Sequence[ast.AST] to Sequence[type[ast.AST]] to correctly
represent that it contains AST node types rather than instances.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 523656fc-0e5b-4fe5-9f7e-7cade595b509

📥 Commits

Reviewing files that changed from the base of the PR and between f329f15 and 2f083a9.

📒 Files selected for processing (3)
  • monai/utils/__init__.py
  • monai/utils/safeeval.py
  • tests/utils/test_alias.py
✅ Files skipped from review due to trivial changes (1)
  • tests/utils/test_alias.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • monai/utils/init.py

Comment thread monai/utils/safeeval.py Outdated
ericspod added 2 commits June 20, 2026 13:41
Signed-off-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@monai/utils/safeeval.py`:
- Line 73: The truthiness check on globals_vars in the eval call incorrectly
treats an empty dictionary as falsy, converting it to None which exposes
builtins instead of blocking them. Replace the condition `if globals_vars` with
an identity check `if globals_vars is not None` in the return statement to
ensure that empty dictionaries are preserved and passed to eval, while still
allowing None values to pass through unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9397626c-6b78-4aea-98f0-b1bdd255fa45

📥 Commits

Reviewing files that changed from the base of the PR and between 2f083a9 and 1ca759d.

📒 Files selected for processing (1)
  • monai/utils/safeeval.py

Comment thread monai/utils/safeeval.py
ericspod added 2 commits June 20, 2026 13:57
Signed-off-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com>
Signed-off-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com>
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.

1 participant