sql-data-guard 0.1.7__tar.gz → 0.1.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {sql_data_guard-0.1.7/src/sql_data_guard.egg-info → sql_data_guard-0.1.9}/PKG-INFO +1 -1
  2. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/pyproject.toml +1 -1
  3. sql_data_guard-0.1.9/src/sql_data_guard/mcpwrapper/mcp_wrapper.py +129 -0
  4. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/src/sql_data_guard/restriction_verification.py +2 -2
  5. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9/src/sql_data_guard.egg-info}/PKG-INFO +1 -1
  6. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/src/sql_data_guard.egg-info/SOURCES.txt +1 -0
  7. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/test/test_sql_guard_validation_unit.py +1 -0
  8. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/LICENSE.md +0 -0
  9. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/README.md +0 -0
  10. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/setup.cfg +0 -0
  11. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/src/sql_data_guard/__init__.py +0 -0
  12. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/src/sql_data_guard/rest/__init__.py +0 -0
  13. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/src/sql_data_guard/rest/sql_data_guard_rest.py +0 -0
  14. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/src/sql_data_guard/restriction_validation.py +0 -0
  15. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/src/sql_data_guard/sql_data_guard.py +0 -0
  16. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/src/sql_data_guard/verification_context.py +0 -0
  17. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/src/sql_data_guard/verification_utils.py +0 -0
  18. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/src/sql_data_guard.egg-info/dependency_links.txt +0 -0
  19. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/src/sql_data_guard.egg-info/requires.txt +0 -0
  20. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/src/sql_data_guard.egg-info/top_level.txt +0 -0
  21. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/test/test_duckdb_unit.py +0 -0
  22. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/test/test_rest_api_unit.py +0 -0
  23. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/test/test_sql_guard_curr_unit.py +0 -0
  24. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/test/test_sql_guard_joins_unit.py +0 -0
  25. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/test/test_sql_guard_llm.py +0 -0
  26. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/test/test_sql_guard_unit.py +0 -0
  27. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/test/test_sql_guard_updates_unit.py +0 -0
  28. {sql_data_guard-0.1.7 → sql_data_guard-0.1.9}/test/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sql-data-guard
3
- Version: 0.1.7
3
+ Version: 0.1.9
4
4
  Summary: Safety Layer for LLM Database Interactions
5
5
  Author-email: Imperva - Threat Reseach Infra <ww.dis.imperva.threat-research-infra@thalesgroup.com>
6
6
  License: MIT License
@@ -9,7 +9,7 @@ package-dir = {"" = "src"}
9
9
  extra-index-url = ["https://pypi.org/simple"]
10
10
  [project]
11
11
  name = "sql-data-guard"
12
- version = "0.1.7"
12
+ version = "0.1.9"
13
13
  dependencies = [
14
14
  "sqlglot"
15
15
  ]
