maco 1.2.3__py3-none-any.whl → 1.2.4__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.
File without changes
@@ -3,7 +3,7 @@ from typing import List, Optional
3
3
 
4
4
  from maco import extractor, model, yara
5
5
 
6
- from complex import complex_utils
6
+ from demo_extractors.complex import complex_utils
7
7
 
8
8
 
9
9
  class Complex(extractor.Extractor):
@@ -3,7 +3,7 @@ from typing import Dict, List, Optional
3
3
 
4
4
  from maco import extractor, model, yara
5
5
 
6
- from . import shared
6
+ from demo_extractors import shared
7
7
 
8
8
 
9
9
  class LimitOther(extractor.Extractor):
maco/collector.py CHANGED
@@ -2,8 +2,9 @@
2
2
 
3
3
  import inspect
4
4
  import logging
5
+ import logging.handlers
5
6
  import os
6
- from multiprocessing import Manager, Process
7
+ from multiprocessing import Manager, Process, Queue
7
8
  from tempfile import NamedTemporaryFile
8
9
  from types import ModuleType
9
10
  from typing import Any, BinaryIO, Dict, List, Union
@@ -86,21 +87,33 @@ class Collector:
86
87
  )
87
88
  namespaced_rules[name] = member.yara_rule or extractor.DEFAULT_YARA_RULE.format(name=name)
88
89
 
90
+ # multiprocess logging is awkward - set up a queue to ensure we can log
91
+ logging_queue = Queue()
92
+ queue_handler = logging.handlers.QueueListener(logging_queue,*logging.getLogger().handlers)
93
+ queue_handler.start()
94
+
89
95
  # Find the extractors within the given directory
90
96
  # Execute within a child process to ensure main process interpreter is kept clean
91
97
  p = Process(
92
- target=utils.import_extractors,
98
+ target=utils.proxy_logging,
93
99
  args=(
94
- path_extractors,
95
- yara.compile(source=utils.MACO_YARA_RULE),
100
+ logging_queue,
101
+ utils.import_extractors,
96
102
  extractor_module_callback,
97
- logger,
98
- create_venv and os.path.isdir(path_extractors),
103
+ ),
104
+ kwargs=dict(
105
+ root_directory=path_extractors,
106
+ scanner=yara.compile(source=utils.MACO_YARA_RULE),
107
+ create_venv=create_venv and os.path.isdir(path_extractors),
99
108
  ),
100
109
  )
101
110
  p.start()
102
111
  p.join()
103
112
 
113
+ # stop multiprocess logging
114
+ queue_handler.stop()
115
+ logging_queue.close()
116
+
104
117
  self.extractors = dict(extractors)
105
118
  if not self.extractors:
106
119
  raise ExtractorLoadError("no extractors were loaded")
maco/utils.py CHANGED
@@ -4,11 +4,14 @@ import importlib.machinery
4
4
  import importlib.util
5
5
  import inspect
6
6
  import json
7
+ import logging.handlers
7
8
  import os
8
9
  import re
9
10
  import shutil
10
11
  import subprocess
11
12
  import sys
13
+ import multiprocessing
14
+ import logging
12
15
  import tempfile
13
16
 
14
17
  from maco import yara
@@ -28,10 +31,12 @@ from typing import Callable, Dict, List, Set, Tuple
28
31
 
29
32
  from maco.extractor import Extractor
30
33
 
34
+ logger = logging.getLogger("maco.lib.utils")
35
+
31
36
  VENV_DIRECTORY_NAME = ".venv"
32
37
 
33
- RELATIVE_FROM_RE = re.compile("from (\.+)")
34
- RELATIVE_FROM_IMPORT_RE = re.compile("from (\.+) import")
38
+ RELATIVE_FROM_RE = re.compile(r"from (\.+)")
39
+ RELATIVE_FROM_IMPORT_RE = re.compile(r"from (\.+) import")
35
40
 
36
41
  try:
37
42
  # Attempt to use the uv package manager (Recommended)
@@ -69,6 +74,7 @@ import importlib
69
74
  import json
70
75
  import os
71
76
  import sys
77
+ import logging
72
78
 
73
79
  try:
74
80
  from maco import yara
@@ -76,6 +82,19 @@ except:
76
82
  import yara
77
83
 
78
84
  from base64 import b64encode
