txt2detection 1.0.2__tar.gz → 1.0.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.

Potentially problematic release.


This version of txt2detection might be problematic. Click here for more details.

Files changed (55) hide show
  1. {txt2detection-1.0.2 → txt2detection-1.0.3}/PKG-INFO +25 -1
  2. {txt2detection-1.0.2 → txt2detection-1.0.3}/README.md +24 -0
  3. {txt2detection-1.0.2 → txt2detection-1.0.3}/pyproject.toml +1 -1
  4. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/src/test_main.py +6 -0
  5. {txt2detection-1.0.2 → txt2detection-1.0.3}/txt2detection/__main__.py +8 -1
  6. {txt2detection-1.0.2 → txt2detection-1.0.3}/txt2detection/ai_extractor/base.py +11 -1
  7. {txt2detection-1.0.2 → txt2detection-1.0.3}/txt2detection/bundler.py +2 -2
  8. txt2detection-1.0.3/txt2detection/credential_checker.py +80 -0
  9. {txt2detection-1.0.2 → txt2detection-1.0.3}/.env.example +0 -0
  10. {txt2detection-1.0.2 → txt2detection-1.0.3}/.env.markdown +0 -0
  11. {txt2detection-1.0.2 → txt2detection-1.0.3}/.github/workflows/create-release.yml +0 -0
  12. {txt2detection-1.0.2 → txt2detection-1.0.3}/.github/workflows/run-tests.yml +0 -0
  13. {txt2detection-1.0.2 → txt2detection-1.0.3}/.gitignore +0 -0
  14. {txt2detection-1.0.2 → txt2detection-1.0.3}/LICENSE +0 -0
  15. {txt2detection-1.0.2 → txt2detection-1.0.3}/config/detection_languages.yaml +0 -0
  16. {txt2detection-1.0.2 → txt2detection-1.0.3}/docs/README.md +0 -0
  17. {txt2detection-1.0.2 → txt2detection-1.0.3}/docs/txt2detection.png +0 -0
  18. {txt2detection-1.0.2 → txt2detection-1.0.3}/requirements.txt +0 -0
  19. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/files/CVE-2024-56520.txt +0 -0
  20. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/files/EC2-exfil.txt +0 -0
  21. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/files/observables.txt +0 -0
  22. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/files/sigma-rule-custom-tags.yml +0 -0
  23. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/files/sigma-rule-existing-related.yml +0 -0
  24. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/files/sigma-rule-master.yml +0 -0
  25. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/files/sigma-rule-no-author.yml +0 -0
  26. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/files/sigma-rule-no-date.yml +0 -0
  27. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/files/sigma-rule-no-description.yml +0 -0
  28. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/files/sigma-rule-no-level.yml +0 -0
  29. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/files/sigma-rule-no-license.yml +0 -0
  30. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/files/sigma-rule-no-status.yml +0 -0
  31. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/files/sigma-rule-no-tags.yml +0 -0
  32. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/files/sigma-rule-no-title.yml +0 -0
  33. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/files/sigma-rule-observables.yml +0 -0
  34. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/files/sigma-rule-one-date.yml +0 -0
  35. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/manual-tests/README.md +0 -0
  36. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/src/__init__.py +0 -0
  37. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/src/requirements.txt +0 -0
  38. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/src/test_bundler.py +0 -0
  39. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/src/test_main_run_txt2detction.py +0 -0
  40. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/src/test_models.py +0 -0
  41. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/src/test_observables.py +0 -0
  42. {txt2detection-1.0.2 → txt2detection-1.0.3}/tests/src/test_utils.py +0 -0
  43. {txt2detection-1.0.2 → txt2detection-1.0.3}/txt2detection/__init__.py +0 -0
  44. {txt2detection-1.0.2 → txt2detection-1.0.3}/txt2detection/ai_extractor/__init__.py +0 -0
  45. {txt2detection-1.0.2 → txt2detection-1.0.3}/txt2detection/ai_extractor/anthropic.py +0 -0
  46. {txt2detection-1.0.2 → txt2detection-1.0.3}/txt2detection/ai_extractor/deepseek.py +0 -0
  47. {txt2detection-1.0.2 → txt2detection-1.0.3}/txt2detection/ai_extractor/gemini.py +0 -0
  48. {txt2detection-1.0.2 → txt2detection-1.0.3}/txt2detection/ai_extractor/openai.py +0 -0
  49. {txt2detection-1.0.2 → txt2detection-1.0.3}/txt2detection/ai_extractor/openrouter.py +0 -0
  50. {txt2detection-1.0.2 → txt2detection-1.0.3}/txt2detection/ai_extractor/prompts.py +0 -0
  51. {txt2detection-1.0.2 → txt2detection-1.0.3}/txt2detection/ai_extractor/utils.py +0 -0
  52. {txt2detection-1.0.2 → txt2detection-1.0.3}/txt2detection/models.py +0 -0
  53. {txt2detection-1.0.2 → txt2detection-1.0.3}/txt2detection/observables.py +0 -0
  54. {txt2detection-1.0.2 → txt2detection-1.0.3}/txt2detection/utils.py +0 -0
  55. {txt2detection-1.0.2 → txt2detection-1.0.3}/txt2detection.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: txt2detection
