rosetta-ce 1.6.9__py3-none-any.whl → 1.7.1__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.

Potentially problematic release.


This version of rosetta-ce might be problematic. Click here for more details.

rosetta/rfaker.py CHANGED
@@ -2,9 +2,10 @@ import random
2
2
  import requests
3
3
  import warnings
4
4
  import ipaddress
5
- import json
6
5
  import csv
7
6
  import hashlib
7
+ import itertools
8
+ import time
8
9
  from enum import Enum
9
10
  from functools import reduce
10
11
  from faker import Faker
@@ -180,7 +181,7 @@ class Observables:
180
181
  warnings.warn(f"No source of a bad ip , generating a random IP.")
181
182
  for i in range(count):
182
183
  gen_observables.append(faker.ipv4())
183
- if observable_type == ObservableType.IP and known == ObservableKnown.GOOD:
184
+ elif observable_type == ObservableType.IP and known == ObservableKnown.GOOD:
184
185
  for source in GOOD_IP_SOURCES:
185
186
  try:
186
187
  gen_observables = cls._get_observables_from_source(source)[:count]
@@ -192,7 +193,7 @@ class Observables:
192
193
  warnings.warn(f"No source of a good ip , generating a random IP.")
193
194
  for i in range(count):
194
195
  gen_observables.append(faker.ipv4())
195
- if observable_type == ObservableType.URL and known == ObservableKnown.BAD:
196
+ elif observable_type == ObservableType.URL and known == ObservableKnown.BAD:
196
197
  for source in BAD_URL_SOURCES:
197
198
  try:
198
199
  gen_observables = cls._get_observables_from_source(source)[:count]
@@ -204,7 +205,7 @@ class Observables:
204
205
  warnings.warn(f"No source of a bad url , generating a random url.")
205
206
  for i in range(count):
206
207
  gen_observables.append(faker.url())
207
- if observable_type == ObservableType.URL and known == ObservableKnown.GOOD:
208
+ elif observable_type == ObservableType.URL and known == ObservableKnown.GOOD:
208
209
  for source in GOOD_URL_SOURCES:
209
210
  try:
210
211
  gen_observables = cls._get_observables_from_source(source)[:count]
@@ -216,7 +217,7 @@ class Observables:
216
217
  warnings.warn(f"No source of a good url , generating a random url.")
217
218
  for i in range(count):
218
219
  gen_observables.append(faker.url())
219
- if observable_type == ObservableType.SHA256 and known == ObservableKnown.BAD:
220
+ elif observable_type == ObservableType.SHA256 and known == ObservableKnown.BAD:
220
221
  for source in BAD_SHA256_SOURCES:
221
222
  try:
222
223
  gen_observables = cls._get_observables_from_source(source)[:count]
@@ -229,7 +230,7 @@ class Observables:
229
230
  for i in range(count):
230
231
  random_string = faker.text(max_nb_chars=50)
231
232
  gen_observables.append(hashlib.sha256(random_string.encode()).hexdigest())
232
- if observable_type == ObservableType.SHA256 and known == ObservableKnown.GOOD:
233
+ elif observable_type == ObservableType.SHA256 and known == ObservableKnown.GOOD:
233
234
  for source in GOOD_SHA256_SOURCES:
234
235
  try:
235
236
  gen_observables = cls._get_observables_from_source(source)[:count]
@@ -242,7 +243,7 @@ class Observables:
242
243
  for i in range(count):
243
244
  random_string = faker.text(max_nb_chars=50)
244
245
  gen_observables.append(hashlib.sha256(random_string.encode()).hexdigest())
245
- if observable_type == ObservableType.CVE:
246
+ elif observable_type == ObservableType.CVE:
246
247
  for source in CVE_SOURCES:
247
248
  try:
248
249
  gen_observables = cls._get_observables_from_source(source)[:count]
@@ -255,7 +256,7 @@ class Observables:
255
256
  for i in range(count):
256
257
  fake_cve = "CVE-" + faker.numerify(text="####-####")
257
258
  gen_observables.append(fake_cve)
258
- if observable_type == ObservableType.TERMS:
259
+ elif observable_type == ObservableType.TERMS:
259
260
  for source in TERMS_SOURCES:
260
261
  try:
261
262
  gen_observables = cls._get_observables_from_source(source)[:count]
@@ -272,633 +273,606 @@ class Observables:
272
273
 
273
274
  class Events:
274
275
 
275
- @staticmethod
276
- def _create_faker():
277
- """
278
- Returns:
279
- Faker instance.
280
- """
281
- return Faker()
276
+ faker = Faker()
277
+ field_timings = {}
282
278
 
283
279
  @staticmethod
284
- def _create_generator():
285
- """
286
- Returns:
287
- Faker instance.
288
- """
289
- return Observables()
290
-
291
- @classmethod
292
- def set_field(cls, field, observables: Optional[Observables] = None):
280
+ def _set_field(field):
293
281
  """
294
282
  Returns:
295
283
  Field value.
296
284
  """
