-
Notifications
You must be signed in to change notification settings - Fork 168
fix(env_service): prevent arbitrary module loading via env_type validation #47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |
| import asyncio | ||
| from dataclasses import dataclass | ||
| import importlib | ||
| import re | ||
| import os | ||
| import sys | ||
| import time | ||
|
|
@@ -58,6 +59,28 @@ def ensure_env(name: str, rel_path: str) -> None: | |
| if PROJECT_ROOT not in sys.path: | ||
| sys.path.insert(0, PROJECT_ROOT) | ||
|
|
||
| ALLOWED_ENV_TYPES = frozenset( | ||
| d.name | ||
| for d in Path( | ||
| os.path.join(os.path.dirname(__file__), "environments"), | ||
| ).iterdir() | ||
| if d.is_dir() and not d.name.startswith("_") | ||
| ) | ||
|
Comment on lines
+62
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The initialization of ALLOWED_ENV_TYPES = frozenset(
d.name
for d in (Path(__file__).parent / "environments").iterdir()
if d.is_dir() and not d.name.startswith("_")
) |
||
|
|
||
|
|
||
| def _validate_env_type(env_type: str) -> None: | ||
| """Validate that env_type is a known, safe environment name.""" | ||
| if not re.match(r"^[a-zA-Z0-9_]+$", env_type): | ||
| raise ValueError( | ||
| f"Invalid env_type: {env_type!r}. " | ||
| "Must contain only alphanumeric characters and underscores.", | ||
| ) | ||
| if env_type not in ALLOWED_ENV_TYPES: | ||
| raise ValueError( | ||
| f"Unknown env_type: {env_type!r}. " | ||
| f"Available: {sorted(ALLOWED_ENV_TYPES)}", | ||
| ) | ||
|
|
||
|
|
||
| def import_and_register_env(env_name, env_file=None): | ||
| """ | ||
|
|
@@ -71,6 +94,7 @@ def import_and_register_env(env_name, env_file=None): | |
| Returns: | ||
| The registered environment class or None on failure. | ||
| """ | ||
| _validate_env_type(env_name) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While _validate_env_type(env_name)
if env_file is not None and not re.match(r"^[a-zA-Z0-9_]+$", env_file):
raise ValueError(
f"Invalid env_file: {env_file!r}. "
"Must contain only alphanumeric characters and underscores.",
) |
||
| try: | ||
| if env_file is None: | ||
| env_file = f"{env_name}_env" | ||
|
|
@@ -181,6 +205,7 @@ def get_remote_env_cls(self, env_type: str): | |
| Returns: | ||
| The remote environment class. | ||
| """ | ||
| _validate_env_type(env_type) | ||
| if env_type in self.remote_env: | ||
| return self.remote_env[env_type] | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code on line 114 (not shown in this diff hunk) uses
importlib.util.spec_from_file_location. However,importlib.utilis not automatically imported when youimport importlib. This will result in anAttributeErrorat runtime when attempting to register an environment. You should explicitly importimportlib.util.