3
- Version: 1.0.2
3
+ Version: 1.0.3
4
4
  Summary: A command line tool that takes a txt file containing threat intelligence and turns it into a detection rule.
5
5
  Project-URL: Homepage, https://github.com/muchdogesec/txt2detection
6
6
  Project-URL: Issues, https://github.com/muchdogesec/txt2detection/issues
@@ -100,6 +100,30 @@ cp .env.example .env
100
100
 
101
101
  To see more information about how to set the variables, and what they do, read the `.env.markdown` file.
102
102
 
103
+ Then test your configoration
104
+
105
+ ```shell
106
+ python3 txt2detection.py \
107
+ check-credentials
108
+ ```
109
+
110
+ It will return a response to show what API keys are working
111
+
112
+ ```txt
113
+ ============= Service Statuses ===============
114
+ ctibutler : authorized ✔
115
+ vulmatch : authorized ✔
116
+
117
+ LLMS:
118
+ openai : authorized ✔
119
+ deepseek : unsupported –
120
+ gemini : unsupported –
121
+ openrouter : unsupported –
122
+ anthropic : unsupported –
123
+ ```
124
+
125
+ Not all services need to be configured, if you have no intention of using them.
126
+
103
127
  ### Run
104
128
 
105
129
  ```shell
@@ -67,6 +67,30 @@ cp .env.example .env
67
67
 
68
68
  To see more information about how to set the variables, and what they do, read the `.env.markdown` file.
69
69
 
70
+ Then test your configoration
71
+
72
+ ```shell
73
+ python3 txt2detection.py \
74
+ check-credentials
75
+ ```
76
+
77
+ It will return a response to show what API keys are working
78
+
79
+ ```txt
80
+ ============= Service Statuses ===============
81
+ ctibutler : authorized ✔
82
+ vulmatch : authorized ✔
83
+
84
+ LLMS:
85
+ openai : authorized ✔
86
+ deepseek : unsupported –
87
+ gemini : unsupported –
88
+ openrouter : unsupported –
89
+ anthropic : unsupported –
90
+ ```
91
+
92
+ Not all services need to be configured, if you have no intention of using them.
93
+
70
94
  ### Run
71
95
 
72
96
  ```shell
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "txt2detection"
7
- version = "1.0.2"
7
+ version = "1.0.3"
8
8
  authors = [
9
9
  { name = "dogesec" }
10
10
  ]
@@ -135,3 +135,9 @@ def test_parse_args_sigma_mode_no_ai_provider(monkeypatch):
135
135
  assert getattr(args, "ai_provider", None) is None
136
136
  assert hasattr(args, "report_id")
137
137
  assert hasattr(args, "sigma_file")
138
+
139
+
140
+ def test_parse_args_check_credentials(monkeypatch):
141
+ monkeypatch.setattr(sys, "argv", ["prog", "check-credentials"])
142
+ with pytest.raises(SystemExit):
143
+ parse_args()
@@ -13,6 +13,7 @@ import uuid
13
13
  from stix2 import Identity
14
14
  import yaml
15
15
 
16
+ from txt2detection import credential_checker
16
17
  from txt2detection.ai_extractor.base import BaseAIExtractor
17
18
  from txt2detection.models import TAG_PATTERN, DetectionContainer, Level, SigmaRuleDetection
18
19
  from txt2detection.utils import validate_token_count
@@ -91,6 +92,7 @@ def parse_args():
91
92
  file = mode.add_parser('file', help="process a file input using ai")
92
93
  text = mode.add_parser('text', help="process a text argument using ai")
93
94
  sigma = mode.add_parser('sigma', help="process a sigma file without ai")
95
+ check_credentials = mode.add_parser('check-credentials', help="show status of external services with respect to credentials")
94
96
 
95
97
  for mode_parser in [file, text, sigma]:
96
98
  mode_parser.add_argument('--report_id', type=uuid.UUID, help='report_id to use for generated report')
@@ -115,7 +117,10 @@ def parse_args():
115
117
  sigma.add_argument('--level', help="If passed, will overwrite any existing `level` recorded in the rule", choices=Level._member_names_)
116
118
 
117
119
  args: Args = parser.parse_args()
118
- print(args)
120
+ if args.mode == "check-credentials":
121
+ statuses = credential_checker.check_statuses(test_llms=True)
122
+ credential_checker.format_statuses(statuses)
123
+ sys.exit(0)
119
124
 
120
125
  if args.mode != 'sigma':
121
126
  assert args.ai_provider, "--ai_provider is required in file or txt mode"
@@ -129,6 +134,8 @@ def parse_args():
129
134
  return args
130
135
 
