Skip to content
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

Update main.star #2019

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Update main.star #2019

wants to merge 1 commit into from

Conversation

futreall
Copy link

@futreall futreall commented Sep 24, 2024

1.Error checking and logging: Added error checks for command executions with meaningful error messages. This makes debugging easier and helps identify failure points more clearly.

2.Avoiding code duplication: Created the run_command function to avoid repeating code for command execution and error handling, simplifying the structure and making future modifications easier.

3.Wallet handling: Moved wallet type handling into a separate get_wallet_command function. This makes it easier to extend for new wallet types in the future.

4.Safe access to deployment data: Used .get() method to safely access dictionary values in deployment to prevent KeyError if a key is missing.

5.String constants for file paths and commands: Moved frequently used strings such as file paths and commands into constants for better maintainability and to avoid potential errors if these values need to be changed in the future.

6.Security improvements: Introduced the use of shlex.quote to safely handle user input (like RPC URLs and wallet values) and prevent command injection vulnerabilities.

Summary by CodeRabbit

  • New Features
    • Introduced new command execution and wallet retrieval functions to enhance script functionality.
  • Improvements
    • Refactored deployment process to include validation checks for required parameters, improving reliability.
    • Enhanced error handling and logging for better clarity during script execution.

1.Error checking and logging: Added error checks for command executions with meaningful error messages. This makes debugging easier and helps identify failure points more clearly.

2.Avoiding code duplication: Created the run_command function to avoid repeating code for command execution and error handling, simplifying the structure and making future modifications easier.

3.Wallet handling: Moved wallet type handling into a separate get_wallet_command function. This makes it easier to extend for new wallet types in the future.

4.Safe access to deployment data: Used .get() method to safely access dictionary values in deployment to prevent KeyError if a key is missing.

5.String constants for file paths and commands: Moved frequently used strings such as file paths and commands into constants for better maintainability and to avoid potential errors if these values need to be changed in the future.

6.Security improvements: Introduced the use of shlex.quote to safely handle user input (like RPC URLs and wallet values) and prevent command injection vulnerabilities.

Signed-off-by: futreall <[email protected]>
Copy link
Contributor

coderabbitai bot commented Sep 24, 2024

Walkthrough

The pull request introduces modifications to the deploy_contracts function and related utilities in the testing/forge-script/main.star file. Key changes include the addition of the run_command and get_wallet_command functions, refactoring of the deploy_contracts function to include validation checks for required fields, and improvements in command execution and error handling. The updates enhance the structure and clarity of the deployment script while maintaining its core functionality.

Changes

File Change Summary
testing/forge-script/main.star - Added run_command(plan, service_name, command, error_message) function.
- Added get_wallet_command(wallet) function.
- Refactored deploy_contracts(plan, deployment) to include validation checks and improved logic.
- Updated command execution for local and git dependencies to utilize run_command.
- Enhanced logging and error handling for transaction file retrieval.

Possibly related PRs

Suggested reviewers

  • itsdevbear
  • corduroybera
  • ocnc

Poem

In the script where contracts lay,
A rabbit hops to clear the way.
With commands now neat and bright,
We deploy with all our might!
Errors handled, logs in sight,
Hooray for changes, what a delight! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    -- I pushed a fix in commit <commit_id>, please review it.
    -- Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    -- @coderabbitai generate unit testing code for this file.
    -- @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    -- @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    -- @coderabbitai read src/utils.ts and generate unit testing code.
    -- @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    -- @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@futreall
Copy link
Author

Hey team, happy to make any necessary adjustments or improvements if needed. Let me know if there's anything you'd like me to tweak or clarify!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE

Commits

Files that changed from the base of the PR and between 767e5b9 and faab4f7.

Files selected for processing (1)
  • testing/forge-script/main.star (1 hunks)

Comment on lines +67 to +77
contract_path = "{}/{}".format(SOURCE_DIR_PATH, contracts_path) if contracts_path else SOURCE_DIR_PATH