297
- field_value = None
298
- faker = cls._create_faker()
299
- if field == "pid":
300
- field_value = faker.random_int(min=1000, max=65535)
301
- if field == "src_host":
302
- field_value = random.choice(observables.src_host) if observables and observables.src_host \
303
- else faker.hostname()
304
- if field == "dst_host":
305
- field_value = random.choice(observables.dst_host) if observables and observables.dst_host \
306
- else faker.hostname()
307
- if field == "user":
308
- field_value = random.choice(observables.user) if observables and observables.user \
309
- else faker.user_name()
310
- if field == "unix_process":
311
- field_value = random.choice(observables.unix_process) if observables and observables.unix_process \
312
- else "sudo"
313
- if field == "unix_child_process":
314
- field_value = random.choice(observables.unix_child_process) if observables and \
315
- observables.unix_child_process else "sudo"
316
- if field == "unix_cmd":
317
- field_value = random.choice(observables.unix_cmd) if observables and observables.unix_cmd \
318
- else random.choice(UNIX_CMD)
319
- if field == "technique":
320
- field_value = random.choice(observables.technique) if observables and observables.technique \
321
- else random.choice(ATTACK_TECHNIQUES)
322
- if field == "entry_type":
323
- field_value = random.choice(observables.entry_type) if observables and observables.entry_type \
324
- else faker.sentence(nb_words=2)
325
- if field == "sensor":
326
- field_value = random.choice(observables.sensor) if observables and observables.sensor \
327
- else faker.sentence(nb_words=1)
328
- if field == "event_id":
329
- field_value = random.choice(observables.event_id) if observables and observables.event_id \
330
- else faker.random_int(min=10, max=1073741824)
331
- if field == "error_code":
332
- field_value = random.choice(observables.error_code) if observables and observables.error_code \
333
- else faker.random_int(min=1000, max=5000)
334
- if field == "terms":
335
- field_value = random.choice(observables.terms) if observables and observables.terms \
336
- else faker.sentence(nb_words=10)
337
- if field == "alert_types":
338
- field_value = random.choice(observables.alert_types) if observables and observables.alert_types \
339
- else faker.sentence(nb_words=1)
340
- if field == "action_status":
341
- field_value = random.choice(observables.action_status) if observables and observables.action_status \
342
- else random.choice(ACTIONS)
343
- if field == "severity":
344
- field_value = random.choice(observables.severity) if observables and observables.severity \
345
- else random.choice(SEVERITIES)
346
- if field == "local_ip":
347
- field_value = random.choice(observables.local_ip) if observables and observables.local_ip \
348
- else faker.ipv4()
349
- if field == "local_port":
350
- field_value = faker.random_int(min=1024, max=65535)
351
- if field == "remote_ip":
352
- field_value = random.choice(observables.remote_ip) if observables and observables.remote_ip \
353
- else Observables.generator(observable_type=ObservableType.IP, known=ObservableKnown.BAD, count=1)[0]
354
- if field == "local_ip_v6":
355
- field_value = random.choice(observables.local_ip_v6) if observables and observables.local_ip_v6 \
356
- else faker.ipv6()
357
- if field == "remote_ip_v6":
358
- field_value = random.choice(observables.remote_ip_v6) if observables and observables.remote_ip_v6 \
359
- else faker.ipv6()
360
- if field == "remote_port":
361
- field_value = random.choice(observables.remote_port) if observables and observables.remote_port \
362
- else faker.random_int(min=1024, max=65535)
363
- if field == "dst_url":
364
- field_value = random.choice(observables.url) if observables and observables.url \
365
- else Observables.generator(observable_type=ObservableType.URL, known=ObservableKnown.BAD, count=1)
366
- if field == "inbound_bytes":
367
- field_value = random.choice(observables.inbound_bytes) if observables and observables.inbound_bytes \
368
- else faker.random_int(min=10, max=1073741824)
369
- if field == "outbound_bytes":
370
- field_value = random.choice(observables.outbound_bytes) if observables and observables.outbound_bytes \
371
- else faker.random_int(min=10, max=1073741824)
372
- if field == "app":
373
- field_value = random.choice(observables.app) if observables and observables.app \
374
- else faker.sentence(nb_words=2)
375
- if field == "os":
376
- field_value = random.choice(observables.os) if observables and observables.os \
377
- else random.choice(OS_LIST)
378
- if field == "protocol":
379
- field_value = random.choice(observables.protocol) if observables and observables.protocol \
380
- else random.choice(PROTOCOLS)
381
- if field == "rule_id":
382
- field_value = random.choice(observables.event_id) if observables and observables.event_id \
383
- else faker.random_int(min=1, max=200)
384
- if field == "action":
385
- field_value = random.choice(observables.action) if observables and observables.action \
386
- else random.choice(ACTIONS)
387
- if field == "src_domain":
388
- field_value = random.choice(observables.src_domain) if observables and observables.src_domain \
389
- else faker.domain_name()
390
- if field == "dst_domain":
391
- field_value = random.choice(observables.dst_domain) if observables and observables.dst_domain \
392
- else faker.domain_name()
393
- if field == "sender_email":
394
- field_value = random.choice(observables.sender_email) if observables and observables.sender_email \
395
- else faker.email()
396
- if field == "recipient_email":
397
- field_value = random.choice(observables.recipient_email) if observables and observables.recipient_email \
398
- else faker.email()
399
- if field == "email_subject":
400
- field_value = random.choice(observables.email_subject) if observables and observables.email_subject \
401
- else faker.sentence(nb_words=6)
402
- if field == "email_body":
403
- field_value = random.choice(observables.email_body) if observables and observables.email_body else \
404
- faker.sentence(nb_words=50)
405
- if field == "attachment_hash":
406
- field_value = random.choice(observables.file_hash) if observables and observables.file_hash \
407
- else Observables.generator(observable_type=ObservableType.SHA256, known=ObservableKnown.BAD,
408
- count=1)
409
- if field == "spam_score":
410
- field_value = faker.random_int(min=1, max=5)
411
- if field == "method":
412
- field_value = random.choice(observables.technique).get('mechanism') if observables and \
413
- observables.technique else random.choice(TECHNIQUES).get('mechanism')
414
- if field == "url":
415
- field_value = random.choice(observables.technique).get('indicator') if observables and \
416
- observables.technique else random.choice(TECHNIQUES).get('indicator')
417
- if field == "user_agent":
418
- field_value = faker.user_agent()
419
- if field == "referer":
420
- field_value = random.choice(observables.url) if observables and observables.url \
421
- else Observables.generator(observable_type=ObservableType.URL, known=ObservableKnown.BAD, count=1)
422
- if field == "response_code":
423
- field_value = random.choice(observables.error_code) if observables and observables.error_code \
424
- else random.choice(ERROR_CODE)
425
- if field == "response_size":
426
- field_value = faker.random_int(min=100, max=10000)
427
- if field == "attack_type":
428
- field_value = random.choice(observables.technique).get('technique') if observables and \
429
- observables.technique else random.choice(TECHNIQUES).get('technique')
430
- if field == "cookies":
431
- field_value = f"{faker.word()}={faker.uuid4()}"
432
- if field == "guid":
433
- field_value = faker.uuid4()
434
- if field == "transmitted_services":
435
- field_value = faker.sentence(nb_words=5)
436
- if field == "process_id":
437
- field_value = faker.random_int()
438
- if field == "new_process_id":
439
- field_value = faker.random_int()
440
- if field == "thread_id":
441
- field_value = faker.random_int()
442
- if field == "target_pid":
443
- field_value = faker.random_int()
444
- if field == "subject_login_id":
445
- field_value = faker.random_int()
446
- if field == "win_user_id":
447
- field_value = "S-1-" + str(faker.random_int())
448
- if field == "destination_login_id":
449
- field_value = faker.random_int()
450
- if field == "privilege_list":
451
- field_value = faker.sentence(nb_words=5)
452
- if field == "event_record_id":
453
- field_value = random.choice(observables.event_id) if observables and observables.event_id \
454
- else faker.random.randint(1, 999)
455
- if field == "win_process":
456
- field_value = random.choice(observables.win_process) if observables and observables.win_process \
457
- else random.choice(WIN_PROCESSES)
458
- if field == "win_cmd":
459
- field_value = random.choice(observables.win_cmd) if observables and observables.win_cmd \
460
- else random.choice(WINDOWS_CMD)
461
- if field == "win_child_process":
462
- field_value = random.choice(observables.win_child_process) if \
463
- observables and observables.win_child_process else random.choice(WIN_PROCESSES)
464
- if field == "source_network_address":
465
- field_value = random.choice(observables.local_ip) if observables and observables.local_ip \
466
- else faker.ipv4_private()
467
- if field == "file_name":
468
- field_value = random.choice(observables.file_name) if observables and observables.file_name \
469
- else faker.file_name()
470
- if field == "cve":
471
- field_value = random.choice(observables.cve) if observables and observables.cve \
472
- else Observables.generator(observable_type=ObservableType.CVE, count=1)
473
- if field == "file_hash":
474
- field_value = random.choice(observables.file_hash) if observables and observables.file_hash \
475
- else Observables.generator(observable_type=ObservableType.SHA256, known=ObservableKnown.BAD,
476
- count=1)
477
- if field == "incident_types":
478
- field_value = random.choice(observables.incident_types) if observables and observables.incident_types \
479
- else INCIDENTS_TYPES
480
- if field == "analysts":
481
- field_value = random.choice(observables.analysts) if observables and observables.analysts \
482
- else [faker.unique.first_name() for _ in range(10)]
483
- if field == "duration":
484
- field_value = random.randint(1, 5)
485
- if field == "log_id":
486
- field_value = faker.uuid4()
487
- if field == "alert_name":
488
- field_value = random.choice(observables.alert_name) if observables and observables.alert_name \
489
- else faker.sentence(nb_words=4)
490
- if field == "query_type":
491
- field_value = random.choice(observables.query_type) if \
492
- observables and observables.query_type else random.choice(QUERY_TYPE)
493
- if field == "database_name":
494
- field_value = random.choice(observables.database_name) if \
495
- observables and observables.database_name else random.choice(DATABASE_NAME)
496
- if field == "query":
497
- field_value = random.choice(observables.query) if \
498
- observables and observables.query else random.choice(QUERY)
499
- return field_value
500
285
 
