maco 1.2.1__tar.gz → 1.2.3__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.
Files changed (63) hide show
  1. {maco-1.2.1/maco.egg-info → maco-1.2.3}/PKG-INFO +1 -1
  2. {maco-1.2.1/model_setup → maco-1.2.3}/maco/base_test.py +1 -2
  3. {maco-1.2.1/model_setup → maco-1.2.3}/maco/cli.py +12 -8
  4. {maco-1.2.1/model_setup → maco-1.2.3}/maco/collector.py +21 -3
  5. {maco-1.2.1 → maco-1.2.3}/maco/utils.py +1 -1
  6. {maco-1.2.1 → maco-1.2.3/maco.egg-info}/PKG-INFO +1 -1
  7. {maco-1.2.1 → maco-1.2.3}/maco.egg-info/SOURCES.txt +1 -0
  8. {maco-1.2.1 → maco-1.2.3/model_setup}/maco/base_test.py +1 -2
  9. {maco-1.2.1 → maco-1.2.3/model_setup}/maco/cli.py +12 -8
  10. {maco-1.2.1 → maco-1.2.3/model_setup}/maco/collector.py +21 -3
  11. {maco-1.2.1 → maco-1.2.3}/model_setup/maco/utils.py +1 -1
  12. maco-1.2.3/tests/test_cli.py +20 -0
  13. {maco-1.2.1 → maco-1.2.3}/tests/test_helpers.py +2 -2
  14. {maco-1.2.1 → maco-1.2.3}/.gitignore +0 -0
  15. {maco-1.2.1 → maco-1.2.3}/.vscode/settings.json +0 -0
  16. {maco-1.2.1 → maco-1.2.3}/LICENSE.md +0 -0
  17. {maco-1.2.1 → maco-1.2.3}/README.md +0 -0
  18. {maco-1.2.1 → maco-1.2.3}/demo_extractors/complex/__init__.py +0 -0
  19. {maco-1.2.1 → maco-1.2.3}/demo_extractors/complex/complex.py +0 -0
  20. {maco-1.2.1 → maco-1.2.3}/demo_extractors/complex/complex_utils.py +0 -0
  21. {maco-1.2.1 → maco-1.2.3}/demo_extractors/elfy.py +0 -0
  22. {maco-1.2.1 → maco-1.2.3}/demo_extractors/limit_other.py +0 -0
  23. {maco-1.2.1 → maco-1.2.3}/demo_extractors/nothing.py +0 -0
  24. {maco-1.2.1 → maco-1.2.3}/demo_extractors/shared.py +0 -0
  25. {maco-1.2.1 → maco-1.2.3}/maco/__init__.py +0 -0
  26. {maco-1.2.1 → maco-1.2.3}/maco/extractor.py +0 -0
  27. {maco-1.2.1 → maco-1.2.3}/maco/model/__init__.py +0 -0
  28. {maco-1.2.1 → maco-1.2.3}/maco/model/model.py +0 -0
  29. {maco-1.2.1 → maco-1.2.3}/maco/yara.py +0 -0
  30. {maco-1.2.1 → maco-1.2.3}/maco.egg-info/dependency_links.txt +0 -0
  31. {maco-1.2.1 → maco-1.2.3}/maco.egg-info/entry_points.txt +0 -0
  32. {maco-1.2.1 → maco-1.2.3}/maco.egg-info/requires.txt +0 -0
  33. {maco-1.2.1 → maco-1.2.3}/maco.egg-info/top_level.txt +0 -0
  34. {maco-1.2.1 → maco-1.2.3}/model_setup/LICENSE.md +0 -0
  35. {maco-1.2.1 → maco-1.2.3}/model_setup/README.md +0 -0
  36. {maco-1.2.1 → maco-1.2.3}/model_setup/maco/__init__.py +0 -0
  37. {maco-1.2.1 → maco-1.2.3}/model_setup/maco/extractor.py +0 -0
  38. {maco-1.2.1 → maco-1.2.3}/model_setup/maco/model/__init__.py +0 -0
  39. {maco-1.2.1 → maco-1.2.3}/model_setup/maco/model/model.py +0 -0
  40. {maco-1.2.1 → maco-1.2.3}/model_setup/maco/yara.py +0 -0
  41. {maco-1.2.1 → maco-1.2.3}/model_setup/pyproject.toml +0 -0
  42. {maco-1.2.1 → maco-1.2.3}/model_setup/setup.py +0 -0
  43. {maco-1.2.1 → maco-1.2.3}/pipelines/publish.yaml +0 -0
  44. {maco-1.2.1 → maco-1.2.3}/pipelines/test.yaml +0 -0
  45. {maco-1.2.1 → maco-1.2.3}/pyproject.toml +0 -0
  46. {maco-1.2.1 → maco-1.2.3}/requirements.txt +0 -0
  47. {maco-1.2.1 → maco-1.2.3}/setup.cfg +0 -0
  48. {maco-1.2.1 → maco-1.2.3}/tests/data/example.txt.cart +0 -0
  49. {maco-1.2.1 → maco-1.2.3}/tests/data/trigger_complex.txt +0 -0
  50. {maco-1.2.1 → maco-1.2.3}/tests/extractors/__init__.py +0 -0
  51. {maco-1.2.1 → maco-1.2.3}/tests/extractors/basic.py +0 -0
  52. {maco-1.2.1 → maco-1.2.3}/tests/extractors/basic_longer.py +0 -0
  53. {maco-1.2.1 → maco-1.2.3}/tests/extractors/bob/__init__.py +0 -0
  54. {maco-1.2.1 → maco-1.2.3}/tests/extractors/bob/bob.py +0 -0
  55. {maco-1.2.1 → maco-1.2.3}/tests/extractors/test_basic.py +0 -0
  56. {maco-1.2.1 → maco-1.2.3}/tests/pytest.ini +0 -0
  57. {maco-1.2.1 → maco-1.2.3}/tests/requirements.txt +0 -0
  58. {maco-1.2.1 → maco-1.2.3}/tests/test_base_test.py +0 -0
  59. {maco-1.2.1 → maco-1.2.3}/tests/test_demo_extractors.py +0 -0
  60. {maco-1.2.1 → maco-1.2.3}/tests/test_detection.py +0 -0
  61. {maco-1.2.1 → maco-1.2.3}/tests/test_extractor.py +0 -0
  62. {maco-1.2.1 → maco-1.2.3}/tests/test_model.py +0 -0
  63. {maco-1.2.1 → maco-1.2.3}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: maco
