Replace JIRA Arbitrary Code Execution vulnerability with finer grain API wrapper (#6992)

This fixes #4833 and the critical vulnerability
https://nvd.nist.gov/vuln/detail/CVE-2023-34540

Previously, the JIRA API Wrapper had a mode that simply pipelined user
input into an `exec()` function.
[The intended use of the 'other' mode is to cover any of Atlassian's API
that don't have an existing
interface](cc33bde74f/langchain/tools/jira/prompt.py (L24))

Fortunately all of the [Atlassian JIRA API methods are subfunctions of
their `Jira`
class](https://atlassian-python-api.readthedocs.io/jira.html), so this
implementation calls these subfunctions directly.

As well as passing a string representation of the function to call, the
implementation flexibly allows for optionally passing args and/or
keyword-args. These are given as part of the dictionary input. Example:
```
    {
        "function": "update_issue_field",   #function to execute
        "args": [                           #list of ordered args similar to other examples in this JiraAPIWrapper
            "key",
            {"summary": "New summary"}
        ],
        "kwargs": {}                        #dict of key value keyword-args pairs
    }
```

the above is equivalent to `self.jira.update_issue_field("key",
{"summary": "New summary"})`

Alternate query schema designs are welcome to make querying easier
without passing and evaluating arbitrary python code. I considered
parsing (without evaluating) input python code and extracting the
function, args, and kwargs from there and then pipelining them into the
callable function via `*f(args, **kwargs)` - but this seemed more
direct.

@vowelparrot @dev2049

---------

Co-authored-by: Jamal Rahman <jamal.rahman@builder.ai>
pull/6556/head
Jamal 1 year ago committed by GitHub
parent 61938a02a1
commit a2f191a322
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -25,11 +25,12 @@ JIRA_CATCH_ALL_PROMPT = """
This tool is a wrapper around atlassian-python-api's Jira API.
There are other dedicated tools for fetching all projects, and creating and searching for issues,
use this tool if you need to perform any other actions allowed by the atlassian-python-api Jira API.
The input to this tool is line of python code that calls a function from atlassian-python-api's Jira API
For example, to update the summary field of an issue, you would pass in the following string:
self.jira.update_issue_field(key, {{"summary": "New summary"}})
The input to this tool is a dictionary specifying a function from atlassian-python-api's Jira API,
as well as a list of arguments and dictionary of keyword arguments to pass into the function.
For example, to get all the users in a group, while increasing the max number of results to 100, you would
pass in the following dictionary: {{"function": "get_all_users_from_group", "args": ["group"], "kwargs": {{"limit":100}} }}
or to find out how many projects are in the Jira instance, you would pass in the following string:
self.jira.projects()
{{"function": "projects"}}
For more information on the Jira API, refer to https://atlassian-python-api.readthedocs.io/jira.html
"""

@ -188,10 +188,15 @@ class JiraAPIWrapper(BaseModel):
return self.confluence.create_page(**dict(params))
def other(self, query: str) -> str:
context = {"self": self}
exec(f"result = {query}", context)
result = context["result"]
return str(result)
try:
import json
except ImportError:
raise ImportError(
"json is not installed. Please install it with `pip install json`"
)
params = json.loads(query)
jira_function = getattr(self.jira, params["function"])
return jira_function(*params.get("args", []), **params.get("kwargs", {}))
def run(self, mode: str, query: str) -> str:
if mode == "jql":

@ -41,3 +41,24 @@ def test_create_confluence_page() -> None:
output = jira.run("create_page", create_page_dict)
assert "type" in output
assert "page" in output
def test_other() -> None:
"""Non-exhaustive test for accessing other JIRA API methods"""
jira = JiraAPIWrapper()
issue_create_dict = """
{
"function":"issue_create",
"kwargs": {
"fields": {
"summary": "Test Summary",
"description": "Test Description",
"issuetype": {"name": "Bug"},
"project": {"key": "TP"}
}
}
}
"""
output = jira.run("other", issue_create_dict)
assert "id" in output
assert "key" in output

Loading…
Cancel
Save