if dependency_type == "local":
# Run shell script
plan.exec(
service_name = foundry_service.name,
recipe = ExecRecipe(
command = ["/bin/sh", "-c", "sh {}/{}".format(DEPENDENCY_DIR_PATH, dependency_path)],
),
)
run_command(plan, foundry_service.name, "sh {}/{}".format(DEPENDENCY_DIR_PATH, dependency_path), "Local dependency execution failed")
elif dependency_type == "git":
plan.exec(
service_name = foundry_service.name,
recipe = ExecRecipe(
command = ["/bin/sh", "-c", "cd {} && sh {}".format(contract_path, dependency_path)],
),
)
run_command(plan, foundry_service.name, "cd {} && sh {}".format(contract_path, dependency_path), "Git dependency execution failed")

if script_path:
result = plan.exec(
service_name = foundry_service.name,
recipe = ExecRecipe(
command = ["/bin/sh", "-c", "cd {} && forge build".format(contract_path)],
),
)
plan.verify(result["code"], "==", 0)
# Logging the build and deployment process
plan.print("Running deployment script: {}".format(script_path))
run_command(plan, foundry_service.name, "cd {} && forge build".format(contract_path), "Forge build failed")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Sanitize contracts_path when used in shell commands

The contracts_path variable is derived from deployment data and used in shell commands without proper sanitization. This could lead to command injection vulnerabilities.

Apply shlex.quote to contracts_path when constructing shell commands:

 contract_path = "{}/{}".format(SOURCE_DIR_PATH, contracts_path) if contracts_path else SOURCE_DIR_PATH

+contract_path_quoted = shlex.quote(contract_path)

-    run_command(plan, foundry_service.name, "cd {} && forge build".format(contract_path), "Forge build failed")
+    run_command(plan, foundry_service.name, "cd {} && forge build".format(contract_path_quoted), "Forge build failed")

 ...

-        "cd {} && {}".format(contract_path, TRANSACTION_FILE_CMD)
+        "cd {} && {}".format(contract_path_quoted, TRANSACTION_FILE_CMD)

 ...

-    "cat {}/{}".format(contract_path, OUTPUT_FILE),
+    "cat {}/{}".format(contract_path_quoted, OUTPUT_FILE),

Also applies to: 83-89, 96-96, 105-105

Comment on lines +75 to +77
# Logging the build and deployment process
plan.print("Running deployment script: {}".format(script_path))
run_command(plan, foundry_service.name, "cd {} && forge build".format(contract_path), "Forge build failed")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Sanitize script_path and contract_name to prevent command injection

The script_path and contract_name variables are used in shell commands without sanitization. This could lead to vulnerabilities if they contain malicious input.

Apply shlex.quote to script_path and contract_name when constructing shell commands:

 plan.print("Running deployment script: {}".format(script_path))
+script_path_quoted = shlex.quote(script_path)
+contract_name_quoted = shlex.quote(contract_name)
 
