django-restit 4.2.78__py3-none-any.whl → 4.2.83__py3-none-any.whl
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.
- account/models/member.py +2 -2
- account/models/notify.py +5 -1
- auditlog/migrations/0002_alter_persistentlog_session.py +20 -0
- {django_restit-4.2.78.dist-info → django_restit-4.2.83.dist-info}/METADATA +1 -1
- {django_restit-4.2.78.dist-info → django_restit-4.2.83.dist-info}/RECORD +18 -14
- inbox/migrations/0005_alter_mailbox_state.py +18 -0
- incident/migrations/0015_rule_title_template_alter_incident_state.py +23 -0
- incident/models/event.py +9 -2
- incident/models/ossec.py +2 -0
- incident/models/rules.py +2 -0
- incident/parsers/ossec.py +258 -176
- incident/rpc.py +11 -7
- location/models/ip.py +2 -0
- location/providers/iplookup/__init__.py +5 -1
- rest/models/base.py +4 -0
- wiki/migrations/0002_alter_pagemedia_entry.py +19 -0
- {django_restit-4.2.78.dist-info → django_restit-4.2.83.dist-info}/LICENSE.md +0 -0
- {django_restit-4.2.78.dist-info → django_restit-4.2.83.dist-info}/WHEEL +0 -0
account/models/member.py
CHANGED
@@ -224,11 +224,11 @@ class Member(User, RestModel, MetaDataModel):
|
|
224
224
|
|
225
225
|
@property
|
226
226
|
def force_single_session(self):
|
227
|
-
return self.hasPermission("force_single_session")
|
227
|
+
return self.hasPermission("force_single_session", ignore_su=True)
|
228
228
|
|
229
229
|
@property
|
230
230
|
def email_disabled(self):
|
231
|
-
return self.hasPermission("email_disabled")
|
231
|
+
return self.hasPermission("email_disabled", ignore_su=True)
|
232
232
|
|
233
233
|
@property
|
234
234
|
def has_totp(self):
|
account/models/notify.py
CHANGED
@@ -130,6 +130,8 @@ class NotificationRecord(models.Model, RestModel):
|
|
130
130
|
|
131
131
|
@classmethod
|
132
132
|
def canSend(cls):
|
133
|
+
if not settings.get("THROTTLE_EMAILS", False):
|
134
|
+
return True
|
133
135
|
max_emails_per_minute = settings.get("MAX_EMAILS_PER_MINUTE", 30)
|
134
136
|
last_email = NotificationRecord.objects.filter(state=1).last()
|
135
137
|
now = datetime.now()
|
@@ -206,7 +208,7 @@ class NotificationRecord(models.Model, RestModel):
|
|
206
208
|
|
207
209
|
@classmethod
|
208
210
|
def _notifyViaEmail(cls, member, subject, message, template, context,
|
209
|
-
attachments, from_email=
|
211
|
+
attachments, from_email=settings.DEFAULT_FROM_EMAIL):
|
210
212
|
# lets verify the db is working
|
211
213
|
if template:
|
212
214
|
if context is None:
|
@@ -215,6 +217,8 @@ class NotificationRecord(models.Model, RestModel):
|
|
215
217
|
context["body"] = message
|
216
218
|
context["unsubscribe_token"] = member.getUUID()
|
217
219
|
message = inbox.utils.renderTemplate(template, context)
|
220
|
+
if from_email is None:
|
221
|
+
from_email = settings.DEFAULT_FROM_EMAIL
|
218
222
|
|
219
223
|
nr = NotificationMemberRecord(member=member, to_addr=member.email)
|
220
224
|
email_record = NotificationRecord(
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Generated by Django 4.2.11 on 2024-05-13 02:34
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
import django.db.models.deletion
|
5
|
+
|
6
|
+
|
7
|
+
class Migration(migrations.Migration):
|
8
|
+
|
9
|
+
dependencies = [
|
10
|
+
('sessionlog', '0001_initial'),
|
11
|
+
('auditlog', '0001_initial'),
|
12
|
+
]
|
13
|
+
|
14
|
+
operations = [
|
15
|
+
migrations.AlterField(
|
16
|
+
model_name='persistentlog',
|
17
|
+
name='session',
|
18
|
+
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='sessionlog.sessionlog'),
|
19
|
+
),
|
20
|
+
]
|
@@ -28,9 +28,9 @@ account/models/device.py,sha256=TloXvvrx3khF3BeGFuVYn6DhXjOW0AMZb4F9Fl5nBII,5491
|
|
28
28
|
account/models/feeds.py,sha256=vI7fG4ASY1M0Zjke24RdnfDcuWeATl_yR_25jPmT64g,2011
|
29
29
|
account/models/group.py,sha256=iDD_oSgswKV_t_gXZuVK80MvICrZZqdANm2jtGtOFy8,21985
|
30
30
|
account/models/legacy.py,sha256=zYdtv4LC0ooxPVqWM-uToPwV-lYWQLorSE6p6yn1xDw,2720
|
31
|
-
account/models/member.py,sha256=
|
31
|
+
account/models/member.py,sha256=fzSVVAdbUa1knp1O4JTnYZFYRas7-zDZaOPjZAMCC1Q,52992
|
32
32
|
account/models/membership.py,sha256=90EpAhOsGaqphDAkONP6j_qQ0OWSRaQsI8H7E7fgMkE,9249
|
33
|
-
account/models/notify.py,sha256=
|
33
|
+
account/models/notify.py,sha256=iAq8tjyqouUelYgsMhWlchYmEAuCsKAyNIr5F8_xUeU,15258
|
34
34
|
account/models/passkeys.py,sha256=TJxITUi4DT4_1tW2K7ZlOcRjJuMVl2NtKz7pKQU8-Tw,1516
|
35
35
|
account/models/session.py,sha256=ELkWjB_2KXQvPtRPrvuGJpJsqrxCQX_4J53SbqGz_2U,3737
|
36
36
|
account/models/settings.py,sha256=gOyRWBVd3BQpjfj_hJPtqX3H46ztyRAFxBrPbv11lQg,2137
|
@@ -65,6 +65,7 @@ auditlog/cloudwatch.py,sha256=R-B_ByVM3We26YnDoFYIQeWV31CUyS63QTojRAkfWa8,2805
|
|
65
65
|
auditlog/decorators.py,sha256=ZoIv0fhZjxtMEV15NcKijW4xPF5UEScPna60zB3TxZo,6553
|
66
66
|
auditlog/middleware.py,sha256=Q4bXg8rnm8y2fMnAsN6ha3Fz6TW8jIzLnvpu4H9SpWE,1537
|
67
67
|
auditlog/migrations/0001_initial.py,sha256=X171gKQZIaTO9FGNG1yKTjGSZS0ZjZj5gvimF9-_kks,3309
|
68
|
+
auditlog/migrations/0002_alter_persistentlog_session.py,sha256=DkkcIobbHdbniKg5bOlRmiF-Nc4hX55Y6KuQySrCcJ8,541
|
68
69
|
auditlog/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
69
70
|
auditlog/models.py,sha256=skDAiuzR4chC-WNIaH2nm_VVcbnDD6ZtUxBwhk7UY8U,16517
|
70
71
|
auditlog/periodic.py,sha256=AUhDeVsZtC47BJ-lklvYEegHoxAzj1RpIvRFSsM7g5E,363
|
@@ -79,6 +80,7 @@ inbox/migrations/0001_initial.py,sha256=P1OmbSHZGhj3wVBdFKWEzNrPdbyKzR9fFBXP8rhX
|
|
79
80
|
inbox/migrations/0002_alter_message_cc.py,sha256=dsnDHCs1-dFZfSEWJmufBOs5gvNbI7u99kru6fVas0Y,380
|
80
81
|
inbox/migrations/0003_attachment_content_type.py,sha256=dh_km90V6R3O0-N2oNTWhWLZZ96MylRgDY7Poua9CZ8,416
|
81
82
|
inbox/migrations/0004_mailtemplate.py,sha256=yV51UdsRWmKC5Dy34-h2bXBeYeFtjoWQ7kOw7cuYCQo,1140
|
83
|
+
inbox/migrations/0005_alter_mailbox_state.py,sha256=trr-CCLupHQ7e-tjJK08LACdxhCApGMNBTOeWFcyXnI,393
|
82
84
|
inbox/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
83
85
|
inbox/models/__init__.py,sha256=yARvP31nhJGLjqP-U_ONi2OLjiTUFspdH0AlKynt4Y8,174
|
84
86
|
inbox/models/bounce.py,sha256=3b_pCKH3gwb3NE8I1XlVI6JeoVmobZyKidsILH-jIRg,2881
|
@@ -106,17 +108,18 @@ incident/migrations/0011_ticket.py,sha256=Ml5E_Qi4Z0MD89fetoOFOL3rPlVQdjaaDCcFBf
|
|
106
108
|
incident/migrations/0012_rule_match_by.py,sha256=PGclGnnc_8JEsJZ8znoXm-iAC6Y0i2WM6C2cmFgdKlA,372
|
107
109
|
incident/migrations/0013_rulecheck_is_required.py,sha256=cL7tOj5XGPpKd2f5BojIKfNJeDB1IL-jGRU6-g-Co5o,387
|
108
110
|
incident/migrations/0014_event_group_alter_rulecheck_index.py,sha256=v3gm5k0LVoas27qUDOt7el7YtK4yjFVLeEpuFUCoXaQ,724
|
111
|
+
incident/migrations/0015_rule_title_template_alter_incident_state.py,sha256=FPUDhFwqBC39EjeknRT7BPddEf6ExCjsXVb9LMqIn3U,687
|
109
112
|
incident/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
110
113
|
incident/models/__init__.py,sha256=NMphuhb0RTMf7Ov4QkNv7iv6_I8Wtr3xQ54yjX_a31M,209
|
111
|
-
incident/models/event.py,sha256=
|
114
|
+
incident/models/event.py,sha256=Dw6fUi2tbLeA_ZRDcvGQNFkCkMGMBdtNeaLikXdAyE8,7769
|
112
115
|
incident/models/incident.py,sha256=HPbi6J9qm7_-FMjnDUPV9NcbmP_60WU-IO9HJSpoLTY,19360
|
113
|
-
incident/models/ossec.py,sha256=
|
114
|
-
incident/models/rules.py,sha256=
|
116
|
+
incident/models/ossec.py,sha256=eUDRGawzuLWobKEVGKfdZisDnyjS_Hlxi0T_GCSLCCI,2252
|
117
|
+
incident/models/rules.py,sha256=aRkJ0ZnTv87nAUC1sHVkPExfb3OJ8fgHQIhnCIpIbhQ,7001
|
115
118
|
incident/models/ticket.py,sha256=S3kqGQpYLE6Y4M9IKu_60sgW-f592xNr8uufqHnvDoU,2302
|
116
119
|
incident/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
117
|
-
incident/parsers/ossec.py,sha256=
|
120
|
+
incident/parsers/ossec.py,sha256=jyJmNBwnQS1tjZMwYhslnCpZviCHXnozv88BPT-ytCw,11592
|
118
121
|
incident/periodic.py,sha256=eX1rQK6v65A9ugofTvJPSmAWei6C-3EYgzCMuGZ03jM,381
|
119
|
-
incident/rpc.py,sha256=
|
122
|
+
incident/rpc.py,sha256=viJt873b8T8SiAq10EM57lF8g7ghyj3ymdkaXzh2Ass,8181
|
120
123
|
incident/templates/email/incident_change.html,sha256=tQYphypwLukkVdwH0TB2Szz2VEJ7GnsfRS3_ZJ-MYeE,13895
|
121
124
|
incident/templates/email/incident_msg.html,sha256=MZdKhTddUF2MpiH8Z3RTQEmW_ko1n3ajeZ11KLtiLlU,13780
|
122
125
|
incident/templates/email/incident_new.html,sha256=W6nwFQROnyDfMlXub8s02ws4hGnJp16pfgp9xTm_aEc,15185
|
@@ -132,12 +135,12 @@ location/migrations/0004_remove_address_modified_by_address_group_and_more.py,sh
|
|
132
135
|
location/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
133
136
|
location/models/__init__.py,sha256=rZhldkoKmoJQXjBAK1IIQn7K_OOJvFtIGOGVl_szqbE,230
|
134
137
|
location/models/address.py,sha256=wl0bToZ6VrJP923IIfzWqZY9xyKgla6A-uzj8jQFGRI,3149
|
135
|
-
location/models/ip.py,sha256=
|
138
|
+
location/models/ip.py,sha256=Bl-OlwEXGvKYvYSDBSsnQkeAi4ZTKs1mDt3ddc5rq80,6039
|
136
139
|
location/models/legacy.py,sha256=8ROsUSZrjGQkUyXeJvoxPdKAWaKfUH-AL9TIeJb7krg,1994
|
137
140
|
location/models/location.py,sha256=01dJPJecbp5orExsIGWOsBC_KkwFRIW0rGDIwyx1r0w,2316
|
138
141
|
location/models/track.py,sha256=OdhRL1KVXlPcZkp4S6QpKc7Ctoth8VjwHs_dlZ8XHI4,1474
|
139
142
|
location/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
140
|
-
location/providers/iplookup/__init__.py,sha256=
|
143
|
+
location/providers/iplookup/__init__.py,sha256=I0K0HZrluCsBZ1TlGGKnyavEDZ3mT-xfE7Dtq2k-F9k,790
|
141
144
|
location/providers/iplookup/abstractapi.py,sha256=gY8eqpjEasZtiBC6nNu960ZGL96FVwNS2JoZuP1GBO4,2419
|
142
145
|
location/providers/iplookup/extremeip.py,sha256=QNRGhwXXsOuJL2M-xiI2pFN_6LP2HkqSUpFosu5Q04M,1345
|
143
146
|
location/providers/iplookup/geoplugin.py,sha256=RK_6McxHYlVVMVdJ2rCafw-kqMfzMm3g_tJjBwcKXYg,2121
|
@@ -401,7 +404,7 @@ rest/middleware/request.py,sha256=JchRNy5L-bGd-7h-KFYekGRvREe2eCkZXKOYqIkP2hI,41
|
|
401
404
|
rest/middleware/session.py,sha256=zHSoQpIzRLmpqr_JvW406wzpvU3W3gDbm5JhtzLAMlE,10240
|
402
405
|
rest/middleware/session_store.py,sha256=1nSdeXK8PyuYgGgIufqrS6j6QpIrQ7zbMNT0ol75e6U,1901
|
403
406
|
rest/models/__init__.py,sha256=M8pvFDq-WCF-QcM58X7pMufYYe0aaQ3U0PwGe9TKbbY,130
|
404
|
-
rest/models/base.py,sha256=
|
407
|
+
rest/models/base.py,sha256=bPcTeCX7KvhkcVMKEyLmu9eYc7ccJGkIP1n3cctJPVE,69675
|
405
408
|
rest/models/cacher.py,sha256=eKz8TINVhWEqKhJGMsRkKZTtBUIv5rN3NHbZwOC56Uk,578
|
406
409
|
rest/models/metadata.py,sha256=65GvfFbc26_7wJz8qEAzU7fEOZWVz0ttO5j5m_gs4hk,12860
|
407
410
|
rest/net.py,sha256=LcB2QV6VNRtsSdmiQvYZgwQUDwOPMn_VBdRiZ6OpI-I,2974
|
@@ -472,6 +475,7 @@ telephony/phone_util.py,sha256=5NwSBnwBEC3EaeSeN42ggBiAQ00Ujvr6CepDjXLsCyw,5067
|
|
472
475
|
telephony/rpc.py,sha256=PXPDFvgoXkCKlfMzIbt6lYZPay3fcveNj2X4Pjby7p4,3473
|
473
476
|
wiki/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
474
477
|
wiki/migrations/0001_initial.py,sha256=9jvUyjrbJrbDilRnwzQUPhPV8Xi_olEPBk_N0nycvM0,3606
|
478
|
+
wiki/migrations/0002_alter_pagemedia_entry.py,sha256=9CUnfvBmj0D4akCkux7HFuXgw9B9avE8V-iMCm5cjds,485
|
475
479
|
wiki/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
476
480
|
wiki/models/__init__.py,sha256=jE-9r_Hqpyo7ysKu9BschXOn5Zg34wUt894GwJpxA28,132
|
477
481
|
wiki/models/faq.py,sha256=nvcEFerllQKT61kIYlasvZzRKwpXyfmQpiqkpHP1V1o,1745
|
@@ -502,7 +506,7 @@ ws4redis/servers/uwsgi.py,sha256=VyhoCI1DnVFqBiJYHoxqn5Idlf6uJPHvfBKgkjs34mo,172
|
|
502
506
|
ws4redis/settings.py,sha256=K0yBiLUuY81iDM4Yr-k8hbvjn5VVHu5zQhmMK8Dtz0s,1536
|
503
507
|
ws4redis/utf8validator.py,sha256=S0OlfjeGRP75aO6CzZsF4oTjRQAgR17OWE9rgZdMBZA,5122
|
504
508
|
ws4redis/websocket.py,sha256=R0TUyPsoVRD7Y_oU7w2I6NL4fPwiz5Vl94-fUkZgLHA,14848
|
505
|
-
django_restit-4.2.
|
506
|
-
django_restit-4.2.
|
507
|
-
django_restit-4.2.
|
508
|
-
django_restit-4.2.
|
509
|
+
django_restit-4.2.83.dist-info/LICENSE.md,sha256=VHN4hhEeVOoFjtG-5fVv4jesA4SWi0Z-KgOzzN6a1ps,1068
|
510
|
+
django_restit-4.2.83.dist-info/METADATA,sha256=rbSXb9-b2DozTJTvyuPOVSB8x-7l1fWIhCqtR013RVg,7645
|
511
|
+
django_restit-4.2.83.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
512
|
+
django_restit-4.2.83.dist-info/RECORD,,
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Generated by Django 4.2.11 on 2024-05-13 02:34
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
('inbox', '0004_mailtemplate'),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.AlterField(
|
14
|
+
model_name='mailbox',
|
15
|
+
name='state',
|
16
|
+
field=models.IntegerField(db_index=True, default=1),
|
17
|
+
),
|
18
|
+
]
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Generated by Django 4.2.11 on 2024-05-13 02:34
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
('incident', '0014_event_group_alter_rulecheck_index'),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.AddField(
|
14
|
+
model_name='rule',
|
15
|
+
name='title_template',
|
16
|
+
field=models.CharField(default=None, max_length=200, null=True),
|
17
|
+
),
|
18
|
+
migrations.AlterField(
|
19
|
+
model_name='incident',
|
20
|
+
name='state',
|
21
|
+
field=models.IntegerField(choices=[(0, 'new'), (1, 'opened'), (2, 'paused'), (3, 'ignored'), (4, 'resolved'), (5, 'pending')], default=0),
|
22
|
+
),
|
23
|
+
]
|
incident/models/event.py
CHANGED
@@ -73,6 +73,7 @@ class Event(JSONMetaData, rm.RestModel):
|
|
73
73
|
|
74
74
|
level = models.IntegerField(default=0, db_index=True)
|
75
75
|
category = models.CharField(max_length=124, db_index=True)
|
76
|
+
# code = models.IntegerField(default=0, db_index=True)
|
76
77
|
|
77
78
|
group = models.ForeignKey(
|
78
79
|
"account.Group", on_delete=models.SET_NULL,
|
@@ -154,7 +155,7 @@ class Event(JSONMetaData, rm.RestModel):
|
|
154
155
|
incident = None
|
155
156
|
action_count = 0
|
156
157
|
if hit_rule is not None:
|
157
|
-
logger.error(f"RULE HIT: {hit_rule.name}")
|
158
|
+
# logger.error(f"RULE HIT: {hit_rule.name}")
|
158
159
|
priority = hit_rule.priority
|
159
160
|
if hit_rule.action == "ignore":
|
160
161
|
self.save()
|
@@ -183,7 +184,13 @@ class Event(JSONMetaData, rm.RestModel):
|
|
183
184
|
if hit_rule is not None and hit_rule.action_after != 0:
|
184
185
|
incident.state = INCIDENT_STATE_PENDING
|
185
186
|
# TODO possibly make this smarter?
|
186
|
-
if
|
187
|
+
if hit_rule and hit_rule.title_template and "{" in hit_rule.title_template:
|
188
|
+
try:
|
189
|
+
incident.description = hit_rule.title_template.format(event=self)
|
190
|
+
except Exception:
|
191
|
+
logger.exception(hit_rule.title_template)
|
192
|
+
incident.description = self.description
|
193
|
+
elif self.category == "ossec":
|
187
194
|
incident.description = f"{self.hostname}: {self.description}"
|
188
195
|
else:
|
189
196
|
incident.description = self.description
|
incident/models/ossec.py
CHANGED
@@ -47,5 +47,7 @@ class ServerOssecAlert(models.Model, rm.RestModel):
|
|
47
47
|
title = models.CharField(max_length=200, blank=True, null=True, default=None)
|
48
48
|
geoip = models.ForeignKey("location.GeoIP", blank=True, null=True, default=None, on_delete=models.DO_NOTHING)
|
49
49
|
|
50
|
+
metadata = None
|
51
|
+
|
50
52
|
def __str__(self):
|
51
53
|
return f'{self.hostname}: {self.title}'
|
incident/models/rules.py
CHANGED
@@ -43,6 +43,7 @@ class Rule(models.Model, rm.RestModel):
|
|
43
43
|
"created",
|
44
44
|
"priority",
|
45
45
|
"name",
|
46
|
+
"title_template",
|
46
47
|
"category",
|
47
48
|
"priority",
|
48
49
|
"action",
|
@@ -63,6 +64,7 @@ class Rule(models.Model, rm.RestModel):
|
|
63
64
|
modified = models.DateTimeField(auto_now=True)
|
64
65
|
|
65
66
|
name = models.CharField(max_length=200)
|
67
|
+
title_template = models.CharField(max_length=200, default=None, null=True)
|
66
68
|
# the group the rule gets assigned to when triggered
|
67
69
|
group = models.ForeignKey("account.Group", on_delete=models.CASCADE, null=True, default=None)
|
68
70
|
# category allows us to limit running rules to only those with a category
|
incident/parsers/ossec.py
CHANGED
@@ -13,19 +13,47 @@ LEVEL_REMAP_BY_RULE = {
|
|
13
13
|
5710: 5
|
14
14
|
}
|
15
15
|
|
16
|
+
NGINX_PARSE_PATTERN = re.compile(
|
17
|
+
r'(?P<src_ip>\d+\.\d+\.\d+\.\d+) - - \[(?P<http_time>.+?)\] '
|
18
|
+
r'(?P<http_method>\w+) (?P<http_url>.+?) (?P<http_protocol>[\w/.]+) '
|
19
|
+
r'(?P<http_status>\d+) (?P<http_bytes>\d+) (?P<http_referer>.+?) '
|
20
|
+
r'(?P<user_agent>.+?) (?P<http_elapsed>\d\.\d{3})'
|
21
|
+
)
|
22
|
+
|
23
|
+
def parse_nginx_line(line):
|
24
|
+
if "\n" in line:
|
25
|
+
for l in line.split('\n'):
|
26
|
+
match = NGINX_PARSE_PATTERN.match(l)
|
27
|
+
if match:
|
28
|
+
return match.groupdict()
|
29
|
+
return None
|
30
|
+
match = NGINX_PARSE_PATTERN.match(line)
|
31
|
+
if match:
|
32
|
+
return match.groupdict()
|
33
|
+
return None
|
16
34
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
35
|
+
|
36
|
+
def removeNonAscii(input_str, replacement=''):
|
37
|
+
"""
|
38
|
+
Replace all non-ASCII characters and escaped byte sequences in the input string with a specified string.
|
39
|
+
|
40
|
+
Args:
|
41
|
+
input_str (str): The string to process.
|
42
|
+
replacement (str): The string to use as a replacement for non-ASCII characters and escaped byte sequences.
|
43
|
+
|
44
|
+
Returns:
|
45
|
+
str: The processed string with non-ASCII characters and byte sequences replaced.
|
46
|
+
"""
|
47
|
+
# Replace escaped byte sequences with the replacement string
|
48
|
+
cleaned_str = re.sub(r'\\x[0-9a-fA-F]{2}', replacement, input_str)
|
49
|
+
# Replace non-ASCII characters with the replacement string
|
50
|
+
return ''.join(char if (32 <= ord(char) < 128 or char in '\n\r\t') else f"<r{str(ord(char))}>" for char in cleaned_str)
|
23
51
|
|
24
52
|
|
25
53
|
def extractURL(text):
|
26
|
-
match = re.search(r"GET\s+(https?://[^\s]+)\s+HTTP/\d\.\d", text)
|
54
|
+
match = re.search(r"(GET|POST|DELETE|PUT)\s+(https?://[^\s]+)\s+HTTP/\d\.\d", text)
|
27
55
|
if match:
|
28
|
-
return match.group(
|
56
|
+
return match.group(2)
|
29
57
|
return None
|
30
58
|
|
31
59
|
|
@@ -36,6 +64,13 @@ def extractDomain(text):
|
|
36
64
|
return None
|
37
65
|
|
38
66
|
|
67
|
+
def extractIP(text):
|
68
|
+
match = re.search(r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b", text)
|
69
|
+
if match:
|
70
|
+
return match.group(1)
|
71
|
+
return None
|
72
|
+
|
73
|
+
|
39
74
|
def extractUrlPath(text):
|
40
75
|
match = re.search(r"https?://[^/]+(/[^?]*)", text)
|
41
76
|
if match:
|
@@ -43,185 +78,232 @@ def extractUrlPath(text):
|
|
43
78
|
return None
|
44
79
|
|
45
80
|
|
81
|
+
def extractUserAgent(text):
|
82
|
+
# this only works if the referrer is '-'
|
83
|
+
match = re.search(r"' - (.+?) \d+\.\d+ \d+'", text)
|
84
|
+
if match:
|
85
|
+
return match.group(1)
|
86
|
+
return None
|
87
|
+
|
88
|
+
|
46
89
|
def extractMetaData(alert):
|
47
|
-
|
48
|
-
if irule in [31301, 31302, 31303]:
|
49
|
-
patterns = {
|
50
|
-
"src_ip": re.compile(r"Src IP: (\S+)"),
|
51
|
-
"path": re.compile(r"request: (\S+ \S+)"),
|
52
|
-
"http_server": re.compile(r"server: (\S+)"),
|
53
|
-
"http_host": re.compile(r"host: (\S+)"),
|
54
|
-
"http_referrer": re.compile(r"referrer: (\S+)")
|
55
|
-
}
|
56
|
-
# Search for matches in the text
|
57
|
-
return {key: pattern.search(alert.text).group(1) for key, pattern in patterns.items() if pattern.search(alert.text)}
|
58
|
-
return {}
|
90
|
+
return parse_alert_metadata(alert)
|
59
91
|
|
60
92
|
|
61
|
-
|
62
|
-
|
93
|
+
DEFAULT_META_PATTERNS = {
|
94
|
+
"src_ip": re.compile(r"Src IP: (\S+)"),
|
95
|
+
"src_port": re.compile(r"Src Port: (\S+)"),
|
96
|
+
"user": re.compile(r"User: (\S+)"),
|
97
|
+
"path": re.compile(r"request: (\S+ \S+)"),
|
98
|
+
"http_server": re.compile(r"server: (\S+),"),
|
99
|
+
"http_host": re.compile(r"host: (\S+)"),
|
100
|
+
"http_referrer": re.compile(r"referrer: (\S+),"),
|
101
|
+
"client": re.compile(r"client: (\S+),"),
|
102
|
+
"upstream": re.compile(r"upstream: (\S+),")
|
103
|
+
}
|
104
|
+
|
105
|
+
|
106
|
+
def parse_alert_metadata(alert):
|
107
|
+
patterns = DEFAULT_META_PATTERNS
|
108
|
+
if alert.rule_id == "2501":
|
109
|
+
match = re.search(r'user (\w+) (\d+\.\d+\.\d+\.\d+) port (\d+)', alert.text)
|
110
|
+
if match:
|
111
|
+
return dict(username=match.group(1), src_ip=match.group(2), src_port=match.group(3))
|
112
|
+
elif alert.rule_id.startswith("311") or alert.rule_id in ["31516", "31508", "31516"]:
|
113
|
+
data = parse_nginx_line(alert.text)
|
114
|
+
if data:
|
115
|
+
return data
|
116
|
+
elif alert.rule_id in ["31301"]:
|
117
|
+
match = re.search(r'\((?P<error_code>\d+): (?P<error_message>.*?)\)', alert.text)
|
118
|
+
data = match_patterns(patterns, alert.text)
|
119
|
+
data["action"] = "error"
|
120
|
+
if match:
|
121
|
+
data.update(match.groupdict())
|
122
|
+
return data
|
123
|
+
elif alert.rule_id in ["31302", "31303"]:
|
124
|
+
match = re.search(r'\[(warn|crit|error)\].*?: (.*?),', alert.text)
|
125
|
+
data = match_patterns(patterns, alert.text)
|
126
|
+
|
127
|
+
if match:
|
128
|
+
data["action"] = match.group(1)
|
129
|
+
emsg = match.group(2)
|
130
|
+
if emsg[0] == "*":
|
131
|
+
emsg = emsg[emsg.find(' ')+1:]
|
132
|
+
data["error_message"] = emsg
|
133
|
+
return data
|
134
|
+
elif alert.rule_id == "551":
|
135
|
+
match = re.search(r"Integrity checksum changed for: '(\S+)'", alert.text)
|
136
|
+
if match:
|
137
|
+
return dict(filename=match.group(1), action="changed")
|
138
|
+
elif alert.rule_id == "554":
|
139
|
+
match = re.search(r"New file '(\S+)' added", alert.text)
|
140
|
+
if match:
|
141
|
+
return dict(filename=match.group(1), action="added")
|
142
|
+
elif alert.rule_id == "5402":
|
143
|
+
match = re.search(r'(?P<username>[\w-]+) : PWD=(?P<pwd>\S+) ; USER=(?P<user>\w+) ; COMMAND=(?P<command>.+)', alert.text)
|
144
|
+
if match:
|
145
|
+
return match.groupdict()
|
146
|
+
match = re.search(r'(?P<username>[\w-]+) : TTY=(?P<tty>\S+) ; PWD=(?P<pwd>\S+) ; USER=(?P<user>\w+) ; COMMAND=(?P<command>.+)', alert.text)
|
147
|
+
if match:
|
148
|
+
return match.groupdict()
|
149
|
+
elif alert.rule_id in ["5501", "5502"]:
|
150
|
+
match = re.search(r"session (?P<action>\S+) for user (?P<username>\S+)*", alert.text)
|
151
|
+
if match:
|
152
|
+
return match.groupdict()
|
153
|
+
elif alert.rule_id in ["5704", "5705"]:
|
154
|
+
match = re.search(r"(?P<src_ip>\d{1,3}(?:\.\d{1,3}){3}) port (?P<src_port>\d+)", alert.text)
|
155
|
+
if match:
|
156
|
+
return match.groupdict()
|
157
|
+
elif alert.rule_id == "5715":
|
158
|
+
match = re.search(r'Accepted publickey for (?P<username>\S+) from (?P<src_ip>\d+\.\d+\.\d+\.\d+) .*: (?P<ssh_key_type>\S+) (?P<ssh_signature>\S+)', alert.text)
|
159
|
+
if match:
|
160
|
+
return match.groupdict()
|
161
|
+
elif alert.rule_id == "2932":
|
162
|
+
match = re.search(r"Installed: (\S+)", alert.text)
|
163
|
+
if match:
|
164
|
+
return dict(package=match.group(1))
|
165
|
+
return match_patterns(patterns, alert.text)
|
166
|
+
|
167
|
+
def match_patterns(patterns, text):
|
168
|
+
# Search for matches in the text
|
169
|
+
return {key: pattern.search(text).group(1) for key, pattern in patterns.items() if pattern.search(text)}
|
170
|
+
|
171
|
+
|
172
|
+
def parse_alert_json(data):
|
63
173
|
try:
|
64
|
-
|
174
|
+
if isinstance(data, str):
|
175
|
+
data = objict.fromJSON(data.replace('\n', '\\n'))
|
65
176
|
except Exception:
|
66
177
|
data = objict.fromJSON(removeNonAscii(data))
|
67
178
|
for key in data:
|
68
179
|
data[key] = data[key].strip()
|
180
|
+
if data.text:
|
181
|
+
data.text = removeNonAscii(data.text)
|
182
|
+
return data
|
69
183
|
|
70
|
-
|
71
|
-
|
72
|
-
if
|
73
|
-
|
184
|
+
|
185
|
+
def ignore_alert(alert):
|
186
|
+
if alert.rule_id in IGNORE_RULES:
|
187
|
+
return True
|
188
|
+
if alert.rule_id == "510" and "/dev/.mount/utab" in alert.text:
|
189
|
+
return True
|
190
|
+
return False
|
191
|
+
|
192
|
+
|
193
|
+
def parse_alert_id(details):
|
194
|
+
match = re.search(r"Alert (\d+\.\d+):", details)
|
195
|
+
if match:
|
196
|
+
return match.group(1)
|
197
|
+
return ""
|
198
|
+
|
199
|
+
|
200
|
+
def parse_rule_details(details):
|
201
|
+
alert_id = parse_alert_id(details)
|
202
|
+
rule_pattern = r"Rule: (\d+) \(level (\d+)\) -> '([^']+)'"
|
203
|
+
match = re.search(rule_pattern, details)
|
204
|
+
if match:
|
205
|
+
return objict(
|
206
|
+
rid=int(match.group(1)), level=int(match.group(2)),
|
207
|
+
title=match.group(3), alert_id=alert_id)
|
208
|
+
return objict(alert_id=alert_id)
|
209
|
+
|
210
|
+
|
211
|
+
def parse_when(alert):
|
212
|
+
return datetime.utcfromtimestamp(int(alert.alert_id[:alert.alert_id.find(".")]))
|
213
|
+
|
214
|
+
|
215
|
+
def truncate_str(text, length):
|
216
|
+
if len(text) > length:
|
217
|
+
text = text[:length]
|
218
|
+
text = text[:text.rfind(' ')] + "..."
|
219
|
+
return text
|
220
|
+
|
221
|
+
|
222
|
+
def update_by_rule(data, geoip=None):
|
223
|
+
if data.rule_id == "2501":
|
224
|
+
data.title = f"SSH Auth Attempt {data.username}@{data.hostname} from {data.src_ip}"
|
225
|
+
elif data.rule_id == "2503" and data.src_ip:
|
226
|
+
data.title = f"SSH Auth Blocked from {data.src_ip}"
|
227
|
+
elif data.rule_id == "31101" and data.http_status:
|
228
|
+
data.title = f"Web {data.http_status} {data.http_method} {data.http_url} from {data.src_ip}"
|
229
|
+
elif data.rule_id == "31104" and data.http_status:
|
230
|
+
data.title = f"Web Attack {data.http_status} {data.http_method} {data.http_url} from {data.src_ip}"
|
231
|
+
elif data.rule_id == "31111" and data.http_status:
|
232
|
+
if geoip and geoip.isp:
|
233
|
+
data.title = f"No referrer for .js - {data.http_status} {data.http_method} {data.http_url} from {data.src_ip}({geoip.isp})"
|
234
|
+
else:
|
235
|
+
data.title = f"No referrer for .js - {data.http_status} {data.http_method} {data.http_url} from {data.src_ip}"
|
236
|
+
elif data.rule_id in ["31151", "31152", "31153"] and data.http_status:
|
237
|
+
url = truncate_str(data.http_url, 50)
|
238
|
+
data.title = f"Suspected Web Scan {url} from {data.src_ip}"
|
239
|
+
elif data.rule_id == "31120" and data.http_status:
|
240
|
+
url = truncate_str(data.http_url, 50)
|
241
|
+
data.title = f"Web Error {data.http_status} {data.http_method} {url} from {data.src_ip}"
|
242
|
+
elif data.rule_id.startswith("311") and data.http_status:
|
243
|
+
url = truncate_str(data.http_url, 50)
|
244
|
+
data.title = f"Web {data.http_status} {data.http_method} {url} from {data.src_ip}"
|
245
|
+
elif data.rule_id in ["31301", "31302", "31303"] and data.error_message:
|
246
|
+
if data.upstream and "ws/events" in data.upstream:
|
247
|
+
data.title = f"Websocket Error: {data.error_message} on {data.hostname}"
|
248
|
+
else:
|
249
|
+
emsg = truncate_str(data.error_message, 50)
|
250
|
+
data.title = f"Nginx {data.action}: {emsg} from {data.src_ip}"
|
251
|
+
elif data.rule_id == "31516" and data.http_url:
|
252
|
+
url = truncate_str(data.http_url, 50)
|
253
|
+
data.title = f"Web Suspicious {data.http_status} {data.http_method} {url} from {data.src_ip}"
|
254
|
+
elif data.rule_id == "533":
|
255
|
+
data.title = f"Network Open Port Change Detected on {data.hostname}"
|
256
|
+
elif data.rule_id == "5402":
|
257
|
+
cmd = truncate_str(data.command, 50)
|
258
|
+
data.title = f"Sudo(user: {data.user}) executed '{cmd}' on {data.hostname}"
|
259
|
+
elif data.rule_id in ["551", "554"] and data.filename:
|
260
|
+
name = truncate_str(data.filename, 50)
|
261
|
+
data.title = f"File {data.action.capitalize()} on {data.hostname}: {name}"
|
262
|
+
elif data.rule_id in ["5501", "5502"]:
|
263
|
+
if "sudo" in data.text:
|
264
|
+
data.title = f"Server Login {data.action} via sudo on {data.hostname}"
|
265
|
+
else:
|
266
|
+
data.title = f"Server Login {data.action} on {data.hostname}"
|
267
|
+
elif data.rule_id == "5715":
|
268
|
+
data.title = f"SSH Login Detected: {data.username}@{data.hostname} from {data.src_ip}"
|
269
|
+
elif data.rule_id == "2932" and data.package:
|
270
|
+
package = truncate_str(data.package, 60)
|
271
|
+
data.title = f"Package Installed on {data.hostname}: {package}"
|
272
|
+
elif data.src_ip and data.src_ip not in data.title:
|
273
|
+
data.title = f"{data.title} Source IP: {data.src_ip}"
|
274
|
+
if len(data.title) > 199:
|
275
|
+
data.title = data.title[:199]
|
276
|
+
|
277
|
+
def parse_incoming_alert(data):
|
278
|
+
alert = parse_alert_json(data)
|
279
|
+
if ignore_alert(alert):
|
74
280
|
return None
|
75
|
-
|
281
|
+
alert.update(parse_rule_details(alert.text))
|
282
|
+
if alert.title is None:
|
76
283
|
return None
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
alert = am.ServerOssecAlert(**data)
|
82
|
-
alert.when = datetime.utcfromtimestamp(int(data.alert_id[:data.alert_id.find(".")]))
|
83
|
-
# now lets parse the title
|
84
|
-
title = alert.text[alert.text.find("Rule:") + 5:]
|
85
|
-
# level to int
|
86
|
-
level = title[title.find('(level') + 7:]
|
87
|
-
alert.level = int(level[:level.find(')')].strip())
|
88
|
-
title = title[:title.find('\n')].strip()
|
89
|
-
pos = title.find("->")
|
90
|
-
if pos > 0:
|
91
|
-
title = title[pos + 2:]
|
92
|
-
alert.title = title
|
93
|
-
if alert.title.startswith("'"):
|
94
|
-
alert.title = alert.title[1:-1]
|
95
|
-
|
96
|
-
if data.hostname == "test":
|
97
|
-
if data.rule_id == "31120" or "Web server" in title:
|
98
|
-
return None
|
99
|
-
|
100
|
-
# helpers.log_print(title, alert.title)
|
101
|
-
# source ip (normally public ip of host)
|
102
|
-
pos = alert.text.find("Src IP:")
|
103
|
-
if pos > 1:
|
104
|
-
src_ip = alert.text[alert.text.find("Src IP:") + 7:]
|
105
|
-
alert.src_ip = src_ip[:src_ip.find('\n')].strip()
|
106
|
-
|
107
|
-
irule = int(alert.rule_id)
|
108
|
-
if irule == 5710:
|
109
|
-
m = re.search(r"Invalid user (\S+) from (\S+)", data.text)
|
110
|
-
if m and m.groups():
|
111
|
-
alert.username = m.group(1)
|
112
|
-
alert.src_ip = m.group(2).strip()
|
113
|
-
alert.title = f"Attempt to login with invalid user: {alert.username}"
|
114
|
-
else:
|
115
|
-
m = re.search(r"Invalid user from (\S+)", data.text)
|
116
|
-
if m and m.groups():
|
117
|
-
alert.username = "(empty string)"
|
118
|
-
alert.src_ip = m.group(1).strip()
|
119
|
-
elif irule == 2932:
|
120
|
-
m = re.search(r"Installed: (\S+)", data.text)
|
121
|
-
if m and m.groups():
|
122
|
-
package = m.group(1)
|
123
|
-
alert.title = "Yum Package Installed: {}".format(package)
|
124
|
-
elif irule == 551:
|
125
|
-
# Integrity checksum changed for: '/etc/ld.so.cache'
|
126
|
-
m = re.search(r"Integrity checksum changed for: '(\S+)'", data.text)
|
127
|
-
if m and m.groups():
|
128
|
-
action = m.group(1)
|
129
|
-
alert.title = "File Changed: {}".format(action)
|
130
|
-
elif irule == 5715:
|
131
|
-
m = re.search(r"Accepted publickey for (\S+).*ssh2: ([^\n\r]*)", data.text)
|
132
|
-
if m and m.groups():
|
133
|
-
ssh_sig = m.group(2)
|
134
|
-
if " " in ssh_sig:
|
135
|
-
kind, ssh_sig = ssh_sig.split(' ')
|
136
|
-
alert.level = 8
|
137
|
-
alert.username = m.group(1)
|
138
|
-
alert.ssh_sig = ssh_sig
|
139
|
-
alert.ssh_kind = kind
|
140
|
-
alert.title = f"SSH LOGIN:{alert.username}@{alert.hostname} from {alert.src_ip}"
|
141
|
-
# member = findUserBySshSig(ssh_sig)
|
142
|
-
# if member:
|
143
|
-
# alert.title = "SSH LOGIN user: {}".format(member.username)
|
144
|
-
elif irule == 5501 or irule == 5502:
|
145
|
-
# pam_unix(sshd:session): session opened for user git by (uid=0)
|
146
|
-
m = re.search(r"session (\S+) for user (\S+)*", data.text)
|
147
|
-
if m and m.groups():
|
148
|
-
alert.action = m.group(1)
|
149
|
-
alert.username = m.group(2)
|
150
|
-
alert.title = f"session {alert.action} for user {alert.username}"
|
151
|
-
elif irule == 5402:
|
152
|
-
# TTY=pts/0 ; PWD=/opt/mm_protector ; USER=root ; COMMAND=/sbin/iptables -F
|
153
|
-
m = re.search(r"sudo(?:\[\d+\])?:\s*(\S+).*?COMMAND=([^\n\r]*)", data.text)
|
154
|
-
# m = re.search(r"sudo:\s*(\S+).*COMMAND=([^\n\r]*)", data.text)
|
155
|
-
if m and m.groups():
|
156
|
-
alert.username = m.group(1)
|
157
|
-
alert.title = "sudo {}".format(m.group(2)).replace("#040", " ")
|
158
|
-
alert.level = 7
|
159
|
-
elif irule == 5706:
|
160
|
-
m = re.search(r"identification string from (\S+) port (\S+)", data.text)
|
161
|
-
if m and m.groups():
|
162
|
-
alert.src_ip = m.group(1)
|
163
|
-
elif irule == 5702:
|
164
|
-
m = re.search(r"getaddrinfo for (\S+)", data.text)
|
165
|
-
if m and m.groups():
|
166
|
-
remote_host = m.group(1)
|
167
|
-
alert.title = f"Reverse lookup failed for '{remote_host}'"
|
168
|
-
elif irule == 554:
|
169
|
-
m = re.search(r"New file '(\S+)' added", data.text)
|
170
|
-
if m and m.groups():
|
171
|
-
remote_file = m.group(1)
|
172
|
-
alert.title = f"New file detected: '{remote_file}'"
|
173
|
-
elif irule == 31101:
|
174
|
-
m = re.search(r"(GET|POST|DELETE|PUT)\s+(http://[^\s]+)\s+HTTP/\d\.\d\s+(\d+)", data.text)
|
175
|
-
if m and m.groups():
|
176
|
-
code = m.group(3)
|
177
|
-
method = m.group(1)
|
178
|
-
request_path = m.group(2)
|
179
|
-
alert.title = f"HTTP {code}: {METHOD} {request_path}"
|
180
|
-
elif irule == 31104 or irule == 31516:
|
181
|
-
m = re.search(r"(GET|POST|DELETE|PUT)\s+(http://[^\s]+)\s+HTTP/\d\.\d\s+(\d+)", data.text)
|
182
|
-
if m and m.groups():
|
183
|
-
code = m.group(3)
|
184
|
-
method = m.group(1)
|
185
|
-
request_path = m.group(2)
|
186
|
-
kind = "Common"
|
187
|
-
if irule == 31516:
|
188
|
-
kind = "Suspect"
|
189
|
-
alert.title = f"{kind} Attack {code}: {METHOD} {request_path}"
|
190
|
-
elif irule in [31301, 31302, 31303]:
|
191
|
-
m = re.search(r"(\[error\]|\[crit\])[^\*]*\*\d*\s+(.*?),", data.text)
|
192
|
-
if m and len(m.groups()) >=2:
|
193
|
-
alert.title = error
|
194
|
-
elif irule == 100020:
|
195
|
-
m = re.search(r"\[(\S+)\]", data.text)
|
196
|
-
if m and m.groups():
|
197
|
-
alert.src_ip = m.group(1)
|
198
|
-
elif "web,accesslog," in data.text and "https:" in data.text:
|
199
|
-
alert.ssh_sig = extractURL(data.text)
|
200
|
-
if alert.ssh_sig:
|
201
|
-
alert.hostname = extractDomain(alert.ssh_sig)
|
202
|
-
|
203
|
-
if alert.ext_ip is None:
|
284
|
+
alert.update(parse_alert_metadata(alert))
|
285
|
+
if alert.src_ip in ["-", None] and alert.client:
|
286
|
+
alert.src_ip = alert.client
|
287
|
+
if alert.ext_ip in ["-", None]:
|
204
288
|
alert.ext_ip = alert.src_ip
|
205
|
-
|
206
|
-
# lets do a lookup for the src
|
207
|
-
alert.geoip = GeoIP.lookup(alert.src_ip)
|
208
|
-
|
209
|
-
if irule == 31111:
|
210
|
-
url = alert.ssh_sig
|
211
|
-
hostname = alert.hostname
|
212
|
-
if url:
|
213
|
-
hostname = extractDomain(url)
|
214
|
-
if alert.geoip and alert.geoip.isp:
|
215
|
-
alert.title = f"Suspicious fetch of .js, {hostname} ISP: {alert.geoip.isp}"
|
216
|
-
else:
|
217
|
-
alert.title = f"Suspicious fetch of .js, {hostname}"
|
218
|
-
# finally here we change the alert level
|
219
|
-
if irule in LEVEL_REMAP_BY_RULE:
|
220
|
-
alert.level = LEVEL_REMAP_BY_RULE[irule]
|
221
|
-
if alert.title[0] in ["'", '"']:
|
222
|
-
alert.title = alert.title[1:-1]
|
223
|
-
if len(alert.title) > 80:
|
224
|
-
alert.title = alert.title[:80] + "..."
|
225
|
-
alert.save()
|
289
|
+
update_by_rule(alert)
|
226
290
|
return alert
|
227
291
|
|
292
|
+
|
293
|
+
def parseAlert(request, data):
|
294
|
+
pdata = parse_incoming_alert(data)
|
295
|
+
if pdata is None:
|
296
|
+
return None
|
297
|
+
field_names = am.ServerOssecAlert.get_model_field_names()
|
298
|
+
soa = am.ServerOssecAlert(**{key:value for key, value in pdata.items() if key in field_names})
|
299
|
+
soa.when = parse_when(pdata)
|
300
|
+
soa.metadata = pdata
|
301
|
+
if soa.src_ip is not None and len(soa.src_ip) > 6 and soa.src_ip != "127.0.0.1":
|
302
|
+
soa.geoip = GeoIP.lookup(soa.src_ip)
|
303
|
+
update_by_rule(pdata, soa.geoip)
|
304
|
+
soa.title = pdata.title
|
305
|
+
soa.save()
|
306
|
+
return soa
|
307
|
+
|
308
|
+
|
309
|
+
|
incident/rpc.py
CHANGED
@@ -61,6 +61,7 @@ def ossec_alert_creat_from_request(request):
|
|
61
61
|
if payload:
|
62
62
|
try:
|
63
63
|
# TODO make this a task (background it)
|
64
|
+
rh.log_error("parsing payload", payload)
|
64
65
|
od = ossec.parseAlert(request, payload)
|
65
66
|
# lets now create a local event
|
66
67
|
if od is not None:
|
@@ -78,14 +79,14 @@ def ossec_alert_creat_from_request(request):
|
|
78
79
|
elif od.level <= 3:
|
79
80
|
level = 8
|
80
81
|
metadata = od.toDict(graph="default")
|
81
|
-
metadata.update(
|
82
|
+
metadata.update(od.metadata)
|
82
83
|
# we reuse the ssh_sig because it is a text field to store urls
|
83
|
-
ssh_sig = metadata.get("ssh_sig", None)
|
84
|
-
if ssh_sig is not None and ssh_sig.startswith("http"):
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
84
|
+
# ssh_sig = metadata.get("ssh_sig", None)
|
85
|
+
# if ssh_sig is not None and ssh_sig.startswith("http"):
|
86
|
+
# metadata["url"] = ssh_sig
|
87
|
+
# metadata["domain"] = ossec.extractDomain(ssh_sig)
|
88
|
+
# metadata["path"] = ossec.extractUrlPath(ssh_sig)
|
89
|
+
# metadata.pop("ssh_sig")
|
89
90
|
if od.geoip:
|
90
91
|
metadata["country"] = od.geoip.country
|
91
92
|
metadata["city"] = od.geoip.city
|
@@ -103,7 +104,9 @@ def ossec_alert_creat_from_request(request):
|
|
103
104
|
"reporter_ip": od.src_ip,
|
104
105
|
"metadata": metadata
|
105
106
|
})
|
107
|
+
return rv.restStatus(request, True)
|
106
108
|
except Exception as err:
|
109
|
+
rh.log_exception()
|
107
110
|
stack = rh.getStackString()
|
108
111
|
# rh.log_exception("during ossec alert", payload)
|
109
112
|
metadata = dict(ip=request.ip, payload=payload)
|
@@ -115,6 +118,7 @@ def ossec_alert_creat_from_request(request):
|
|
115
118
|
"category": "ossec_error",
|
116
119
|
"metadata": metadata
|
117
120
|
})
|
121
|
+
rh.log_error("ossec alert", request.DATA.asDict())
|
118
122
|
return rv.restStatus(request, False, error="no alert data")
|
119
123
|
|
120
124
|
|
location/models/ip.py
CHANGED
rest/models/base.py
CHANGED
@@ -1718,6 +1718,10 @@ class RestModel(object):
|
|
1718
1718
|
def get_related_name_fields(cls):
|
1719
1719
|
return [f.related_name for f in cls._meta.related_objects]
|
1720
1720
|
|
1721
|
+
@classmethod
|
1722
|
+
def get_model_field_names(cls):
|
1723
|
+
return [f.name for f in cls._meta.get_fields()]
|
1724
|
+
|
1721
1725
|
@classmethod
|
1722
1726
|
def get_fk_model(cls, fieldname):
|
1723
1727
|
'''returns None if not foreignkey, otherswise the relevant model'''
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Generated by Django 4.2.11 on 2024-05-13 02:34
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
import django.db.models.deletion
|
5
|
+
|
6
|
+
|
7
|
+
class Migration(migrations.Migration):
|
8
|
+
|
9
|
+
dependencies = [
|
10
|
+
('wiki', '0001_initial'),
|
11
|
+
]
|
12
|
+
|
13
|
+
operations = [
|
14
|
+
migrations.AlterField(
|
15
|
+
model_name='pagemedia',
|
16
|
+
name='entry',
|
17
|
+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='media_library', to='wiki.page'),
|
18
|
+
),
|
19
|
+
]
|
File without changes
|
File without changes
|