assemblyline-core 4.6.0.dev11__tar.gz → 4.6.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/PKG-INFO +1 -1
- assemblyline_core-4.6.0.2/assemblyline_core/VERSION +1 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/dispatching/dispatcher.py +36 -8
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/plumber/run_plumber.py +123 -50
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/scaler/controllers/docker_ctl.py +9 -4
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core.egg-info/PKG-INFO +1 -1
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/test/test_plumber.py +52 -3
- assemblyline_core-4.6.0.dev11/assemblyline_core/VERSION +0 -1
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/LICENCE.md +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/README.md +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/__init__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/alerter/__init__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/alerter/processing.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/alerter/run_alerter.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/archiver/__init__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/archiver/run_archiver.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/badlist_client.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/dispatching/__init__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/dispatching/__main__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/dispatching/client.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/dispatching/schedules.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/dispatching/timeout.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/expiry/__init__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/expiry/run_expiry.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/ingester/__init__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/ingester/__main__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/ingester/constants.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/ingester/ingester.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/metrics/__init__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/metrics/es_metrics.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/metrics/heartbeat_formatter.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/metrics/helper.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/metrics/metrics_server.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/metrics/run_heartbeat_manager.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/metrics/run_metrics_aggregator.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/metrics/run_statistics_aggregator.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/plumber/__init__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/replay/__init__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/replay/client.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/replay/creator/__init__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/replay/creator/run.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/replay/creator/run_worker.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/replay/loader/__init__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/replay/loader/run.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/replay/loader/run_worker.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/replay/replay.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/safelist_client.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/scaler/__init__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/scaler/collection.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/scaler/controllers/__init__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/scaler/controllers/interface.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/scaler/controllers/kubernetes_ctl.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/scaler/run_scaler.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/scaler/scaler_server.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/server_base.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/signature_client.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/submission_client.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/tasking_client.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/updater/__init__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/updater/helper.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/updater/run_updater.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/vacuum/__init__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/vacuum/crawler.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/vacuum/department_map.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/vacuum/safelist.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/vacuum/stream_map.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/vacuum/worker.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/workflow/__init__.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/workflow/run_workflow.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core.egg-info/SOURCES.txt +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core.egg-info/dependency_links.txt +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core.egg-info/requires.txt +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core.egg-info/top_level.txt +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/setup.cfg +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/setup.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/test/test_alerter.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/test/test_badlist_client.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/test/test_dispatcher.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/test/test_expiry.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/test/test_replay.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/test/test_safelist_client.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/test/test_scaler.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/test/test_scheduler.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/test/test_signature_client.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/test/test_simulation.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/test/test_tasking_client.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/test/test_vacuum.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/test/test_worker_ingest.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/test/test_worker_submit.py +0 -0
- {assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/test/test_workflow.py +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
4.6.0.2
|
@@ -54,7 +54,7 @@ from assemblyline.odm.messages.task import Task as ServiceTask
|
|
54
54
|
from assemblyline.odm.models.error import Error
|
55
55
|
from assemblyline.odm.models.result import Result
|
56
56
|
from assemblyline.odm.models.service import Service
|
57
|
-
from assemblyline.odm.models.submission import Submission
|
57
|
+
from assemblyline.odm.models.submission import Submission, TraceEvent
|
58
58
|
from assemblyline.odm.models.user import User
|
59
59
|
from assemblyline.remote.datatypes.events import EventWatcher
|
60
60
|
from assemblyline.remote.datatypes.exporting_counter import export_metrics_once
|
@@ -311,6 +311,16 @@ class SubmissionTask:
|
|
311
311
|
"""Shortcut to read submission SID"""
|
312
312
|
return self.submission.sid
|
313
313
|
|
314
|
+
def trace(self, event_type: str, sha256: Optional[str] = None,
|
315
|
+
service: Optional[str] = None, message: Optional[str] = None) -> None:
|
316
|
+
if self.submission.params.trace:
|
317
|
+
self.submission.tracing_events.append(TraceEvent({
|
318
|
+
'event_type': event_type,
|
319
|
+
'service': service,
|
320
|
+
'file': sha256,
|
321
|
+
'message': message,
|
322
|
+
}))
|
323
|
+
|
314
324
|
def forbid_for_children(self, sha256: str, service_name: str):
|
315
325
|
"""Mark that children of a given file should not be routed to a service."""
|
316
326
|
try:
|
@@ -729,6 +739,7 @@ class Dispatcher(ThreadedCoreBase):
|
|
729
739
|
|
730
740
|
if not self.active_submissions.exists(sid):
|
731
741
|
self.log.info("[%s] New submission received", sid)
|
742
|
+
task.trace('submission_start')
|
732
743
|
self.active_submissions.add(sid, {
|
733
744
|
'completed_queue': task.completed_queue,
|
734
745
|
'submission': submission.as_primitives()
|
@@ -742,6 +753,7 @@ class Dispatcher(ThreadedCoreBase):
|
|
742
753
|
}).as_primitives())
|
743
754
|
|
744
755
|
else:
|
756
|
+
task.trace('submission_start', message='Received a pre-existing submission')
|
745
757
|
self.log.warning(f"[{sid}] Received a pre-existing submission, check if it is complete")
|
746
758
|
|
747
759
|
# Refresh the quota hold
|
@@ -840,6 +852,7 @@ class Dispatcher(ThreadedCoreBase):
|
|
840
852
|
sid = submission.sid
|
841
853
|
if self.apm_client:
|
842
854
|
elasticapm.label(sid=sid, sha256=sha256)
|
855
|
+
task.trace('dispatch_file', sha256=sha256)
|
843
856
|
|
844
857
|
file_depth: int = task.file_depth[sha256]
|
845
858
|
# If its the first time we've seen this file, we won't have a schedule for it
|
@@ -859,6 +872,9 @@ class Dispatcher(ThreadedCoreBase):
|
|
859
872
|
task.file_schedules[sha256] = self.scheduler.build_schedule(submission, file_info.type,
|
860
873
|
file_depth, forbidden_services,
|
861
874
|
task.service_access_control)
|
875
|
+
schedule_summary = [list(stage.keys()) for stage in task.file_schedules[sha256]]
|
876
|
+
task.trace('schedule_built', sha256=sha256, message=str(schedule_summary))
|
877
|
+
|
862
878
|
|
863
879
|
file_info = task.file_info[sha256]
|
864
880
|
schedule: list = list(task.file_schedules[sha256])
|
@@ -990,9 +1006,9 @@ class Dispatcher(ThreadedCoreBase):
|
|
990
1006
|
|
991
1007
|
if sent or enqueued or running:
|
992
1008
|
# If we have confirmed that we are waiting, or have taken an action, log that.
|
993
|
-
|
994
|
-
|
995
|
-
|
1009
|
+
logs = f"sent to: {sent} already in queue for: {enqueued} running on: {running}"
|
1010
|
+
self.log.info(f"[{sid}] File {sha256} {logs}")
|
1011
|
+
task.trace('dispatch_file_result', sha256=sha256, message=logs)
|
996
1012
|
return False
|
997
1013
|
elif skipped:
|
998
1014
|
# Not waiting for anything, and have started skipping what is left over
|
@@ -1007,10 +1023,12 @@ class Dispatcher(ThreadedCoreBase):
|
|
1007
1023
|
|
1008
1024
|
self.counter.increment('files_completed')
|
1009
1025
|
if len(task.queue_keys) > 0 or len(task.running_services) > 0:
|
1010
|
-
|
1011
|
-
|
1026
|
+
logs = f"queued: {len(task.queue_keys)} running: {len(task.running_services)}"
|
1027
|
+
self.log.info(f"[{sid}] Finished processing file '{sha256}', submission incomplete ({logs})")
|
1028
|
+
task.trace('file_finished', sha256=sha256, message=logs)
|
1012
1029
|
else:
|
1013
1030
|
self.log.info(f"[{sid}] Finished processing file '{sha256}', checking if submission complete")
|
1031
|
+
task.trace('file_finished', sha256=sha256)
|
1014
1032
|
return self.check_submission(task)
|
1015
1033
|
return False
|
1016
1034
|
|
@@ -1104,14 +1122,17 @@ class Dispatcher(ThreadedCoreBase):
|
|
1104
1122
|
# file isn't done yet, and hasn't been filtered by any of the previous few steps
|
1105
1123
|
# poke those files.
|
1106
1124
|
if pending_files:
|
1125
|
+
task.trace('submission_check', message=f"Dispatching {list(pending_files)}")
|
1107
1126
|
self.log.debug(f"[{task.submission.sid}] Dispatching {len(pending_files)} files: {list(pending_files)}")
|
1108
1127
|
for file_hash in pending_files:
|
1109
1128
|
if self.dispatch_file(task, file_hash):
|
1110
1129
|
return True
|
1111
1130
|
elif processing_files:
|
1131
|
+
task.trace('submission_check', message=f"Waiting for {list(processing_files)}")
|
1112
1132
|
self.log.debug("[%s] Not finished waiting on %d files: %s",
|
1113
1133
|
task.submission.sid, len(processing_files), list(processing_files))
|
1114
1134
|
else:
|
1135
|
+
task.trace('submission_check', message="Finished")
|
1115
1136
|
self.log.debug("[%s] Finalizing submission.", task.submission.sid)
|
1116
1137
|
max_score = max(file_scores.values()) if file_scores else 0 # Submissions with no results have no score
|
1117
1138
|
if self.tasks.pop(task.sid, None):
|
@@ -1341,6 +1362,8 @@ class Dispatcher(ThreadedCoreBase):
|
|
1341
1362
|
self.log.warning(f'[{message.sid}] Service started missing data.')
|
1342
1363
|
continue
|
1343
1364
|
|
1365
|
+
task.trace('service_start', sha256=message.sha, service=message.service_name,
|
1366
|
+
message=message.worker_id)
|
1344
1367
|
key = (message.sha, message.service_name)
|
1345
1368
|
if task.queue_keys.pop(key, None) is not None:
|
1346
1369
|
# If this task is already finished (result message processed before start
|
@@ -1384,8 +1407,9 @@ class Dispatcher(ThreadedCoreBase):
|
|
1384
1407
|
continue
|
1385
1408
|
|
1386
1409
|
if task:
|
1387
|
-
|
1388
|
-
|
1410
|
+
log = f'Service timeout at {now_as_iso()} on worker {message.worker_id}'
|
1411
|
+
task.service_logs[(message.sha, message.service_name)].append(log)
|
1412
|
+
task.trace('service_timeout', sha256=message.sha, service=message.service_name, message=log)
|
1389
1413
|
self.timeout_service(task, message.sha, message.service_name, message.worker_id)
|
1390
1414
|
|
1391
1415
|
elif kind == Action.dispatch_file:
|
@@ -1429,6 +1453,8 @@ class Dispatcher(ThreadedCoreBase):
|
|
1429
1453
|
self.log.exception(f"Malformed result message, missing key: {missing}")
|
1430
1454
|
return
|
1431
1455
|
|
1456
|
+
task.trace('process_result', sha256=sha256, service=service_name, message="Processing result " + summary.key)
|
1457
|
+
|
1432
1458
|
# Add SHA256s of files that allowed to run regardless of Dynamic Recursion Prevention
|
1433
1459
|
task.dynamic_recursion_bypass = task.dynamic_recursion_bypass.union(set(dynamic_recursion_bypass))
|
1434
1460
|
|
@@ -1599,6 +1625,8 @@ class Dispatcher(ThreadedCoreBase):
|
|
1599
1625
|
@elasticapm.capture_span(span_type='dispatcher')
|
1600
1626
|
def process_service_error(self, task: SubmissionTask, error_key, error: Error):
|
1601
1627
|
self.log.info(f'[{task.submission.sid}] Error from service {error.response.service_name} on {error.sha256}')
|
1628
|
+
task.trace('process_error', sha256=error.sha256, service=error.response.service_name,
|
1629
|
+
message="Service error " + error_key)
|
1602
1630
|
self.clear_timeout(task, error.sha256, error.response.service_name)
|
1603
1631
|
key = (error.sha256, error.response.service_name)
|
1604
1632
|
if error.response.status == "FAIL_NONRECOVERABLE":
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/plumber/run_plumber.py
RENAMED
@@ -7,22 +7,22 @@ disabled or deleted for which a service queue exists, the dispatcher will be inf
|
|
7
7
|
had an error.
|
8
8
|
"""
|
9
9
|
import threading
|
10
|
-
import
|
10
|
+
from copy import deepcopy
|
11
11
|
from typing import Optional
|
12
12
|
|
13
13
|
from assemblyline.common.constants import service_queue_name
|
14
|
-
from assemblyline.common.forge import
|
14
|
+
from assemblyline.common.forge import get_config, get_service_queue
|
15
15
|
from assemblyline.common.isotime import DAY_IN_SECONDS, now_as_iso
|
16
16
|
from assemblyline.odm.models.apikey import get_apikey_id
|
17
17
|
from assemblyline.odm.models.error import Error
|
18
18
|
from assemblyline.odm.models.service import Service
|
19
19
|
from assemblyline.odm.models.user import load_roles, load_roles_form_acls
|
20
|
+
from assemblyline.odm.models.user_settings import SubmissionProfileParams
|
20
21
|
from assemblyline.remote.datatypes import retry_call
|
21
22
|
from assemblyline.remote.datatypes.queues.named import NamedQueue
|
22
23
|
from assemblyline_core.dispatching.client import DispatchClient
|
23
24
|
from assemblyline_core.server_base import CoreBase, ServiceStage
|
24
25
|
|
25
|
-
|
26
26
|
DAY = 60 * 60 * 24
|
27
27
|
TASK_DELETE_CHUNK = 10000
|
28
28
|
|
@@ -56,8 +56,14 @@ class Plumber(CoreBase):
|
|
56
56
|
name="redis_notification_queue_cleanup")
|
57
57
|
nq_thread.start()
|
58
58
|
|
59
|
+
# Start a user apikey cleanup thread
|
59
60
|
ua_thread = threading.Thread(target=self.user_apikey_cleanup, daemon=True, name="user_apikey_cleanup")
|
60
61
|
ua_thread.start()
|
62
|
+
|
63
|
+
# Start a user setting migration thread
|
64
|
+
usm_thread = threading.Thread(target=self.migrate_user_settings, daemon=True, name="migrate_user_settings")
|
65
|
+
usm_thread.start()
|
66
|
+
|
61
67
|
self.service_queue_plumbing()
|
62
68
|
|
63
69
|
def service_queue_plumbing(self):
|
@@ -193,60 +199,127 @@ class Plumber(CoreBase):
|
|
193
199
|
self.sleep(2)
|
194
200
|
self.log.info(f"Done watching {service_name} service queue")
|
195
201
|
|
196
|
-
|
197
|
-
|
198
202
|
def user_apikey_cleanup(self):
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
total = 1
|
203
|
-
cur_total = 0
|
204
|
-
|
205
|
-
config = get_config()
|
206
|
-
apikey_max_dtl = config.auth.apikey_max_dtl
|
207
|
-
|
208
|
-
expiry_ts = now_as_iso(apikey_max_dtl * DAY_IN_SECONDS) if apikey_max_dtl is not None else None
|
209
|
-
|
210
|
-
while cur_total < total:
|
211
|
-
result = self.datastore.user.search(query, offset=offset, rows=rows)
|
212
|
-
total = result.get('total', 0)
|
213
|
-
cur_total = cur_total + (result.get("count", total))
|
214
|
-
|
215
|
-
# check for API keys in total
|
216
|
-
users = result.get('items', [])
|
203
|
+
expiry_ts = None
|
204
|
+
if self.config.auth.apikey_max_dtl is not None:
|
205
|
+
expiry_ts = now_as_iso(self.config.auth.apikey_max_dtl * DAY_IN_SECONDS)
|
217
206
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
apikeys = user.apikeys
|
207
|
+
for user in self.datastore.user.stream_search(query="*", fl="*", as_obj=False):
|
208
|
+
uname = user['uname']
|
209
|
+
apikeys = user['apikeys']
|
222
210
|
|
223
|
-
|
224
|
-
|
225
|
-
|
211
|
+
for key in apikeys:
|
212
|
+
old_apikey = apikeys[key]
|
213
|
+
key_id = get_apikey_id(key, uname)
|
226
214
|
|
227
|
-
|
228
|
-
|
215
|
+
roles = None
|
216
|
+
if old_apikey['acl'] == ["C"]:
|
229
217
|
|
230
|
-
|
231
|
-
if r in load_roles(user['type'], user['roles'])]
|
232
|
-
|
233
|
-
else:
|
234
|
-
roles = [r for r in load_roles_form_acls(old_apikey['acl'], roles)
|
218
|
+
roles = [r for r in old_apikey['roles']
|
235
219
|
if r in load_roles(user['type'], user['roles'])]
|
236
|
-
new_apikey = {
|
237
|
-
"password": old_apikey['password'],
|
238
|
-
"acl": old_apikey['acl'],
|
239
|
-
"uname": uname,
|
240
|
-
"key_name": key,
|
241
|
-
"roles": roles,
|
242
|
-
"expiry_ts": expiry_ts
|
243
|
-
}
|
244
|
-
self.datastore.apikey.save(key_id, new_apikey)
|
245
|
-
|
246
|
-
user['apikeys'] = {}
|
247
|
-
self.datastore.user.save(uname, user)
|
248
|
-
|
249
220
|
|
221
|
+
else:
|
222
|
+
roles = [r for r in load_roles_form_acls(old_apikey['acl'], roles)
|
223
|
+
if r in load_roles(user['type'], user['roles'])]
|
224
|
+
new_apikey = {
|
225
|
+
"password": old_apikey['password'],
|
226
|
+
"acl": old_apikey['acl'],
|
227
|
+
"uname": uname,
|
228
|
+
"key_name": key,
|
229
|
+
"roles": roles,
|
230
|
+
"expiry_ts": expiry_ts
|
231
|
+
}
|
232
|
+
self.datastore.apikey.save(key_id, new_apikey)
|
233
|
+
|
234
|
+
user['apikeys'] = {}
|
235
|
+
self.datastore.user.save(uname, user)
|
236
|
+
|
237
|
+
# Commit changes made to indices
|
238
|
+
self.datastore.user.commit()
|
239
|
+
self.datastore.apikey.commit()
|
240
|
+
|
241
|
+
def migrate_user_settings(self):
|
242
|
+
service_list = self.datastore.list_all_services(as_obj=False)
|
243
|
+
|
244
|
+
# Migrate user settings to the new format
|
245
|
+
for doc in self.datastore.user_settings.scan_with_search_after(query={
|
246
|
+
"bool": {
|
247
|
+
"must": {
|
248
|
+
"query_string": {
|
249
|
+
"query": "*",
|
250
|
+
}
|
251
|
+
}
|
252
|
+
}
|
253
|
+
}):
|
254
|
+
user_settings = doc["_source"]
|
255
|
+
if user_settings.get('submission_profiles'):
|
256
|
+
# User settings already migrated, skip
|
257
|
+
continue
|
258
|
+
|
259
|
+
# Create the list of submission profiles
|
260
|
+
submission_profiles = {
|
261
|
+
# Grant everyone a default of which all settings from before 4.6 should be transferred
|
262
|
+
"default" : SubmissionProfileParams(user_settings).as_primitives(strip_null=True)
|
263
|
+
}
|
264
|
+
|
265
|
+
profile_error_checks = {}
|
266
|
+
# Try to apply the original settings to the system-defined profiles
|
267
|
+
for profile in self.config.submission.profiles:
|
268
|
+
updates = deepcopy(user_settings)
|
269
|
+
profile_error_checks.setdefault(profile.name, 0)
|
270
|
+
validated_profile = profile.params.as_primitives(strip_null=True)
|
271
|
+
|
272
|
+
updates.setdefault("services", {})
|
273
|
+
updates["services"].setdefault("selected", [])
|
274
|
+
updates["services"].setdefault("excluded", [])
|
275
|
+
|
276
|
+
# Append the exclusion list set by the profile
|
277
|
+
updates['services']['excluded'] = updates['services']['excluded'] + \
|
278
|
+
list(validated_profile.get("services", {}).get("excluded", []))
|
279
|
+
|
280
|
+
# Ensure the selected services are not in the excluded list
|
281
|
+
for service in service_list:
|
282
|
+
if service['name'] in updates['services']['selected'] and service['name'] in updates['services']['excluded']:
|
283
|
+
updates['services']['selected'].remove(service['name'])
|
284
|
+
profile_error_checks[profile.name] += 1
|
285
|
+
elif service['category'] in updates['services']['selected'] and service['category'] in updates['services']['excluded']:
|
286
|
+
updates['services']['selected'].remove(service['category'])
|
287
|
+
profile_error_checks[profile.name] += 1
|
288
|
+
|
289
|
+
|
290
|
+
# Check the services parameters
|
291
|
+
for param_type, list_of_params in profile.restricted_params.items():
|
292
|
+
# Check if there are restricted submission parameters
|
293
|
+
if param_type == "submission":
|
294
|
+
requested_params = (set(list_of_params) & set(updates.keys())) - set({'services', 'service_spec'})
|
295
|
+
if requested_params:
|
296
|
+
# Track the number of errors for each profile
|
297
|
+
profile_error_checks[profile.name] += len(requested_params)
|
298
|
+
for param in requested_params:
|
299
|
+
# Remove the parameter from the updates
|
300
|
+
updates.pop(param, None)
|
301
|
+
|
302
|
+
# Check if there are restricted service parameters
|
303
|
+
else:
|
304
|
+
service_spec = updates.get('service_spec', {}).get(param_type, {})
|
305
|
+
requested_params = set(list_of_params) & set(service_spec)
|
306
|
+
if requested_params:
|
307
|
+
# Track the number of errors for each profile
|
308
|
+
profile_error_checks[profile.name] += len(requested_params)
|
309
|
+
for param in requested_params:
|
310
|
+
# Remove the parameter from the updates
|
311
|
+
service_spec.pop(param, None)
|
312
|
+
|
313
|
+
if not service_spec:
|
314
|
+
# Remove the service spec if empty
|
315
|
+
updates['service_spec'].pop(param_type, None)
|
316
|
+
submission_profiles[profile.name] = SubmissionProfileParams(updates).as_primitives(strip_null=True)
|
317
|
+
|
318
|
+
# Assign the profile with the least number of errors
|
319
|
+
user_settings['submission_profiles'] = submission_profiles
|
320
|
+
user_settings['preferred_submission_profile'] = sorted(profile_error_checks.items(), key=lambda x: x[1])[0][0]
|
321
|
+
|
322
|
+
self.datastore.user_settings.save(doc["_id"], user_settings)
|
250
323
|
|
251
324
|
|
252
325
|
|
@@ -1,13 +1,16 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
|
2
|
+
|
3
3
|
import os
|
4
4
|
import threading
|
5
5
|
import time
|
6
|
-
from collections import defaultdict
|
7
|
-
from typing import List, Optional, Tuple, Dict
|
8
6
|
import uuid
|
7
|
+
from collections import defaultdict
|
8
|
+
from typing import Dict, List, Optional, Tuple
|
9
|
+
|
10
|
+
import docker
|
9
11
|
|
10
12
|
from assemblyline.odm.models.service import DependencyConfig, DockerConfig
|
13
|
+
|
11
14
|
from .interface import ControllerInterface, ServiceControlError
|
12
15
|
|
13
16
|
# Where to find the update directory inside this container.
|
@@ -382,7 +385,7 @@ class DockerController(ControllerInterface):
|
|
382
385
|
container.labels]
|
383
386
|
running = running[0:-delta]
|
384
387
|
for container in running:
|
385
|
-
container.kill()
|
388
|
+
container.kill(signal='SIGTERM')
|
386
389
|
|
387
390
|
if delta > 0:
|
388
391
|
# Start delta instances of the service
|
@@ -531,6 +534,7 @@ class DockerController(ControllerInterface):
|
|
531
534
|
dynamically rather than in prepare_network.
|
532
535
|
"""
|
533
536
|
from docker.errors import NotFound
|
537
|
+
|
534
538
|
# Create network for service
|
535
539
|
network_name = f'{COMPOSE_PROJECT}_service-net-{service_name}'
|
536
540
|
try:
|
@@ -553,6 +557,7 @@ class DockerController(ControllerInterface):
|
|
553
557
|
This lets us override the auth_config on a per image basis.
|
554
558
|
"""
|
555
559
|
from docker.errors import ImageNotFound
|
560
|
+
|
556
561
|
# Split the image string into "[registry/]image_name" and "tag"
|
557
562
|
repository, _, tag = service.container_config.image.rpartition(':')
|
558
563
|
if '/' in tag:
|
@@ -1,14 +1,16 @@
|
|
1
1
|
from time import sleep
|
2
2
|
from unittest import mock
|
3
3
|
|
4
|
-
from assemblyline.odm.messages.task import Task
|
5
|
-
from assemblyline.odm.models.service import Service
|
6
|
-
from assemblyline.odm.random_data import random_model_obj
|
7
4
|
from assemblyline_core.plumber.run_plumber import Plumber
|
8
5
|
from assemblyline_core.server_base import ServiceStage
|
9
6
|
from mocking import TrueCountTimes
|
10
7
|
from redis import Redis
|
11
8
|
|
9
|
+
from assemblyline.odm.messages.task import Task
|
10
|
+
from assemblyline.odm.models.service import Service
|
11
|
+
from assemblyline.odm.models.user import ApiKey, User
|
12
|
+
from assemblyline.odm.random_data import random_model_obj
|
13
|
+
|
12
14
|
|
13
15
|
def test_expire_missing_service():
|
14
16
|
redis = mock.MagicMock(spec=Redis)
|
@@ -107,3 +109,50 @@ def test_cleanup_old_tasks(datastore_connection):
|
|
107
109
|
q="task.start_time_in_millis:0",
|
108
110
|
track_total_hits=True,
|
109
111
|
size=0)['hits']['total']['value'] == 0
|
112
|
+
|
113
|
+
def test_user_setting_migrations(datastore_connection):
|
114
|
+
from assemblyline.odm.models.config import SubmissionProfileParams
|
115
|
+
|
116
|
+
SubmissionProfileParams.fields().keys()
|
117
|
+
# Create a bunch of random "old" tasks and clean them up
|
118
|
+
redis = mock.MagicMock(spec=Redis)
|
119
|
+
redis_persist = mock.MagicMock(spec=Redis)
|
120
|
+
plumber = Plumber(redis=redis, redis_persist=redis_persist, datastore=datastore_connection, delay=1)
|
121
|
+
|
122
|
+
# Create a user with old settings (format prior to 4.6)
|
123
|
+
settings = {'classification': 'TLP:CLEAR', 'deep_scan': False, 'description': '', 'download_encoding': 'cart', 'default_external_sources': ['Malware Bazaar', 'VirusTotal'], 'default_zip_password': 'zippy', 'executive_summary': False, 'expand_min_score': 500, 'generate_alert': False, 'ignore_cache': False, 'ignore_dynamic_recursion_prevention': False, 'ignore_recursion_prevention': False, 'ignore_filtering': False, 'malicious': False, 'priority': 369, 'profile': False, 'service_spec': {'AVClass': {'include_malpedia_dataset': False}}, 'services': {'selected': ['Extraction', 'ConfigExtractor', 'YARA'], 'excluded': [], 'rescan': [], 'resubmit': [], 'runtime_excluded': []}, 'submission_view': 'report', 'ttl': 0}
|
124
|
+
|
125
|
+
user_account = random_model_obj(User, as_json=True)
|
126
|
+
user_account['uname'] = "admin"
|
127
|
+
user_account['apikeys'] = {'test': random_model_obj(ApiKey, as_json=True)}
|
128
|
+
datastore_connection.ds.client.index(index="user_settings", id="admin", document=settings)
|
129
|
+
datastore_connection.ds.client.index(index="user", id="admin", document=user_account)
|
130
|
+
|
131
|
+
datastore_connection.user_settings.commit()
|
132
|
+
datastore_connection.user.commit()
|
133
|
+
|
134
|
+
# Initiate the migration
|
135
|
+
plumber.user_apikey_cleanup()
|
136
|
+
plumber.migrate_user_settings()
|
137
|
+
|
138
|
+
# Check that the settings have been migrated
|
139
|
+
migrated_settings = datastore_connection.user_settings.get("admin", as_obj=False)
|
140
|
+
|
141
|
+
# Check to see if API keys for the user were transferred to the new index
|
142
|
+
assert datastore_connection.apikey.search('uname:admin', rows=0)['total'] > 0
|
143
|
+
|
144
|
+
# Deprecated settings should be removed
|
145
|
+
assert "ignore_dynamic_recursion_prevention" not in migrated_settings
|
146
|
+
|
147
|
+
# All former submission settings at the root-level should be moved to submission profiles
|
148
|
+
assert all([key not in migrated_settings for key in SubmissionProfileParams.fields().keys()] )
|
149
|
+
|
150
|
+
for settings in migrated_settings['submission_profiles'].values():
|
151
|
+
assert settings['classification'] == 'TLP:C'
|
152
|
+
assert settings['deep_scan'] is False
|
153
|
+
assert settings['generate_alert'] is False
|
154
|
+
assert settings['ignore_cache'] is False
|
155
|
+
assert settings['priority'] == 369
|
156
|
+
# Full service spec should be preserved in default profile (along with others by default if there's no restricted parameters)
|
157
|
+
assert settings['service_spec'] == {'AVClass': {'include_malpedia_dataset': False}}
|
158
|
+
assert settings['ttl'] == 0
|
@@ -1 +0,0 @@
|
|
1
|
-
4.6.0.dev11
|
File without changes
|
File without changes
|
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/alerter/__init__.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/alerter/processing.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/alerter/run_alerter.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/archiver/__init__.py
RENAMED
File without changes
|
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/badlist_client.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/dispatching/client.py
RENAMED
File without changes
|
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/dispatching/timeout.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/expiry/__init__.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/expiry/run_expiry.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/ingester/__init__.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/ingester/__main__.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/ingester/constants.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/ingester/ingester.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/metrics/__init__.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/metrics/es_metrics.py
RENAMED
File without changes
|
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/metrics/helper.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/plumber/__init__.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/replay/__init__.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/replay/client.py
RENAMED
File without changes
|
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/replay/creator/run.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/replay/loader/run.py
RENAMED
File without changes
|
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/replay/replay.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/safelist_client.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/scaler/__init__.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/scaler/collection.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/scaler/run_scaler.py
RENAMED
File without changes
|
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/server_base.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/signature_client.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/submission_client.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/tasking_client.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/updater/__init__.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/updater/helper.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/updater/run_updater.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/vacuum/__init__.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/vacuum/crawler.py
RENAMED
File without changes
|
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/vacuum/safelist.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/vacuum/stream_map.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/vacuum/worker.py
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core/workflow/__init__.py
RENAMED
File without changes
|
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core.egg-info/SOURCES.txt
RENAMED
File without changes
|
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core.egg-info/requires.txt
RENAMED
File without changes
|
{assemblyline_core-4.6.0.dev11 → assemblyline_core-4.6.0.2}/assemblyline_core.egg-info/top_level.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|