ulinzilogs 0.1.1__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.
- ulinzilogs-0.1.1/PKG-INFO +219 -0
- ulinzilogs-0.1.1/README.md +199 -0
- ulinzilogs-0.1.1/pyproject.toml +28 -0
- ulinzilogs-0.1.1/setup.cfg +4 -0
- ulinzilogs-0.1.1/ulinzilogs/__init__.py +4 -0
- ulinzilogs-0.1.1/ulinzilogs/cli.py +39 -0
- ulinzilogs-0.1.1/ulinzilogs/middleware.py +136 -0
- ulinzilogs-0.1.1/ulinzilogs/utils.py +19 -0
- ulinzilogs-0.1.1/ulinzilogs.egg-info/PKG-INFO +219 -0
- ulinzilogs-0.1.1/ulinzilogs.egg-info/SOURCES.txt +12 -0
- ulinzilogs-0.1.1/ulinzilogs.egg-info/dependency_links.txt +1 -0
- ulinzilogs-0.1.1/ulinzilogs.egg-info/entry_points.txt +2 -0
- ulinzilogs-0.1.1/ulinzilogs.egg-info/requires.txt +15 -0
- ulinzilogs-0.1.1/ulinzilogs.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ulinzilogs
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Security logging middleware for FastAPI, Flask, and Django
|
|
5
|
+
Author-email: PashyGeek <codyypashy@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/PashyGeek/ulinzilogs
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: requests>=2.28.0
|
|
10
|
+
Provides-Extra: fastapi
|
|
11
|
+
Requires-Dist: starlette>=0.20.0; extra == "fastapi"
|
|
12
|
+
Provides-Extra: flask
|
|
13
|
+
Requires-Dist: flask>=2.0.0; extra == "flask"
|
|
14
|
+
Provides-Extra: django
|
|
15
|
+
Requires-Dist: django>=3.2; extra == "django"
|
|
16
|
+
Provides-Extra: all
|
|
17
|
+
Requires-Dist: starlette>=0.20.0; extra == "all"
|
|
18
|
+
Requires-Dist: flask>=2.0.0; extra == "all"
|
|
19
|
+
Requires-Dist: django>=3.2; extra == "all"
|
|
20
|
+
|
|
21
|
+
# Ulinzilogs π‘οΈ
|
|
22
|
+
|
|
23
|
+
> **Lightweight, nonβblocking security logging middleware for Python web apps**
|
|
24
|
+
> Built for developers who want **visibility without performance pain**.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## π What is Ulinzilogs?
|
|
29
|
+
|
|
30
|
+
**Ulinzilogs** is a securityβfocused middleware that silently watches incoming HTTP requests in your Python web application, **redacts sensitive data**, and **ships sanitized logs** to the Ulinzi Security API for **AIβpowered threat analysis**.
|
|
31
|
+
|
|
32
|
+
Think of it as:
|
|
33
|
+
|
|
34
|
+
> π *A security camera for your API β always on, never in the way.*
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## β¨ Key Features
|
|
39
|
+
|
|
40
|
+
* π **NonβBlocking by Design** β Uses background threads so your request/response cycle stays fast.
|
|
41
|
+
* π **Automatic Data Redaction** β Sensitive fields are replaced with `[REDACTED]` before leaving your server.
|
|
42
|
+
* π **MultiβFramework Support** β Works out of the box with FastAPI, Flask, and Django.
|
|
43
|
+
* π§Ή **Clean & Safe Headers** β Excludes `Authorization` and `Cookie` headers automatically.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## π¦ Installation
|
|
48
|
+
|
|
49
|
+
Install only what your stack needs:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# FastAPI / Starlette
|
|
53
|
+
pip install ulinzilogs[fastapi]
|
|
54
|
+
|
|
55
|
+
# Flask
|
|
56
|
+
pip install ulinzilogs[flask]
|
|
57
|
+
|
|
58
|
+
# Django
|
|
59
|
+
pip install ulinzilogs[django]
|
|
60
|
+
|
|
61
|
+
# All frameworks
|
|
62
|
+
pip install ulinzilogs[all]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## π Getting Your Project Key
|
|
68
|
+
|
|
69
|
+
After installing, run:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
ulinzi-init
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This generates a **unique Project Key** for your app and prints setup instructions.
|
|
76
|
+
|
|
77
|
+
> β οΈ **Important:** This key is generated locally on your machine.
|
|
78
|
+
> To activate it, copy the key and register it at **[ulinzilogs.com](https://ulinzilogs.com)**.
|
|
79
|
+
> Once registered, your dashboard will start receiving logs from your app.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## βοΈ Setup & Usage
|
|
84
|
+
|
|
85
|
+
### π’ FastAPI
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from fastapi import FastAPI
|
|
89
|
+
from ulinzilogs import UlinziFastAPIMiddleware
|
|
90
|
+
|
|
91
|
+
app = FastAPI()
|
|
92
|
+
|
|
93
|
+
app.add_middleware(
|
|
94
|
+
UlinziFastAPIMiddleware,
|
|
95
|
+
api_key="YOUR_ULINZI_KEY"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
@app.get("/")
|
|
99
|
+
async def root():
|
|
100
|
+
return {"message": "Protected by Ulinzi"}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### π΅ Flask
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from flask import Flask
|
|
109
|
+
from ulinzilogs import UlinziFlaskMiddleware
|
|
110
|
+
|
|
111
|
+
app = Flask(__name__)
|
|
112
|
+
UlinziFlaskMiddleware(app, api_key="YOUR_ULINZI_KEY")
|
|
113
|
+
|
|
114
|
+
@app.route("/")
|
|
115
|
+
def hello():
|
|
116
|
+
return "Protected by Ulinzi"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### π£ Django
|
|
122
|
+
|
|
123
|
+
In `settings.py`:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
MIDDLEWARE = [
|
|
127
|
+
# ... other middleware
|
|
128
|
+
'ulinzilogs.UlinziDjangoMiddleware',
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
ULINZI_API_KEY = "YOUR_ULINZI_KEY"
|
|
132
|
+
ULINZI_APP_NAME = "MyDjangoApp" # optional label shown in dashboard
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## π§ How Ulinzilogs Works
|
|
138
|
+
Client Request
|
|
139
|
+
β
|
|
140
|
+
βΌ
|
|
141
|
+
βββββββββββββββββ
|
|
142
|
+
β Middleware β
|
|
143
|
+
ββββββββ¬βββββββββ
|
|
144
|
+
β
|
|
145
|
+
βΌ
|
|
146
|
+
ββββββββββββββββββββββ
|
|
147
|
+
β Redact Sensitive β
|
|
148
|
+
β Fields & Headers β
|
|
149
|
+
ββββββββ¬ββββββββββββββ
|
|
150
|
+
β
|
|
151
|
+
βΌ (Background Thread)
|
|
152
|
+
ββββββββββββββββββββββ
|
|
153
|
+
β Send to Ulinzi β
|
|
154
|
+
β Security API β
|
|
155
|
+
ββββββββ¬ββββββββββββββ
|
|
156
|
+
β
|
|
157
|
+
βΌ
|
|
158
|
+
ββββββββββββββββββββββ
|
|
159
|
+
β AI Threat Analysis β
|
|
160
|
+
β (SQLi, XSS, etc.) β
|
|
161
|
+
ββββββββββββββββββββββ
|
|
162
|
+
|
|
163
|
+
1. **Intercept** β Captures request metadata (IP, URL, method, body).
|
|
164
|
+
2. **Redact** β Removes secrets before transmission.
|
|
165
|
+
3. **Dispatch** β Sends logs asynchronously in a background thread.
|
|
166
|
+
4. **Analyze** β Central AI scans for malicious patterns.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## π Sensitive Data Protection
|
|
171
|
+
|
|
172
|
+
The following keys are **automatically redacted**:
|
|
173
|
+
|
|
174
|
+
| Field | Redacted? |
|
|
175
|
+
|-------|-----------|
|
|
176
|
+
| `password` | β
Yes |
|
|
177
|
+
| `secret` | β
Yes |
|
|
178
|
+
| `token` | β
Yes |
|
|
179
|
+
| `key` | β
Yes |
|
|
180
|
+
| `cvv` | β
Yes |
|
|
181
|
+
| `api_key` | β
Yes |
|
|
182
|
+
|
|
183
|
+
Example:
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"email": "user@mail.com",
|
|
188
|
+
"password": "[REDACTED]"
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Your users' secrets **never leave your server in plaintext**.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## β‘ Performance Philosophy
|
|
197
|
+
|
|
198
|
+
* No request blocking
|
|
199
|
+
* No database writes on your server
|
|
200
|
+
* No logging delays
|
|
201
|
+
|
|
202
|
+
Ulinzilogs runs **beside** your app β not **inside** its critical path.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## π§© Package Structure
|
|
207
|
+
ulinzilogs/
|
|
208
|
+
βββ init.py
|
|
209
|
+
βββ middleware.py
|
|
210
|
+
βββ utils.py
|
|
211
|
+
βββ cli.py
|
|
212
|
+
βββ pyproject.toml
|
|
213
|
+
βββ README.md
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## π License
|
|
218
|
+
|
|
219
|
+
Copyright (c) 2026 PashyGeek. Licensed under the MIT License β see the LICENSE file for details.
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Ulinzilogs π‘οΈ
|
|
2
|
+
|
|
3
|
+
> **Lightweight, nonβblocking security logging middleware for Python web apps**
|
|
4
|
+
> Built for developers who want **visibility without performance pain**.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## π What is Ulinzilogs?
|
|
9
|
+
|
|
10
|
+
**Ulinzilogs** is a securityβfocused middleware that silently watches incoming HTTP requests in your Python web application, **redacts sensitive data**, and **ships sanitized logs** to the Ulinzi Security API for **AIβpowered threat analysis**.
|
|
11
|
+
|
|
12
|
+
Think of it as:
|
|
13
|
+
|
|
14
|
+
> π *A security camera for your API β always on, never in the way.*
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## β¨ Key Features
|
|
19
|
+
|
|
20
|
+
* π **NonβBlocking by Design** β Uses background threads so your request/response cycle stays fast.
|
|
21
|
+
* π **Automatic Data Redaction** β Sensitive fields are replaced with `[REDACTED]` before leaving your server.
|
|
22
|
+
* π **MultiβFramework Support** β Works out of the box with FastAPI, Flask, and Django.
|
|
23
|
+
* π§Ή **Clean & Safe Headers** β Excludes `Authorization` and `Cookie` headers automatically.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## π¦ Installation
|
|
28
|
+
|
|
29
|
+
Install only what your stack needs:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# FastAPI / Starlette
|
|
33
|
+
pip install ulinzilogs[fastapi]
|
|
34
|
+
|
|
35
|
+
# Flask
|
|
36
|
+
pip install ulinzilogs[flask]
|
|
37
|
+
|
|
38
|
+
# Django
|
|
39
|
+
pip install ulinzilogs[django]
|
|
40
|
+
|
|
41
|
+
# All frameworks
|
|
42
|
+
pip install ulinzilogs[all]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## π Getting Your Project Key
|
|
48
|
+
|
|
49
|
+
After installing, run:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
ulinzi-init
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This generates a **unique Project Key** for your app and prints setup instructions.
|
|
56
|
+
|
|
57
|
+
> β οΈ **Important:** This key is generated locally on your machine.
|
|
58
|
+
> To activate it, copy the key and register it at **[ulinzilogs.com](https://ulinzilogs.com)**.
|
|
59
|
+
> Once registered, your dashboard will start receiving logs from your app.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## βοΈ Setup & Usage
|
|
64
|
+
|
|
65
|
+
### π’ FastAPI
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from fastapi import FastAPI
|
|
69
|
+
from ulinzilogs import UlinziFastAPIMiddleware
|
|
70
|
+
|
|
71
|
+
app = FastAPI()
|
|
72
|
+
|
|
73
|
+
app.add_middleware(
|
|
74
|
+
UlinziFastAPIMiddleware,
|
|
75
|
+
api_key="YOUR_ULINZI_KEY"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
@app.get("/")
|
|
79
|
+
async def root():
|
|
80
|
+
return {"message": "Protected by Ulinzi"}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
### π΅ Flask
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from flask import Flask
|
|
89
|
+
from ulinzilogs import UlinziFlaskMiddleware
|
|
90
|
+
|
|
91
|
+
app = Flask(__name__)
|
|
92
|
+
UlinziFlaskMiddleware(app, api_key="YOUR_ULINZI_KEY")
|
|
93
|
+
|
|
94
|
+
@app.route("/")
|
|
95
|
+
def hello():
|
|
96
|
+
return "Protected by Ulinzi"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
### π£ Django
|
|
102
|
+
|
|
103
|
+
In `settings.py`:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
MIDDLEWARE = [
|
|
107
|
+
# ... other middleware
|
|
108
|
+
'ulinzilogs.UlinziDjangoMiddleware',
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
ULINZI_API_KEY = "YOUR_ULINZI_KEY"
|
|
112
|
+
ULINZI_APP_NAME = "MyDjangoApp" # optional label shown in dashboard
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## π§ How Ulinzilogs Works
|
|
118
|
+
Client Request
|
|
119
|
+
β
|
|
120
|
+
βΌ
|
|
121
|
+
βββββββββββββββββ
|
|
122
|
+
β Middleware β
|
|
123
|
+
ββββββββ¬βββββββββ
|
|
124
|
+
β
|
|
125
|
+
βΌ
|
|
126
|
+
ββββββββββββββββββββββ
|
|
127
|
+
β Redact Sensitive β
|
|
128
|
+
β Fields & Headers β
|
|
129
|
+
ββββββββ¬ββββββββββββββ
|
|
130
|
+
β
|
|
131
|
+
βΌ (Background Thread)
|
|
132
|
+
ββββββββββββββββββββββ
|
|
133
|
+
β Send to Ulinzi β
|
|
134
|
+
β Security API β
|
|
135
|
+
ββββββββ¬ββββββββββββββ
|
|
136
|
+
β
|
|
137
|
+
βΌ
|
|
138
|
+
ββββββββββββββββββββββ
|
|
139
|
+
β AI Threat Analysis β
|
|
140
|
+
β (SQLi, XSS, etc.) β
|
|
141
|
+
ββββββββββββββββββββββ
|
|
142
|
+
|
|
143
|
+
1. **Intercept** β Captures request metadata (IP, URL, method, body).
|
|
144
|
+
2. **Redact** β Removes secrets before transmission.
|
|
145
|
+
3. **Dispatch** β Sends logs asynchronously in a background thread.
|
|
146
|
+
4. **Analyze** β Central AI scans for malicious patterns.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## π Sensitive Data Protection
|
|
151
|
+
|
|
152
|
+
The following keys are **automatically redacted**:
|
|
153
|
+
|
|
154
|
+
| Field | Redacted? |
|
|
155
|
+
|-------|-----------|
|
|
156
|
+
| `password` | β
Yes |
|
|
157
|
+
| `secret` | β
Yes |
|
|
158
|
+
| `token` | β
Yes |
|
|
159
|
+
| `key` | β
Yes |
|
|
160
|
+
| `cvv` | β
Yes |
|
|
161
|
+
| `api_key` | β
Yes |
|
|
162
|
+
|
|
163
|
+
Example:
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"email": "user@mail.com",
|
|
168
|
+
"password": "[REDACTED]"
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Your users' secrets **never leave your server in plaintext**.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## β‘ Performance Philosophy
|
|
177
|
+
|
|
178
|
+
* No request blocking
|
|
179
|
+
* No database writes on your server
|
|
180
|
+
* No logging delays
|
|
181
|
+
|
|
182
|
+
Ulinzilogs runs **beside** your app β not **inside** its critical path.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## π§© Package Structure
|
|
187
|
+
ulinzilogs/
|
|
188
|
+
βββ init.py
|
|
189
|
+
βββ middleware.py
|
|
190
|
+
βββ utils.py
|
|
191
|
+
βββ cli.py
|
|
192
|
+
βββ pyproject.toml
|
|
193
|
+
βββ README.md
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## π License
|
|
198
|
+
|
|
199
|
+
Copyright (c) 2026 PashyGeek. Licensed under the MIT License β see the LICENSE file for details.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ulinzilogs"
|
|
7
|
+
version = "0.1.1"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="PashyGeek", email="codyypashy@gmail.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "Security logging middleware for FastAPI, Flask, and Django"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.8"
|
|
14
|
+
dependencies = [
|
|
15
|
+
"requests>=2.28.0",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.optional-dependencies]
|
|
19
|
+
fastapi = ["starlette>=0.20.0"]
|
|
20
|
+
flask = ["flask>=2.0.0"]
|
|
21
|
+
django = ["django>=3.2"]
|
|
22
|
+
all = ["starlette>=0.20.0", "flask>=2.0.0", "django>=3.2"]
|
|
23
|
+
|
|
24
|
+
[project.scripts]
|
|
25
|
+
ulinzi-init = "ulinzilogs.cli:main"
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
"Homepage" = "https://github.com/PashyGeek/ulinzilogs"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
def main():
|
|
5
|
+
"""
|
|
6
|
+
Generates a unique project key and prints setup instructions.
|
|
7
|
+
"""
|
|
8
|
+
project_key = str(uuid.uuid4())
|
|
9
|
+
|
|
10
|
+
print("\n" + "="*60)
|
|
11
|
+
print(" π‘οΈ ULINZI LOGS PACKAGED INSTALLED SUCCESSFULLY! π‘οΈ")
|
|
12
|
+
print("="*60)
|
|
13
|
+
print(f"\nπ YOUR UNIQUE PROJECT KEY: {project_key}\n")
|
|
14
|
+
print("PLEASE SAVE THIS KEY! You will need it to configure your app.")
|
|
15
|
+
print("-" * 60)
|
|
16
|
+
|
|
17
|
+
print("\nπ NEXT STEPS:\n")
|
|
18
|
+
|
|
19
|
+
print("1οΈβ£ Django Configuration (settings.py):")
|
|
20
|
+
print(f" ULINZI_API_KEY = '{project_key}'")
|
|
21
|
+
print(" MIDDLEWARE = [")
|
|
22
|
+
print(" ...,")
|
|
23
|
+
print(" 'ulinzilogs.middleware.UlinziDjangoMiddleware',")
|
|
24
|
+
print(" ]")
|
|
25
|
+
|
|
26
|
+
print("\n2οΈβ£ FastAPI Configuration:")
|
|
27
|
+
print(" from ulinzilogs.middleware import UlinziFastAPIMiddleware")
|
|
28
|
+
print(" app = FastAPI()")
|
|
29
|
+
print(f" app.add_middleware(UlinziFastAPIMiddleware, api_key='{project_key}')")
|
|
30
|
+
|
|
31
|
+
print("\n3οΈβ£ Flask Configuration:")
|
|
32
|
+
print(" from ulinzilogs.middleware import UlinziFlaskMiddleware")
|
|
33
|
+
print(" app = Flask(__name__)")
|
|
34
|
+
print(f" UlinziFlaskMiddleware(app, api_key='{project_key}')")
|
|
35
|
+
|
|
36
|
+
print("\n" + "="*60 + "\n")
|
|
37
|
+
|
|
38
|
+
if __name__ == "__main__":
|
|
39
|
+
main()
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import threading
|
|
3
|
+
import requests
|
|
4
|
+
from .utils import redact_sensitive_data
|
|
5
|
+
|
|
6
|
+
# --- The "Universal" Core ---
|
|
7
|
+
class UlinziLogger:
|
|
8
|
+
def __init__(self, api_key: str, app_name: str = "Unknown App", ingest_url: str = None):
|
|
9
|
+
self.api_key = api_key
|
|
10
|
+
self.app_name = app_name
|
|
11
|
+
self.ingest_url = ingest_url or "https://api.ulinzilogs.com/v1/ingest"
|
|
12
|
+
|
|
13
|
+
def send_logs(self, payload: dict):
|
|
14
|
+
if not self.api_key:
|
|
15
|
+
return
|
|
16
|
+
payload["app_name"] = self.app_name
|
|
17
|
+
try:
|
|
18
|
+
headers = {
|
|
19
|
+
"X-API-KEY": self.api_key,
|
|
20
|
+
"Content-Type": "application/json"
|
|
21
|
+
}
|
|
22
|
+
requests.post(self.ingest_url, json=payload, headers=headers, timeout=5)
|
|
23
|
+
except:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
# --- 1. FastAPI / Starlette Flavor ---
|
|
27
|
+
try:
|
|
28
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
29
|
+
HAS_STARLETTE = True
|
|
30
|
+
except ImportError:
|
|
31
|
+
BaseHTTPMiddleware = object
|
|
32
|
+
HAS_STARLETTE = False
|
|
33
|
+
|
|
34
|
+
class UlinziFastAPIMiddleware(BaseHTTPMiddleware):
|
|
35
|
+
def __init__(self, app, api_key: str, app_name: str = None):
|
|
36
|
+
if not HAS_STARLETTE:
|
|
37
|
+
raise ImportError(
|
|
38
|
+
"UlinziFastAPIMiddleware requires Starlette. "
|
|
39
|
+
"Install it with: pip install ulinzilogs[fastapi]"
|
|
40
|
+
)
|
|
41
|
+
super().__init__(app)
|
|
42
|
+
self.logger = UlinziLogger(api_key, app_name or "FastAPI-App")
|
|
43
|
+
|
|
44
|
+
async def dispatch(self, request, call_next):
|
|
45
|
+
log_data = {
|
|
46
|
+
"ip": request.client.host,
|
|
47
|
+
"method": request.method,
|
|
48
|
+
"url": str(request.url),
|
|
49
|
+
"host": request.headers.get("host"),
|
|
50
|
+
"user_agent": request.headers.get("user-agent"),
|
|
51
|
+
"headers": {k: v for k, v in request.headers.items() if k.lower() not in ("authorization", "cookie")}
|
|
52
|
+
}
|
|
53
|
+
try:
|
|
54
|
+
body = await request.body()
|
|
55
|
+
if body:
|
|
56
|
+
request._body = body
|
|
57
|
+
log_data["body"] = redact_sensitive_data(json.loads(body))
|
|
58
|
+
except:
|
|
59
|
+
log_data["body"] = None
|
|
60
|
+
|
|
61
|
+
response = await call_next(request)
|
|
62
|
+
log_data["status_code"] = response.status_code
|
|
63
|
+
threading.Thread(target=self.logger.send_logs, args=(log_data,)).start()
|
|
64
|
+
return response
|
|
65
|
+
|
|
66
|
+
# --- 2. Flask Flavor ---
|
|
67
|
+
try:
|
|
68
|
+
from flask import request as flask_request
|
|
69
|
+
HAS_FLASK = True
|
|
70
|
+
except ImportError:
|
|
71
|
+
HAS_FLASK = False
|
|
72
|
+
|
|
73
|
+
class UlinziFlaskMiddleware:
|
|
74
|
+
def __init__(self, app, api_key: str, app_name: str = None):
|
|
75
|
+
if not HAS_FLASK:
|
|
76
|
+
raise ImportError(
|
|
77
|
+
"UlinziFlaskMiddleware requires Flask. "
|
|
78
|
+
"Install it with: pip install ulinzilogs[flask]"
|
|
79
|
+
)
|
|
80
|
+
self.app = app
|
|
81
|
+
self.logger = UlinziLogger(api_key, app_name or "Flask-App")
|
|
82
|
+
|
|
83
|
+
@app.after_request
|
|
84
|
+
def after_request(response):
|
|
85
|
+
log_data = {
|
|
86
|
+
"ip": flask_request.remote_addr,
|
|
87
|
+
"method": flask_request.method,
|
|
88
|
+
"url": flask_request.url,
|
|
89
|
+
"host": flask_request.host,
|
|
90
|
+
"user_agent": flask_request.headers.get("User-Agent"),
|
|
91
|
+
"headers": {k: v for k, v in flask_request.headers.items() if k.lower() not in ("authorization", "cookie")},
|
|
92
|
+
"status_code": response.status_code
|
|
93
|
+
}
|
|
94
|
+
if flask_request.is_json:
|
|
95
|
+
try:
|
|
96
|
+
log_data["body"] = redact_sensitive_data(flask_request.get_json())
|
|
97
|
+
except:
|
|
98
|
+
log_data["body"] = None
|
|
99
|
+
threading.Thread(target=self.logger.send_logs, args=(log_data,)).start()
|
|
100
|
+
return response
|
|
101
|
+
|
|
102
|
+
# --- 3. Django Flavor ---
|
|
103
|
+
class UlinziDjangoMiddleware:
|
|
104
|
+
def __init__(self, get_response):
|
|
105
|
+
self.get_response = get_response
|
|
106
|
+
try:
|
|
107
|
+
from django.conf import settings
|
|
108
|
+
self.api_key = getattr(settings, "ULINZI_API_KEY", None)
|
|
109
|
+
self.app_name = getattr(settings, "ULINZI_APP_NAME", "Django-App")
|
|
110
|
+
self.ingest_url = getattr(settings, "ULINZI_INGEST_URL", None) # FOR TESTING PURPOSES ONLY
|
|
111
|
+
except ImportError:
|
|
112
|
+
raise ImportError(
|
|
113
|
+
"UlinziDjangoMiddleware requires Django. "
|
|
114
|
+
"Install it with: pip install ulinzilogs[django]"
|
|
115
|
+
)
|
|
116
|
+
self.logger = UlinziLogger(self.api_key, self.app_name, self.ingest_url)
|
|
117
|
+
|
|
118
|
+
def __call__(self, request):
|
|
119
|
+
log_data = {
|
|
120
|
+
"ip": request.META.get('REMOTE_ADDR'),
|
|
121
|
+
"method": request.method,
|
|
122
|
+
"url": request.build_absolute_uri(),
|
|
123
|
+
"host": request.get_host(),
|
|
124
|
+
"user_agent": request.META.get('HTTP_USER_AGENT'),
|
|
125
|
+
"headers": {k: v for k, v in request.headers.items() if k.lower() not in ("authorization", "cookie")}
|
|
126
|
+
}
|
|
127
|
+
if request.body:
|
|
128
|
+
try:
|
|
129
|
+
log_data["body"] = redact_sensitive_data(json.loads(request.body))
|
|
130
|
+
except:
|
|
131
|
+
log_data["body"] = None
|
|
132
|
+
|
|
133
|
+
response = self.get_response(request)
|
|
134
|
+
log_data["status_code"] = response.status_code
|
|
135
|
+
threading.Thread(target=self.logger.send_logs, args=(log_data,)).start()
|
|
136
|
+
return response
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
def redact_sensitive_data(data: Any) -> Any:
|
|
5
|
+
"""
|
|
6
|
+
Recursively scans dictionaries/lists and replaces sensitive
|
|
7
|
+
values with [REDACTED].
|
|
8
|
+
"""
|
|
9
|
+
sensitive_keys = {'password', 'secret', 'token', 'key', 'cvv', 'api_key'}
|
|
10
|
+
|
|
11
|
+
if isinstance(data, dict):
|
|
12
|
+
return {
|
|
13
|
+
k: ("[REDACTED]" if k.lower() in sensitive_keys else redact_sensitive_data(v))
|
|
14
|
+
for k, v in data.items()
|
|
15
|
+
}
|
|
16
|
+
elif isinstance(data, list):
|
|
17
|
+
return [redact_sensitive_data(item) for item in data]
|
|
18
|
+
|
|
19
|
+
return data
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ulinzilogs
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Security logging middleware for FastAPI, Flask, and Django
|
|
5
|
+
Author-email: PashyGeek <codyypashy@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/PashyGeek/ulinzilogs
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: requests>=2.28.0
|
|
10
|
+
Provides-Extra: fastapi
|
|
11
|
+
Requires-Dist: starlette>=0.20.0; extra == "fastapi"
|
|
12
|
+
Provides-Extra: flask
|
|
13
|
+
Requires-Dist: flask>=2.0.0; extra == "flask"
|
|
14
|
+
Provides-Extra: django
|
|
15
|
+
Requires-Dist: django>=3.2; extra == "django"
|
|
16
|
+
Provides-Extra: all
|
|
17
|
+
Requires-Dist: starlette>=0.20.0; extra == "all"
|
|
18
|
+
Requires-Dist: flask>=2.0.0; extra == "all"
|
|
19
|
+
Requires-Dist: django>=3.2; extra == "all"
|
|
20
|
+
|
|
21
|
+
# Ulinzilogs π‘οΈ
|
|
22
|
+
|
|
23
|
+
> **Lightweight, nonβblocking security logging middleware for Python web apps**
|
|
24
|
+
> Built for developers who want **visibility without performance pain**.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## π What is Ulinzilogs?
|
|
29
|
+
|
|
30
|
+
**Ulinzilogs** is a securityβfocused middleware that silently watches incoming HTTP requests in your Python web application, **redacts sensitive data**, and **ships sanitized logs** to the Ulinzi Security API for **AIβpowered threat analysis**.
|
|
31
|
+
|
|
32
|
+
Think of it as:
|
|
33
|
+
|
|
34
|
+
> π *A security camera for your API β always on, never in the way.*
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## β¨ Key Features
|
|
39
|
+
|
|
40
|
+
* π **NonβBlocking by Design** β Uses background threads so your request/response cycle stays fast.
|
|
41
|
+
* π **Automatic Data Redaction** β Sensitive fields are replaced with `[REDACTED]` before leaving your server.
|
|
42
|
+
* π **MultiβFramework Support** β Works out of the box with FastAPI, Flask, and Django.
|
|
43
|
+
* π§Ή **Clean & Safe Headers** β Excludes `Authorization` and `Cookie` headers automatically.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## π¦ Installation
|
|
48
|
+
|
|
49
|
+
Install only what your stack needs:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# FastAPI / Starlette
|
|
53
|
+
pip install ulinzilogs[fastapi]
|
|
54
|
+
|
|
55
|
+
# Flask
|
|
56
|
+
pip install ulinzilogs[flask]
|
|
57
|
+
|
|
58
|
+
# Django
|
|
59
|
+
pip install ulinzilogs[django]
|
|
60
|
+
|
|
61
|
+
# All frameworks
|
|
62
|
+
pip install ulinzilogs[all]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## π Getting Your Project Key
|
|
68
|
+
|
|
69
|
+
After installing, run:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
ulinzi-init
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This generates a **unique Project Key** for your app and prints setup instructions.
|
|
76
|
+
|
|
77
|
+
> β οΈ **Important:** This key is generated locally on your machine.
|
|
78
|
+
> To activate it, copy the key and register it at **[ulinzilogs.com](https://ulinzilogs.com)**.
|
|
79
|
+
> Once registered, your dashboard will start receiving logs from your app.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## βοΈ Setup & Usage
|
|
84
|
+
|
|
85
|
+
### π’ FastAPI
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from fastapi import FastAPI
|
|
89
|
+
from ulinzilogs import UlinziFastAPIMiddleware
|
|
90
|
+
|
|
91
|
+
app = FastAPI()
|
|
92
|
+
|
|
93
|
+
app.add_middleware(
|
|
94
|
+
UlinziFastAPIMiddleware,
|
|
95
|
+
api_key="YOUR_ULINZI_KEY"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
@app.get("/")
|
|
99
|
+
async def root():
|
|
100
|
+
return {"message": "Protected by Ulinzi"}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### π΅ Flask
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from flask import Flask
|
|
109
|
+
from ulinzilogs import UlinziFlaskMiddleware
|
|
110
|
+
|
|
111
|
+
app = Flask(__name__)
|
|
112
|
+
UlinziFlaskMiddleware(app, api_key="YOUR_ULINZI_KEY")
|
|
113
|
+
|
|
114
|
+
@app.route("/")
|
|
115
|
+
def hello():
|
|
116
|
+
return "Protected by Ulinzi"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### π£ Django
|
|
122
|
+
|
|
123
|
+
In `settings.py`:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
MIDDLEWARE = [
|
|
127
|
+
# ... other middleware
|
|
128
|
+
'ulinzilogs.UlinziDjangoMiddleware',
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
ULINZI_API_KEY = "YOUR_ULINZI_KEY"
|
|
132
|
+
ULINZI_APP_NAME = "MyDjangoApp" # optional label shown in dashboard
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## π§ How Ulinzilogs Works
|
|
138
|
+
Client Request
|
|
139
|
+
β
|
|
140
|
+
βΌ
|
|
141
|
+
βββββββββββββββββ
|
|
142
|
+
β Middleware β
|
|
143
|
+
ββββββββ¬βββββββββ
|
|
144
|
+
β
|
|
145
|
+
βΌ
|
|
146
|
+
ββββββββββββββββββββββ
|
|
147
|
+
β Redact Sensitive β
|
|
148
|
+
β Fields & Headers β
|
|
149
|
+
ββββββββ¬ββββββββββββββ
|
|
150
|
+
β
|
|
151
|
+
βΌ (Background Thread)
|
|
152
|
+
ββββββββββββββββββββββ
|
|
153
|
+
β Send to Ulinzi β
|
|
154
|
+
β Security API β
|
|
155
|
+
ββββββββ¬ββββββββββββββ
|
|
156
|
+
β
|
|
157
|
+
βΌ
|
|
158
|
+
ββββββββββββββββββββββ
|
|
159
|
+
β AI Threat Analysis β
|
|
160
|
+
β (SQLi, XSS, etc.) β
|
|
161
|
+
ββββββββββββββββββββββ
|
|
162
|
+
|
|
163
|
+
1. **Intercept** β Captures request metadata (IP, URL, method, body).
|
|
164
|
+
2. **Redact** β Removes secrets before transmission.
|
|
165
|
+
3. **Dispatch** β Sends logs asynchronously in a background thread.
|
|
166
|
+
4. **Analyze** β Central AI scans for malicious patterns.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## π Sensitive Data Protection
|
|
171
|
+
|
|
172
|
+
The following keys are **automatically redacted**:
|
|
173
|
+
|
|
174
|
+
| Field | Redacted? |
|
|
175
|
+
|-------|-----------|
|
|
176
|
+
| `password` | β
Yes |
|
|
177
|
+
| `secret` | β
Yes |
|
|
178
|
+
| `token` | β
Yes |
|
|
179
|
+
| `key` | β
Yes |
|
|
180
|
+
| `cvv` | β
Yes |
|
|
181
|
+
| `api_key` | β
Yes |
|
|
182
|
+
|
|
183
|
+
Example:
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"email": "user@mail.com",
|
|
188
|
+
"password": "[REDACTED]"
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Your users' secrets **never leave your server in plaintext**.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## β‘ Performance Philosophy
|
|
197
|
+
|
|
198
|
+
* No request blocking
|
|
199
|
+
* No database writes on your server
|
|
200
|
+
* No logging delays
|
|
201
|
+
|
|
202
|
+
Ulinzilogs runs **beside** your app β not **inside** its critical path.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## π§© Package Structure
|
|
207
|
+
ulinzilogs/
|
|
208
|
+
βββ init.py
|
|
209
|
+
βββ middleware.py
|
|
210
|
+
βββ utils.py
|
|
211
|
+
βββ cli.py
|
|
212
|
+
βββ pyproject.toml
|
|
213
|
+
βββ README.md
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## π License
|
|
218
|
+
|
|
219
|
+
Copyright (c) 2026 PashyGeek. Licensed under the MIT License β see the LICENSE file for details.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
ulinzilogs/__init__.py
|
|
4
|
+
ulinzilogs/cli.py
|
|
5
|
+
ulinzilogs/middleware.py
|
|
6
|
+
ulinzilogs/utils.py
|
|
7
|
+
ulinzilogs.egg-info/PKG-INFO
|
|
8
|
+
ulinzilogs.egg-info/SOURCES.txt
|
|
9
|
+
ulinzilogs.egg-info/dependency_links.txt
|
|
10
|
+
ulinzilogs.egg-info/entry_points.txt
|
|
11
|
+
ulinzilogs.egg-info/requires.txt
|
|
12
|
+
ulinzilogs.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ulinzilogs
|