3
- Version: 1.2.1
3
+ Version: 1.2.3
4
4
  Author: sl-govau
5
5
  Maintainer: cccs-rs
6
6
  License: MIT License
@@ -46,8 +46,7 @@ class BaseTest(unittest.TestCase):
46
46
  runs = self.c.match(stream)
47
47
  if not runs:
48
48
  raise NoHitException("no yara rule hit")
49
- hits = runs[self.name]
50
- resp = self.c.extract(stream, hits, self.name)
49
+ resp = self.c.extract(stream, self.name)
51
50
  return resp
52
51
 
53
52
  def _get_location(self) -> str:
@@ -8,7 +8,7 @@ import json
8
8
  import logging
9
9
  import os
10
10
  import sys
11
- from typing import BinaryIO, List
11
+ from typing import BinaryIO, List, Tuple
12
12
 
13
13
  import cart
14
14
 
@@ -57,7 +57,7 @@ def process_file(
57
57
  # run and store results for extractor
58
58
  logger.info(f"run {extractor_name} extractor from rules {[x.rule for x in hits]}")
59
59
  try:
60
- resp = collected.extract(stream, hits, extractor_name)
60
+ resp = collected.extract(stream, extractor_name)
61
61
  except Exception as e:
62
62
  logger.exception(f"extractor error with {path_file} ({e})")
63
63
  resp = None
@@ -91,18 +91,22 @@ def process_filesystem(
91
91
  force: bool,
92
92
  include_base64: bool,
93
93
  create_venv: bool = False,
94
- ):
94
+ ) -> Tuple[int, int, int]:
95
+ """Process filesystem with extractors and print results of extraction.
96
+
97
+ Returns total number of analysed files, yara hits and successful maco extractions.
98
+ """
95
99
  if force:
96
100
  logger.warning("force execute will cause errors if an extractor requires a yara rule hit during execution")
97
101
  collected = collector.Collector(path_extractors, include=include, exclude=exclude, create_venv=create_venv)
98
102
 
99
103
  logger.info(f"extractors loaded: {[x for x in collected.extractors.keys()]}\n")
100
104
  for _, extractor in collected.extractors.items():
101
- extractor = extractor["module"]
105
+ extractor_meta = extractor["metadata"]
102
106
  logger.info(
103
- f"{extractor.family} by {extractor.author}"
104
- f" {extractor.last_modified} {extractor.sharing}"
105
- f"\n{extractor.__doc__}\n"
107
+ f"{extractor_meta['family']} by {extractor_meta['author']}"
108
+ f" {extractor_meta['last_modified']} {extractor_meta['sharing']}"
109
+ f"\n{extractor_meta['description']}\n"
106
110
  )
107
111
 
108
112
  num_analysed = 0
@@ -144,7 +148,7 @@ def process_filesystem(
144
148
  finally:
145
149
  logger.info("")
146
150
  logger.info(f"{num_analysed} analysed, {num_hits} hits, {num_extracted} extracted")
147
-
151
+ return num_analysed, num_hits, num_extracted
148
152
 
149
153
  def main():
150
154
  parser = argparse.ArgumentParser(description="Run extractors over samples.")
@@ -40,7 +40,11 @@ def _verify_response(resp: Union[BaseModel, dict]) -> Dict:
40
40
 
41
41
  class Collector:
42
42
  def __init__(
43
- self, path_extractors: str, include: List[str] = None, exclude: List[str] = None, create_venv: bool = False
43
+ self,
44
+ path_extractors: str,
45
+ include: List[str] = None,
46
+ exclude: List[str] = None,
47
+ create_venv: bool = False,
44
48
  ):
45
49
  """Discover and load extractors from file system."""
46
50
  path_extractors = os.path.realpath(path_extractors)
@@ -72,6 +76,13 @@ class Collector:
72
76
  module_path=module.__file__,
73
77
  module_name=member.__module__,
74
78
  extractor_class=member.__name__,
79
+ metadata={
80
+ "family": member.family,
81
+ "author": member.author,
82
+ "last_modified": member.last_modified,
83
+ "sharing": member.sharing,
84
+ "description": member.__doc__,
85
+ },
75
86
  )
76
87
  namespaced_rules[name] = member.yara_rule or extractor.DEFAULT_YARA_RULE.format(name=name)
77
88
 
@@ -116,7 +127,6 @@ class Collector:
116
127
  def extract(
117
128
  self,
118
129
  stream: BinaryIO,
119
- matches: List[yara.Match],
120
130
  extractor_name: str,
121
131
  ) -> Dict[str, Any]:
122
132
  """Run extractor with stream and verify output matches the model."""
@@ -127,7 +137,15 @@ class Collector:
127
137
  sample_path.write(stream.read())
128
138
  sample_path.flush()
129
139
  # enforce types and verify properties, and remove defaults
130
- return _verify_response(utils.run_extractor(sample_path.name, **extractor))
140
+ return _verify_response(
141
+ utils.run_extractor(
142
+ sample_path.name,
143
+ module_name=extractor["module_name"],
144
+ extractor_class=extractor["extractor_class"],
145
+ module_path=extractor["module_path"],
146
+ venv=extractor["venv"],
147
+ )
148
+ )
131
149
  except Exception:
132
150
  # caller can deal with the exception
133
151
  raise