286
+ faker = Events.faker
287
+
288
+ # Define default generators for each field
289
+ default_generators = {
290
+ "pid": lambda: faker.random_int(min=1000, max=65535),
291
+ "src_host": faker.hostname,
292
+ "dst_host": faker.hostname,
293
+ "user": faker.user_name,
294
+ "unix_process": lambda: "sudo",
295
+ "unix_child_process": lambda: "sudo",
296
+ "unix_cmd": lambda: random.choice(UNIX_CMD),
297
+ "technique": lambda: random.choice(ATTACK_TECHNIQUES),
298
+ "entry_type": lambda: faker.sentence(nb_words=2),
299
+ "sensor": lambda: faker.sentence(nb_words=1),
300
+ "event_id": lambda: faker.random_int(min=10, max=1073741824),
301
+ "error_code": lambda: faker.random_int(min=1000, max=5000),
302
+ "terms": lambda: faker.sentence(nb_words=10),
303
+ "alert_types": lambda: faker.sentence(nb_words=1),
304
+ "action_status": lambda: random.choice(ACTIONS),
305
+ "severity": lambda: random.choice(SEVERITIES),
306
+ "local_ip": faker.ipv4,
307
+ "local_port": lambda: faker.random_int(min=1024, max=65535),
308
+ "remote_ip": lambda: Observables.generator(
309
+ observable_type=ObservableType.IP,
310
+ known=ObservableKnown.BAD,
311
+ count=1
312
+ )[0],
313
+ "local_ip_v6": faker.ipv6,
314
+ "remote_ip_v6": faker.ipv6,
315
+ "remote_port": lambda: faker.random_int(min=1024, max=65535),
316
+ "dst_url": lambda: Observables.generator(
317
+ observable_type=ObservableType.URL,
318
+ known=ObservableKnown.BAD,
319
+ count=1
320
+ )[0],
321
+ "inbound_bytes": lambda: faker.random_int(min=10, max=1073741824),
322
+ "outbound_bytes": lambda: faker.random_int(min=10, max=1073741824),
323
+ "app": lambda: faker.sentence(nb_words=2),
324
+ "os": lambda: random.choice(OS_LIST),
325
+ "protocol": lambda: random.choice(PROTOCOLS),
326
+ "rule_id": lambda: faker.random_int(min=1, max=200),
327
+ "action": lambda: random.choice(ACTIONS),
328
+ "src_domain": faker.domain_name,
329
+ "dst_domain": faker.domain_name,
330
+ "sender_email": faker.email,
331
+ "recipient_email": faker.email,
332
+ "email_subject": lambda: faker.sentence(nb_words=6),
333
+ "email_body": lambda: faker.sentence(nb_words=50),
334
+ "attachment_hash": lambda: Observables.generator(
335
+ observable_type=ObservableType.SHA256,
336
+ known=ObservableKnown.BAD,
337
+ count=1
338
+ )[0],
339
+ "spam_score": lambda: faker.random_int(min=1, max=5),
340
+ "method": lambda: random.choice(TECHNIQUES).get('mechanism'),
341
+ "url": lambda: random.choice(TECHNIQUES).get('indicator'),
342
+ "user_agent": faker.user_agent,
343
+ "referer": lambda: Observables.generator(
344
+ observable_type=ObservableType.URL,
345
+ known=ObservableKnown.BAD,
346
+ count=1
347
+ )[0],
348
+ "response_code": lambda: random.choice(ERROR_CODE),
349
+ "response_size": lambda: faker.random_int(min=100, max=10000),
350
+ "attack_type": lambda: random.choice(TECHNIQUES).get('technique'),
351
+ "cookies": lambda: f"{faker.word()}={faker.uuid4()}",
352
+ "guid": faker.uuid4,
353
+ "transmitted_services": lambda: faker.sentence(nb_words=5),
354
+ "process_id": faker.random_int,
355
+ "new_process_id": faker.random_int,
356
+ "thread_id": faker.random_int,
357
+ "target_pid": faker.random_int,
358
+ "subject_login_id": faker.random_int,
359
+ "win_user_id": lambda: "S-1-" + str(faker.random_int()),
360
+ "destination_login_id": faker.random_int,
361
+ "privilege_list": lambda: faker.sentence(nb_words=5),
362
+ "event_record_id": lambda: faker.random_int(min=1, max=999),
363
+ "win_process": lambda: random.choice(WIN_PROCESSES),
364
+ "win_cmd": lambda: random.choice(WINDOWS_CMD),
365
+ "win_child_process": lambda: random.choice(WIN_PROCESSES),
366
+ "source_network_address": faker.ipv4_private,
367
+ "file_name": faker.file_name,
368
+ "cve": lambda: Observables.generator(
369
+ observable_type=ObservableType.CVE,
370
+ count=1
371
+ )[0],
372
+ "file_hash": lambda: Observables.generator(
373
+ observable_type=ObservableType.SHA256,
374
+ known=ObservableKnown.BAD,
375
+ count=1
376
+ )[0],
377
+ "incident_types": lambda: random.choice(INCIDENTS_TYPES),
378
+ "analysts": lambda: [faker.unique.first_name() for _ in range(10)],
379
+ "duration": lambda: random.randint(1, 5),
380
+ "log_id": faker.uuid4,
381
+ "alert_name": lambda: faker.sentence(nb_words=4),
382
+ "query_type": lambda: random.choice(QUERY_TYPE),
383
+ "database_name": lambda: random.choice(DATABASE_NAME),
384
+ "query": lambda: random.choice(QUERY),
385
+ }
386
+
387
+ if field in default_generators:
388
+ generator = default_generators[field]
389
+ field_value = generator() if callable(generator) else generator()
390
+ else:
391
+ field_value = faker.word()
392
+
393
+ return field_value
394
+
501
395
  @classmethod