131
136
 
137
+
138
+
132
139
  def run_txt2detection(name, identity, tlp_level, input_text: str, labels: list[str], report_id: str|uuid.UUID, ai_provider: BaseAIExtractor, **kwargs) -> Bundler:
133
140
  if sigma := kwargs.get('sigma_file'):
134
141
  detection = get_sigma_detections(sigma)
@@ -54,4 +54,14 @@ class BaseAIExtractor():
54
54
 
55
55
  @property
56
56
  def extractor_name(self):
57
- return f"{self.provider}:{self.llm.model}"
57
+ return f"{self.provider}:{self.llm.model}"
58
+
59
+ def check_credential(self):
60
+ try:
61
+ return "authorized" if self._check_credential() else "unauthorized"
62
+ except:
63
+ return "unknown"
64
+
65
+ def _check_credential(self):
66
+ self.llm.complete("say 'hi'")
67
+ return True
@@ -244,7 +244,7 @@ class Bundler:
244
244
 
245
245
  headers = {}
246
246
  if api_key := os.environ.get('CTIBUTLER_API_KEY'):
247
- headers['Authorization'] = "Bearer " + api_key
247
+ headers['API-KEY'] = api_key
248
248
 
249
249
  return self._get_objects(endpoint, headers)
250
250
 
@@ -255,7 +255,7 @@ class Bundler:
255
255
  endpoint = urljoin(os.environ['VULMATCH_BASE_URL'] + '/', f"v1/cve/objects/?cve_id="+','.join(cve_ids))
256
256
  headers = {}
257
257
  if api_key := os.environ.get('VULMATCH_API_KEY'):
258
- headers['Authorization'] = "Bearer " + api_key
258
+ headers['API-KEY'] = api_key
259
259
 
260
260
  return self._get_objects(endpoint, headers)
261
261
 
@@ -0,0 +1,80 @@
1
+ import argparse
2
+ import os
3
+ import random
4
+ from urllib.parse import urljoin
5
+ import requests
6
+
7
+
8
+
9
+ def check_llms():
10
+ from txt2detection.__main__ import parse_model
11
+
12
+ auth_info = dict()
13
+ for model_name in ["openai", "deepseek", "gemini", "openrouter", "anthropic"]:
14
+ try:
15
+ model = parse_model(model_name)
16
+ auth_info[model_name] = model.check_credential()
17
+ except argparse.ArgumentTypeError:
18
+ auth_info[model_name] = "unsupported"
19
+ except:
20
+ auth_info[model_name] = "unauthorized"
21
+ return auth_info
22
+
23
+
24
+ def check_ctibutler_vulmatch(service):
25
+ session = requests.Session()
26
+ if service == 'vulmatch':
27
+ base_url = os.getenv('VULMATCH_BASE_URL')
28
+ url = urljoin(base_url, 'v1/cve/objects/vulnerability--f552f6f4-39da-48dc-8717-323772c99588/')
29
+ session.headers['API-KEY'] = os.environ.get('VULMATCH_API_KEY')
30
+ elif service == 'ctibutler':
31
+ base_url = os.getenv('CTIBUTLER_BASE_URL')
32
+ url = urljoin(base_url, 'v1/location/versions/available/')
33
+ session.headers['API-KEY'] = os.environ.get('CTIBUTLER_API_KEY')
34
+
35
+ try:
36
+ resp = session.get(url)
37
+ match resp.status_code:
38
+ case 401 | 403:
39
+ return "unauthorized"
40
+ case 200:
41
+ return "authorized"
42
+ case _:
43
+ return "unknown"
44
+ except:
45
+ return "offline"
46
+
47
+
48
+ def check_statuses(test_llms=False):
49
+ statuses = dict(
50
+ ctibutler=check_ctibutler_vulmatch("ctibutler"),
51
+ vulmatch=check_ctibutler_vulmatch("vulmatch"),
52
+ )
53
+ if test_llms:
54
+ statuses.update(llms=check_llms())
55
+ return statuses
56
+
57
+
58
+ def format_statuses(status_dict):
59
+ def get_marker(status):
60
+ """Return a checkmark, cross, or dash based on status."""
61
+ match status.lower():
62
+ case "authorized":
63
+ return "✔"
64
+ case "unauthorized":
65
+ return "✖"
66
+ case "unknown" | "offline" | "unsupported":
67
+ return "–"
68
+ case _:
69
+ return "?"
70
+
71
+ print("============= Service Statuses ===============")
72
+ for key, value in status_dict.items():
73
+ if key == "llms" and isinstance(value, dict):
74
+ print(f"\n {key.upper()}:")
75
+ for llm_name, llm_status in value.items():
76
+ marker = get_marker(llm_status)
77
+ print(f" {llm_name:<12}: {llm_status:<15} {marker}")
78
+ else:
79
+ marker = get_marker(value)
80
+ print(f" {key:<12}: {value:<15} {marker}")
File without changes
File without changes