85
+
86
+ # ensure we have a logger to stderr
87
+ import logging
88
+ logger = logging.getLogger()
89
+ logger.setLevel(logging.DEBUG)
90
+ sh = logging.StreamHandler()
91
+ logger.addHandler(sh)
92
+ sh.setLevel(logging.DEBUG)
93
+ formatter = logging.Formatter(
94
+ fmt="%(asctime)s, [%(levelname)s] %(module)s.%(funcName)s: %(message)s", datefmt="%Y-%m-%d (%H:%M:%S)"
95
+ )
96
+ sh.setFormatter(formatter)
97
+
79
98
  parent_package_path = "{parent_package_path}"
80
99
  sys.path.insert(1, parent_package_path)
81
100
  mod = importlib.import_module("{module_name}")
@@ -101,7 +120,7 @@ with open("{output_path}", 'w') as fp:
101
120
  json.dump(result.dict(exclude_defaults=True, exclude_none=True), fp, cls=Base64Encoder)
102
121
  """
103
122
 
104
- MACO_YARA_RULE = """
123
+ MACO_YARA_RULE = r"""
105
124
  rule MACO {
106
125
  meta:
107
126
  desc = "Used to match on Python files that contain MACO extractors"
@@ -292,15 +311,10 @@ def register_extractors(
292
311
  ):
293
312
  package_name = os.path.basename(current_directory)
294
313
  parent_directory = os.path.dirname(current_directory)
295
- symlink = None
296
- while package_name in sys.modules:
297
- # Package name conflicts with an existing loaded module, let's deconflict that
298
- package_name = f"_{package_name}"
299
-
300
- # We'll need to create a link back to the original
301
- if package_name not in sys.modules:
302
- symlink = os.path.join(parent_directory, package_name)
303
- os.symlink(current_directory, symlink)
314
+ if package_name in sys.modules:
315
+ # this may happen as part of testing if some part of the extractor code was directly imported
316
+ logger.warning(f"Looks like {package_name} is already loaded. "
317
+ "If your maco extractor overlaps an existing package name this could cause problems.")
304
318
 
305
319
  try:
306
320
  # Modify the PATH so we can recognize this new package on import
@@ -351,10 +365,6 @@ def register_extractors(
351
365
  # Remove any modules that were loaded to deconflict with later modules loads
352
366
  [sys.modules.pop(k) for k in set(sys.modules.keys()) - default_loaded_modules]
353
367
 
354
- # Cleanup any symlinks
355
- if symlink:
356
- os.remove(symlink)
357
-
358
368
  # If there still exists extractor files we haven't found yet, try searching in the available subdirectories
359
369
  if extractor_files:
360
370
  for dir in os.listdir(current_directory):
@@ -379,14 +389,22 @@ def register_extractors(
379
389
  # We were able to find all the extractor files
380
390
  break
381
391
 
392
+ def proxy_logging(queue: multiprocessing.Queue, callback: Callable[[ModuleType, str], None], *args, **kwargs):
393
+ """Ensures logging is set up correctly for a child process and then executes the callback."""
394
+ logger = logging.getLogger()
395
+ qh = logging.handlers.QueueHandler(queue)
396
+ qh.setLevel(logging.DEBUG)
397
+ logger.addHandler(qh)
398
+ callback(*args, **kwargs, logger=logger)
382
399
 
383
400
  def import_extractors(
401
+ extractor_module_callback: Callable[[ModuleType, str], bool],
402
+ *,
384
403
  root_directory: str,
385
404
  scanner: yara.Rules,
386
- extractor_module_callback: Callable[[ModuleType, str], bool],
387
- logger: Logger,
388
405
  create_venv: bool = False,
389
406
  python_version: str = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
407
+ logger: Logger,
390
408
  ):
391
409
  extractor_dirs, extractor_files = scan_for_extractors(root_directory, scanner, logger)
392
410
 
@@ -453,14 +471,20 @@ def run_extractor(
453
471
  cwd=cwd,
454
472
  capture_output=True,
455
473
  )
474
+ stderr = proc.stderr.decode()
456
475
  try:
457
476
  # Load results and return them
458
477
  output.seek(0)
459
- return json.load(output, cls=json_decoder)
460
- except Exception:
478
+ loaded = json.load(output, cls=json_decoder)
479
+ except Exception as e:
461
480
  # If there was an error raised during runtime, then propagate
462
481
  delim = f'File "{module_path}"'
463
- exception = proc.stderr.decode()
482
+ exception = stderr
464
483
  if delim in exception:
465
484
  exception = f"{delim}{exception.split(delim, 1)[1]}"
466
- raise Exception(exception)
485
+ # print extractor logging at error level
486
+ logger.error(f"maco extractor raised exception, stderr:\n{stderr}")
487
+ raise Exception(exception) from e
488
+ # ensure that extractor logging is available
489
+ logger.info(f"maco extractor stderr:\n{stderr}")
490
+ return loaded
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: maco
3
- Version: 1.2.3
3
+ Version: 1.2.4
4
4
  Author: sl-govau
5
5
  Maintainer: cccs-rs
6
6
  License: MIT License
@@ -1,25 +1,26 @@
1
+ demo_extractors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1
2
  demo_extractors/elfy.py,sha256=AAFr5i1aivPwO4nycyXJEud57EpVNA-5k_2GicWesbY,771
2
- demo_extractors/limit_other.py,sha256=oscnNFkwD9Dm8Lae66GsRaAwUoTWUmkYpJu_wIsJ6sg,930
3
+ demo_extractors/limit_other.py,sha256=RAFx_0K_WnhUURA5uwXGmjrWODrAuLZFBxqZcWaxf64,944
3
4
  demo_extractors/nothing.py,sha256=3aeQJTY-dakmVXmyfmrRM8YCQVT7q3bq880DFH1Ol_Y,607
4
5
  demo_extractors/shared.py,sha256=Wlvy77SCAR97gxi8uUhGYyjxGmDb-pOSvN8b1rXrVWs,304
5
6
  demo_extractors/complex/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- demo_extractors/complex/complex.py,sha256=TkNmR9UUYo1f2JVpJhGasLG-5wHZI05JYxMIXj16GKM,2307
7
+ demo_extractors/complex/complex.py,sha256=JFKqBGKwkuDSz4zZUJuqhCLUQv6dlCMhBqNj33grBsE,2323
7
8
  demo_extractors/complex/complex_utils.py,sha256=aec8kJsYUrMPo-waihkVLt-0QpiOPkw7dDqfT9MNuHk,123
8
9
  maco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
10
  maco/base_test.py,sha256=pqet9ofMwFRTj3JHgPdh9WHwdyp8kxNMi1vJNUzkSNA,2518
10
11
  maco/cli.py,sha256=1I3U54yPddTxqWclCtZ5Ma5hW6RoVTZMzLSFOjjfM1g,8008
11
- maco/collector.py,sha256=sxP374lK16lzdi0lhm5e4GT3a5lzeyE1VCicE4m_E3k,6003
12
+ maco/collector.py,sha256=Vlo7KcJC7TKZFTElv8i_f_hvWEnlWCRzOP1xOc9x7vk,6532
12
13
  maco/extractor.py,sha256=4ZQd8OfvEQYUIkUS3LzZ5tcioembuLhT9_uRVNKSsyM,2750
13
- maco/utils.py,sha256=nyRTTsiwIE3L9XjFME6R_vR_EvLluwNL0kLb0r96jcg,18238
14
+ maco/utils.py,sha256=vQeJKw4whWTXp1mTd2oEhfvL4nvvgAzWjBnCp2XxWLI,19275
14
15
  maco/yara.py,sha256=vPzCqauVp52ivcTdt8zwrYqDdkLutGlesma9DhKPzHw,2925
15
16
  maco/model/__init__.py,sha256=SJrwdn12wklUFm2KoIgWjX_KgvJxCM7Ca9ntXOneuzc,31
16
17
  maco/model/model.py,sha256=ngen4ViyLdRo_z_TqZBjw2DN0NrRLpuxOy15-6QmtNw,23536
17
18
  model_setup/maco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
19
  model_setup/maco/base_test.py,sha256=pqet9ofMwFRTj3JHgPdh9WHwdyp8kxNMi1vJNUzkSNA,2518
19
20
  model_setup/maco/cli.py,sha256=1I3U54yPddTxqWclCtZ5Ma5hW6RoVTZMzLSFOjjfM1g,8008
20
- model_setup/maco/collector.py,sha256=sxP374lK16lzdi0lhm5e4GT3a5lzeyE1VCicE4m_E3k,6003
21
+ model_setup/maco/collector.py,sha256=Vlo7KcJC7TKZFTElv8i_f_hvWEnlWCRzOP1xOc9x7vk,6532
21
22
  model_setup/maco/extractor.py,sha256=4ZQd8OfvEQYUIkUS3LzZ5tcioembuLhT9_uRVNKSsyM,2750
22
- model_setup/maco/utils.py,sha256=nyRTTsiwIE3L9XjFME6R_vR_EvLluwNL0kLb0r96jcg,18238
23
+ model_setup/maco/utils.py,sha256=vQeJKw4whWTXp1mTd2oEhfvL4nvvgAzWjBnCp2XxWLI,19275
23
24
  model_setup/maco/yara.py,sha256=vPzCqauVp52ivcTdt8zwrYqDdkLutGlesma9DhKPzHw,2925
24
25
  model_setup/maco/model/__init__.py,sha256=SJrwdn12wklUFm2KoIgWjX_KgvJxCM7Ca9ntXOneuzc,31
25
26
  model_setup/maco/model/model.py,sha256=ngen4ViyLdRo_z_TqZBjw2DN0NrRLpuxOy15-6QmtNw,23536
@@ -27,15 +28,16 @@ pipelines/publish.yaml,sha256=xt3WNU-5kIICJgKIiiE94M3dWjS3uEiun-n4OmIssK8,1471
27
28
  pipelines/test.yaml,sha256=3KOoo-8SqP_bTAscsz5V3xxnuL91J-62mTjnQD1Btag,1019
28
29
  tests/data/example.txt.cart,sha256=j4ZdDnFNVq7lb-Qi4pY4evOXKQPKG-GSg-n-uEqPhV0,289
29
30
  tests/data/trigger_complex.txt,sha256=uqnLSrnyDGCmXwuPmZ2s8vdhH0hJs8DxvyaW_tuYY24,64
31
+ tests/data/trigger_complex.txt.cart,sha256=Z7qF1Zi640O45Znkl9ooP2RhSLAEqY0NRf51d-q7utU,345
30
32
  tests/extractors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
33
  tests/extractors/basic.py,sha256=r5eLCL6Ynr14nCBgtbLvUbm0NdrXizyc9c-4xBCNShU,828
32
34
  tests/extractors/basic_longer.py,sha256=1ClU2QD-Y0TOl_loNFvEqIEpTR5TSVJ6zg9ZmC-ESJo,860
33
35
  tests/extractors/test_basic.py,sha256=FLKekfSGM69HaiF7Vu_7D7KDXHZko-9hZkMO8_DoyYA,697
34
36
  tests/extractors/bob/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
37
  tests/extractors/bob/bob.py,sha256=Gy5p8KssJX87cwa9vVv8UBODF_ulbUteZXh15frW2hs,247
36
- maco-1.2.3.dist-info/LICENSE.md,sha256=gMSjshPhXvV_F1qxmeNkKdBqGWkd__fEJf4glS504bM,1478
37
- maco-1.2.3.dist-info/METADATA,sha256=81_65WF-TCXVfF3NeY5m3zCr831F7PVsTswjvZ34Ok4,15610
38
- maco-1.2.3.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
39
- maco-1.2.3.dist-info/entry_points.txt,sha256=TpcwG1gedIg8Y7a9ZOv8aQpuwEUftCefDrAjzeP-o6U,39
40
- maco-1.2.3.dist-info/top_level.txt,sha256=iMRwuzmrHA3zSwiSeMIl6FWhzRpn_st-I4fAv-kw5_o,49
41
- maco-1.2.3.dist-info/RECORD,,
38
+ maco-1.2.4.dist-info/LICENSE.md,sha256=gMSjshPhXvV_F1qxmeNkKdBqGWkd__fEJf4glS504bM,1478
39
+ maco-1.2.4.dist-info/METADATA,sha256=EoAEnCfbaXe8eUFAMHj3-C9lQIDR9Ss31NcY4CbEXjI,15610
40
+ maco-1.2.4.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
41
+ maco-1.2.4.dist-info/entry_points.txt,sha256=TpcwG1gedIg8Y7a9ZOv8aQpuwEUftCefDrAjzeP-o6U,39
42
+ maco-1.2.4.dist-info/top_level.txt,sha256=iMRwuzmrHA3zSwiSeMIl6FWhzRpn_st-I4fAv-kw5_o,49
43
+ maco-1.2.4.dist-info/RECORD,,
@@ -2,8 +2,9 @@
2
2
 
3
3
  import inspect
4
4
  import logging
5
+ import logging.handlers
5
6
  import os
6
- from multiprocessing import Manager, Process
7
+ from multiprocessing import Manager, Process, Queue
7
8
  from tempfile import NamedTemporaryFile
8
9
  from types import ModuleType
9
10
  from typing import Any, BinaryIO, Dict, List, Union
@@ -86,21 +87,33 @@ class Collector:
86
87
  )
87
88
  namespaced_rules[name] = member.yara_rule or extractor.DEFAULT_YARA_RULE.format(name=name)
88
89
 
90
+ # multiprocess logging is awkward - set up a queue to ensure we can log
91
+ logging_queue = Queue()
92
+ queue_handler = logging.handlers.QueueListener(logging_queue,*logging.getLogger().handlers)
93
+ queue_handler.start()
94
+
89
95
  # Find the extractors within the given directory
90
96
  # Execute within a child process to ensure main process interpreter is kept clean
91
97
  p = Process(
92
- target=utils.import_extractors,
98
+ target=utils.proxy_logging,
93
99
  args=(
94
- path_extractors,
95
- yara.compile(source=utils.MACO_YARA_RULE),
100
+ logging_queue,
101
+ utils.import_extractors,
96
102
  extractor_module_callback,
97
- logger,
98
- create_venv and os.path.isdir(path_extractors),
103
+ ),
104
+ kwargs=dict(
105
+ root_directory=path_extractors,
106
+ scanner=yara.compile(source=utils.MACO_YARA_RULE),
107
+ create_venv=create_venv and os.path.isdir(path_extractors),
99
108
  ),
100
109
  )
101
110
  p.start()
102
111
  p.join()
103
112
 
113
+ # stop multiprocess logging
114
+ queue_handler.stop()
115
+ logging_queue.close()
116
+
104
117
  self.extractors = dict(extractors)
105
118
  if not self.extractors:
106
119
  raise ExtractorLoadError("no extractors were loaded")
model_setup/maco/utils.py CHANGED
@@ -4,11 +4,14 @@ import importlib.machinery
4
4
  import importlib.util
5
5
  import inspect
6
6
  import json
7
+ import logging.handlers
7
8
  import os
8
9
  import re
9
10
  import shutil
10
11
  import subprocess
11
12
  import sys
13
+ import multiprocessing
14
+ import logging
12
15
  import tempfile
13
16
 
14
17
  from maco import yara
@@ -28,10 +31,12 @@ from typing import Callable, Dict, List, Set, Tuple
28
31
 
29
32
  from maco.extractor import Extractor
30
33
 
34
+ logger = logging.getLogger("maco.lib.utils")
35
+
31
36
  VENV_DIRECTORY_NAME = ".venv"
32
37
 
33
- RELATIVE_FROM_RE = re.compile("from (\.+)")
34
- RELATIVE_FROM_IMPORT_RE = re.compile("from (\.+) import")
38
+ RELATIVE_FROM_RE = re.compile(r"from (\.+)")
39
+ RELATIVE_FROM_IMPORT_RE = re.compile(r"from (\.+) import")
35
40
 
36
41
  try:
37
42
  # Attempt to use the uv package manager (Recommended)
@@ -69,6 +74,7 @@ import importlib
69
74
  import json
70
75
  import os
71
76
  import sys
77
+ import logging
72
78
 
73
79
  try:
74
80
  from maco import yara
@@ -76,6 +82,19 @@ except:
76
82
  import yara
77
83
 
78
84
  from base64 import b64encode
85
+
86
+ # ensure we have a logger to stderr
87
+ import logging
88
+ logger = logging.getLogger()
89
+ logger.setLevel(logging.DEBUG)
90
+ sh = logging.StreamHandler()
91
+ logger.addHandler(sh)
92
+ sh.setLevel(logging.DEBUG)
93
+ formatter = logging.Formatter(
94
+ fmt="%(asctime)s, [%(levelname)s] %(module)s.%(funcName)s: %(message)s", datefmt="%Y-%m-%d (%H:%M:%S)"
95
+ )
96
+ sh.setFormatter(formatter)
97
+
79
98
  parent_package_path = "{parent_package_path}"
80
99
  sys.path.insert(1, parent_package_path)
81
100
  mod = importlib.import_module("{module_name}")
@@ -101,7 +120,7 @@ with open("{output_path}", 'w') as fp:
101
120
  json.dump(result.dict(exclude_defaults=True, exclude_none=True), fp, cls=Base64Encoder)
102
121
  """