@@ -0,0 +1,129 @@
1
+ import json
2
+ import os
3
+ import sys
4
+ import threading
5
+ from typing import Optional, Dict, List
6
+
7
+ import docker
8
+
9
+ from sql_data_guard import verify_sql
10
+
11
+
12
+ def load_config() -> dict:
13
+ return json.load(open("/conf/config.json"))
14
+
15
+
16
+ def _get_volumes() -> List[str]:
17
+ volumes = config["mcp-server"].get("volumes", [])
18
+ if "PWD" in os.environ:
19
+ volumes = [v.replace("$PWD", os.environ["PWD"]) for v in volumes]
20
+ return volumes
21
+
22
+
23
+ def start_inner_container():
24
+ client = docker.from_env()
25
+ container = client.containers.run(
26
+ config["mcp-server"]["image"],
27
+ (
28
+ " ".join(config["mcp-server"]["args"])
29
+ if "args" in config["mcp-server"]
30
+ else None
31
+ ),
32
+ volumes=_get_volumes(),
33
+ network_mode=config["mcp-server"].get("network-mode"),
34
+ stdin_open=True,
35
+ auto_remove=True,
36
+ detach=True,
37
+ stdout=True,
38
+ )
39
+
40
+ def stream_output_inject_response():
41
+ for line in container.logs(stream=True):
42
+ line_json = json.loads(line)
43
+ request_id = line_json["id"]
44
+ if request_id in errors:
45
+ if "result" in line_json and "content" in line_json["result"]:
46
+ line_json["result"]["content"].insert(
47
+ 0,
48
+ {
49
+ "type": "text",
50
+ "text": f"[{errors[request_id]}]",
51
+ "isError": True,
52
+ },
53
+ )
54
+ del errors[request_id]
55
+ sys.stdout.write(json.dumps(line_json) + "\n")
56
+ sys.stdout.flush()
57
+
58
+ def stream_output():
59
+ for line in container.logs(stream=True):
60
+ sys.stdout.write(line.decode("utf-8"))
61
+ sys.stdout.flush()
62
+
63
+ threading.Thread(
64
+ target=stream_output_inject_response if inject_response else stream_output,
65
+ daemon=True,
66
+ ).start()
67
+ return container
68
+
69
+
70
+ def main():
71
+ container = start_inner_container()
72
+ try:
73
+ socket = container.attach_socket(params={"stdin": True, "stream": True})
74
+ # noinspection PyProtectedMember
75
+ socket._sock.setblocking(True)
76
+ for line in sys.stdin:
77
+ line = input_line(line)
78
+ # noinspection PyProtectedMember
79
+ socket._sock.sendall(line.encode("utf-8"))
80
+ except (KeyboardInterrupt, EOFError):
81
+ pass
82
+ finally:
83
+ container.stop()
84
+
85
+
86
+ def get_sql(json_line: dict) -> Optional[str]:
87
+ if json_line["method"] == "tools/call":
88
+ for tool in config["mcp-tools"]:
89
+ if tool["tool-name"] == json_line["params"]["name"]:
90
+ return json_line["params"]["arguments"][tool["arg-name"]]
91
+ return None
92
+
93
+
94
+ def input_line(line: str) -> str:
95
+ json_line = json.loads(line.encode("utf-8"))
96
+ sql = get_sql(json_line)
97
+ if sql:
98
+ result = verify_sql(
99
+ sql,
100
+ config["sql-data-guard"],
101
+ config["sql-data-guard"]["dialect"],
102
+ )
103
+ if not result["allowed"]:
104
+ sys.stderr.write(
105
+ f"ID: {json_line['id']} Blocked SQL: {sql}\nErrors: {list(result['errors'])}\n"
106
+ )
107
+ if result["fixed"]:
108
+ sys.stderr.write(f"Fixed SQL: {result['fixed']}\n")
109
+ updated_sql = result["fixed"]
110
+ else:
111
+ updated_sql = "SELECT 'Blocked by SQL Data Guard' AS message"
112
+ if not inject_response:
113
+ for error in result["errors"]:
114
+ updated_sql += f"\nUNION ALL SELECT '{error}' AS message"
115
+ if inject_response:
116
+ result["errors"] = list(result["errors"])
117
+ if result["fixed"]:
118
+ result["fixed"] = result["fixed"].replace("'", "''")
119
+ errors[json_line["id"]] = result
120
+ json_line["params"]["arguments"]["query"] = updated_sql
121
+ line = json.dumps(json_line) + "\n"
122
+ return line
123
+
124
+
125
+ if __name__ == "__main__":
126
+ config = load_config()
127
+ inject_response = config["sql-data-guard"]["inject-response"]
128
+ errors: Dict[int, dict] = {}
129
+ main()
@@ -3,8 +3,8 @@ from typing import List
3
3
  import sqlglot
4
4
  import sqlglot.expressions as expr
5
5
 
6
- from sql_data_guard.verification_context import VerificationContext
7
- from sql_data_guard.verification_utils import split_to_expressions
6
+ from .verification_context import VerificationContext
7
+ from .verification_utils import split_to_expressions
8
8
 
9
9
 
10
10
  def verify_restrictions(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sql-data-guard
3
- Version: 0.1.7
3
+ Version: 0.1.9
4
4
  Summary: Safety Layer for LLM Database Interactions
5
5
  Author-email: Imperva - Threat Reseach Infra <ww.dis.imperva.threat-research-infra@thalesgroup.com>
6
6
  License: MIT License
@@ -12,6 +12,7 @@ src/sql_data_guard.egg-info/SOURCES.txt
12
12
  src/sql_data_guard.egg-info/dependency_links.txt
13
13
  src/sql_data_guard.egg-info/requires.txt
14
14
  src/sql_data_guard.egg-info/top_level.txt
15
+ src/sql_data_guard/mcpwrapper/mcp_wrapper.py
15
16
  src/sql_data_guard/rest/__init__.py
16
17
  src/sql_data_guard/rest/sql_data_guard_rest.py
17
18
  test/test_duckdb_unit.py
@@ -1,4 +1,5 @@
1
1
  import pytest
2
+
2
3
  from sql_data_guard.restriction_validation import (
3
4
  validate_restrictions,
4
5
  UnsupportedRestrictionError,
File without changes
File without changes