feat: v0.1.96#1124
Conversation
…ecution_mode: srt) Adds Anthropic sandbox-runtime (srt) as a 3rd command_line_execution_mode alongside local/docker. Default-off; opt-in via config. OS-level filesystem + network isolation derived from the same PathPermissionManager policy as the app layer (defense in depth). - SrtManager derives per-agent settings (allowWrite/denyWrite/denyRead, deny-all network, built-in secret-store read-deny baseline) and wraps commands via 'srt --settings cfg sh -c <cmd>' (sh -c form required so srt does not consume the server's -- separator). - Command-line MCP + filesystem-tools MCP servers are OS-wrapped; npx/npm and the no-roots wrapper auto-skip (registry/cache writes the sandbox blocks) and keep their app-layer protection. - Native-sandbox backends (codex --full-auto, claude_code) degrade srt->local via has_native_execution_sandbox() to avoid nested-sandbox hangs; stored config normalized so raw reads see local. - Subagents inherit parent command_line_srt_* settings (parity with Docker). - New command_line_srt_* params added to the single-source exclusion list; 'srt' added to the MCP executable allowlist. Example config + tests included. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…andbox escapes Adds a key-agnostic escape scan (_validate_no_path_arg_escapes) that walks the full tool-args tree (nested dicts + lists) and denies any value resolving outside all managed areas. Closes fail-open gaps surfaced by an adversarial audit: - path under an unrecognized arg key (e.g. output_path/dst) bypassed the boundary - list-valued and nested-dict path args were never checked - move/copy 'source' pointing outside (delete-external / exfiltrate-external) No false positives: non-path strings resolve harmlessly inside the workspace, and content-bearing keys are skipped. Symlinks/.. were already handled via .resolve(). 15-vector adversarial test suite added. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…o workspace+context)
SRT reads are allow-all by default, so the prior secret-denylist still left the
whole filesystem readable. Add command_line_srt_read_mode:
- confined (default): denyRead=$HOME, allowRead=workspace+context+temp; system
paths outside $HOME stay readable so commands still run. Denies personal data,
secrets, and other projects.
- strict: denyRead='/', allowRead=managed + system runtime baseline + extras.
- open: allow-all reads minus a built-in secret denylist + extras.
Plus command_line_srt_allow_read to widen the allow-list per config. Both params
wired through base.py (+validation), the single-source exclusion list, and
FilesystemManager. Note: allowRead wins over denyRead in SRT, so protected
sub-paths inside an allowed context are read-deniable only in 'open' mode (their
write-immunity still holds everywhere). Live-verified: confined denies $HOME
reads while python/system reads and workspace I/O keep working.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds opt-in OS-level command execution sandboxing (Anthropic SRT), SrtManager and wrapping helpers, FilesystemManager and MCP wiring for SRT, permission-hook hardening to block path-argument escapes, backend native-sandbox detection/fallback, docs/configs, tests, and bumps version to 0.1.96. ChangesOS-level sandboxing (SRT) and permission hardening
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 4❌ Failed checks (3 warnings, 1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
…arts under confined SRT Live-smoke-testing the srt_sandbox.yaml demo surfaced that the OS-wrapped workspace_tools MCP server failed to start under the default confined read mode: SRT denied reading the server's own code because, in a normal dev/editable install, the venv (fastmcp + deps + interpreter), the massgen package source, and git's user config all live under $HOME — exactly the region confined denies. First the server's own _workspace_tools_server.py script was unreadable; after re-allowing the code roots, GitPython's import-time `git version` then failed reading ~/.gitconfig. Either way the server never connected and the agent silently fell back to shell-only (security held — fail-closed — but the filesystem MCP file-op tools were unavailable whenever srt was on). Fix: build_settings() now re-allows the framework's runtime read roots for the fs_tools profile only (sys.prefix/sys.base_prefix, the massgen package dir, and ~/.gitconfig + ~/.config/git). This is framework code/config, not user data, so secrets (.ssh/.aws/...) and other projects stay denied; the agent's own execution profile is untouched. The confined read mode landed in a later commit than the fs-tools wrapping, and the unit tests only checked argv/wiring shape — not a live server handshake — so this slipped through. TDD: added test_fs_tools_profile_confined_allows_reading_framework_runtime (red first) asserting the fs_tools allowRead covers the framework roots + git config while $HOME stays in denyRead, plus test_execution_profile_does_not_widen_for_framework_runtime to keep the agent sandbox tight. Verified live: the agent now creates files via the filesystem MCP tools while a shell read of a $HOME secret is still OS-denied. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Release-prep doc sweep for v0.1.96 (the SRT sandboxing release): - CHANGELOG: full v0.1.96 entry (theme, SRT mode, read confinement, hardened permission hook, native-backend degrade, tests, framework-read-roots fix). - README: Latest Features + Recent Achievements rewritten for v0.1.96; v0.1.95 moved to Previous Achievements; TOC anchors + bottom roadmap shifted. - ROADMAP: current version → v0.1.96, new completed section; planned multimodal image/video edit work shifts to v0.1.97 (ROADMAP_v0.1.97.md content). - docs/source/index.rst + massgen/configs/README.md: v0.1.96 release entries. - Announcement: new concise current-release.md (no link in the posted body, pending social links). v0.1.95 announcement was archived in the prior commit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat: OS-level SRT agent sandboxing + permission-hook hardening
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
massgen/filesystem_manager/_path_permission_manager.py (1)
1131-1133:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftFile-specific isolation can still be bypassed through unrecognized path keys.
Because Line 1131 allows when extraction misses, and
_is_path_within_any_managed_areatreatsfile_context_parentas allowed, a sibling path under an unknown key can pass the new scan and skip downstream file-specific denial.Suggested hardening patch
@@ def _validate_no_path_arg_escapes(self, tool_args: dict[str, Any]) -> tuple[bool, str | None]: @@ if not self._is_path_within_any_managed_area(resolved): return ( False, f"Access denied: argument '{key}'='{cand}' resolves to '{resolved}', outside allowed directories.", ) + # If a path is only covered by file_context_parent, require explicit + # permission (i.e., the exact allowed file in file-specific context). + if self.get_permission(resolved) is None: + for managed_path in self.managed_paths: + if managed_path.path_type == "file_context_parent" and managed_path.contains(resolved): + return ( + False, + f"Access denied: argument '{key}' resolves to '{resolved}', " + "which is not an explicitly allowed file in this directory.", + ) return (True, None)Also applies to: 1437-1450, 1490-1494
🤖 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 `@massgen/filesystem_manager/_path_permission_manager.py` around lines 1131 - 1133, The current logic returns allowed when file_path extraction fails, which lets unrecognized path keys bypass file-specific isolation; instead, change the behavior so that a missing or unresolvable file_path is treated as denied by default: update the guard that currently does "if not file_path: return (True, None)" to return a denial (e.g., False with an explanatory reason) so access is blocked unless explicitly permitted by a global allow; ensure the change is applied consistently to the other similar guards noted (the analogous checks around the blocks using _is_path_within_any_managed_area and file_context_parent at the other locations) and update any callers that expect the old (True,None) tuple to handle the denial reason.
🧹 Nitpick comments (3)
massgen/configs/tools/filesystem/sandbox/srt_sandbox.yaml (1)
27-27: ⚡ Quick winUse a cost-effective default model in this example config.
For a sandbox behavior demo config, switch to
gpt-5-mini(orgpt-5-nano) to align with repo config conventions and reduce default run cost.Suggested change
- model: "gpt-5" + model: "gpt-5-mini"As per coding guidelines,
massgen/configs/**/*.yamlshould “Prefer cost-effective models (gpt-5-nano, gpt-5-mini, gemini-2.5-flash)”.🤖 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 `@massgen/configs/tools/filesystem/sandbox/srt_sandbox.yaml` at line 27, The sandbox YAML currently sets model: "gpt-5"; change this to a cost-effective default such as "gpt-5-mini" (or "gpt-5-nano") to follow repo conventions for massgen/configs/** and reduce run cost—update the model field in srt_sandbox.yaml accordingly so the example uses the cheaper model by default.Source: Coding guidelines
massgen/filesystem_manager/_srt_manager.py (1)
177-198: 💤 Low valueMissing docstring for
__init__.Per coding guidelines, new functions should include Google-style docstrings. The constructor has many parameters that would benefit from documentation.
📝 Suggested docstring
def __init__( self, path_permission_manager: PathPermissionManager, *, network_allowed_domains: list[str] | None = None, extra_deny_read: list[str] | None = None, allow_unix_sockets: list[str] | None = None, read_mode: str = READ_MODE_CONFINED, allow_read: list[str] | None = None, fs_tools_extra_writable: list[str | Path] | None = None, settings_dir: str | Path | None = None, srt_path: str = DEFAULT_SRT_BINARY, ) -> None: + """Initialize SrtManager for OS-level command sandboxing. + + Args: + path_permission_manager: The agent's path permission manager to derive + SRT settings from. + network_allowed_domains: Domains to allow network access to (default: none). + extra_deny_read: Additional paths to deny read access. + allow_unix_sockets: Unix socket paths to allow. + read_mode: Read confinement mode - "confined", "strict", or "open". + allow_read: Additional paths to explicitly allow reading. + fs_tools_extra_writable: Extra writable paths for fs_tools profile. + settings_dir: Directory for SRT settings files. + srt_path: Path to the srt binary. + """ self.path_permission_manager = path_permission_manager🤖 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 `@massgen/filesystem_manager/_srt_manager.py` around lines 177 - 198, Add a Google-style docstring to the SrtManager.__init__ constructor in _srt_manager.py: document the purpose of the constructor, each parameter (path_permission_manager, network_allowed_domains, extra_deny_read, allow_unix_sockets, read_mode, allow_read, fs_tools_extra_writable, settings_dir, srt_path), their types and default behaviors, any validation (e.g. read_mode fallback to READ_MODE_CONFINED, Path resolution for fs_tools_extra_writable and settings_dir), and that it returns None; keep it concise and follow the project's Google-style format.Source: Coding guidelines
massgen/filesystem_manager/_path_permission_manager.py (1)
1437-1450: ⚡ Quick winUse Google-style docstrings for the new helper methods.
_is_path_within_any_managed_areaand_validate_no_path_arg_escapesshould include explicitArgsandReturnssections to match repo docstring requirements.As per coding guidelines,
**/*.py: For new or changed functions, include Google-style docstrings.Also applies to: 1452-1496
🤖 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 `@massgen/filesystem_manager/_path_permission_manager.py` around lines 1437 - 1450, Add Google-style docstrings with explicit Args and Returns sections to the two helpers: _is_path_within_any_managed_area and _validate_no_path_arg_escapes; for each function document the parameters (e.g., path: pathlib.Path or arg_name/arg_value for the validator) and the return type/meaning (bool or None/raises), include short description lines and examples only if applicable, and ensure the docstring format matches other repo examples (Args: name (type): description; Returns: type: description). Use the function names to locate and update their docstrings accordingly.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 `@massgen/backend/base.py`:
- Around line 175-185: The code currently overwrites
kwargs["command_line_execution_mode"] from "srt" to "local", losing the
requested SRT policy; instead preserve the original requested mode in kwargs and
only set a local effective mode for immediate use. Change the block around
execution_mode/has_native_execution_sandbox() so you set a new local variable
(e.g. effective_execution_mode = "local") for runtime behavior while leaving
execution_mode and kwargs["command_line_execution_mode"] as "srt" (or explicitly
store the original into a separate key like
"requested_command_line_execution_mode" if you need an explicit name), and
ensure downstream callers (e.g. claude_code.py logic that checks
enable_mcp_command_line and execute_command) can still see the requested "srt"
to disable native Bash execution even though the MCP will operate in the
effective local mode.
In `@massgen/backend/codex.py`:
- Around line 2569-2571: Update the docstring for the
has_native_execution_sandbox method to use the project's Google-style format:
replace the current short triple-quoted string with a Google-style docstring
that describes the method and adds a Returns: section specifying the return type
and meaning (e.g., "bool: True if Codex confines execution via --full-auto
(Landlock/Seatbelt)"). Locate the method by name has_native_execution_sandbox
and update its docstring accordingly.
In `@massgen/tests/test_path_permission_hook_adversarial.py`:
- Around line 18-140: Add Google-style docstrings to each new or changed
function: the pytest fixture pm, the helper _denied, and each test function
(e.g., test_absolute_outside_write_denied, test_dotdot_traversal_write_denied,
test_symlink_through_workspace_denied, test_unrecognized_path_key_write_denied,
test_arbitrary_key_absolute_path_write_denied,
test_list_valued_path_write_denied, test_move_source_outside_denied,
test_copy_source_outside_denied, test_in_workspace_write_allowed,
test_content_with_pathlike_text_not_blocked,
test_content_equal_to_absolute_path_not_blocked,
test_nested_dict_path_escape_denied, test_dict_in_list_path_escape_denied,
test_value_key_escape_denied, test_read_tool_unrecognized_key_exfil_denied)
following the repo convention: short one-line summary, Args section for
parameters (e.g., tmp_path for pm, m/tool/args for _denied), and a brief Returns
section where applicable; place the docstring immediately under each def.
In `@massgen/tests/test_srt_backend_degrade.py`:
- Around line 12-64: Add Google-style function-level docstrings to each new or
modified function and class in this diff: _StubBackend, its methods
stream_with_tools/get_provider_name/get_filesystem_support/has_native_execution_sandbox,
and all test functions test_api_backend_without_native_sandbox_keeps_srt,
test_native_sandbox_backend_degrades_srt_to_local,
test_degrade_does_not_touch_docker_or_local,
test_base_default_has_no_native_execution_sandbox, and
test_codex_and_claude_code_declare_native_sandbox; for each, include a short
one-line summary, Args and Returns (if any) sections per the repository’s
Google-style docstring format so the functions meet the project’s docstring
linting requirements.
- Around line 44-50: The test test_degrade_does_not_touch_docker_or_local only
asserts the "local" branch; add a check for the "docker" branch by creating a
second instance of the _NativeStub (or reuse b) with
command_line_execution_mode="docker" and assert that its
filesystem_manager.command_line_execution_mode == "docker" so the docker path is
covered; reference the _NativeStub class and the b
variable/filesystem_manager.command_line_execution_mode to locate where to add
the new assertion.
In `@massgen/tests/test_srt_filesystem_integration.py`:
- Around line 18-158: Add Google-style function docstrings to the new fixtures
and test functions in this module: srt_fs_manager, local_fs_manager and each
test_* function (e.g., test_command_line_config_has_srt_args,
test_command_line_config_writes_valid_settings_file,
test_local_mode_has_no_srt_args, test_fs_tools_server_is_srt_wrapped_via_sh_c,
test_fs_tools_server_not_wrapped_in_local_mode,
test_srt_is_an_allowed_mcp_executable,
test_srt_wrapped_fs_tools_config_passes_mcp_security,
test_wrap_skips_npx_launcher, test_wrap_applies_to_non_network_launcher,
test_wrap_skips_no_roots_wrapper, test_wrap_skips_absolute_path_npx); for each
add a concise Google-style docstring describing purpose, key
behavior/assertions, and any important parameters/fixtures used so the functions
meet the project docstring guideline.
In `@massgen/tests/test_srt_manager.py`:
- Around line 27-295: Add Google-style docstrings to each new or modified
function/fixture in this file (e.g., the pytest fixture pm_with_paths, helper
_is_read_allowed, and any new test functions such as
test_fs_tools_profile_confined_allows_reading_framework_runtime and
test_execution_profile_widens_writes_for_temp_and_snapshot). For each docstring
include a one-line summary, Args: (describe parameters like tmp_path,
allow_read, target), Returns: (describe return types such as dict or bool), and
any Raises: if applicable; for fixtures mention what the fixture yields/returns
(a dict with keys
"pm","workspace","temp_ws","ctx_write","ctx_read","protected"). Keep docstrings
concise and use Google-style sections.
In `@massgen/tests/test_subagent_manager.py`:
- Around line 3901-3928: Add a Google-style docstring to the test function
test_srt_settings_inherited_by_subagent that briefly describes the test's
purpose (verifying SRT-related backend settings are inherited by generated
subagent config), lists any parameters (none) and expected behavior/assertions,
and includes an example or notes if needed; place the docstring as the first
statement inside the function body of test_srt_settings_inherited_by_subagent in
massgen/tests/test_subagent_manager.py so linters and documentation checks pass.
---
Outside diff comments:
In `@massgen/filesystem_manager/_path_permission_manager.py`:
- Around line 1131-1133: The current logic returns allowed when file_path
extraction fails, which lets unrecognized path keys bypass file-specific
isolation; instead, change the behavior so that a missing or unresolvable
file_path is treated as denied by default: update the guard that currently does
"if not file_path: return (True, None)" to return a denial (e.g., False with an
explanatory reason) so access is blocked unless explicitly permitted by a global
allow; ensure the change is applied consistently to the other similar guards
noted (the analogous checks around the blocks using
_is_path_within_any_managed_area and file_context_parent at the other locations)
and update any callers that expect the old (True,None) tuple to handle the
denial reason.
---
Nitpick comments:
In `@massgen/configs/tools/filesystem/sandbox/srt_sandbox.yaml`:
- Line 27: The sandbox YAML currently sets model: "gpt-5"; change this to a
cost-effective default such as "gpt-5-mini" (or "gpt-5-nano") to follow repo
conventions for massgen/configs/** and reduce run cost—update the model field in
srt_sandbox.yaml accordingly so the example uses the cheaper model by default.
In `@massgen/filesystem_manager/_path_permission_manager.py`:
- Around line 1437-1450: Add Google-style docstrings with explicit Args and
Returns sections to the two helpers: _is_path_within_any_managed_area and
_validate_no_path_arg_escapes; for each function document the parameters (e.g.,
path: pathlib.Path or arg_name/arg_value for the validator) and the return
type/meaning (bool or None/raises), include short description lines and examples
only if applicable, and ensure the docstring format matches other repo examples
(Args: name (type): description; Returns: type: description). Use the function
names to locate and update their docstrings accordingly.
In `@massgen/filesystem_manager/_srt_manager.py`:
- Around line 177-198: Add a Google-style docstring to the SrtManager.__init__
constructor in _srt_manager.py: document the purpose of the constructor, each
parameter (path_permission_manager, network_allowed_domains, extra_deny_read,
allow_unix_sockets, read_mode, allow_read, fs_tools_extra_writable,
settings_dir, srt_path), their types and default behaviors, any validation (e.g.
read_mode fallback to READ_MODE_CONFINED, Path resolution for
fs_tools_extra_writable and settings_dir), and that it returns None; keep it
concise and follow the project's Google-style format.
🪄 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: 09f38dd2-b19a-4c66-9b33-33ad82dd6e35
📒 Files selected for processing (26)
CHANGELOG.mdPR_DRAFT_sandboxing.mdREADME.mdREADME_PYPI.mdROADMAP.mdROADMAP_v0.1.97.mddocs/announcements/archive/v0.1.95.mddocs/announcements/current-release.mddocs/source/index.rstmassgen/backend/_excluded_params.pymassgen/backend/base.pymassgen/backend/claude_code.pymassgen/backend/codex.pymassgen/configs/README.mdmassgen/configs/tools/filesystem/sandbox/srt_sandbox.yamlmassgen/filesystem_manager/_code_execution_server.pymassgen/filesystem_manager/_filesystem_manager.pymassgen/filesystem_manager/_path_permission_manager.pymassgen/filesystem_manager/_srt_manager.pymassgen/mcp_tools/security.pymassgen/subagent/manager.pymassgen/tests/test_path_permission_hook_adversarial.pymassgen/tests/test_srt_backend_degrade.pymassgen/tests/test_srt_filesystem_integration.pymassgen/tests/test_srt_manager.pymassgen/tests/test_subagent_manager.py
✅ Files skipped from review due to trivial changes (9)
- docs/source/index.rst
- PR_DRAFT_sandboxing.md
- massgen/configs/README.md
- docs/announcements/current-release.md
- docs/announcements/archive/v0.1.95.md
- ROADMAP_v0.1.97.md
- CHANGELOG.md
- README.md
- ROADMAP.md
There was a problem hiding this comment.
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 `@docs/announcements/github-release-v0.1.96.md`:
- Line 5: Change the feature subsection headings that currently use level-3
headings to level-2 headings: replace "### 🛡️ OS-Level Execution Sandbox" and
the other feature subsection headings in this document (the feature headings at
the five subsection positions) from `###` to `##` so the document restores a
proper h1->h2->h3 hierarchy; update each heading token (e.g., the "OS-Level
Execution Sandbox" heading and the other four feature headings) accordingly.
🪄 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: 81da9f65-c7eb-4208-bb4c-1889f5de7317
📒 Files selected for processing (4)
CHANGELOG.mddocs/announcements/github-release-v0.1.94.mddocs/announcements/github-release-v0.1.95.mddocs/announcements/github-release-v0.1.96.md
💤 Files with no reviewable changes (2)
- docs/announcements/github-release-v0.1.95.md
- docs/announcements/github-release-v0.1.94.md
✅ Files skipped from review due to trivial changes (1)
- CHANGELOG.md
PR Title Format
Your PR title must follow the format:
<type>: <brief description>Valid types:
fix:- Bug fixesfeat:- New featuresbreaking:- Breaking changesdocs:- Documentation updatesrefactor:- Code refactoringtest:- Test additions/modificationschore:- Maintenance tasksperf:- Performance improvementsstyle:- Code style changesci:- CI/CD configuration changesExamples:
fix: resolve memory leak in data processingfeat: add export to CSV functionalitybreaking: change API response formatdocs: update installation guideDescription
Brief description of the changes in this PR
Type of change
fix:) - Non-breaking change which fixes an issuefeat:) - Non-breaking change which adds functionalitybreaking:) - Fix or feature that would cause existing functionality to not work as expecteddocs:) - Documentation updatesrefactor:) - Code changes that neither fix a bug nor add a featuretest:) - Adding missing tests or correcting existing testschore:) - Maintenance tasks, dependency updates, etc.perf:) - Code changes that improve performancestyle:) - Changes that do not affect the meaning of the code (formatting, missing semi-colons, etc.)ci:) - Changes to CI/CD configuration files and scriptsChecklist
Pre-commit status
How to Test
Add test method for this PR.
Test CLI Command
Write down the test bash command. If there is pre-requests, please emphasize.
Expected Results
Description/screenshots of expected results.
Additional context
Add any other context about the PR here.
Summary by CodeRabbit