From 374bb408521fa0a1c692591be7aa6e0ddac2233c Mon Sep 17 00:00:00 2001 From: Eugene Yurtsev Date: Tue, 10 Sep 2024 15:59:03 -0400 Subject: [PATCH] community: Force opt-in for qa chains (#26278) The underlying code is already documented as requiring appropriate RBAC control, but adding a forced user opt-in to make sure that users that don't read documentation are still aware of what's required from a security perspective. https://huntr.com/bounties/8f4ad910-7fdc-4089-8f0a-b5df5f32e7c5 --- .../chains/graph_qa/arangodb.py | 31 +++++++++++++++++++ .../chains/graph_qa/cypher.py | 30 ++++++++++++++++++ .../chains/graph_qa/falkordb.py | 31 +++++++++++++++++++ .../chains/graph_qa/gremlin.py | 31 +++++++++++++++++++ .../chains/graph_qa/hugegraph.py | 31 +++++++++++++++++++ .../chains/graph_qa/kuzu.py | 31 +++++++++++++++++++ .../chains/graph_qa/nebulagraph.py | 31 +++++++++++++++++++ .../chains/graph_qa/neptune_cypher.py | 31 +++++++++++++++++++ .../chains/graph_qa/neptune_sparql.py | 31 +++++++++++++++++++ .../chains/graph_qa/ontotext_graphdb.py | 31 +++++++++++++++++++ .../chains/graph_qa/sparql.py | 31 +++++++++++++++++++ .../tests/unit_tests/chains/test_graph_qa.py | 6 ++++ 12 files changed, 346 insertions(+) diff --git a/libs/community/langchain_community/chains/graph_qa/arangodb.py b/libs/community/langchain_community/chains/graph_qa/arangodb.py index 0af3e49b626..933cf91737c 100644 --- a/libs/community/langchain_community/chains/graph_qa/arangodb.py +++ b/libs/community/langchain_community/chains/graph_qa/arangodb.py @@ -57,6 +57,37 @@ class ArangoGraphQAChain(Chain): # Specify the maximum amount of AQL Generation attempts that should be made max_aql_generation_attempts: int = 3 + allow_dangerous_requests: bool = False + """Forced user opt-in to acknowledge that the chain can make dangerous requests. + + *Security note*: Make sure that the database connection uses credentials + that are narrowly-scoped to only include necessary permissions. + Failure to do so may result in data corruption or loss, since the calling + code may attempt commands that would result in deletion, mutation + of data if appropriately prompted or reading sensitive data if such + data is present in the database. + The best way to guard against such negative outcomes is to (as appropriate) + limit the permissions granted to the credentials used with this tool. + + See https://python.langchain.com/docs/security for more information. + """ + + def __init__(self, **kwargs: Any) -> None: + """Initialize the chain.""" + super().__init__(**kwargs) + if self.allow_dangerous_requests is not True: + raise ValueError( + "In order to use this chain, you must acknowledge that it can make " + "dangerous requests by setting `allow_dangerous_requests` to `True`." + "You must narrowly scope the permissions of the database connection " + "to only include necessary permissions. Failure to do so may result " + "in data corruption or loss or reading sensitive data if such data is " + "present in the database." + "Only use this chain if you understand the risks and have taken the " + "necessary precautions. " + "See https://python.langchain.com/docs/security for more information." + ) + @property def input_keys(self) -> List[str]: return [self.input_key] diff --git a/libs/community/langchain_community/chains/graph_qa/cypher.py b/libs/community/langchain_community/chains/graph_qa/cypher.py index a3175603786..91a5ba60662 100644 --- a/libs/community/langchain_community/chains/graph_qa/cypher.py +++ b/libs/community/langchain_community/chains/graph_qa/cypher.py @@ -180,6 +180,36 @@ class GraphCypherQAChain(Chain): """Optional cypher validation tool""" use_function_response: bool = False """Whether to wrap the database context as tool/function response""" + allow_dangerous_requests: bool = False + """Forced user opt-in to acknowledge that the chain can make dangerous requests. + + *Security note*: Make sure that the database connection uses credentials + that are narrowly-scoped to only include necessary permissions. + Failure to do so may result in data corruption or loss, since the calling + code may attempt commands that would result in deletion, mutation + of data if appropriately prompted or reading sensitive data if such + data is present in the database. + The best way to guard against such negative outcomes is to (as appropriate) + limit the permissions granted to the credentials used with this tool. + + See https://python.langchain.com/docs/security for more information. + """ + + def __init__(self, **kwargs: Any) -> None: + """Initialize the chain.""" + super().__init__(**kwargs) + if self.allow_dangerous_requests is not True: + raise ValueError( + "In order to use this chain, you must acknowledge that it can make " + "dangerous requests by setting `allow_dangerous_requests` to `True`." + "You must narrowly scope the permissions of the database connection " + "to only include necessary permissions. Failure to do so may result " + "in data corruption or loss or reading sensitive data if such data is " + "present in the database." + "Only use this chain if you understand the risks and have taken the " + "necessary precautions. " + "See https://python.langchain.com/docs/security for more information." + ) @property def input_keys(self) -> List[str]: diff --git a/libs/community/langchain_community/chains/graph_qa/falkordb.py b/libs/community/langchain_community/chains/graph_qa/falkordb.py index 360c4ead909..ebc5896192b 100644 --- a/libs/community/langchain_community/chains/graph_qa/falkordb.py +++ b/libs/community/langchain_community/chains/graph_qa/falkordb.py @@ -66,6 +66,37 @@ class FalkorDBQAChain(Chain): return_direct: bool = False """Whether or not to return the result of querying the graph directly.""" + allow_dangerous_requests: bool = False + """Forced user opt-in to acknowledge that the chain can make dangerous requests. + + *Security note*: Make sure that the database connection uses credentials + that are narrowly-scoped to only include necessary permissions. + Failure to do so may result in data corruption or loss, since the calling + code may attempt commands that would result in deletion, mutation + of data if appropriately prompted or reading sensitive data if such + data is present in the database. + The best way to guard against such negative outcomes is to (as appropriate) + limit the permissions granted to the credentials used with this tool. + + See https://python.langchain.com/docs/security for more information. + """ + + def __init__(self, **kwargs: Any) -> None: + """Initialize the chain.""" + super().__init__(**kwargs) + if self.allow_dangerous_requests is not True: + raise ValueError( + "In order to use this chain, you must acknowledge that it can make " + "dangerous requests by setting `allow_dangerous_requests` to `True`." + "You must narrowly scope the permissions of the database connection " + "to only include necessary permissions. Failure to do so may result " + "in data corruption or loss or reading sensitive data if such data is " + "present in the database." + "Only use this chain if you understand the risks and have taken the " + "necessary precautions. " + "See https://python.langchain.com/docs/security for more information." + ) + @property def input_keys(self) -> List[str]: """Return the input keys. diff --git a/libs/community/langchain_community/chains/graph_qa/gremlin.py b/libs/community/langchain_community/chains/graph_qa/gremlin.py index d323a194622..0223429e5b5 100644 --- a/libs/community/langchain_community/chains/graph_qa/gremlin.py +++ b/libs/community/langchain_community/chains/graph_qa/gremlin.py @@ -63,6 +63,37 @@ class GremlinQAChain(Chain): return_direct: bool = False return_intermediate_steps: bool = False + allow_dangerous_requests: bool = False + """Forced user opt-in to acknowledge that the chain can make dangerous requests. + + *Security note*: Make sure that the database connection uses credentials + that are narrowly-scoped to only include necessary permissions. + Failure to do so may result in data corruption or loss, since the calling + code may attempt commands that would result in deletion, mutation + of data if appropriately prompted or reading sensitive data if such + data is present in the database. + The best way to guard against such negative outcomes is to (as appropriate) + limit the permissions granted to the credentials used with this tool. + + See https://python.langchain.com/docs/security for more information. + """ + + def __init__(self, **kwargs: Any) -> None: + """Initialize the chain.""" + super().__init__(**kwargs) + if self.allow_dangerous_requests is not True: + raise ValueError( + "In order to use this chain, you must acknowledge that it can make " + "dangerous requests by setting `allow_dangerous_requests` to `True`." + "You must narrowly scope the permissions of the database connection " + "to only include necessary permissions. Failure to do so may result " + "in data corruption or loss or reading sensitive data if such data is " + "present in the database." + "Only use this chain if you understand the risks and have taken the " + "necessary precautions. " + "See https://python.langchain.com/docs/security for more information." + ) + @property def input_keys(self) -> List[str]: """Input keys. diff --git a/libs/community/langchain_community/chains/graph_qa/hugegraph.py b/libs/community/langchain_community/chains/graph_qa/hugegraph.py index f88d8d456b4..613d07b461c 100644 --- a/libs/community/langchain_community/chains/graph_qa/hugegraph.py +++ b/libs/community/langchain_community/chains/graph_qa/hugegraph.py @@ -39,6 +39,37 @@ class HugeGraphQAChain(Chain): input_key: str = "query" #: :meta private: output_key: str = "result" #: :meta private: + allow_dangerous_requests: bool = False + """Forced user opt-in to acknowledge that the chain can make dangerous requests. + + *Security note*: Make sure that the database connection uses credentials + that are narrowly-scoped to only include necessary permissions. + Failure to do so may result in data corruption or loss, since the calling + code may attempt commands that would result in deletion, mutation + of data if appropriately prompted or reading sensitive data if such + data is present in the database. + The best way to guard against such negative outcomes is to (as appropriate) + limit the permissions granted to the credentials used with this tool. + + See https://python.langchain.com/docs/security for more information. + """ + + def __init__(self, **kwargs: Any) -> None: + """Initialize the chain.""" + super().__init__(**kwargs) + if self.allow_dangerous_requests is not True: + raise ValueError( + "In order to use this chain, you must acknowledge that it can make " + "dangerous requests by setting `allow_dangerous_requests` to `True`." + "You must narrowly scope the permissions of the database connection " + "to only include necessary permissions. Failure to do so may result " + "in data corruption or loss or reading sensitive data if such data is " + "present in the database." + "Only use this chain if you understand the risks and have taken the " + "necessary precautions. " + "See https://python.langchain.com/docs/security for more information." + ) + @property def input_keys(self) -> List[str]: """Input keys. diff --git a/libs/community/langchain_community/chains/graph_qa/kuzu.py b/libs/community/langchain_community/chains/graph_qa/kuzu.py index b79d1e69a86..235db378c20 100644 --- a/libs/community/langchain_community/chains/graph_qa/kuzu.py +++ b/libs/community/langchain_community/chains/graph_qa/kuzu.py @@ -73,6 +73,37 @@ class KuzuQAChain(Chain): input_key: str = "query" #: :meta private: output_key: str = "result" #: :meta private: + allow_dangerous_requests: bool = False + """Forced user opt-in to acknowledge that the chain can make dangerous requests. + + *Security note*: Make sure that the database connection uses credentials + that are narrowly-scoped to only include necessary permissions. + Failure to do so may result in data corruption or loss, since the calling + code may attempt commands that would result in deletion, mutation + of data if appropriately prompted or reading sensitive data if such + data is present in the database. + The best way to guard against such negative outcomes is to (as appropriate) + limit the permissions granted to the credentials used with this tool. + + See https://python.langchain.com/docs/security for more information. + """ + + def __init__(self, **kwargs: Any) -> None: + """Initialize the chain.""" + super().__init__(**kwargs) + if self.allow_dangerous_requests is not True: + raise ValueError( + "In order to use this chain, you must acknowledge that it can make " + "dangerous requests by setting `allow_dangerous_requests` to `True`." + "You must narrowly scope the permissions of the database connection " + "to only include necessary permissions. Failure to do so may result " + "in data corruption or loss or reading sensitive data if such data is " + "present in the database." + "Only use this chain if you understand the risks and have taken the " + "necessary precautions. " + "See https://python.langchain.com/docs/security for more information." + ) + @property def input_keys(self) -> List[str]: """Return the input keys. diff --git a/libs/community/langchain_community/chains/graph_qa/nebulagraph.py b/libs/community/langchain_community/chains/graph_qa/nebulagraph.py index f0e305a7edd..8b37613e43b 100644 --- a/libs/community/langchain_community/chains/graph_qa/nebulagraph.py +++ b/libs/community/langchain_community/chains/graph_qa/nebulagraph.py @@ -39,6 +39,37 @@ class NebulaGraphQAChain(Chain): input_key: str = "query" #: :meta private: output_key: str = "result" #: :meta private: + allow_dangerous_requests: bool = False + """Forced user opt-in to acknowledge that the chain can make dangerous requests. + + *Security note*: Make sure that the database connection uses credentials + that are narrowly-scoped to only include necessary permissions. + Failure to do so may result in data corruption or loss, since the calling + code may attempt commands that would result in deletion, mutation + of data if appropriately prompted or reading sensitive data if such + data is present in the database. + The best way to guard against such negative outcomes is to (as appropriate) + limit the permissions granted to the credentials used with this tool. + + See https://python.langchain.com/docs/security for more information. + """ + + def __init__(self, **kwargs: Any) -> None: + """Initialize the chain.""" + super().__init__(**kwargs) + if self.allow_dangerous_requests is not True: + raise ValueError( + "In order to use this chain, you must acknowledge that it can make " + "dangerous requests by setting `allow_dangerous_requests` to `True`." + "You must narrowly scope the permissions of the database connection " + "to only include necessary permissions. Failure to do so may result " + "in data corruption or loss or reading sensitive data if such data is " + "present in the database." + "Only use this chain if you understand the risks and have taken the " + "necessary precautions. " + "See https://python.langchain.com/docs/security for more information." + ) + @property def input_keys(self) -> List[str]: """Return the input keys. diff --git a/libs/community/langchain_community/chains/graph_qa/neptune_cypher.py b/libs/community/langchain_community/chains/graph_qa/neptune_cypher.py index 01de2641f6b..c17053b3d09 100644 --- a/libs/community/langchain_community/chains/graph_qa/neptune_cypher.py +++ b/libs/community/langchain_community/chains/graph_qa/neptune_cypher.py @@ -120,6 +120,37 @@ class NeptuneOpenCypherQAChain(Chain): extra_instructions: Optional[str] = None """Extra instructions by the appended to the query generation prompt.""" + allow_dangerous_requests: bool = False + """Forced user opt-in to acknowledge that the chain can make dangerous requests. + + *Security note*: Make sure that the database connection uses credentials + that are narrowly-scoped to only include necessary permissions. + Failure to do so may result in data corruption or loss, since the calling + code may attempt commands that would result in deletion, mutation + of data if appropriately prompted or reading sensitive data if such + data is present in the database. + The best way to guard against such negative outcomes is to (as appropriate) + limit the permissions granted to the credentials used with this tool. + + See https://python.langchain.com/docs/security for more information. + """ + + def __init__(self, **kwargs: Any) -> None: + """Initialize the chain.""" + super().__init__(**kwargs) + if self.allow_dangerous_requests is not True: + raise ValueError( + "In order to use this chain, you must acknowledge that it can make " + "dangerous requests by setting `allow_dangerous_requests` to `True`." + "You must narrowly scope the permissions of the database connection " + "to only include necessary permissions. Failure to do so may result " + "in data corruption or loss or reading sensitive data if such data is " + "present in the database." + "Only use this chain if you understand the risks and have taken the " + "necessary precautions. " + "See https://python.langchain.com/docs/security for more information." + ) + @property def input_keys(self) -> List[str]: """Return the input keys. diff --git a/libs/community/langchain_community/chains/graph_qa/neptune_sparql.py b/libs/community/langchain_community/chains/graph_qa/neptune_sparql.py index 62aa49079fc..f2341be8eda 100644 --- a/libs/community/langchain_community/chains/graph_qa/neptune_sparql.py +++ b/libs/community/langchain_community/chains/graph_qa/neptune_sparql.py @@ -113,6 +113,37 @@ class NeptuneSparqlQAChain(Chain): extra_instructions: Optional[str] = None """Extra instructions by the appended to the query generation prompt.""" + allow_dangerous_requests: bool = False + """Forced user opt-in to acknowledge that the chain can make dangerous requests. + + *Security note*: Make sure that the database connection uses credentials + that are narrowly-scoped to only include necessary permissions. + Failure to do so may result in data corruption or loss, since the calling + code may attempt commands that would result in deletion, mutation + of data if appropriately prompted or reading sensitive data if such + data is present in the database. + The best way to guard against such negative outcomes is to (as appropriate) + limit the permissions granted to the credentials used with this tool. + + See https://python.langchain.com/docs/security for more information. + """ + + def __init__(self, **kwargs: Any) -> None: + """Initialize the chain.""" + super().__init__(**kwargs) + if self.allow_dangerous_requests is not True: + raise ValueError( + "In order to use this chain, you must acknowledge that it can make " + "dangerous requests by setting `allow_dangerous_requests` to `True`." + "You must narrowly scope the permissions of the database connection " + "to only include necessary permissions. Failure to do so may result " + "in data corruption or loss or reading sensitive data if such data is " + "present in the database." + "Only use this chain if you understand the risks and have taken the " + "necessary precautions. " + "See https://python.langchain.com/docs/security for more information." + ) + @property def input_keys(self) -> List[str]: return [self.input_key] diff --git a/libs/community/langchain_community/chains/graph_qa/ontotext_graphdb.py b/libs/community/langchain_community/chains/graph_qa/ontotext_graphdb.py index 71081a99c48..292b5571df6 100644 --- a/libs/community/langchain_community/chains/graph_qa/ontotext_graphdb.py +++ b/libs/community/langchain_community/chains/graph_qa/ontotext_graphdb.py @@ -46,6 +46,37 @@ class OntotextGraphDBQAChain(Chain): input_key: str = "query" #: :meta private: output_key: str = "result" #: :meta private: + allow_dangerous_requests: bool = False + """Forced user opt-in to acknowledge that the chain can make dangerous requests. + + *Security note*: Make sure that the database connection uses credentials + that are narrowly-scoped to only include necessary permissions. + Failure to do so may result in data corruption or loss, since the calling + code may attempt commands that would result in deletion, mutation + of data if appropriately prompted or reading sensitive data if such + data is present in the database. + The best way to guard against such negative outcomes is to (as appropriate) + limit the permissions granted to the credentials used with this tool. + + See https://python.langchain.com/docs/security for more information. + """ + + def __init__(self, **kwargs: Any) -> None: + """Initialize the chain.""" + super().__init__(**kwargs) + if self.allow_dangerous_requests is not True: + raise ValueError( + "In order to use this chain, you must acknowledge that it can make " + "dangerous requests by setting `allow_dangerous_requests` to `True`." + "You must narrowly scope the permissions of the database connection " + "to only include necessary permissions. Failure to do so may result " + "in data corruption or loss or reading sensitive data if such data is " + "present in the database." + "Only use this chain if you understand the risks and have taken the " + "necessary precautions. " + "See https://python.langchain.com/docs/security for more information." + ) + @property def input_keys(self) -> List[str]: return [self.input_key] diff --git a/libs/community/langchain_community/chains/graph_qa/sparql.py b/libs/community/langchain_community/chains/graph_qa/sparql.py index b61e262ffba..2eb27112654 100644 --- a/libs/community/langchain_community/chains/graph_qa/sparql.py +++ b/libs/community/langchain_community/chains/graph_qa/sparql.py @@ -47,6 +47,37 @@ class GraphSparqlQAChain(Chain): output_key: str = "result" #: :meta private: sparql_query_key: str = "sparql_query" #: :meta private: + allow_dangerous_requests: bool = False + """Forced user opt-in to acknowledge that the chain can make dangerous requests. + + *Security note*: Make sure that the database connection uses credentials + that are narrowly-scoped to only include necessary permissions. + Failure to do so may result in data corruption or loss, since the calling + code may attempt commands that would result in deletion, mutation + of data if appropriately prompted or reading sensitive data if such + data is present in the database. + The best way to guard against such negative outcomes is to (as appropriate) + limit the permissions granted to the credentials used with this tool. + + See https://python.langchain.com/docs/security for more information. + """ + + def __init__(self, **kwargs: Any) -> None: + """Initialize the chain.""" + super().__init__(**kwargs) + if self.allow_dangerous_requests is not True: + raise ValueError( + "In order to use this chain, you must acknowledge that it can make " + "dangerous requests by setting `allow_dangerous_requests` to `True`." + "You must narrowly scope the permissions of the database connection " + "to only include necessary permissions. Failure to do so may result " + "in data corruption or loss or reading sensitive data if such data is " + "present in the database." + "Only use this chain if you understand the risks and have taken the " + "necessary precautions. " + "See https://python.langchain.com/docs/security for more information." + ) + @property def input_keys(self) -> List[str]: """Return the input keys. diff --git a/libs/community/tests/unit_tests/chains/test_graph_qa.py b/libs/community/tests/unit_tests/chains/test_graph_qa.py index d654a96eb77..4a48a5b50f7 100644 --- a/libs/community/tests/unit_tests/chains/test_graph_qa.py +++ b/libs/community/tests/unit_tests/chains/test_graph_qa.py @@ -59,6 +59,7 @@ def test_graph_cypher_qa_chain_prompt_selection_1() -> None: return_intermediate_steps=False, qa_prompt=qa_prompt, cypher_prompt=cypher_prompt, + allow_dangerous_requests=True, ) assert chain.qa_chain.prompt == qa_prompt # type: ignore[union-attr] assert chain.cypher_generation_chain.prompt == cypher_prompt @@ -71,6 +72,7 @@ def test_graph_cypher_qa_chain_prompt_selection_2() -> None: graph=FakeGraphStore(), verbose=True, return_intermediate_steps=False, + allow_dangerous_requests=True, ) assert chain.qa_chain.prompt == CYPHER_QA_PROMPT # type: ignore[union-attr] assert chain.cypher_generation_chain.prompt == CYPHER_GENERATION_PROMPT @@ -87,6 +89,7 @@ def test_graph_cypher_qa_chain_prompt_selection_3() -> None: return_intermediate_steps=False, cypher_llm_kwargs={"memory": readonlymemory}, qa_llm_kwargs={"memory": readonlymemory}, + allow_dangerous_requests=True, ) assert chain.qa_chain.prompt == CYPHER_QA_PROMPT # type: ignore[union-attr] assert chain.cypher_generation_chain.prompt == CYPHER_GENERATION_PROMPT @@ -107,6 +110,7 @@ def test_graph_cypher_qa_chain_prompt_selection_4() -> None: return_intermediate_steps=False, cypher_llm_kwargs={"prompt": cypher_prompt, "memory": readonlymemory}, qa_llm_kwargs={"prompt": qa_prompt, "memory": readonlymemory}, + allow_dangerous_requests=True, ) assert chain.qa_chain.prompt == qa_prompt # type: ignore[union-attr] assert chain.cypher_generation_chain.prompt == cypher_prompt @@ -130,6 +134,7 @@ def test_graph_cypher_qa_chain_prompt_selection_5() -> None: cypher_prompt=cypher_prompt, cypher_llm_kwargs={"memory": readonlymemory}, qa_llm_kwargs={"memory": readonlymemory}, + allow_dangerous_requests=True, ) assert False except ValueError: @@ -181,6 +186,7 @@ def test_graph_cypher_qa_chain() -> None: return_intermediate_steps=False, cypher_llm_kwargs={"prompt": prompt, "memory": readonlymemory}, memory=memory, + allow_dangerous_requests=True, ) chain.run("Test question") chain.run("Test new question")