@@ -265,7 +265,7 @@ def find_and_insert_venv(path: str, venvs: List[str]):
265
265
  venv = None
266
266
  for venv in sorted(venvs, reverse=True):
267
267
  venv_parent = os.path.dirname(venv)
268
- if path.startswith(venv_parent):
268
+ if path.startswith(f"{venv_parent}/"):
269
269
  # Found the virtual environment that's the closest to extractor
270
270
  break
271
271
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: maco
3
- Version: 1.2.1
3
+ Version: 1.2.3
4
4
  Author: sl-govau
5
5
  Maintainer: cccs-rs
6
6
  License: MIT License
@@ -46,6 +46,7 @@ pipelines/test.yaml
46
46
  tests/pytest.ini
47
47
  tests/requirements.txt
48
48
  tests/test_base_test.py
49
+ tests/test_cli.py
49
50
  tests/test_demo_extractors.py
50
51
  tests/test_detection.py
51
52
  tests/test_extractor.py
@@ -46,8 +46,7 @@ class BaseTest(unittest.TestCase):
46
46
  runs = self.c.match(stream)
47
47
  if not runs:
48
48
  raise NoHitException("no yara rule hit")
49
- hits = runs[self.name]
50
- resp = self.c.extract(stream, hits, self.name)
49
+ resp = self.c.extract(stream, self.name)
51
50
  return resp
52
51
 
53
52
  def _get_location(self) -> str:
@@ -8,7 +8,7 @@ import json
8
8
  import logging
9
9
  import os
10
10
  import sys
11
- from typing import BinaryIO, List
11
+ from typing import BinaryIO, List, Tuple
12
12
 
13
13
  import cart
14
14
 
@@ -57,7 +57,7 @@ def process_file(
57
57
  # run and store results for extractor
58
58
  logger.info(f"run {extractor_name} extractor from rules {[x.rule for x in hits]}")
59
59
  try:
60
- resp = collected.extract(stream, hits, extractor_name)
60
+ resp = collected.extract(stream, extractor_name)
61
61
  except Exception as e:
62
62
  logger.exception(f"extractor error with {path_file} ({e})")
63
63
  resp = None
@@ -91,18 +91,22 @@ def process_filesystem(
91
91
  force: bool,
92
92
  include_base64: bool,
93
93
  create_venv: bool = False,
94
- ):
94
+ ) -> Tuple[int, int, int]:
95
+ """Process filesystem with extractors and print results of extraction.
96
+
97
+ Returns total number of analysed files, yara hits and successful maco extractions.
98
+ """
95
99
  if force:
96
100
  logger.warning("force execute will cause errors if an extractor requires a yara rule hit during execution")
97
101
  collected = collector.Collector(path_extractors, include=include, exclude=exclude, create_venv=create_venv)
98
102
 
99
103
  logger.info(f"extractors loaded: {[x for x in collected.extractors.keys()]}\n")
100
104
  for _, extractor in collected.extractors.items():
101
- extractor = extractor["module"]
105
+ extractor_meta = extractor["metadata"]
102
106
  logger.info(
103
- f"{extractor.family} by {extractor.author}"
104
- f" {extractor.last_modified} {extractor.sharing}"
105
- f"\n{extractor.__doc__}\n"
107
+ f"{extractor_meta['family']} by {extractor_meta['author']}"
108
+ f" {extractor_meta['last_modified']} {extractor_meta['sharing']}"
109
+ f"\n{extractor_meta['description']}\n"
106
110
  )
107
111
 
108
112
  num_analysed = 0
