pywebguard 1.0.0__tar.gz → 1.0.2__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 (68) hide show
  1. {pywebguard-1.0.0 → pywebguard-1.0.2}/PKG-INFO +34 -50
  2. {pywebguard-1.0.0 → pywebguard-1.0.2}/README.md +32 -2
  3. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard.egg-info/PKG-INFO +34 -50
  4. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard.egg-info/SOURCES.txt +1 -21
  5. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard.egg-info/requires.txt +3 -0
  6. {pywebguard-1.0.0 → pywebguard-1.0.2}/setup.py +1 -1
  7. pywebguard-1.0.0/tests/__init__.py +0 -1
  8. pywebguard-1.0.0/tests/conftest.py +0 -211
  9. pywebguard-1.0.0/tests/test_cli.py +0 -330
  10. pywebguard-1.0.0/tests/test_core.py +0 -304
  11. pywebguard-1.0.0/tests/test_filters.py +0 -260
  12. pywebguard-1.0.0/tests/test_frameworks.py +0 -130
  13. pywebguard-1.0.0/tests/test_frameworks_fastapi.py +0 -318
  14. pywebguard-1.0.0/tests/test_frameworks_flask.py +0 -179
  15. pywebguard-1.0.0/tests/test_limiters.py +0 -354
  16. pywebguard-1.0.0/tests/test_security.py +0 -330
  17. pywebguard-1.0.0/tests/test_storage.py +0 -206
  18. pywebguard-1.0.0/tests/test_storage_memory.py +0 -64
  19. pywebguard-1.0.0/tests/test_storage_mongodb.py +0 -265
  20. pywebguard-1.0.0/tests/test_storage_postgresql.py +0 -347
  21. pywebguard-1.0.0/tests/test_storage_redis.py +0 -145
  22. pywebguard-1.0.0/tests/test_storage_sqlite.py +0 -132
  23. pywebguard-1.0.0/tests/test_storage_tinydb.py +0 -110
  24. pywebguard-1.0.0/tests/test_user_agent.py +0 -182
  25. pywebguard-1.0.0/tests/test_utils_ip.py +0 -102
  26. pywebguard-1.0.0/tests/test_utils_request.py +0 -90
  27. {pywebguard-1.0.0 → pywebguard-1.0.2}/LICENSE +0 -0
  28. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/__init__.py +0 -0
  29. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/cli.py +0 -0
  30. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/core/__init__.py +0 -0
  31. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/core/base.py +0 -0
  32. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/core/config.py +0 -0
  33. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/filters/__init__.py +0 -0
  34. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/filters/base.py +0 -0
  35. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/filters/ip_filter.py +0 -0
  36. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/filters/user_agent.py +0 -0
  37. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/frameworks/__init__.py +0 -0
  38. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/frameworks/_fastapi.py +0 -0
  39. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/frameworks/_flask.py +0 -0
  40. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/limiters/__init__.py +0 -0
  41. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/limiters/base.py +0 -0
  42. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/limiters/rate_limit.py +0 -0
  43. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/logging/__init__.py +0 -0
  44. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/logging/backends/__init__.py +0 -0
  45. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/logging/backends/_elasticsearch.py +0 -0
  46. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/logging/backends/_meilisearch.py +0 -0
  47. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/logging/backends/_mongodb.py +0 -0
  48. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/logging/base.py +0 -0
  49. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/logging/logger.py +0 -0
  50. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/security/__init__.py +0 -0
  51. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/security/base.py +0 -0
  52. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/security/cors.py +0 -0
  53. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/security/penetration.py +0 -0
  54. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/storage/__init__.py +0 -0
  55. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/storage/_mongodb.py +0 -0
  56. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/storage/_postgresql.py +0 -0
  57. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/storage/_redis.py +0 -0
  58. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/storage/_sqlite.py +0 -0
  59. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/storage/_tinydb.py +0 -0
  60. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/storage/base.py +0 -0
  61. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/storage/memory.py +0 -0
  62. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/utils/__init__.py +0 -0
  63. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/utils/ip.py +0 -0
  64. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard/utils/request.py +0 -0
  65. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard.egg-info/dependency_links.txt +0 -0
  66. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard.egg-info/entry_points.txt +0 -0
  67. {pywebguard-1.0.0 → pywebguard-1.0.2}/pywebguard.egg-info/top_level.txt +0 -0
  68. {pywebguard-1.0.0 → pywebguard-1.0.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pywebguard
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: A comprehensive security library for Python web applications
5
5
  Home-page: https://github.com/py-daily/pywebguard
6
6
  Author: py-daily
@@ -22,68 +22,52 @@ Classifier: Topic :: Internet :: WWW/HTTP
22
22
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
23
  Requires-Python: >=3.8
24
24
  Description-Content-Type: text/markdown
25
- License-File: LICENSE
26
- Requires-Dist: pydantic>=2.0.0
27
- Requires-Dist: ipaddress>=1.0.23
28
- Requires-Dist: python-dotenv>=0.19.0
29
25
  Provides-Extra: fastapi
30
- Requires-Dist: fastapi>=0.115.12; extra == "fastapi"
31
- Requires-Dist: starlette>=0.14.0; extra == "fastapi"
32
- Requires-Dist: uvicorn>=0.34.2; extra == "fastapi"
33
26
  Provides-Extra: flask
34
- Requires-Dist: flask>=3.1.1; extra == "flask"
35
27
  Provides-Extra: redis
36
- Requires-Dist: redis>=6.1.0; extra == "redis"
37
- Requires-Dist: aioredis>=2.0.1; extra == "redis"
38
28
  Provides-Extra: sqlite
39
- Requires-Dist: aiosqlite>=0.21.0; extra == "sqlite"
40
29
  Provides-Extra: tinydb
41
- Requires-Dist: tinydb>=4.8.2; extra == "tinydb"
42
30
  Provides-Extra: mongodb
43
- Requires-Dist: pymongo>=4.13.0; extra == "mongodb"
44
31
  Provides-Extra: postgresql
45
- Requires-Dist: asyncpg>=0.30.0; extra == "postgresql"
46
- Requires-Dist: psycopg2-binary>=2.9.10; extra == "postgresql"
47
32
  Provides-Extra: elasticsearch
48
- Requires-Dist: elasticsearch>=9.0.1; extra == "elasticsearch"
49
33
  Provides-Extra: all-storage
50
- Requires-Dist: redis>=6.1.0; extra == "all-storage"
51
- Requires-Dist: aioredis>=2.0.1; extra == "all-storage"
52
- Requires-Dist: aiosqlite>=0.21.0; extra == "all-storage"
53
- Requires-Dist: tinydb>=4.8.2; extra == "all-storage"
54
- Requires-Dist: pymongo>=4.13.0; extra == "all-storage"
55
- Requires-Dist: asyncpg>=0.30.0; extra == "all-storage"
56
- Requires-Dist: psycopg2-binary>=2.9.10; extra == "all-storage"
57
- Requires-Dist: elasticsearch>=9.0.1; extra == "all-storage"
58
34
  Provides-Extra: all-frameworks
59
- Requires-Dist: fastapi>=0.115.12; extra == "all-frameworks"
60
- Requires-Dist: starlette>=0.14.0; extra == "all-frameworks"
61
- Requires-Dist: uvicorn>=0.34.2; extra == "all-frameworks"
62
- Requires-Dist: flask>=3.1.1; extra == "all-frameworks"
63
35
  Provides-Extra: all
64
- Requires-Dist: fastapi>=0.115.12; extra == "all"
65
- Requires-Dist: starlette>=0.14.0; extra == "all"
66
- Requires-Dist: uvicorn>=0.34.2; extra == "all"
67
- Requires-Dist: flask>=3.1.1; extra == "all"
68
- Requires-Dist: redis>=6.1.0; extra == "all"
69
- Requires-Dist: aioredis>=2.0.1; extra == "all"
70
- Requires-Dist: aiosqlite>=0.21.0; extra == "all"
71
- Requires-Dist: tinydb>=4.8.2; extra == "all"
72
- Requires-Dist: pymongo>=4.13.0; extra == "all"
73
- Requires-Dist: asyncpg>=0.30.0; extra == "all"
74
- Requires-Dist: psycopg2-binary>=2.9.10; extra == "all"
75
- Requires-Dist: elasticsearch>=9.0.1; extra == "all"
76
36
  Provides-Extra: dev
77
- Requires-Dist: pytest>=7.0.0; extra == "dev"
78
- Requires-Dist: pytest-cov>=6.1.1; extra == "dev"
79
- Requires-Dist: pytest-asyncio>=0.26.0; extra == "dev"
80
- Requires-Dist: black>=25.1.0; extra == "dev"
81
- Requires-Dist: isort>=6.0.1; extra == "dev"
82
- Requires-Dist: mypy>=1.15.0; extra == "dev"
83
- Requires-Dist: flake8>=7.2.0; extra == "dev"
37
+ License-File: LICENSE
84
38
 
85
39
 
40
+ <!-- START doctoc generated TOC please keep comment here to allow auto update -->
41
+ <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
42
+ **Table of Contents** *generated with [DocToc](https://github.com/ktechhub/doctoc)*
43
+
44
+ <!---toc start-->
45
+
46
+ * [PyWebGuard](#pywebguard)
47
+ * [Key Features](#key-features)
48
+ * [Quick Start](#quick-start)
49
+ * [Define route-specific rate limits](#define-route-specific-rate-limits)
50
+ * [Initialize storage (async in-memory for this example)](#initialize-storage-async-in-memory-for-this-example)
51
+ * [Uncomment to use Redis storage instead or import any of the storage supports](#uncomment-to-use-redis-storage-instead-or-import-any-of-the-storage-supports)
52
+ * [storage = AsyncRedisStorage(url="redis://localhost:6379")](#storage--asyncredisstorageurlredislocalhost6379)
53
+ * [Add PyWebGuard middleware](#add-pywebguard-middleware)
54
+ * [Example of a path that might trigger penetration detection](#example-of-a-path-that-might-trigger-penetration-detection)
55
+ * [Helper endpoint to check remaining rate limits](#helper-endpoint-to-check-remaining-rate-limits)
56
+ * [Endpoint to check if an IP is banned](#endpoint-to-check-if-an-ip-is-banned)
57
+ * [Admin endpoint to get metrics](#admin-endpoint-to-get-metrics)
58
+ * [Admin endpoint to ban an IP](#admin-endpoint-to-ban-an-ip)
59
+ * [Admin endpoint to unban an IP](#admin-endpoint-to-unban-an-ip)
60
+ * [Documentation](#documentation)
61
+ * [Contributors](#contributors)
62
+ * [License](#license)
63
+
64
+ <!---toc end-->
65
+
66
+ <!-- END doctoc generated TOC please keep comment here to allow auto update -->
86
67
  # PyWebGuard
68
+ [![PyPI version](https://badge.fury.io/py/pywebguard.svg)](https://badge.fury.io/py/pywebguard)
69
+ [![Python Versions](https://img.shields.io/pypi/pyversions/pywebguard.svg)](https://pypi.org/project/pywebguard/)
70
+ [![License](https://img.shields.io/github/license/py-daily/pywebguard.svg)](https://github.com/py-daily/pywebguard/blob/main/LICENSE)
87
71
 
88
72
  A comprehensive security library for Python web applications, providing middleware for IP filtering, rate limiting, and other security features with both synchronous and asynchronous support.
89
73
 
@@ -412,7 +396,6 @@ if __name__ == "__main__":
412
396
 
413
397
 
414
398
  ## Documentation
415
-
416
399
  - [Installation Guide](docs/installation.md)
417
400
  - [CLI Usage](docs/cli.md)
418
401
  - [Core Features](docs/core/)
@@ -422,9 +405,10 @@ if __name__ == "__main__":
422
405
  ## Contributors
423
406
 
424
407
  <a href="https://github.com/py-daily/pywebguard/graphs/contributors">
425
- <img src="https://contrib.rocks/image?repo=py-daily/pywebguard" width="800" />
408
+ <img src="https://contrib.rocks/image?repo=py-daily/pywebguard" width="80" />
426
409
  </a>
427
410
 
411
+
428
412
  ## License
429
413
 
430
414
  This project is licensed under the [MIT License](LICENSE).
@@ -1,4 +1,34 @@
1
+ <!-- START doctoc generated TOC please keep comment here to allow auto update -->
2
+ <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
3
+ **Table of Contents** *generated with [DocToc](https://github.com/ktechhub/doctoc)*
4
+
5
+ <!---toc start-->
6
+
7
+ * [PyWebGuard](#pywebguard)
8
+ * [Key Features](#key-features)
9
+ * [Quick Start](#quick-start)
10
+ * [Define route-specific rate limits](#define-route-specific-rate-limits)
11
+ * [Initialize storage (async in-memory for this example)](#initialize-storage-async-in-memory-for-this-example)
12
+ * [Uncomment to use Redis storage instead or import any of the storage supports](#uncomment-to-use-redis-storage-instead-or-import-any-of-the-storage-supports)
13
+ * [storage = AsyncRedisStorage(url="redis://localhost:6379")](#storage--asyncredisstorageurlredislocalhost6379)
14
+ * [Add PyWebGuard middleware](#add-pywebguard-middleware)
15
+ * [Example of a path that might trigger penetration detection](#example-of-a-path-that-might-trigger-penetration-detection)
16
+ * [Helper endpoint to check remaining rate limits](#helper-endpoint-to-check-remaining-rate-limits)
17
+ * [Endpoint to check if an IP is banned](#endpoint-to-check-if-an-ip-is-banned)
18
+ * [Admin endpoint to get metrics](#admin-endpoint-to-get-metrics)
19
+ * [Admin endpoint to ban an IP](#admin-endpoint-to-ban-an-ip)
20
+ * [Admin endpoint to unban an IP](#admin-endpoint-to-unban-an-ip)
21
+ * [Documentation](#documentation)
22
+ * [Contributors](#contributors)
23
+ * [License](#license)
24
+
25
+ <!---toc end-->
26
+
27
+ <!-- END doctoc generated TOC please keep comment here to allow auto update -->
1
28
  # PyWebGuard
29
+ [![PyPI version](https://badge.fury.io/py/pywebguard.svg)](https://badge.fury.io/py/pywebguard)
30
+ [![Python Versions](https://img.shields.io/pypi/pyversions/pywebguard.svg)](https://pypi.org/project/pywebguard/)
31
+ [![License](https://img.shields.io/github/license/py-daily/pywebguard.svg)](https://github.com/py-daily/pywebguard/blob/main/LICENSE)
2
32
 
3
33
  A comprehensive security library for Python web applications, providing middleware for IP filtering, rate limiting, and other security features with both synchronous and asynchronous support.
4
34
 
@@ -327,7 +357,6 @@ if __name__ == "__main__":
327
357
 
328
358
 
329
359
  ## Documentation
330
-
331
360
  - [Installation Guide](docs/installation.md)
332
361
  - [CLI Usage](docs/cli.md)
333
362
  - [Core Features](docs/core/)
@@ -337,9 +366,10 @@ if __name__ == "__main__":
337
366
  ## Contributors
338
367
 
339
368
  <a href="https://github.com/py-daily/pywebguard/graphs/contributors">
340
- <img src="https://contrib.rocks/image?repo=py-daily/pywebguard" width="800" />
369
+ <img src="https://contrib.rocks/image?repo=py-daily/pywebguard" width="80" />
341
370
  </a>
342
371
 
372
+
343
373
  ## License
344
374
 
345
375
  This project is licensed under the [MIT License](LICENSE).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pywebguard
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: A comprehensive security library for Python web applications
5
5
  Home-page: https://github.com/py-daily/pywebguard
6
6
  Author: py-daily
@@ -22,68 +22,52 @@ Classifier: Topic :: Internet :: WWW/HTTP
22
22
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
23
  Requires-Python: >=3.8
24
24
  Description-Content-Type: text/markdown
25
- License-File: LICENSE
26
- Requires-Dist: pydantic>=2.0.0
27
- Requires-Dist: ipaddress>=1.0.23
28
- Requires-Dist: python-dotenv>=0.19.0
29
25
  Provides-Extra: fastapi
30
- Requires-Dist: fastapi>=0.115.12; extra == "fastapi"
31
- Requires-Dist: starlette>=0.14.0; extra == "fastapi"
32
- Requires-Dist: uvicorn>=0.34.2; extra == "fastapi"
33
26
  Provides-Extra: flask
34
- Requires-Dist: flask>=3.1.1; extra == "flask"
35
27
  Provides-Extra: redis
36
- Requires-Dist: redis>=6.1.0; extra == "redis"
37
- Requires-Dist: aioredis>=2.0.1; extra == "redis"
38
28
  Provides-Extra: sqlite
39
- Requires-Dist: aiosqlite>=0.21.0; extra == "sqlite"
40
29
  Provides-Extra: tinydb
41
- Requires-Dist: tinydb>=4.8.2; extra == "tinydb"
42
30
  Provides-Extra: mongodb
43
- Requires-Dist: pymongo>=4.13.0; extra == "mongodb"
44
31
  Provides-Extra: postgresql
45
- Requires-Dist: asyncpg>=0.30.0; extra == "postgresql"
46
- Requires-Dist: psycopg2-binary>=2.9.10; extra == "postgresql"
47
32
  Provides-Extra: elasticsearch
48
- Requires-Dist: elasticsearch>=9.0.1; extra == "elasticsearch"
49
33
  Provides-Extra: all-storage
50
- Requires-Dist: redis>=6.1.0; extra == "all-storage"
51
- Requires-Dist: aioredis>=2.0.1; extra == "all-storage"
52
- Requires-Dist: aiosqlite>=0.21.0; extra == "all-storage"
53
- Requires-Dist: tinydb>=4.8.2; extra == "all-storage"
54
- Requires-Dist: pymongo>=4.13.0; extra == "all-storage"
55
- Requires-Dist: asyncpg>=0.30.0; extra == "all-storage"
56
- Requires-Dist: psycopg2-binary>=2.9.10; extra == "all-storage"
57
- Requires-Dist: elasticsearch>=9.0.1; extra == "all-storage"
58
34
  Provides-Extra: all-frameworks
59
- Requires-Dist: fastapi>=0.115.12; extra == "all-frameworks"
60
- Requires-Dist: starlette>=0.14.0; extra == "all-frameworks"
61
- Requires-Dist: uvicorn>=0.34.2; extra == "all-frameworks"
62
- Requires-Dist: flask>=3.1.1; extra == "all-frameworks"
63
35
  Provides-Extra: all
64
- Requires-Dist: fastapi>=0.115.12; extra == "all"
65
- Requires-Dist: starlette>=0.14.0; extra == "all"
66
- Requires-Dist: uvicorn>=0.34.2; extra == "all"
67
- Requires-Dist: flask>=3.1.1; extra == "all"
68
- Requires-Dist: redis>=6.1.0; extra == "all"
69
- Requires-Dist: aioredis>=2.0.1; extra == "all"
70
- Requires-Dist: aiosqlite>=0.21.0; extra == "all"
71
- Requires-Dist: tinydb>=4.8.2; extra == "all"
72
- Requires-Dist: pymongo>=4.13.0; extra == "all"
73
- Requires-Dist: asyncpg>=0.30.0; extra == "all"
74
- Requires-Dist: psycopg2-binary>=2.9.10; extra == "all"
75
- Requires-Dist: elasticsearch>=9.0.1; extra == "all"
76
36
  Provides-Extra: dev
77
- Requires-Dist: pytest>=7.0.0; extra == "dev"
78
- Requires-Dist: pytest-cov>=6.1.1; extra == "dev"
79
- Requires-Dist: pytest-asyncio>=0.26.0; extra == "dev"
80
- Requires-Dist: black>=25.1.0; extra == "dev"
81
- Requires-Dist: isort>=6.0.1; extra == "dev"
82
- Requires-Dist: mypy>=1.15.0; extra == "dev"
83
- Requires-Dist: flake8>=7.2.0; extra == "dev"
37
+ License-File: LICENSE
84
38
 
85
39
 
40
+ <!-- START doctoc generated TOC please keep comment here to allow auto update -->
41
+ <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
42
+ **Table of Contents** *generated with [DocToc](https://github.com/ktechhub/doctoc)*
43
+
44
+ <!---toc start-->
45
+
46
+ * [PyWebGuard](#pywebguard)
47
+ * [Key Features](#key-features)
48
+ * [Quick Start](#quick-start)
49
+ * [Define route-specific rate limits](#define-route-specific-rate-limits)
50
+ * [Initialize storage (async in-memory for this example)](#initialize-storage-async-in-memory-for-this-example)
51
+ * [Uncomment to use Redis storage instead or import any of the storage supports](#uncomment-to-use-redis-storage-instead-or-import-any-of-the-storage-supports)
52
+ * [storage = AsyncRedisStorage(url="redis://localhost:6379")](#storage--asyncredisstorageurlredislocalhost6379)
53
+ * [Add PyWebGuard middleware](#add-pywebguard-middleware)
54
+ * [Example of a path that might trigger penetration detection](#example-of-a-path-that-might-trigger-penetration-detection)
55
+ * [Helper endpoint to check remaining rate limits](#helper-endpoint-to-check-remaining-rate-limits)
56
+ * [Endpoint to check if an IP is banned](#endpoint-to-check-if-an-ip-is-banned)
57
+ * [Admin endpoint to get metrics](#admin-endpoint-to-get-metrics)
58
+ * [Admin endpoint to ban an IP](#admin-endpoint-to-ban-an-ip)
59
+ * [Admin endpoint to unban an IP](#admin-endpoint-to-unban-an-ip)
60
+ * [Documentation](#documentation)
61
+ * [Contributors](#contributors)
62
+ * [License](#license)
63
+
64
+ <!---toc end-->
65
+
66
+ <!-- END doctoc generated TOC please keep comment here to allow auto update -->
86
67
  # PyWebGuard
68
+ [![PyPI version](https://badge.fury.io/py/pywebguard.svg)](https://badge.fury.io/py/pywebguard)
69
+ [![Python Versions](https://img.shields.io/pypi/pyversions/pywebguard.svg)](https://pypi.org/project/pywebguard/)
70
+ [![License](https://img.shields.io/github/license/py-daily/pywebguard.svg)](https://github.com/py-daily/pywebguard/blob/main/LICENSE)
87
71
 
88
72
  A comprehensive security library for Python web applications, providing middleware for IP filtering, rate limiting, and other security features with both synchronous and asynchronous support.
89
73
 
@@ -412,7 +396,6 @@ if __name__ == "__main__":
412
396
 
413
397
 
414
398
  ## Documentation
415
-
416
399
  - [Installation Guide](docs/installation.md)
417
400
  - [CLI Usage](docs/cli.md)
418
401
  - [Core Features](docs/core/)
@@ -422,9 +405,10 @@ if __name__ == "__main__":
422
405
  ## Contributors
423
406
 
424
407
  <a href="https://github.com/py-daily/pywebguard/graphs/contributors">
425
- <img src="https://contrib.rocks/image?repo=py-daily/pywebguard" width="800" />
408
+ <img src="https://contrib.rocks/image?repo=py-daily/pywebguard" width="80" />
426
409
  </a>
427
410
 
411
+
428
412
  ## License
429
413
 
430
414
  This project is licensed under the [MIT License](LICENSE).
@@ -43,24 +43,4 @@ pywebguard/storage/base.py
43
43
  pywebguard/storage/memory.py
44
44
  pywebguard/utils/__init__.py
45
45
  pywebguard/utils/ip.py
46
- pywebguard/utils/request.py
47
- tests/__init__.py
48
- tests/conftest.py
49
- tests/test_cli.py
50
- tests/test_core.py
51
- tests/test_filters.py
52
- tests/test_frameworks.py
53
- tests/test_frameworks_fastapi.py
54
- tests/test_frameworks_flask.py
55
- tests/test_limiters.py
56
- tests/test_security.py
57
- tests/test_storage.py
58
- tests/test_storage_memory.py
59
- tests/test_storage_mongodb.py
60
- tests/test_storage_postgresql.py
61
- tests/test_storage_redis.py
62
- tests/test_storage_sqlite.py
63
- tests/test_storage_tinydb.py
64
- tests/test_user_agent.py
65
- tests/test_utils_ip.py
66
- tests/test_utils_request.py
46
+ pywebguard/utils/request.py
@@ -4,6 +4,7 @@ python-dotenv>=0.19.0
4
4
 
5
5
  [all]
6
6
  fastapi>=0.115.12
7
+ httpx>=0.28.1
7
8
  starlette>=0.14.0
8
9
  uvicorn>=0.34.2
9
10
  flask>=3.1.1
@@ -18,6 +19,7 @@ elasticsearch>=9.0.1
18
19
 
19
20
  [all-frameworks]
20
21
  fastapi>=0.115.12
22
+ httpx>=0.28.1
21
23
  starlette>=0.14.0
22
24
  uvicorn>=0.34.2
23
25
  flask>=3.1.1
@@ -46,6 +48,7 @@ elasticsearch>=9.0.1
46
48
 
47
49
  [fastapi]
48
50
  fastapi>=0.115.12
51
+ httpx>=0.28.1
49
52
  starlette>=0.14.0
50
53
  uvicorn>=0.34.2
51
54
 
@@ -13,7 +13,7 @@ with io.open(path.join(here, "README.md"), encoding="utf-8") as f:
13
13
  long_description = "\n" + f.read()
14
14
 
15
15
 
16
- fastapi = ["fastapi>=0.115.12", "starlette>=0.14.0", "uvicorn>=0.34.2"]
16
+ fastapi = ["fastapi>=0.115.12", "httpx>=0.28.1", "starlette>=0.14.0", "uvicorn>=0.34.2"]
17
17
  flask = ["flask>=3.1.1"]
18
18
  # Storage backends
19
19
  redis = ["redis>=6.1.0", "aioredis>=2.0.1"]
@@ -1 +0,0 @@
1
- """Test package for PyWebGuard."""
@@ -1,211 +0,0 @@
1
- """Common test fixtures for PyWebGuard tests."""
2
-
3
- import pytest
4
- import time
5
- from typing import Dict, Any, Optional, Generator, Callable
6
-
7
- from pywebguard.core.config import (
8
- GuardConfig,
9
- IPFilterConfig,
10
- RateLimitConfig,
11
- UserAgentConfig,
12
- )
13
- from pywebguard.core.base import Guard, AsyncGuard
14
- from pywebguard.storage.memory import MemoryStorage, AsyncMemoryStorage
15
- from pywebguard.storage.base import BaseStorage, AsyncBaseStorage
16
-
17
-
18
- # Mock request and response classes for testing
19
- class MockRequest:
20
- """Mock request object for testing."""
21
-
22
- def __init__(
23
- self,
24
- remote_addr: str = "127.0.0.1",
25
- user_agent: str = "Mozilla/5.0",
26
- method: str = "GET",
27
- path: str = "/",
28
- query_string: str = "",
29
- headers: Optional[Dict[str, str]] = None,
30
- ):
31
- self.remote_addr = remote_addr
32
- self.user_agent = user_agent
33
- self.method = method
34
- self.path = path
35
- self.query_string = query_string
36
- self.headers = headers or {}
37
-
38
- # For FastAPI compatibility
39
- class Client:
40
- def __init__(self, host: str):
41
- self.host = host
42
-
43
- self.client = Client(remote_addr)
44
-
45
- # For URL object in FastAPI
46
- class URL:
47
- def __init__(self, path: str):
48
- self.path = path
49
-
50
- self.url = URL(path)
51
-
52
- # For query params in FastAPI
53
- self.query_params = {}
54
- if query_string:
55
- for param in query_string.split("&"):
56
- if "=" in param:
57
- key, value = param.split("=", 1)
58
- self.query_params[key] = value
59
- else:
60
- self.query_params[param] = ""
61
-
62
-
63
- class MockResponse:
64
- """Mock response object for testing."""
65
-
66
- def __init__(
67
- self,
68
- status_code: int = 200,
69
- headers: Optional[Dict[str, str]] = None,
70
- ):
71
- self.status_code = status_code
72
- self.headers = headers or {}
73
-
74
-
75
- @pytest.fixture
76
- def basic_config() -> GuardConfig:
77
- """Create a basic GuardConfig for testing."""
78
- return GuardConfig(
79
- ip_filter=IPFilterConfig(
80
- enabled=True,
81
- whitelist=["127.0.0.1"],
82
- blacklist=["10.0.0.1"],
83
- ),
84
- rate_limit=RateLimitConfig(
85
- enabled=True,
86
- requests_per_minute=60,
87
- burst_size=10,
88
- ),
89
- user_agent=UserAgentConfig(
90
- enabled=True,
91
- allowed_agents=["Werkzeug/3.1.3"],
92
- ),
93
- )
94
-
95
-
96
- @pytest.fixture
97
- def memory_storage() -> MemoryStorage:
98
- """Create a memory storage instance for testing."""
99
- return MemoryStorage()
100
-
101
-
102
- @pytest.fixture
103
- def async_memory_storage() -> AsyncMemoryStorage:
104
- """Create an async memory storage instance for testing."""
105
- return AsyncMemoryStorage()
106
-
107
-
108
- @pytest.fixture
109
- def guard(basic_config: GuardConfig, memory_storage: MemoryStorage) -> Guard:
110
- """Create a Guard instance with test configuration."""
111
- return Guard(config=basic_config, storage=memory_storage)
112
-
113
-
114
- @pytest.fixture
115
- def async_guard(
116
- basic_config: GuardConfig, async_memory_storage: AsyncMemoryStorage
117
- ) -> AsyncGuard:
118
- """Create an AsyncGuard instance with test configuration."""
119
- return AsyncGuard(config=basic_config, storage=async_memory_storage)
120
-
121
-
122
- @pytest.fixture
123
- def mock_request() -> MockRequest:
124
- """Create a mock request for testing."""
125
- return MockRequest()
126
-
127
-
128
- @pytest.fixture
129
- def mock_response() -> MockResponse:
130
- """Create a mock response for testing."""
131
- return MockResponse()
132
-
133
-
134
- @pytest.fixture
135
- def mock_blocked_ip_request() -> MockRequest:
136
- """Create a mock request with a blocked IP."""
137
- return MockRequest(remote_addr="10.0.0.1")
138
-
139
-
140
- @pytest.fixture
141
- def mock_blocked_ua_request() -> MockRequest:
142
- """Create a mock request with a blocked user agent."""
143
- return MockRequest(user_agent="curl/7.64.1")
144
-
145
-
146
- @pytest.fixture
147
- def rate_limited_guard(
148
- basic_config: GuardConfig, memory_storage: MemoryStorage
149
- ) -> Guard:
150
- """Create a Guard instance with a very low rate limit for testing."""
151
- config = GuardConfig(
152
- ip_filter=basic_config.ip_filter,
153
- rate_limit=RateLimitConfig(
154
- enabled=True,
155
- requests_per_minute=1, # Very low rate limit for testing
156
- burst_size=0, # Strict: no burst allowed
157
- ),
158
- )
159
- return Guard(config=config, storage=memory_storage)
160
-
161
-
162
- @pytest.fixture
163
- def async_rate_limited_guard(
164
- basic_config: GuardConfig, async_memory_storage: AsyncMemoryStorage
165
- ) -> AsyncGuard:
166
- """Create an AsyncGuard instance with a very low rate limit for testing."""
167
- config = GuardConfig(
168
- ip_filter=basic_config.ip_filter,
169
- rate_limit=RateLimitConfig(
170
- enabled=True,
171
- requests_per_minute=1, # Very low rate limit for testing
172
- burst_size=0, # Strict: no burst allowed
173
- ),
174
- )
175
- return AsyncGuard(config=config, storage=async_memory_storage)
176
-
177
-
178
- @pytest.fixture
179
- def route_rate_limited_guard(
180
- basic_config: GuardConfig, memory_storage: MemoryStorage
181
- ) -> Guard:
182
- """Create a Guard instance with route-specific rate limits for testing."""
183
- guard = Guard(config=basic_config, storage=memory_storage)
184
-
185
- # Add route-specific rate limits
186
- guard.add_route_rate_limit(
187
- "/api/limited",
188
- {
189
- "requests_per_minute": 1,
190
- "burst_size": 0, # Strict: no burst allowed
191
- },
192
- )
193
- return guard
194
-
195
-
196
- @pytest.fixture
197
- def async_route_rate_limited_guard(
198
- basic_config: GuardConfig, async_memory_storage: AsyncMemoryStorage
199
- ) -> AsyncGuard:
200
- """Create an AsyncGuard instance with route-specific rate limits for testing."""
201
- guard = AsyncGuard(config=basic_config, storage=async_memory_storage)
202
-
203
- # Add route-specific rate limits
204
- guard.add_route_rate_limit(
205
- "/api/limited",
206
- {
207
- "requests_per_minute": 1,
208
- "burst_size": 0, # Strict: no burst allowed
209
- },
210
- )
211
- return guard