Skip to content

Conversation

Saedbhati
Copy link
Collaborator

Description

Describe your changes in detail (optional if the linked issue already contains a detailed description of the changes).
fixes #3203

Checklist

Go over all the following points, and put an x in all the boxes that apply.

  • I have read the CONTRIBUTION guide (required)
  • I have linked this PR to an issue using the Development section on the right sidebar or by adding Fixes #issue-number in the PR description (required)
  • I have checked if any dependencies need to be added or updated in pyproject.toml and uv lock
  • I have updated the tests accordingly (required for a bug fix or a new feature)
  • I have updated the documentation if needed:
  • I have added examples if this is a new feature

If you are unsure about any of these, don't hesitate to ask. We are here to help!

Copy link
Contributor

coderabbitai bot commented Sep 26, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat_zoho_mail

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.

scope=scopes,
)
print("\nToken response:")
print(json.dumps(token_resp, indent=2))

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (secret)
as clear text.

Copilot Autofix

AI 11 days ago

To fix this issue, we must avoid logging cleartext sensitive data such as OAuth tokens, the client secret, and refresh tokens or secrets that could be included in either a successful response or within error information.

The correct way is to:

  • Never print the entire token_resp out to the logs.
  • Instead, explicitly print only non-sensitive subfields as needed (e.g., print e.g. "Access token received" or the keys but not values, or simply suppress printing altogether unless debugging).
  • If details from the response must be shown, redact any sensitive fields like access_token, refresh_token, and any client_secret or request_params/request_json objects.
  • Specifically for the snippet, remove or replace the line print(json.dumps(token_resp, indent=2)) with a safe message, or print only a fixed set of non-sensitive keys (e.g. the keys present in the response).

Only make these changes in examples/toolkits/zoho_toolkit.py, as shown.


Suggested changeset 1
examples/toolkits/zoho_toolkit.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/examples/toolkits/zoho_toolkit.py b/examples/toolkits/zoho_toolkit.py
--- a/examples/toolkits/zoho_toolkit.py
+++ b/examples/toolkits/zoho_toolkit.py
@@ -118,7 +118,14 @@
         scope=scopes,
     )
     print("\nToken response:")
-    print(json.dumps(token_resp, indent=2))
+    # Do not print entire response as it may contain sensitive information.
+    # Only indicate success or reason for failure (if available).
+    if "access_token" in token_resp:
+        print("Access token received.")
+    elif "error" in token_resp:
+        print(f"Token request failed: {token_resp.get('error')}")
+    else:
+        print("Token response received, but access_token not found.")
     access_token = token_resp.get("access_token") or ""
     if not access_token:
         print("Failed to obtain access token.")
EOF
@@ -118,7 +118,14 @@
scope=scopes,
)
print("\nToken response:")
print(json.dumps(token_resp, indent=2))
# Do not print entire response as it may contain sensitive information.
# Only indicate success or reason for failure (if available).
if "access_token" in token_resp:
print("Access token received.")
elif "error" in token_resp:
print(f"Token request failed: {token_resp.get('error')}")
else:
print("Token response received, but access_token not found.")
access_token = token_resp.get("access_token") or ""
if not access_token:
print("Failed to obtain access token.")
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines 200 to 205
on_subtask_completed (Optional[Callable[[Task], None]], optional):
Callback function to be called when a subtask is completed.
(default: :obj:`None`)
on_subtask_failed (Optional[Callable[[Task], None]], optional):
Callback function to be called when a subtask fails.
(default: :obj:`None`)
Copy link
Member

Choose a reason for hiding this comment

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

why we need to add these 2 argument in this PR, could you describe more for this change?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was working on this issue
, but when I saw that coolbeevip was already working on it, I decided to drop it. I’m not sure how I ended up committing, but I will remove it.

@a7m-1st
Copy link
Collaborator

a7m-1st commented Sep 29, 2025

@Saedbhati while testing, I provided:

$env:ZOHO_CLIENT_ID
$env:ZOHO_CLIENT_SECRET
$env:ZOHO_REDIRECT_URI
$env:ZOHO_TO_ADDRESS

Which I got from the api console (Workspace account):
image

But I am facing this issue, the outh generation seems fine though. Is this the correct way to get started with the api?

image

@a7m-1st
Copy link
Collaborator

a7m-1st commented Sep 29, 2025

Program flow for me:
image

@Saedbhati
Copy link
Collaborator Author

@a7m-1st
image
you have to set redirect url to http://localhost:8000/callback

@a7m-1st
Copy link
Collaborator

a7m-1st commented Sep 29, 2025

Oh all right, didn't realize that. I will try again later. Thanks @Saedbhati

Copy link
Collaborator

@waleedalzarooni waleedalzarooni left a comment

Choose a reason for hiding this comment

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

Hey Saed good work!

I left some comments, most are quick fixes, let me know if you need any clarification!

"Content-Type": "application/json",
}
try:
response = requests.request(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Other toolkits include a timeout parameter here just in case, may be useful if you have time

url = f"{self.base_url}/api/accounts/{self.account_id}/messages/search"
query = f"label:{tag_id}"
params: Dict[str, Any] = {
"query": query,
Copy link
Collaborator

Choose a reason for hiding this comment

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

In line 758 you use searchKey, just wan't to double check this difference is intentional

@staticmethod
def build_authorize_url(
*,
client_type: str,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider adding some input validation for client_type client_id and scope

}
if scope:
params["scope"] = scope
return self._request_json("POST", url, params=params)
Copy link
Collaborator

Choose a reason for hiding this comment

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

OAUTH 2.0 specification (RFC 6749) violation here may cause exposed client secrets susceptible to attack.
params is sent in URL query string as opposed to request body

}
if scope:
params["scope"] = scope
return self._request_json("POST", url, params=params)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe this is the same violation as above

description: Optional[str] = None,
due_in_epoch_ms: Optional[int] = None,
) -> Dict[str, Any]:
r"""Action: Create Task (OAuth Scope: ZohoMail.tasks )
Copy link
Collaborator

Choose a reason for hiding this comment

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

docstring

description: Optional[str] = None,
due_in_epoch_ms: Optional[int] = None,
) -> Dict[str, Any]:
r"""Action: Create Task (OAuth Scope: ZohoMail.tasks )
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
r"""Action: Create Task (OAuth Scope: ZohoMail.tasks )
r"""Action: Create Task (OAuth Scope: ZohoMail.tasks)

folder_id (str): Folder ID.
since_epoch_seconds (Optional[int]): Since epoch seconds.
limit (int): Limit.
sort_order (str): Sort order.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Possibly a more detailed description to help the agent understand what the Args mean, also missing default tags, see CONTRIBUTING.md

def trigger_new_email_matching_search_poll(
self,
query: str,
folder_id: Optional[str] = None,
Copy link
Collaborator

Choose a reason for hiding this comment

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

never used

params["includeto"] = True
if received_time_ms is not None:
params["receivedTime"] = int(received_time_ms)
# Note: folderId is not supported in search API; omit to avoid
Copy link
Collaborator

Choose a reason for hiding this comment

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

the next method uses folderId in the search API request so not sure whether this note is correct or not

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.

[Feature Request] Integrate Zoho Mail into CAMEL with Actions and Triggers
4 participants