502
- def syslog(cls, count: int, datetime_iso: Optional[datetime] = None, observables: Optional[Observables] = None,
503
- required_fields: Optional[str] = None) -> List[str]:
396
+ def syslog(
397
+ cls,
398
+ count: int,
399
+ datetime_iso: Optional[datetime] = None,
400
+ observables: Optional['Observables'] = None,
401
+ required_fields: Optional[str] = None
402
+ ) -> List[str]:
504
403
  """
505
- Generate fake syslog messages.
404
+ Generate fake syslog messages with per-message randomization.
506
405
 
507
406
  Args:
508
- count: The number of syslog messages to generate.
509
- datetime_iso: Optional. The starting datetime_iso for the syslog messages. If not provided, a random time during
510
- the past hour from now will be used.
511
- observables: Optional. An observables object. If not provided, random objservable will be generated
512
- and used.
513
- required_fields: Optional. A list of fields that are required to present in the generated data, whether from
514
- observables or randomely.
515
- Returns:
516
- A list of syslog messages.
517
-
518
- Examples:
519
- >>> Events.syslog(5)
520
- ['Jan 01 05:32:48 myhostname sudo[1023]: username : COMMAND ; cat /etc/shadow',
521
- 'Jan 01 05:17:59 myhostname sudo[2019]: username : COMMAND ; find / -name \'*.log\' -exec rm -f {} \\;',
522
- 'Jan 01 05:46:16 myhostname sudo[3132]: username : COMMAND ; dd if=/dev/zero of=/dev/sda',
523
- 'Jan 01 05:08:08 myhostname sudo[4111]: username : COMMAND ; chmod -R 777 /',
524
- 'Jan 01 05:59:41 myhostname sudo[5195]: username : COMMAND ; chown -R nobody:nogroup /']
407
+ count (int): Number of syslog messages to generate.
408
+ datetime_iso (Optional[datetime]): Starting datetime for the messages.
409
+ observables (Optional[Observables]): Observables object with predefined values.
410
+ required_fields (Optional[str]): Comma-separated string of required fields.
525
411
 
412
+ Returns:
413
+ List[str]: A list of generated syslog messages.
526
414
  """
527
415
  syslog_messages = []
528
- faker = cls._create_faker()
416
+ faker = Events.faker
417
+
418
+ # Predefine default datetime if not provided
529
419
  if datetime_iso is None:
530
420
  datetime_iso = datetime.now() - timedelta(hours=1)
531
421
  datetime_iso += timedelta(seconds=faker.random_int(min=0, max=3599))
532
- if not required_fields:
533
- required_fields = "pid,host,user,unix_process,unix_cmd"
534
- for i in range(count):
535
- datetime_iso += timedelta(seconds=1)
536
- syslog_message = f"{datetime_iso.strftime('%Y-%m-%d %H:%M:%S')}"
537
- for field in required_fields.split(","):
538
- syslog_message += f" {cls.set_field(field, observables)}"
539
- if observables:
540
- for observable, observable_value in vars(observables).items():
541
- if observable_value and observable not in required_fields.split(","):
542
- syslog_message += f" {random.choice(observable_value)}"
543
- syslog_messages.append(syslog_message)
544
- return syslog_messages
545
422
 
546
- @classmethod
547
- def cef(cls, count: int, vendor: Optional[str] = None, product: Optional[str] = None,
548
- version: Optional[str] = None, datetime_iso: Optional[datetime] = None,
549
- observables: Optional[Observables] = None, required_fields: Optional[str] = None) -> List[str]:
550
- """
551
- Generates fake CEF (Common Event Format) messages.
423
+ # Set default required fields
424
+ required_fields_list = required_fields.split(",") if required_fields else [
425
+ "pid", "host", "user", "unix_process", "unix_cmd"
426
+ ]
552
427
 
553
- Args:
554
- count: The number of CEF messages to generate.
555
- datetime_iso: Optional. The starting datetime_iso for the syslog messages. If not provided, a random time during.
556
- vendor: Optional. The vendor.
557
- product: Optional. The product value options include:
558
- - Firewall
559
- - EmailGW
560
- version: Optional. The version.
561
- observables: Optional. An observables object. If not provided, random objservable will be generated
562
- and used.
563
- required_fields: Optional. A list of fields that are required to present in the generated data, whether from
564
- observables or randomely.
565
- Returns:
566
- A list of fake CEF messages in string format.
428
+ # Convert observables to a dictionary for easy access
429
+ observables_dict = vars(observables) if observables else {}
567
430
 
568
- Raises:
569
- None.
570
-
571
- Example Usage:
572
- >>> Events.cef(3)
573
- ['CEF:0|Acme|Firewall|1.0.0|ddab6607-1c35-4e81-a54a-99b1c9b77e49|Firewall ALLOW UDP traffic
574
- from example.com:61434 to 23.216.45.109:47983|1|src=example.com spt=61434 dst=23.216.45.109
575
- dpt=47983 proto=UDP act=ALLOW',
576
- 'CEF:0|Acme|Firewall|1.0.0|25b41f8f-8a63-4162-a69c-cb43f8c8e49f|Firewall DENY TCP traffic
577
- from example.com:11460 to 184.72.194.90:3087|8|src=example.com spt=11460 dst=184.72.194.90
578
- dpt=3087 proto=TCP act=DENY',
579
- 'CEF:0|Acme|Firewall|1.0.0|a3faedaa-5109-4849-b9ec-1ad6c5f8a5ec|Firewall ALLOW TCP traffic
580
- from example.com:25068 to 81.171.9.216:6157|2|src=example.com spt=25068 dst=81.171.9.216
581
- dpt=6157 proto=TCP act=ALLOW']
582
- """
431
+ # Precompute constant fields (if any)
432
+ # For syslog, most fields may vary per message, so we may not have many constants
433
+ constant_fields = {}
434
+
435
+ # Generate syslog messages
436
+ for i in range(count):
437
+ # Update datetime for each log
438
+ current_time = (datetime_iso + timedelta(seconds=i + 1)).strftime('%b %d %H:%M:%S')
439
+
440
+ # Generate required fields per message
441
+ event_fields = {}
442
+ for field in required_fields_list:
443
+ value = None
444
+ if field in observables_dict and observables_dict[field]:
445
+ obs_value = observables_dict[field]
446
+ value = random.choice(obs_value) if isinstance(obs_value, list) else obs_value
447
+ else:
448
+ value = Events._set_field(field)
449
+ event_fields[field] = value
450
+
451
+ # Generate extra fields per message
452
+ extra_fields = []
453
+ for key, value in observables_dict.items():
454
+ if value and key not in required_fields_list:
455
+ val = random.choice(value) if isinstance(value, list) else value
456
+ extra_fields.append(str(val))
457
+
458
+ # Build the syslog message
459
+ syslog_message_parts = [f"{current_time}"]
460
+
461
+ # Insert required fields
462
+ syslog_message_parts.extend(str(event_fields[field]) for field in required_fields_list)
463
+
464
+ # Append additional observables not in required fields
465
+ syslog_message_parts.extend(extra_fields)
466
+
467
+ syslog_messages.append(" ".join(syslog_message_parts))
468
+
469
+ return syslog_messages
470
+
471
+ @classmethod
472
+ def cef(
473
+ cls,
474
+ count: int,
475
+ vendor: Optional[str] = None,
476
+ product: Optional[str] = None,
477
+ version: Optional[str] = None,
478
+ datetime_iso: Optional[datetime] = None,
479
+ observables: Optional['Observables'] = None,
480
+ required_fields: Optional[str] = None
481
+ ) -> List[str]:
583
482
  cef_messages = []
584
- faker = cls._create_faker()
483
+ faker = Events.faker
585
484
  vendor = vendor or faker.company()
485
+ product = product or faker.word()
586
486
  version = version or faker.numerify("1.0.#")