103
122
 
104
- MACO_YARA_RULE = """
123
+ MACO_YARA_RULE = r"""
105
124
  rule MACO {
106
125
  meta:
107
126
  desc = "Used to match on Python files that contain MACO extractors"
@@ -292,15 +311,10 @@ def register_extractors(
292
311
  ):
293
312
  package_name = os.path.basename(current_directory)
294
313
  parent_directory = os.path.dirname(current_directory)
295
- symlink = None
296
- while package_name in sys.modules:
297
- # Package name conflicts with an existing loaded module, let's deconflict that
298
- package_name = f"_{package_name}"
299
-
300
- # We'll need to create a link back to the original
301
- if package_name not in sys.modules:
302
- symlink = os.path.join(parent_directory, package_name)
303
- os.symlink(current_directory, symlink)
314
+ if package_name in sys.modules:
315
+ # this may happen as part of testing if some part of the extractor code was directly imported
316
+ logger.warning(f"Looks like {package_name} is already loaded. "
317
+ "If your maco extractor overlaps an existing package name this could cause problems.")
304
318
 
305
319
  try:
306
320
  # Modify the PATH so we can recognize this new package on import
@@ -351,10 +365,6 @@ def register_extractors(
351
365
  # Remove any modules that were loaded to deconflict with later modules loads
352
366
  [sys.modules.pop(k) for k in set(sys.modules.keys()) - default_loaded_modules]
353
367
 
354
- # Cleanup any symlinks
355
- if symlink:
356
- os.remove(symlink)
357
-
358
368
  # If there still exists extractor files we haven't found yet, try searching in the available subdirectories
359
369
  if extractor_files:
360
370
  for dir in os.listdir(current_directory):
@@ -379,14 +389,22 @@ def register_extractors(
379
389
  # We were able to find all the extractor files
380
390
  break
381
391
 
392
+ def proxy_logging(queue: multiprocessing.Queue, callback: Callable[[ModuleType, str], None], *args, **kwargs):
393
+ """Ensures logging is set up correctly for a child process and then executes the callback."""
394
+ logger = logging.getLogger()
395
+ qh = logging.handlers.QueueHandler(queue)
396
+ qh.setLevel(logging.DEBUG)
397
+ logger.addHandler(qh)
398
+ callback(*args, **kwargs, logger=logger)
382
399
 
383
400
  def import_extractors(
401
+ extractor_module_callback: Callable[[ModuleType, str], bool],
402
+ *,
384
403
  root_directory: str,
385
404
  scanner: yara.Rules,
386
- extractor_module_callback: Callable[[ModuleType, str], bool],
387
- logger: Logger,
388
405
  create_venv: bool = False,
389
406
  python_version: str = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
407
+ logger: Logger,
390
408
  ):
391
409
  extractor_dirs, extractor_files = scan_for_extractors(root_directory, scanner, logger)
392
410
 
@@ -453,14 +471,20 @@ def run_extractor(
453
471
  cwd=cwd,
454
472
  capture_output=True,
455
473
  )
474
+ stderr = proc.stderr.decode()
456
475
  try:
457
476
  # Load results and return them
458
477
  output.seek(0)
459
- return json.load(output, cls=json_decoder)
460
- except Exception:
478
+ loaded = json.load(output, cls=json_decoder)
479
+ except Exception as e:
461
480
  # If there was an error raised during runtime, then propagate
462
481
  delim = f'File "{module_path}"'
463
- exception = proc.stderr.decode()
482
+ exception = stderr
464
483
  if delim in exception:
465
484
  exception = f"{delim}{exception.split(delim, 1)[1]}"
466
- raise Exception(exception)
485
+ # print extractor logging at error level
486
+ logger.error(f"maco extractor raised exception, stderr:\n{stderr}")
487
+ raise Exception(exception) from e
488
+ # ensure that extractor logging is available
489
+ logger.info(f"maco extractor stderr:\n{stderr}")
490
+ return loaded
Binary file
File without changes