@@ -144,7 +148,7 @@ def process_filesystem(
144
148
  finally:
145
149
  logger.info("")
146
150
  logger.info(f"{num_analysed} analysed, {num_hits} hits, {num_extracted} extracted")
147
-
151
+ return num_analysed, num_hits, num_extracted
148
152
 
149
153
  def main():
150
154
  parser = argparse.ArgumentParser(description="Run extractors over samples.")
@@ -40,7 +40,11 @@ def _verify_response(resp: Union[BaseModel, dict]) -> Dict:
40
40
 
41
41
  class Collector:
42
42
  def __init__(
43
- self, path_extractors: str, include: List[str] = None, exclude: List[str] = None, create_venv: bool = False
43
+ self,
44
+ path_extractors: str,
45
+ include: List[str] = None,
46
+ exclude: List[str] = None,
47
+ create_venv: bool = False,
44
48
  ):
45
49
  """Discover and load extractors from file system."""
46
50
  path_extractors = os.path.realpath(path_extractors)
@@ -72,6 +76,13 @@ class Collector:
72
76
  module_path=module.__file__,
73
77
  module_name=member.__module__,
74
78
  extractor_class=member.__name__,
79
+ metadata={
80
+ "family": member.family,
81
+ "author": member.author,
82
+ "last_modified": member.last_modified,
83
+ "sharing": member.sharing,
84
+ "description": member.__doc__,
85
+ },
75
86
  )
76
87
  namespaced_rules[name] = member.yara_rule or extractor.DEFAULT_YARA_RULE.format(name=name)
77
88
 
@@ -116,7 +127,6 @@ class Collector:
116
127
  def extract(
117
128
  self,
118
129
  stream: BinaryIO,
119
- matches: List[yara.Match],
120
130
  extractor_name: str,
121
131
  ) -> Dict[str, Any]:
122
132
  """Run extractor with stream and verify output matches the model."""
@@ -127,7 +137,15 @@ class Collector:
127
137
  sample_path.write(stream.read())
128
138
  sample_path.flush()
129
139
  # enforce types and verify properties, and remove defaults
130
- return _verify_response(utils.run_extractor(sample_path.name, **extractor))
140
+ return _verify_response(
141
+ utils.run_extractor(
142
+ sample_path.name,
143
+ module_name=extractor["module_name"],
144
+ extractor_class=extractor["extractor_class"],
145
+ module_path=extractor["module_path"],
146
+ venv=extractor["venv"],
147
+ )
148
+ )
131
149
  except Exception:
132
150
  # caller can deal with the exception
133
151
  raise
@@ -265,7 +265,7 @@ def find_and_insert_venv(path: str, venvs: List[str]):
265
265
  venv = None
266
266
  for venv in sorted(venvs, reverse=True):
267
267
  venv_parent = os.path.dirname(venv)
268
- if path.startswith(venv_parent):
268
+ if path.startswith(f"{venv_parent}/"):
269
269
  # Found the virtual environment that's the closest to extractor
270
270
  break
271
271
 
@@ -0,0 +1,20 @@
1
+ import os
2
+
3
+ import unittest
4
+ from maco import cli
5
+
6
+
7
+ class TestCLI(unittest.TestCase):
8
+ def test_process_filesystem(self):
9
+ maco_path = os.path.abspath(os.path.join(__file__, "../../demo_extractors"))
10
+ test_path = os.path.abspath(os.path.join(__file__, "../data"))
11
+ results = cli.process_filesystem(
12
+ maco_path,
13
+ test_path,
14
+ include=[],
15
+ exclude=[],
16
+ pretty=True,
17
+ force=False,
18
+ include_base64=False,
19
+ )
20
+ self.assertEqual(results, (2, 2, 2))
@@ -43,11 +43,11 @@ class TestHelpersAnalyseStream(unittest.TestCase):
43
43
 
44
44
  def test_analyse_stream(self):
45
45
  data = b""
46
- resp = self.m.extract(io.BytesIO(data), [], "Complex")
46
+ resp = self.m.extract(io.BytesIO(data), "Complex")
47
47
  self.assertEqual(resp, None)
48
48
 
49
49
  data = b"data"
50
- resp = self.m.extract(io.BytesIO(data), [], "Complex")
50
+ resp = self.m.extract(io.BytesIO(data), "Complex")
51
51
  self.assertEqual(
52
52
  resp,
53
53
  {
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
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
File without changes
File without changes