587
- if datetime_iso is None:
588
- datetime_iso = datetime.now() - timedelta(hours=1)
589
- datetime_iso += timedelta(seconds=faker.random_int(min=0, max=3599))
590
- if not required_fields:
591
- if product == "Firewall":
592
- required_fields = "local_ip,local_port,remote_ip,remote_port,dst_url,inbound_bytes," \
593
- "outbound_bytes,protocol,rule_id,action"
594
- elif product == "EmailGW":
595
- required_fields = "local_ip,src_domain,sender_email,recipient_email,email_subject,email_body," \
596
- "attachment_hash,spam_score,action"
597
- else:
598
- required_fields = "local_ip,local_port,remote_ip,remote_port,protocol,rule_id,action"
487
+ datetime_iso = datetime_iso or datetime.now() - timedelta(hours=1)
488
+ required_fields_list = required_fields.split(",") if required_fields else [
489
+ "local_ip", "local_port", "remote_ip", "remote_port", "protocol", "rule_id", "action"
490
+ ]
491
+
492
+ # Convert observables to a dictionary for easy access
493
+ observables_dict = vars(observables) if observables else {}
494
+
495
+ # Generate events
599
496
  for i in range(count):
600
- datetime_iso += timedelta(seconds=1)
601
- cef_message = f"CEF:0|{vendor}|{product}|{version}|{cls.set_field('log_id', observables)}|{datetime_iso}" \
602
- f"|{cls.set_field('severity', observables)}|"
603
- for field in required_fields.split(","):
604
- cef_message += f" {field}={cls.set_field(field, observables)}"
605
- if observables:
606
- for observable, observable_value in vars(observables).items():
607
- if observable_value and observable not in required_fields.split(","):
608
- cef_message += f" {observable}={random.choice(observable_value)}"
497
+ current_datetime = datetime_iso + timedelta(seconds=i)
498
+ log_id = faker.uuid4()
499
+ severity = 'low' # Or generate per message if needed
500
+
501
+ # Generate field values per message
502
+ event_fields = {}
503
+ for field in required_fields_list:
504
+ value = None
505
+ if field in observables_dict and observables_dict[field]:
506
+ obs_value = observables_dict[field]
507
+ if isinstance(obs_value, list):
508
+ value = random.choice(obs_value)
509
+ else:
510
+ value = obs_value
511
+ else:
512
+ value = Events._set_field(field)
513
+ event_fields[field] = value
514
+
515
+ # Generate extra fields per message
516
+ extra_fields = []
517
+ for key, value in observables_dict.items():
518
+ if value and key not in required_fields_list:
519
+ val = random.choice(value) if isinstance(value, list) else value
520
+ extra_fields.append(f"{key}={val}")
521
+
522
+ extra_fields_str = " " + " ".join(extra_fields) if extra_fields else ""
523
+
524
+ # Build the CEF message
525
+ cef_message = (
526
+ f"CEF:0|{vendor}|{product}|{version}|{log_id}|{current_datetime.strftime('%Y-%m-%dT%H:%M:%S.%fZ')}|{severity}|"
527
+ + " ".join([f"{field}={event_fields[field]}" for field in required_fields_list])
528
+ )
529
+ cef_message += extra_fields_str
609
530
  cef_messages.append(cef_message)
610
- return cef_messages
611
531
 
532
+ return cef_messages
533
+
612
534
  @classmethod
613
- def leef(cls, count, datetime_iso: Optional[datetime] = None, vendor: Optional[str] = None,
614
- product: Optional[str] = None, version: Optional[str] = None,
615
- observables: Optional[Observables] = None, required_fields: Optional[str] = None) -> List[str]:
535
+ def leef(
536
+ cls,
537
+ count: int,
538
+ datetime_iso: Optional[datetime] = None,
539
+ vendor: Optional[str] = None,
540
+ product: Optional[str] = None,
541
+ version: Optional[str] = None,
542
+ observables: Optional['Observables'] = None,
543
+ required_fields: Optional[str] = None
544
+ ) -> List[str]:
616
545
  """
617
- Generates fake LEEF (Log Event Extended Format) messages.
618
-
619
- Parameters:
620
- count (int): The number of LEEF messages to generate.
621
- datetime_iso: Optional. The starting datetime_iso for the syslog messages. If not provided, a random time during.
622
- vendor: Optional. The vendor.
623
- product: Optional. The product.
624
- version: Optional. The version.
625
- observables: An observables object. If not provided, random objservable will be generated and used.
626
- required_fields: Optional. A list of fields that are required to present in the generated data, whether from
627
- observables or randomely.
628
-
629
- Returns:
630
- A list of generated LEEF messages.
631
-
632
- Example:
633
- To generate 10 fake LEEF messages:
634
- ```
635
- >>> messages = Events.leef(count=2)
636
- >>> print(messages)
637
- ['LEEF:1.0|Leef|Payment Portal|1.0|192.168.0.1|mycomputer|08:00:27:da:2e:2e|08:00:27:da:2e:2f|src=10.0.0.1
638
- dst=mycomputer spt=60918 dpt=443 request=https://example.com/?q=<script>alert("xss")</script>
639
- method=GET proto=HTTP/1.1 status=200 request_size=5119 response_size=6472
640
- user_agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko)
641
- Chrome/93.0.4577.63 Safari/537.36',
642
- 'LEEF:1.0|Leef|Payment Portal|1.0|192.168.0.1|mycomputer|08:00:27:da:2e:2e|08:00:27:da:2e:2f|src=10.0.0.2
643
- dst=mycomputer spt=57251 dpt=443 request=https://example.com/admin.php?sessionid=12345 method=POST
644
- proto=HTTP/1.1 status=404 request_size=1216 response_size=9729 user_agent=Mozilla/5.0
645
- (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3']
546
+ Generates optimized fake LEEF (Log Event Extended Format) messages with per-message randomization.
646
547
  """
647
548
  leef_messages = []
648
- faker = cls._create_faker()
549
+ faker = Events.faker
649
550
  vendor = vendor or faker.company()
551
+ product = product or faker.word()
650
552
  version = version or faker.numerify("1.0.#")
651
- event_id = faker.random_int(min=101, max=501)
652
- if datetime_iso is None:
653
- datetime_iso = datetime.now() - timedelta(hours=1)
654
- datetime_iso += timedelta(seconds=faker.random_int(min=0, max=3599))
655
- if not required_fields:
656
- if product == "WAF":
657
- required_fields = "local_ip,local_port,host,method,url,protocol," \
658
- "user_agent,referer,response_code,response_size,rule_id,action,attack_type,cookies"
659
- else:
660
- required_fields = "local_ip,local_port,host,url,protocol,response_code,action"
553
+
554
+ # Set starting datetime and default fields if necessary
555
+ datetime_iso = datetime_iso or datetime.now() - timedelta(hours=1)
556
+ required_fields_list = required_fields.split(",") if required_fields else [
557
+ "local_ip", "local_port", "host", "url", "protocol", "response_code", "action"
558
+ ]
559
+
560
+ # Convert observables to a dictionary for easy access
561
+ observables_dict = vars(observables) if observables else {}
562
+
563
+ # Precompute constant fields
564
+ # Event ID and severity might change per event; move inside the loop if needed
565
+ constant_fields = {
566
+ 'vendor': vendor,
567
+ 'product': product,
568
+ 'version': version,
569
+ }
570
+
571
+ # Generate messages
661
572
  for i in range(count):
