mongo-secure 0.1.0__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.
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.4
2
+ Name: mongo_secure
3
+ Version: 0.1.0
4
+ Summary: Sanitize MongoDB operator strings in selected function arguments.
5
+ Author-email: Thomas Perkins <contact@perkinsfund.org>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/YOUR_USERNAME/mongo_secure
8
+ Project-URL: Repository, https://github.com/YOUR_USERNAME/mongo_secure
9
+ Project-URL: Issues, https://github.com/YOUR_USERNAME/mongo_secure/issues
10
+ Keywords: mongodb,sanitize,decorator,security
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+
24
+ # mongo_secure
25
+
26
+ Sanitize selected function arguments by replacing MongoDB operators.
27
+
28
+ ## Installation
29
+
30
+ ```
31
+ pip install mongo_secure
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ ```python
37
+ from mongo_secure import sanitize
38
+
39
+
40
+ @sanitize("name", "place")
41
+ def hello_world(name, place):
42
+ print(name)
43
+ print(place)
44
+
45
+ hello_world("$toInt", "$gt")
46
+ #<= _toInt
47
+ #<= _gt
48
+ ```
49
+
50
+ Nested objects are sanitized recursively
51
+
52
+ ```python
53
+ from mongo_secure import replace_blocked_mongo_operators
54
+
55
+
56
+ payload = {
57
+ "$set": {
58
+ "age": "$toInt",
59
+ "tags": ["safe", "$where"]
60
+ }
61
+ }
62
+
63
+
64
+ print(replace_blocked_mongo_operators(payload))
65
+ #<= {"_set": {"age": "_toInt", "tags": ["safe", "_where"]}}
66
+ ```
67
+
68
+ ## License
69
+ MIT
@@ -0,0 +1,46 @@
1
+ # mongo_secure
2
+
3
+ Sanitize selected function arguments by replacing MongoDB operators.
4
+
5
+ ## Installation
6
+
7
+ ```
8
+ pip install mongo_secure
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```python
14
+ from mongo_secure import sanitize
15
+
16
+
17
+ @sanitize("name", "place")
18
+ def hello_world(name, place):
19
+ print(name)
20
+ print(place)
21
+
22
+ hello_world("$toInt", "$gt")
23
+ #<= _toInt
24
+ #<= _gt
25
+ ```
26
+
27
+ Nested objects are sanitized recursively
28
+
29
+ ```python
30
+ from mongo_secure import replace_blocked_mongo_operators
31
+
32
+
33
+ payload = {
34
+ "$set": {
35
+ "age": "$toInt",
36
+ "tags": ["safe", "$where"]
37
+ }
38
+ }
39
+
40
+
41
+ print(replace_blocked_mongo_operators(payload))
42
+ #<= {"_set": {"age": "_toInt", "tags": ["safe", "_where"]}}
43
+ ```
44
+
45
+ ## License
46
+ MIT
@@ -0,0 +1,38 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "mongo_secure"
7
+ version = "0.1.0"
8
+ description = "Sanitize MongoDB operator strings in selected function arguments."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "Thomas Perkins", email = "contact@perkinsfund.org" }
14
+ ]
15
+ keywords = ["mongodb", "sanitize", "decorator", "security"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.9",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Typing :: Typed"
27
+ ]
28
+
29
+ [project.urls]
30
+ Homepage = "https://github.com/YOUR_USERNAME/mongo_secure"
31
+ Repository = "https://github.com/YOUR_USERNAME/mongo_secure"
32
+ Issues = "https://github.com/YOUR_USERNAME/mongo_secure/issues"
33
+
34
+ [tool.setuptools.packages.find]
35
+ where = ["src"]
36
+
37
+ [tool.pytest.ini_options]
38
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,10 @@
1
+ from .sanitizer import (
2
+ MONGO_OPERATOR_REPLACEMENTS,
3
+ replace_blocked_mongo_operators,
4
+ sanitize
5
+ )
6
+
7
+
8
+ __all__ = [
9
+
10
+ ]
@@ -0,0 +1,262 @@
1
+ import inspect
2
+
3
+ from functools import wraps
4
+
5
+
6
+ MONGO_OPERATOR_REPLACEMENTS = {
7
+ "$all",
8
+ "$elemMatch",
9
+ "$size",
10
+ "$bitsAllClear",
11
+ "$bitsAllSet",
12
+ "$bitsAnyClear",
13
+ "$bitsAnySet",
14
+ "$eq",
15
+ "$ne",
16
+ "$gt",
17
+ "$gte",
18
+ "$lt",
19
+ "$lte",
20
+ "$in",
21
+ "$nin",
22
+ "$exists",
23
+ "$type",
24
+ "$expr",
25
+ "$jsonSchema",
26
+ "$mod",
27
+ "$regex",
28
+ "$where",
29
+ "$and",
30
+ "$not",
31
+ "$nor",
32
+ "$or",
33
+ "$geoIntersects",
34
+ "$geoWithin",
35
+ "$near",
36
+ "$nearSphere",
37
+ "$meta",
38
+ "$slice",
39
+ "$currentDate",
40
+ "$inc",
41
+ "$min",
42
+ "$max",
43
+ "$mul",
44
+ "$rename",
45
+ "$set",
46
+ "$setOnInsert",
47
+ "$unset",
48
+ "$[]",
49
+ "$addToSet",
50
+ "$pop",
51
+ "$pull",
52
+ "$push",
53
+ "$pullAll",
54
+ "$each",
55
+ "$position",
56
+ "$sort",
57
+ "$bit",
58
+ "$text",
59
+ "$search",
60
+ "$comment",
61
+ "$options",
62
+ "$rand",
63
+ "$natural",
64
+ "$",
65
+ "$setField",
66
+ "$unsetField",
67
+ "$replaceRoot",
68
+ "$replaceWith",
69
+ "$match",
70
+ "$project",
71
+ "$addFields",
72
+ "$group",
73
+ "$limit",
74
+ "$skip",
75
+ "$lookup",
76
+ "$graphLookup",
77
+ "$unwind",
78
+ "$facet",
79
+ "$bucket",
80
+ "$bucketAuto",
81
+ "$count",
82
+ "$sortByCount",
83
+ "$sample",
84
+ "$unionWith",
85
+ "$documents",
86
+ "$densify",
87
+ "$fill",
88
+ "$geoNear",
89
+ "$indexStats",
90
+ "$listLocalSessions",
91
+ "$listSessions",
92
+ "$planCacheStats",
93
+ "$redact",
94
+ "$searchMeta",
95
+ "$setWindowFields",
96
+ "$collStats",
97
+ "$out",
98
+ "$merge",
99
+ "$abs",
100
+ "$add",
101
+ "$ceil",
102
+ "$divide",
103
+ "$exp",
104
+ "$floor",
105
+ "$ln",
106
+ "$log",
107
+ "$log10",
108
+ "$multiply",
109
+ "$pow",
110
+ "$round",
111
+ "$sqrt",
112
+ "$subtract",
113
+ "$trunc",
114
+ "$arrayElemAt",
115
+ "$arrayToObject",
116
+ "$concatArrays",
117
+ "$filter",
118
+ "$first",
119
+ "$firstN",
120
+ "$indexOfArray",
121
+ "$isArray",
122
+ "$last",
123
+ "$lastN",
124
+ "$map",
125
+ "$maxN",
126
+ "$minN",
127
+ "$objectToArray",
128
+ "$range",
129
+ "$reduce",
130
+ "$reverseArray",
131
+ "$sortArray",
132
+ "$zip",
133
+ "$cond",
134
+ "$ifNull",
135
+ "$switch",
136
+ "$cmp",
137
+ "$allElementsTrue",
138
+ "$anyElementTrue",
139
+ "$setDifference",
140
+ "$setEquals",
141
+ "$setIntersection",
142
+ "$setIsSubset",
143
+ "$setUnion",
144
+ "$concat",
145
+ "$dateFromString",
146
+ "$indexOfBytes",
147
+ "$indexOfCP",
148
+ "$ltrim",
149
+ "$regexFind",
150
+ "$regexFindAll",
151
+ "$regexMatch",
152
+ "$replaceOne",
153
+ "$replaceAll",
154
+ "$rtrim",
155
+ "$split",
156
+ "$strLenBytes",
157
+ "$strLenCP",
158
+ "$strcasecmp",
159
+ "$substr",
160
+ "$substrBytes",
161
+ "$substrCP",
162
+ "$toLower",
163
+ "$toString",
164
+ "$trim",
165
+ "$toUpper",
166
+ "$dateAdd",
167
+ "$dateDiff",
168
+ "$dateFromParts",
169
+ "$dateSubtract",
170
+ "$dateToParts",
171
+ "$dateToString",
172
+ "$dateTrunc",
173
+ "$dayOfMonth",
174
+ "$dayOfWeek",
175
+ "$dayOfYear",
176
+ "$hour",
177
+ "$isoDayOfWeek",
178
+ "$isoWeek",
179
+ "$isoWeekYear",
180
+ "$millisecond",
181
+ "$minute",
182
+ "$month",
183
+ "$second",
184
+ "$week",
185
+ "$year",
186
+ "$convert",
187
+ "$toBool",
188
+ "$toDate",
189
+ "$toDecimal",
190
+ "$toDouble",
191
+ "$toInt",
192
+ "$toLong",
193
+ "$toObjectId",
194
+ "$let",
195
+ "$literal",
196
+ "$function",
197
+ "$accumulator",
198
+ "$getField",
199
+ "$mergeObjects",
200
+ "$bsonSize",
201
+ }
202
+
203
+
204
+ def replace_blocked_mongo_operators(value):
205
+ """
206
+ basically just turns any mongo operator that's above into _OPERATION instead of $OPERATION
207
+ """
208
+ try:
209
+ if isinstance(value, dict):
210
+ cleaned = {}
211
+ for key, nested_value in value.items():
212
+ cleaned_key = replace_blocked_mongo_operators(key)
213
+ cleaned_value = replace_blocked_mongo_operators(nested_value)
214
+ cleaned[cleaned_key] = cleaned_value
215
+ return cleaned
216
+ if isinstance(value, list):
217
+ return [replace_blocked_mongo_operators(item) for item in value]
218
+ if isinstance(value, tuple):
219
+ return tuple(replace_blocked_mongo_operators(item) for item in value)
220
+ if isinstance(value, set):
221
+ return {replace_blocked_mongo_operators(item) for item in value}
222
+ if isinstance(value, str):
223
+ if value in MONGO_OPERATOR_REPLACEMENTS:
224
+ return value.replace("$", "_", 1)
225
+ return value
226
+ return value
227
+ except Exception:
228
+ return value
229
+
230
+
231
+ def sanitize(*arg_names):
232
+ """
233
+ decorator that can be used on functions to take an argument name and replace the operators
234
+
235
+ for example:
236
+
237
+ @sanitize_mongo_args("name")
238
+ def hello_world(name):
239
+ print(f"hello {name}")
240
+
241
+ hello_world("$toInt") -> hello _toInt
242
+ hello_world("$dateFromParts") -> hello _dateFromParts
243
+ """
244
+ arg_names = set(arg_names)
245
+ def decorator(func):
246
+ sig = inspect.signature(func)
247
+ @wraps(func)
248
+ def wrapper(*args, **kwargs):
249
+ try:
250
+ bound = sig.bind_partial(*args, **kwargs)
251
+ bound.apply_defaults()
252
+
253
+ for name in arg_names:
254
+ if name in bound.arguments:
255
+ bound.arguments[name] = replace_blocked_mongo_operators(
256
+ bound.arguments[name]
257
+ )
258
+ return func(*bound.args, **bound.kwargs)
259
+ except Exception:
260
+ return None
261
+ return wrapper
262
+ return decorator
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.4
2
+ Name: mongo_secure
3
+ Version: 0.1.0
4
+ Summary: Sanitize MongoDB operator strings in selected function arguments.
5
+ Author-email: Thomas Perkins <contact@perkinsfund.org>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/YOUR_USERNAME/mongo_secure
8
+ Project-URL: Repository, https://github.com/YOUR_USERNAME/mongo_secure
9
+ Project-URL: Issues, https://github.com/YOUR_USERNAME/mongo_secure/issues
10
+ Keywords: mongodb,sanitize,decorator,security
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+
24
+ # mongo_secure
25
+
26
+ Sanitize selected function arguments by replacing MongoDB operators.
27
+
28
+ ## Installation
29
+
30
+ ```
31
+ pip install mongo_secure
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ ```python
37
+ from mongo_secure import sanitize
38
+
39
+
40
+ @sanitize("name", "place")
41
+ def hello_world(name, place):
42
+ print(name)
43
+ print(place)
44
+
45
+ hello_world("$toInt", "$gt")
46
+ #<= _toInt
47
+ #<= _gt
48
+ ```
49
+
50
+ Nested objects are sanitized recursively
51
+
52
+ ```python
53
+ from mongo_secure import replace_blocked_mongo_operators
54
+
55
+
56
+ payload = {
57
+ "$set": {
58
+ "age": "$toInt",
59
+ "tags": ["safe", "$where"]
60
+ }
61
+ }
62
+
63
+
64
+ print(replace_blocked_mongo_operators(payload))
65
+ #<= {"_set": {"age": "_toInt", "tags": ["safe", "_where"]}}
66
+ ```
67
+
68
+ ## License
69
+ MIT
@@ -0,0 +1,9 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/mongo_secure/__init__.py
4
+ src/mongo_secure/sanitizer.py
5
+ src/mongo_secure.egg-info/PKG-INFO
6
+ src/mongo_secure.egg-info/SOURCES.txt
7
+ src/mongo_secure.egg-info/dependency_links.txt
8
+ src/mongo_secure.egg-info/top_level.txt
9
+ tests/test_sanitize.py
@@ -0,0 +1 @@
1
+ mongo_secure
@@ -0,0 +1,47 @@
1
+ from mongo_secure import (
2
+ sanitize,
3
+ replace_blocked_mongo_operators
4
+ )
5
+
6
+
7
+ def test_replaces_basic_string():
8
+ assert replace_blocked_mongo_operators("$toInt") == "_toInt"
9
+
10
+
11
+ def test_does_not_replace_safe_strings():
12
+ assert replace_blocked_mongo_operators("$notARealOperator") == "$notARealOperator"
13
+
14
+
15
+ def test_replaces_dict_keys_and_values():
16
+ assert replace_blocked_mongo_operators({"$set": "$toInt"}) == {"_set": "_toInt"}
17
+
18
+
19
+ def test_replace_nested_structures():
20
+ value = {
21
+ "$set": {
22
+ "items": ["$where", ("$eq", {"$gt": "$lt"})],
23
+ "operators": {"$or", "$and"},
24
+ }
25
+ }
26
+ assert replace_blocked_mongo_operators(value) == {
27
+ "_set": {
28
+ "items": ["_where", ("_eq", {"_gt": "_lt"})],
29
+ "operators": {"_or", "_and"},
30
+ }
31
+ }
32
+
33
+
34
+ def test_decorator_replacements():
35
+ @sanitize("payload")
36
+ def process(payload):
37
+ return payload
38
+
39
+ assert process({"$set": "$toInt"}) == {"_set": "_toInt"}
40
+
41
+
42
+ def test_decorator_leaves_safe_args():
43
+ @sanitize("payload")
44
+ def process(payload):
45
+ return payload
46
+
47
+ assert process("test") == "test"