Repository: redis/redis-py
Pull request #2548 (2023-01-09 19:45:14)

String cleanse
### Pull Request check-list

_Please make sure to review and check all of these items:_

- [x] Does `$ tox` pass with this change (including linting)?
- [x] Do the CI tests pass with this change (enable it first in your forked repo and wait for the github action build to finish)?
- [x] Is the new or changed code fully tested?
- [x] Is a documentation update included (if this change modifies existing APIs, or introduces new ones)?
- [x] Is there an example added to the examples folder (if applicable)?
- [x] Was the change added to CHANGES file?


### Description of change

Added string escape to properly escape \ in helper method `quote_string`
Added tests

cc @chayim


codecov-commenter: # [Codecov](https://codecov.io/gh/redis/redis-py/pull/2548?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis) Report
Base: **92.23**% // Head: **92.20**% // Decreases project coverage by **`-0.03%`** :warning:
> Coverage data is based on head [(`aa69334`)](https://codecov.io/gh/redis/redis-py/pull/2548?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis) compared to base [(`f46d7f3`)](https://codecov.io/gh/redis/redis-py/commit/f46d7f3ef7ce64f90acd4a3dcc14375ab9ae9c9d?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis).
> Patch coverage: 100.00% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #2548      +/-   ##
==========================================
- Coverage   92.23%   92.20%   -0.04%     
==========================================
  Files         115      115              
  Lines       29554    29561       +7     
==========================================
- Hits        27260    27256       -4     
- Misses       2294     2305      +11     
```


| [Impacted Files](https://codecov.io/gh/redis/redis-py/pull/2548?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis) | Coverage Δ | |
|---|---|---|
| [redis/commands/helpers.py](https://codecov.io/gh/redis/redis-py/pull/2548?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis#diff-cmVkaXMvY29tbWFuZHMvaGVscGVycy5weQ==) | `87.50% <100.00%> (+0.13%)` | :arrow_up: |
| [tests/test\_graph.py](https://codecov.io/gh/redis/redis-py/pull/2548?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis#diff-dGVzdHMvdGVzdF9ncmFwaC5weQ==) | `91.56% <100.00%> (ø)` | |
| [tests/test\_helpers.py](https://codecov.io/gh/redis/redis-py/pull/2548?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis#diff-dGVzdHMvdGVzdF9oZWxwZXJzLnB5) | `100.00% <100.00%> (ø)` | |
| [tests/test\_cluster.py](https://codecov.io/gh/redis/redis-py/pull/2548?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis#diff-dGVzdHMvdGVzdF9jbHVzdGVyLnB5) | `96.68% <0.00%> (-0.34%)` | :arrow_down: |
| [tests/test\_asyncio/test\_cluster.py](https://codecov.io/gh/redis/redis-py/pull/2548?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis#diff-dGVzdHMvdGVzdF9hc3luY2lvL3Rlc3RfY2x1c3Rlci5weQ==) | `97.16% <0.00%> (-0.32%)` | :arrow_down: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/redis/redis-py/pull/2548?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis).


Patch:
diff --git a/CHANGES b/CHANGES
index fca8d316..02daf5ee 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,4 @@
+    * Fix string cleanse in Redis Graph
     * Make PythonParser resumable in case of error (#2510)
     * Add `timeout=None` in `SentinelConnectionManager.read_response`
     * Documentation fix: password protected socket connection (#2374)
diff --git a/redis/commands/helpers.py b/redis/commands/helpers.py
index 6989ab59..b65cd1a9 100644
--- a/redis/commands/helpers.py
+++ b/redis/commands/helpers.py
@@ -115,6 +115,7 @@ def quote_string(v):
     if len(v) == 0:
         return '""'
 
+    v = v.replace("\\", "\\\\")
     v = v.replace('"', '\\"')
 
     return f'"{v}"'


Test patch:
diff --git a/tests/test_graph.py b/tests/test_graph.py
index d71df486..4721b2f4 100644
--- a/tests/test_graph.py
+++ b/tests/test_graph.py
@@ -124,7 +124,7 @@ def test_path(client):
 
 @pytest.mark.redismod
 def test_param(client):
-    params = [1, 2.3, "str", True, False, None, [0, 1, 2]]
+    params = [1, 2.3, "str", True, False, None, [0, 1, 2], r"\" RETURN 1337 //"]
     query = "RETURN $param"
     for param in params:
         result = client.graph().query(query, {"param": param})
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index 35958290..57a94d2f 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -80,3 +80,9 @@ def test_quote_string():
     assert quote_string("hello world!") == '"hello world!"'
     assert quote_string("") == '""'
     assert quote_string("hello world!") == '"hello world!"'
+    assert quote_string("abc") == '"abc"'
+    assert quote_string("") == '""'
+    assert quote_string('"') == r'"\""'
+    assert quote_string(r"foo \ bar") == r'"foo \\ bar"'
+    assert quote_string(r"foo \" bar") == r'"foo \\\" bar"'
+    assert quote_string('a"a') == r'"a\"a"'