662
- datetime_iso += timedelta(seconds=1)
663
- leef_message = f"LEEF:1.0|{vendor}|{product}|{version}|{event_id}|" \
664
- f"severity={cls.set_field('severity', observables)} devtime={datetime_iso}"
665
- for field in required_fields.split(","):
666
- leef_message += f" {field}={cls.set_field(field, observables)}"
667
- if observables:
668
- for observable, observable_value in vars(observables).items():
669
- if observable_value and observable not in required_fields.split(","):
670
- leef_message += f" {observable}={random.choice(observable_value)}"
573
+ current_datetime = (datetime_iso + timedelta(seconds=i)).strftime('%b %d %H:%M:%S')
574
+
575
+ # Generate per-message fields
576
+ # Event ID and severity could be variable
577
+ event_id = Events._set_field("event_id")
578
+ severity = Events._set_field("severity")
579
+
580
+ # Generate required fields per message
581
+ event_fields = {}
582
+ for field in required_fields_list:
583
+ value = None
584
+ if field in observables_dict and observables_dict[field]:
585
+ obs_value = observables_dict[field]
586
+ value = random.choice(obs_value) if isinstance(obs_value, list) else obs_value
587
+ else:
588
+ value = Events._set_field(field)
589
+ event_fields[field] = value
590
+
591
+ # Generate extra fields per message
592
+ extra_fields = []
593
+ for key, value in observables_dict.items():
594
+ if value and key not in required_fields_list:
595
+ val = random.choice(value) if isinstance(value, list) else value
596
+ extra_fields.append(f" {key}={val}")
597
+
598
+ # Build the LEEF message
599
+ leef_message = (
600
+ f"LEEF:1.0|{constant_fields['vendor']}|{constant_fields['product']}|{constant_fields['version']}|{event_id}|"
601
+ f"severity={severity} devTime={current_datetime}"
602
+ + "".join(f" {field}={event_fields[field]}" for field in required_fields_list)
603
+ + "".join(extra_fields)
604
+ )
605
+
671
606
  leef_messages.append(leef_message)
607
+
672
608
  return leef_messages
673
609
 
610
+
674
611
  @classmethod
675
- def winevent(cls, count, datetime_iso: Optional[datetime] = None, observables: Optional[Observables] = None) -> \
676
- List[str]:
612
+ def winevent(
613
+ cls,
614
+ count: int,
615
+ datetime_iso: Optional[datetime] = None,
616
+ observables: Optional['Observables'] = None
617
+ ) -> List[str]:
677
618
  """
678
- Generates fake Windows Event Log messages.
619
+ Generates fake Windows Event Log messages with per-message randomization.
679
620
 
680
621
  Args:
681
622
  count (int): The number of fake messages to generate.
682
- datetime_iso: Optional. The starting datetime_iso for the syslog messages. If not provided, a random time during
683
- observables: An observables object. If not provided, random objservable will be generated and used.
623
+ datetime_iso (Optional[datetime]): The starting datetime for the messages.
624
+ observables (Optional[Observables]): An observables object with predefined values.
684
625
 
685
626
  Returns:
686
- list: A list of fake Windows Event Log messages.
687
-
688
- Examples:
689
- >>> Events.winevent(1)
690
- ['<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">...</Event>', ...]
627
+ List[str]: A list of fake Windows Event Log messages.
691
628
  """
692
629
  winevent_messages = []
693
- faker = cls._create_faker()
630
+ faker = Events.faker
631
+
632
+ # Set starting datetime if not provided
694
633
  if datetime_iso is None:
695
634
  datetime_iso = datetime.now() - timedelta(hours=1)
696
635
  datetime_iso += timedelta(seconds=faker.random_int(min=0, max=3599))
636
+
637
+ # Define required fields
638
+ required_fields_list = [
639
+ "process_id",
640
+ "new_process_id",
641
+ "thread_id",
642
+ "target_pid",
643
+ "subject_login_id",
644
+ "user_id",
645
+ "destination_login_id",
646
+ "privilege_list",
647
+ "win_process",
648
+ "src_host",
649
+ "user_name",
650
+ "cmd",
651
+ "source_network_address",
652
+ "local_port",
653
+ "transmitted_services",
654
+ "file_name",
655
+ "src_domain"
656
+ ]
657
+
658
+ # Convert observables to a dictionary for easy access
659
+ observables_dict = vars(observables) if observables else {}
660
+
661
+ # Precompute constant fields (if any)
662
+ constant_fields = {
663
+ # Add any fields that are constant across events
664
+ }
665
+
666
+ # Generate events
697
667
  for i in range(count):
698
- datetime_iso += timedelta(seconds=1)
668
+ # Update datetime for each event
669
+ current_datetime = datetime_iso + timedelta(seconds=i + 1)
670
+ system_time = current_datetime.strftime('%Y-%m-%d %H:%M:%S')
671
+
672
+ # Generate per-event fields
673
+ event_fields = {}
674
+
675
+ # Generate required fields per message
676
+ for field in required_fields_list:
677
+ value = None
678
+ if field in observables_dict and observables_dict[field]:
679
+ obs_value = observables_dict[field]
680
+ value = random.choice(obs_value) if isinstance(obs_value, list) else obs_value
681
+ else:
682
+ value = Events._set_field(field)
683
+ event_fields[field] = value
684
+
685
+ # Generate extra fields per message
686
+ for key, value in observables_dict.items():
687
+ if value and key not in required_fields_list:
688
+ val = random.choice(value) if isinstance(value, list) else value
689
+ event_fields[key] = val
690
+
691
+ # Generate per-event fields that are not in observables
699
692
  guid = faker.uuid4()
700
- local_port = faker.random_int(min=1024, max=65535)
701
- transmitted_services = faker.sentence(nb_words=5)
702
- system_time = datetime_iso
703
- process_id = faker.random_int()
704
- new_process_id = faker.random_int()
705
- thread_id = faker.random_int()
706
- target_pid = faker.random_int()
707
- domain_name = faker.domain_name()
708
- subject_login_id = faker.random_int()
709
- user_id = "S-1-" + str(faker.random_int())
710
- destination_login_id = faker.random_int()
711
- privilege_list = faker.sentence(nb_words=5)
712
- event_record_id = random.choice(observables.event_id) if observables and observables.event_id \
713
- else faker.random.randint(1, 999)
714
- process_name = random.choice(observables.win_process) if observables and observables.win_process \
715
- else random.choice(WIN_PROCESSES)
716
- host = random.choice(observables.src_host) if observables and observables.src_host \
717
- else faker.hostname()
718
- user_name = random.choice(observables.user) if observables and observables.user \
719
- else faker.user_name()
720
- cmd = random.choice(observables.win_cmd) if observables and observables.win_cmd \
721
- else random.choice(WINDOWS_CMD)
722
- source_network_address = random.choice(observables.local_ip) if observables and observables.local_ip \
723
- else faker.ipv4_private()
724
- file_name = random.choice(observables.file_name) if observables and observables.file_name \
725
- else faker.file_name()
693
+
694
+ # Use event_id from observables if available
695
+ if 'event_id' in observables_dict and observables_dict['event_id']:
696
+ event_record_id = (
697
+ random.choice(observables_dict['event_id'])
698
+ if isinstance(observables_dict['event_id'], list)
699
+ else observables_dict['event_id']
700
+ )
701
+ else:
702
+ event_record_id = Events._set_field('event_id')
703
+
704
+ # Prepare event fields
705
+ event_fields.update({
706
+ 'guid': guid,
707
+ 'system_time': system_time,
708
+ 'event_record_id': event_record_id,
709
+ })
710
+
711
+ # Combine with any constant fields
712
+ event_fields.update(constant_fields)
713
+
714
+ # Select a random event template
726
715
  unformatted_event = random.choice(WIN_EVENTS)