-    run_command(plan, foundry_service.name, "cd {} && forge build".format(contract_path), "Forge build failed")
+    run_command(plan, foundry_service.name, "cd {} && forge build".format(contract_path_quoted), "Forge build failed")

 ...

     script_output = exec_on_service(
         plan,
         foundry_service.name,
-        "cd {} && forge script {}:{} --broadcast --rpc-url {} {} --json --skip test > {}".format(
-            contract_path,
-            script_path,
-            contract_name,
+        "cd {} && forge script {}:{} --broadcast --rpc-url {} {} --json --skip test > {}".format(
+            contract_path_quoted,
+            script_path_quoted,
+            contract_name_quoted,
             rpc_url,
             wallet_command,
             OUTPUT_FILE
         ),
     )

Also applies to: 83-89

Comment on lines +83 to +89
"cd {} && forge script {}:{} --broadcast --rpc-url {} {} --json --skip test > {}".format(
contract_path,
script_path,
contract_name,
rpc_url,
wallet_command,
OUTPUT_FILE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve readability by refactoring command construction

The command string used in the forge script execution is lengthy and complex. Refactoring it can enhance readability and maintainability.

Refactor the command construction as follows:

script_command = "cd {} && forge script {}:{} --broadcast --rpc-url {} {} --json --skip test > {}".format(
    contract_path_quoted,
    script_path_quoted,
    contract_name_quoted,
    rpc_url,
    wallet_command,
    OUTPUT_FILE
)

# Then use the command in exec_on_service or run_command
run_command(
    plan,
    foundry_service.name,
    script_command,
    "Forge script execution failed",
)


def run(plan, deployment = {}):
def run(plan, deployment={}):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid mutable default arguments

In the run function, using a mutable default argument like {} for deployment can lead to unexpected behaviors because the default mutable object is shared across all calls to the function.

Apply this diff to fix the issue:

-def run(plan, deployment={}):
+def run(plan, deployment=None):
+    if deployment is None:
+        deployment = {}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def run(plan, deployment={}):
def run(plan, deployment=None):
if deployment is None:
deployment = {}

Comment on lines +58 to +59
dependency_path = dependency.get("path", "")
plan.upload_files(src=DEPENDENCY_DIR, name=DEPENDENCY_DIR)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Check for missing dependency_path before use

The dependency_path is retrieved with a default empty string but used in commands. If it's empty, the command may fail.

Add a check to ensure dependency_path is provided when a dependency is specified:

 dependency_path = dependency.get("path", "")
+if not dependency_path:
+    fail("Dependency path not specified for dependency type '{}'".format(dependency_type))
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
dependency_path = dependency.get("path", "")
plan.upload_files(src=DEPENDENCY_DIR, name=DEPENDENCY_DIR)
dependency_path = dependency.get("path", "")
if not dependency_path:
fail("Dependency path not specified for dependency type '{}'".format(dependency_type))
plan.upload_files(src=DEPENDENCY_DIR, name=DEPENDENCY_DIR)

Comment on lines +44 to +45
contract_name = deployment.get("contract_name", "")
rpc_url = shlex.quote(deployment.get("rpc_url", ""))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Check for missing contract_name and rpc_url before use

The contract_name and rpc_url are retrieved with default empty strings but used in commands. If either is empty, the command may fail or behave unexpectedly.

Add checks to ensure contract_name and rpc_url are provided:

 contract_name = deployment.get("contract_name", "")
+if not contract_name:
+    fail("Contract name not specified in deployment")
 rpc_url = shlex.quote(deployment.get("rpc_url", ""))
+if not deployment.get("rpc_url"):
+    fail("RPC URL not specified in deployment")
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
contract_name = deployment.get("contract_name", "")
rpc_url = shlex.quote(deployment.get("rpc_url", ""))
contract_name = deployment.get("contract_name", "")
if not contract_name:
fail("Contract name not specified in deployment")
rpc_url = shlex.quote(deployment.get("rpc_url", ""))
if not deployment.get("rpc_url"):
fail("RPC URL not specified in deployment")

Comment on lines +31 to 33
if wallet_type == "private_key":
return "--private-key {}".format(shlex.quote(wallet["value"]))
else:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling for missing wallet["value"]

The code assumes that wallet["value"] exists, which may raise a KeyError if the key is missing. It's important to check for the presence of this key before using it.

Add a check to ensure wallet["value"] is present:

 def get_wallet_command(wallet):
     wallet_type = wallet["type"]
     if wallet_type == "private_key":
+        if "value" not in wallet:
+            fail("Wallet value not specified for private_key type")
         return "--private-key {}".format(shlex.quote(wallet["value"]))
     else:
         fail("Wallet type {} not supported.".format(wallet_type))
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if wallet_type == "private_key":
return "--private-key {}".format(shlex.quote(wallet["value"]))
else:
if wallet_type == "private_key":
if "value" not in wallet:
fail("Wallet value not specified for private_key type")
return "--private-key {}".format(shlex.quote(wallet["value"]))
else:

exec_output = exec_on_service(
plan,
foundry_service.name,
"chmod -R 777 /app/contracts && cat {}".format(transaction_file_details["output"])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Sanitize transaction_file_details["output"] to prevent command injection

The transaction_file_details["output"] is used in a shell command without sanitization. This could be exploited if the output contains malicious content.

Apply shlex.quote to transaction_file_details["output"] when constructing shell commands:

 exec_output = exec_on_service(
     plan,
     foundry_service.name,
-    "chmod -R 777 /app/contracts && cat {}".format(transaction_file_details["output"])
+    "chmod -R 777 /app/contracts && cat {}".format(shlex.quote(transaction_file_details["output"]))
 )

Committable suggestion was skipped due to low confidence.

Comment on lines +70 to +72
run_command(plan, foundry_service.name, "sh {}/{}".format(DEPENDENCY_DIR_PATH, dependency_path), "Local dependency execution failed")
elif dependency_type == "git":
plan.exec(
service_name = foundry_service.name,
recipe = ExecRecipe(
command = ["/bin/sh", "-c", "cd {} && sh {}".format(contract_path, dependency_path)],
),
)
run_command(plan, foundry_service.name, "cd {} && sh {}".format(contract_path, dependency_path), "Git dependency execution failed")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Secure dependency_path to prevent command injection

The dependency_path variable is used in shell commands without proper sanitization. This could lead to command injection vulnerabilities if dependency_path contains malicious input.

Apply shlex.quote to dependency_path when constructing shell commands:

 if dependency_type == "local":
-    run_command(plan, foundry_service.name, "sh {}/{}".format(DEPENDENCY_DIR_PATH, dependency_path), "Local dependency execution failed")
+    run_command(plan, foundry_service.name, "sh {}/{}".format(DEPENDENCY_DIR_PATH, shlex.quote(dependency_path)), "Local dependency execution failed")
 elif dependency_type == "git":
-    run_command(plan, foundry_service.name, "cd {} && sh {}".format(contract_path, dependency_path), "Git dependency execution failed")
+    run_command(plan, foundry_service.name, "cd {} && sh {}".format(contract_path, shlex.quote(dependency_path)), "Git dependency execution failed")

Committable suggestion was skipped due to low confidence.

Comment on lines +79 to +115
# Executing forge script
script_output = exec_on_service(
plan,
foundry_service.name,
"cd {} && forge script {}:{} --broadcast --rpc-url {} {} --json --skip test > output.json ".format(
"cd {} && forge script {}:{} --broadcast --rpc-url {} {} --json --skip test > {}".format(
contract_path,
script_path,
contract_name,
rpc_url,
wallet_command,
OUTPUT_FILE
),
)

exec_on_service(
plan,
foundry_service.name,
"cat {}/output.json ".format(contract_path),
"cat {}/{}".format(contract_path, OUTPUT_FILE),
)

if script_path:
# Get the forge script output in a output.json file and grep from it
transaction_file = "grep 'Transactions saved to' output.json | awk -F': ' '{print $2}'"
plan.print("transaction_file", transaction_file)

transaction_file_details = exec_on_service(plan, foundry_service.name, "cd {} && {}".format(contract_path, transaction_file))

# Fetch and check the transaction file
plan.print("Retrieving transaction file...")
transaction_file_details = exec_on_service(
plan,
foundry_service.name,
"cd {} && {}".format(contract_path, TRANSACTION_FILE_CMD)
)

if not transaction_file_details["output"]:
fail("Transaction file not found.")
exec_output = exec_on_service(plan, foundry_service.name, "chmod -R 777 /app/contracts && cat {}".format(transaction_file_details["output"]))

exec_output = exec_on_service(
plan,
foundry_service.name,
"chmod -R 777 /app/contracts && cat {}".format(transaction_file_details["output"])
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Unify command execution and error handling

Multiple commands are executed using exec_on_service without consistent error handling. To maintain consistency and ensure proper error handling, consider using run_command instead of exec_on_service where appropriate.

Refactor the code to use run_command for command execution that requires error checks:

-# Executing forge script
-script_output = exec_on_service(
-    plan,
-    foundry_service.name,
-    "cd {} && forge script ...".format(...),
-)
+run_command(
+    plan,
+    foundry_service.name,
+    "cd {} && forge script ...".format(...),
+    "Forge script execution failed",
+)

 ...

-transaction_file_details = exec_on_service(
-    plan,
-    foundry_service.name,
-    "cd {} && {}".format(contract_path_quoted, TRANSACTION_FILE_CMD)
-)
+transaction_file_details = run_command(
+    plan,
+    foundry_service.name,
+    "cd {} && {}".format(contract_path_quoted, TRANSACTION_FILE_CMD),
+    "Transaction file retrieval failed",
+)

 ...

-exec_output = exec_on_service(
-    plan,
-    foundry_service.name,
-    "chmod -R 777 /app/contracts && cat {}".format(shlex.quote(transaction_file_details["output"]))
-)
+exec_output = run_command(
+    plan,
+    foundry_service.name,
+    "chmod -R 777 /app/contracts && cat {}".format(shlex.quote(transaction_file_details["output"])),
+    "Failed to read transaction file",
+)

Committable suggestion was skipped due to low confidence.

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