727
- win_event = unformatted_event.format(guid=guid, system_time=system_time, event_record_id=event_record_id,
728
- process_id=process_id, process_name=process_name,
729
- new_process_id=new_process_id, thread_id=thread_id,
730
- target_pid=target_pid, host=host, user_id=user_id, user_name=user_name,
731
- domain_name=domain_name, subject_login_id=subject_login_id,
732
- privilege_list=privilege_list, cmd=cmd,
733
- destination_login_id=destination_login_id,
734
- source_network_address=source_network_address, local_port=local_port,
735
- transmitted_services=transmitted_services, file_name=file_name)
716
+
717
+ # Format the event with all fields
718
+ win_event = unformatted_event.format(**event_fields)
719
+
736
720
  winevent_messages.append(win_event)
737
- return winevent_messages
738
721
 
722
+ return winevent_messages
723
+
739
724
  @classmethod
740
- def json(cls, count, datetime_iso: Optional[datetime] = None, vendor: Optional[str] = None,
741
- product: Optional[str] = None, version: Optional[str] = None, observables: Optional[Observables] = None,
742
- required_fields: Optional[str] = None) -> List[dict]:
725
+ def json(
726
+ cls,
727
+ count: int,
728
+ datetime_iso: Optional[datetime] = None,
729
+ vendor: Optional[str] = None,
730
+ product: Optional[str] = None,
731
+ version: Optional[str] = None,
732
+ observables: Optional['Observables'] = None,
733
+ required_fields: Optional[str] = None
734
+ ) -> List[dict]:
743
735
  """
744
- Generate fake JSON messages representing discovered vulnerabilities.
736
+ Generate fake JSON messages representing discovered vulnerabilities with per-message randomization.
745
737
 
746
738
  Args:
747
- count (int): The number of JSON messages to generate.
748
- datetime_iso: Optional. The starting datetime_iso for the syslog messages. If not provided, a random time during.
749
- vendor: Optional. The vendor.
750
- product: Optional. The product value options include:
751
- - VulnScanner
752
- version: Optional. The version.
753
- observables: An observables object. If not provided, random objservable will be generated and used.
754
- required_fields: Optional. A list of fields that are required to present in the generated data, whether from
755
- observables or randomely.
756
- Returns:
757
- List[Dict[str, Union[str, int]]]: A list of dictionaries representing the generated JSON messages.
758
-
759
- Example:
760
- >>> fake_messages = json(5)
761
- >>> len(fake_messages)
762
- 5
763
- >>> isinstance(fake_messages[0], dict)
764
- True
739
+ count (int): Number of JSON messages to generate.
740
+ datetime_iso (Optional[datetime]): Starting datetime for the messages.
741
+ vendor (Optional[str]): Vendor name.
742
+ product (Optional[str]): Product name.
743
+ version (Optional[str]): Product version.
744
+ observables (Optional[Observables]): Observables object with predefined values.
745
+ required_fields (Optional[str]): Comma-separated string of required fields.
765
746
 
747
+ Returns:
748
+ List[dict]: A list of generated JSON messages.
766
749
  """
767
750
  json_messages = []
768
- faker = cls._create_faker()
751
+ faker = Events.faker
752
+
753
+ # Precompute vendor, product, and version details
769
754
  vendor = vendor or faker.company()
755
+ product = product or "UnknownProduct"
770
756
  version = version or faker.numerify("1.0.#")
771
- if datetime_iso is None:
772
- datetime_iso = datetime.now() - timedelta(hours=1)
773
- datetime_iso += timedelta(seconds=faker.random_int(min=0, max=3599))
774
- if not required_fields:
775
- if product == "VulnScanner":
776
- required_fields = "cve_id,host,file_hash"
777
- else:
778
- required_fields = "user,host"
757
+
758
+ # Set initial datetime
759
+ datetime_iso = datetime_iso or datetime.now() - timedelta(hours=1)
760
+
761
+ # Set required fields
762
+ if required_fields:
763
+ required_fields_list = required_fields.split(",")
764
+ else:
765
+ required_fields_list = (
766
+ ["cve_id", "host", "file_hash"] if product == "VulnScanner" else ["user", "host"]
767
+ )
768
+
769
+ # Convert observables to a dictionary for easy access
770
+ observables_dict = vars(observables) if observables else {}
771
+
772
+ # Precompute constant fields
773
+ constant_fields = {
774
+ 'vendor': vendor,
775
+ 'product': product,
776
+ 'version': version,
777
+ }
778
+
779
+ # Generate JSON events
779
780
  for i in range(count):
780
- datetime_iso += timedelta(seconds=1)
781
- event = {
782
- 'vendor': vendor,
783
- 'product': product,
784
- 'version': version,
785
- 'datetime_iso': str(datetime_iso),
786
- 'severity': cls.set_field("severity", observables)
781
+ # Adjust datetime for each message
782
+ current_datetime = datetime_iso + timedelta(seconds=i)
783
+ datetime_str = current_datetime.strftime("%Y-%m-%d %H:%M:%S")
784
+
785
+ # Generate variable fields per message
786
+ event_fields = {
787
+ 'datetime_iso': datetime_str,
788
+ 'severity': Events._set_field('severity'),
787
789
  }
788
- for field in required_fields.split(","):
789
- event[field] = cls.set_field(field, observables)
790
- if observables:
791
- for observable, observable_value in vars(observables).items():
792
- if observable_value and observable not in required_fields.split(","):
793
- event[observable] = random.choice(observable_value)
790
+
791
+ # Generate required fields per message
792
+ for field in required_fields_list:
793
+ value = None
794
+ if field in observables_dict and observables_dict[field]:
795
+ obs_value = observables_dict[field]
796
+ value = random.choice(obs_value) if isinstance(obs_value, list) else obs_value
797
+ else:
798
+ value = Events._set_field(field)
799
+ event_fields[field] = value
800
+
801
+ # Include additional observables not in required fields
802
+ for key, value in observables_dict.items():
803
+ if value and key not in required_fields_list:
804
+ val = random.choice(value) if isinstance(value, list) else value
805
+ event_fields[key] = val
806
+
807
+ # Combine all fields into the event
808
+ event = {**constant_fields, **event_fields}
809
+
810
+ # Append generated event to the list
794
811
  json_messages.append(event)
812
+
795
813
  return json_messages
796
814
 
797
815
  @classmethod
798
816
  def incidents(cls, count, fields: Optional[str] = None, datetime_iso: Optional[datetime] = None,
799
- vendor: Optional[str] = None, product: Optional[str] = None, version: Optional[str] = None,
800
- observables: Optional[Observables] = None, required_fields: Optional[str] = None) -> List[dict]:
817
+ vendor: Optional[str] = None, product: Optional[str] = None, version: Optional[str] = None,
818
+ observables: Optional[Observables] = None, required_fields: Optional[str] = None) -> List[dict]:
801
819
  """
802
820
  Generates a list of fake incident data.
803
821
 
804
822
  Args:
805
823
  count (int): The number of incidents to generate.
806
- fields (str, optional): A comma-separated list of incident fields to include in the output. If None,
807
- all fields will be included. Valid options are: 'id', 'duration', 'type', 'analyst', 'severity',
808
- 'description', 'events'.
809
- vendor: Optional. The vendor.
810
- product: Optional. The product.
811
- version: Optional. The version.
812
- datetime_iso: Optional. The starting datetime_iso for the syslog messages. If not provided, a random time during
813
- observables: An observables object. If not provided, random objservable will be generated and used.
814
- required_fields: Optional. A list of fields that are required to present in the generated data, whether from
815
- observables or randomely.
824
+ fields (str, optional): Comma-separated incident fields to include. If None, all fields are included.
825
+ vendor, product, version (Optional[str]): Details about the event source.
826
+ datetime_iso (Optional[datetime]): Base timestamp for the incidents.
827
+ observables (Optional[Observables]): Optional observables object to provide values.
828
+ required_fields (Optional[str]): Required fields for the events.
816
829
 
817
830
  Returns:
818
- List[Dict]: A list of incident dictionaries. Each dictionary contains the following fields:
819
- - 'id' (int): A unique identifier for the incident.
820
- - 'type' (str): The type of incident.
821
- - 'duration' (int): The duration of the incident in hours.
822
- - 'analyst' (str): The name of the analyst assigned to the incident.
823
- - 'severity' (str, optional): The severity of the incident. Only included if 'severity' is specified
824
- in the 'fields' argument.
825
- - 'description' (str, optional): A brief description of the incident. Only included if 'description' is
826
- specified in the 'fields' argument.
827
- - 'events' (List[Dict], optional): A list of event dictionaries associated with the incident.
828
-
829
- Example:
830
- >>> incidents(count=3, fields='id,type,severity')
831
- [
832
- {'id': 1, 'type': 'Lateral Movement', 'severity': 'Critical'},
833
- {'id': 2, 'type': 'Access Violation', 'severity': 'High'},
834
- {'id': 3, 'type': 'Account Compromised', 'severity': 'Low'}
835
- ]
831
+ List[Dict]: A list of incident dictionaries.
836
832
  """
837
833
  incidents = []
838
- faker = cls._create_faker()
834
+ faker = Events.faker
835
+ datetime_iso = datetime_iso or datetime.now() - timedelta(hours=1)
836
+
837
+ # Generate analyst list if not provided in observables
838
+ incident_types = observables.incident_types if observables and observables.incident_types else INCIDENTS_TYPES
839
+ analysts = observables.analysts if observables and observables.analysts else [faker.unique.first_name() for _ in range(10)]
839
840
 
840
- incident_ids = set()
841
-
842
- incident_types = observables.incident_types if observables and observables.incident_types \
843
- else INCIDENTS_TYPES
844
- analysts = observables.analysts if observables and observables.analysts \
845
- else [faker.unique.first_name() for _ in range(10)]
846
- analyst_incident_map = {}
847
- for analyst in analysts:
848
- mapped_incident_type = incident_types.pop(0)
849
- analyst_incident_map[analyst] = mapped_incident_type
850
- incident_types.append(mapped_incident_type)
841
+ incident_type_cycle = itertools.cycle(incident_types)
851
842
  for i in range(count):
852
- incident = {}
843
+ incident_id = i + 1 # Simplify unique ID generation
853
844
  duration = random.randint(1, 5)
854
- while True:
855
- incident_id = random.randint(1, count)
856
- if incident_id not in incident_ids:
857
- incident_ids.add(incident_id)
858
- break
859
- incident_type = random.choice(incident_types)
845
+ incident_type = next(incident_type_cycle)
860
846
  analyst = random.choice(analysts)
861
- severity = random.choice(observables.severity) if observables and observables.severity \
862
- else faker.random_int(min=1, max=5)
863
- description = random.choice(observables.terms) if observables and observables.terms \
864
- else Observables.generator(observable_type=ObservableType.TERMS, known=ObservableKnown.BAD, count=1000)
865
- if analyst in analyst_incident_map and random.randint(1, 100) == 2:
866
- incident_type = analyst_incident_map[analyst]
867
- duration = random.randint(1, 2)
868
- if fields:
869
- field_list = fields.split(',')
870
- if 'id' in field_list:
871
- incident['id'] = incident_id
872
- if 'duration' in field_list:
873
- incident['duration'] = duration
874
- if 'type' in field_list:
875
- incident['type'] = incident_type
876
- if 'analyst' in field_list:
877
- incident['analyst'] = analyst
878
- if 'severity' in field_list:
879
- incident['severity'] = severity
880
- if 'description' in field_list:
881
- incident_description = faker.paragraph(nb_sentences=1, ext_word_list=description)
882
- incident['description'] = incident_description
883
- if 'events' in field_list:
884
- incident['events'] = [
885
- {"event": cls.syslog(count=1, datetime_iso=datetime_iso, observables=observables,
886
- required_fields=required_fields)[0]},
887
- {"event": cls.cef(count=1, datetime_iso=datetime_iso, vendor=vendor, product=product, version=version
888
- , observables=observables, required_fields=required_fields)[0]},
889
- {"event": cls.leef(count=1, datetime_iso=datetime_iso, vendor=vendor, product=product,
890
- version=version, observables=observables, required_fields=required_fields)[0]},
891
- {"event": cls.winevent(count=1, datetime_iso=datetime_iso, observables=observables)[0]},
892
- {"event": cls.json(count=1, datetime_iso=datetime_iso, vendor=vendor, product=product,
893
- version=version, observables=observables,
894
- required_fields=required_fields)[0]}
895
- ]
896
- else:
897
- incident = {
898
- "id": incident_id,
899
- "type": incident_type,
900
- "duration": duration,
901
- "analyst": analyst
902
- }
847
+ severity = Events._set_field('severity', observables) or faker.random_int(min=1, max=5)
848
+ description = Events._set_field('terms', observables) or faker.sentence(nb_words=10)
849
+
850
+ # Add base fields
851
+ incident = {}
852
+ field_list = fields.split(',') if fields else ['id', 'duration', 'type', 'analyst', 'severity', 'description', 'events']
853
+ if 'id' in field_list:
854
+ incident['id'] = incident_id
855
+ if 'duration' in field_list:
856
+ incident['duration'] = duration
857
+ if 'type' in field_list:
858
+ incident['type'] = incident_type
859
+ if 'analyst' in field_list:
860
+ incident['analyst'] = analyst
861
+ if 'severity' in field_list:
862
+ incident['severity'] = severity
863
+ if 'description' in field_list:
864
+ incident['description'] = description
865
+
866
+ # Generate associated events for each incident
867
+ if 'events' in field_list:
868
+ incident['events'] = [
869
+ {"event": cls.syslog(count=1, datetime_iso=datetime_iso, observables=observables, required_fields=required_fields)[0]},
870
+ {"event": cls.cef(count=1, datetime_iso=datetime_iso, vendor=vendor, product=product, version=version, observables=observables, required_fields=required_fields)[0]},
871
+ {"event": cls.leef(count=1, datetime_iso=datetime_iso, vendor=vendor, product=product, version=version, observables=observables, required_fields=required_fields)[0]},
872
+ {"event": cls.winevent(count=1, datetime_iso=datetime_iso, observables=observables)[0]},
873
+ {"event": cls.json(count=1, datetime_iso=datetime_iso, vendor=vendor, product=product, version=version, observables=observables, required_fields=required_fields)[0]}
874
+ ]
875
+
903
876
  incidents.append(incident)
877
+
904
878
  return incidents