txt2stix 1.0.4__py3-none-any.whl → 1.0.6__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.
@@ -90,3 +90,13 @@ class BaseAIExtractor():
90
90
 
91
91
  def __hash__(self):
92
92
  return hash(self.extractor_name)
93
+
94
+ def check_credential(self):
95
+ try:
96
+ return "authorized" if self._check_credential() else "unauthorized"
97
+ except:
98
+ return "unknown"
99
+
100
+ def _check_credential(self):
101
+ self.llm.complete("say 'hi'")
102
+ return True
@@ -0,0 +1,97 @@
1
+ import argparse
2
+ import os
3
+ import random
4
+ from urllib.parse import urljoin
5
+ import requests
6
+ from stix2extensions.tools import creditcard2stix
7
+ from txt2stix.retriever import STIXObjectRetriever
8
+
9
+
10
+ def check_binlist():
11
+ card = str(random.randrange(432101, 456789))
12
+ api_key = os.getenv("BIN_LIST_API_KEY", "")
13
+ return "authorized" if creditcard2stix.get_bin_data(card, api_key) else "unauthorized"
14
+
15
+
16
+ def check_llms():
17
+ from txt2stix.txt2stix import parse_model
18
+
19
+ auth_info = dict()
20
+ for model_name in ["openai", "deepseek", "gemini", "openrouter", "anthropic"]:
21
+ try:
22
+ model = parse_model(model_name)
23
+ auth_info[model_name] = model.check_credential()
24
+ except argparse.ArgumentTypeError:
25
+ auth_info[model_name] = "unsupported"
26
+ except:
27
+ auth_info[model_name] = "unauthorized"
28
+ return auth_info
29
+
30
+
31
+ def check_ctibutler_vulmatch(service):
32
+ retriever = STIXObjectRetriever(service)
33
+ path = dict(
34
+ ctibutler="v1/location/versions/available/",
35
+ vulmatch="v1/cve/objects/vulnerability--f552f6f4-39da-48dc-8717-323772c99588/",
36
+ )[service]
37
+ try:
38
+ resp = retriever.session.get(urljoin(retriever.api_root, path))
39
+ match resp.status_code:
40
+ case 401 | 403:
41
+ return "unauthorized"
42
+ case 200:
43
+ return "authorized"
44
+ case _:
45
+ return "unknown"
46
+ except:
47
+ return "offline"
48
+
49
+ def check_btcscan():
50
+ url = "https://btcscan.org/api/blocks/tip/height"
51
+ try:
52
+ resp = requests.get(url)
53
+ match resp.status_code:
54
+ case 401 | 403:
55
+ return "unauthorized"
56
+ case 200:
57
+ return "authorized"
58
+ case _:
59
+ return "unknown"
60
+ except:
61
+ return "offline"
62
+
63
+ def check_statuses(test_llms=False):
64
+ statuses = dict(
65
+ ctibutler=check_ctibutler_vulmatch("ctibutler"),
66
+ vulmatch=check_ctibutler_vulmatch("vulmatch"),
67
+ binlist=check_binlist(),
68
+ btcscan=check_btcscan(),
69
+ )
70
+ if test_llms:
71
+ statuses.update(llms=check_llms())
72
+ return statuses
73
+
74
+
75
+ def format_statuses(status_dict):
76
+ def get_marker(status):
77
+ """Return a checkmark, cross, or dash based on status."""
78
+ match status.lower():
79
+ case "authorized":
80
+ return "✔"
81
+ case "unauthorized":
82
+ return "✖"
83
+ case "unknown" | "offline" | "unsupported":
84
+ return "–"
85
+ case _:
86
+ return "?"
87
+
88
+ print("============= Service Statuses ===============")
89
+ for key, value in status_dict.items():
90
+ if key == "llms" and isinstance(value, dict):
91
+ print(f"\n {key.upper()}:")
92
+ for llm_name, llm_status in value.items():
93
+ marker = get_marker(llm_status)
94
+ print(f" {llm_name:<12}: {llm_status:<15} {marker}")
95
+ else:
96
+ marker = get_marker(value)
97
+ print(f" {key:<12}: {value:<15} {marker}")
txt2stix/retriever.py CHANGED
@@ -17,6 +17,10 @@ class STIXObjectRetriever:
17
17
  self.api_key = os.environ.get('VULMATCH_API_KEY')
18
18
  else:
19
19
  raise NotImplementedError("The type `%s` is not supported", host)
20
+ self.session = requests.Session()
21
+ self.session.headers.update({
22
+ "API-KEY": self.api_key,
23
+ })
20
24
 
21
25
  def get_attack_object(self, matrix, attack_id):
22
26
  endpoint = urljoin(self.api_root, f"v1/attack-{matrix}/objects/{attack_id}/")
@@ -48,14 +52,10 @@ class STIXObjectRetriever:
48
52
  return self._retrieve_objects(urljoin(self.api_root, f"v1/{type}/objects/?alias={alias}"))
49
53
 
50
54
  def _retrieve_objects(self, endpoint, key='objects'):
51
- s = requests.Session()
52
- s.headers.update({
53
- "API-KEY": self.api_key,
54
- })
55
55
  data = []
56
56
  page = 1
57
57
  while True:
58
- resp = s.get(endpoint, params=dict(page=page, page_size=50))
58
+ resp = self.session.get(endpoint, params=dict(page=page, page_size=50))
59
59
  resp.raise_for_status()
60
60
  d = resp.json()
61
61
  if len(d[key]) == 0:
txt2stix/txt2stix.py CHANGED
@@ -13,7 +13,7 @@ import sys, os
13
13
  from pydantic import BaseModel
14
14
 
15
15
  from txt2stix.ai_extractor.utils import DescribesIncident
16
- from txt2stix import attack_flow
16
+ from txt2stix import attack_flow, credential_checker
17
17
 
18
18
 
19
19
  from .utils import RELATIONSHIP_TYPES, Txt2StixData, remove_links
@@ -135,6 +135,12 @@ def parse_args():
135
135
  all_extractors = extractions.parse_extraction_config(INCLUDES_PATH)
136
136
 
137
137
  parser = argparse.ArgumentParser(description="File Conversion Tool")
138
+ parser.add_argument('--check_credentials', "--check-credentials", action="store_true", help="Print the validity of the credentials and exit")
139
+ args, _ = parser.parse_known_args()
140
+ if args.check_credentials:
141
+ statuses = credential_checker.check_statuses(test_llms=True)
142
+ credential_checker.format_statuses(statuses)
143
+ sys.exit(0)
138
144
 
139
145
  inf_arg = parser.add_argument(
140
146
  "--input_file",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: txt2stix
3
- Version: 1.0.4
3
+ Version: 1.0.6
4
4
  Summary: txt2stix is a Python script that is designed to identify and extract IoCs and TTPs from text files, identify the relationships between them, convert them to STIX 2.1 objects, and output as a STIX 2.1 bundle.
5
5
  Project-URL: Homepage, https://github.com/muchdogesec/txt2stix
6
6
  Project-URL: Issues, https://github.com/muchdogesec/txt2stix/issues
@@ -111,6 +111,31 @@ cp .env.example .env
111
111
 
112
112
  To see more information about how to set the variables, and what they do, read the `.env.markdown` file.
113
113
 
114
+ Then test your configoration
115
+
116
+ ```shell
117
+ python3 txt2stix.py \
118
+ --check-credentials
119
+ ```
120
+
121
+ It will return a response to show what API keys are working
122
+
123
+ ```txt
124
+ ============= Service Statuses ===============
125
+ ctibutler : authorized ✔
126
+ vulmatch : authorized ✔
127
+ binlist : authorized ✔
128
+
129
+ LLMS:
130
+ openai : authorized ✔
131
+ deepseek : unsupported –
132
+ gemini : unsupported –
133
+ openrouter : unsupported –
134
+ anthropic : unsupported –
135
+ ```
136
+
137
+ Not all services need to be configured, if you have no intention of using them.
138
+
114
139
  ### Usage
115
140
 
116
141
  ```shell
@@ -2,16 +2,17 @@ txt2stix/__init__.py,sha256=Sm_VT913IFuAZ6dJEdVz3baPwC5VYtHySVfBAOUG92w,803
2
2
  txt2stix/attack_flow.py,sha256=WnhihbY_ltWOXrmzigTHIwj1pzSIQqKpu2sPe2M465k,8489
3
3
  txt2stix/bundler.py,sha256=kqUNW9_jktuMyWSkoAa-ydZY-L5gzSSkthb7OdhUiKo,16854
4
4
  txt2stix/common.py,sha256=ISnGNKqJPE1EcfhL-x_4G18mcwt1urmorkW-ru9kV-0,585
5
+ txt2stix/credential_checker.py,sha256=eWDP-jY3-jm8zI0JMoUcyoQZ_JqPNfCIr_HAO8nVYz0,3044
5
6
  txt2stix/extractions.py,sha256=_tlsqYHhfAoV-PJzxRHysrX47uxCsMlSg7PQWxww1u0,2171
6
7
  txt2stix/indicator.py,sha256=c6S0xx0K8JM-PT_Qd1PlN_ZlDXdnEwiRS8529iUp3yg,30774
7
8
  txt2stix/lookups.py,sha256=h42YVtYUkWZm6ZPv2h5hHDHDzDs3yBqrT_T7pj2MDZI,2301
8
- txt2stix/retriever.py,sha256=auKlk6JlRE9en-oiQ5KICMW0IwmU8R558o0K5UmEQZc,5550
9
+ txt2stix/retriever.py,sha256=JHR_bzj442-BZSc1FAE7zMisP-n--Ax54P_JclIVTsQ,5583
9
10
  txt2stix/stix.py,sha256=9nXD9a2dCY4uaatl-mlIA1k3srwQBhGW-tUSho3iYe0,30
10
- txt2stix/txt2stix.py,sha256=9i03BOYz34G_qaf5oTUm3YhDLDeqaPkik3dDyEjpFAE,18208
11
+ txt2stix/txt2stix.py,sha256=b16h8BCSerZrq7PWjXkUZB4WXkDCUDMru1LDJeIXCHo,18587
11
12
  txt2stix/utils.py,sha256=n6mh4t9ZRJ7iT4Jvp9ai_dfCXjgXNcRtF_zXO7nkpnk,3304
12
13
  txt2stix/ai_extractor/__init__.py,sha256=5Tf6Co9THzytBdFEVhD-7vvT05TT3nSpltnAV1sfdoM,349
13
14
  txt2stix/ai_extractor/anthropic.py,sha256=mdz-8CB-BSCEqnK5l35DRZURVPUf508ef2b48XMxmuk,441
14
- txt2stix/ai_extractor/base.py,sha256=mHu6xtWu78aDHnb2ePXR0UCBbROS-jH0kPRgQxfIwhI,3685
15
+ txt2stix/ai_extractor/base.py,sha256=w8FFceCtOZ4_uAaVMTZCzUdKnC3_3nDBafBzHlfHCn0,3959
15
16
  txt2stix/ai_extractor/deepseek.py,sha256=2XehIYbWXG6Odq68nQX4CNtl5GdmBlAmjLP_lG2eEFo,660
16
17
  txt2stix/ai_extractor/gemini.py,sha256=yJC7knYzl-TScyCBd-MTpUf-NT6znC25E7vXxNMqjLU,578
17
18
  txt2stix/ai_extractor/openai.py,sha256=DtllzeVhZw1231hj35vn1U8V2MMzm8wM7mqKLBkxazQ,489
@@ -112,8 +113,8 @@ txt2stix/includes/lookups/threat_actor.txt,sha256=QfDO9maQuqKBgW_Sdd7VGv1SHZ9Ra-
112
113
  txt2stix/includes/lookups/tld.txt,sha256=-MEgJea2NMG_KDsnc4BVvI8eRk5Dm93L-t8SGYx5wMo,8598
113
114
  txt2stix/includes/lookups/tool.txt,sha256=HGKG6JpUE26w6ezzSxOjBkp15UpSaB7N-mZ_NU_3G7A,6
114
115
  txt2stix/includes/tests/test_cases.yaml,sha256=QD1FdIunpPkOpsn6wJRqs2vil_hv8OSVaqUp4a96aZg,22247
115
- txt2stix-1.0.4.dist-info/METADATA,sha256=dyi1Sej16Pl7EBTkcWNnYkoeU-d9R4mIwKSd2sF1Kec,14887
116
- txt2stix-1.0.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
117
- txt2stix-1.0.4.dist-info/entry_points.txt,sha256=x6QPtt65hWeomw4IpJ_wQUesBl1M4WOLODbhOKyWMFg,55
118
- txt2stix-1.0.4.dist-info/licenses/LICENSE,sha256=BK8Ppqlc4pdgnNzIxnxde0taoQ1BgicdyqmBvMiNYgY,11364
119
- txt2stix-1.0.4.dist-info/RECORD,,
116
+ txt2stix-1.0.6.dist-info/METADATA,sha256=0FATDkKqLz19WAyEXCNYJSkvLKa-8jk8jaN2_mZ9DwU,15482
117
+ txt2stix-1.0.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
118
+ txt2stix-1.0.6.dist-info/entry_points.txt,sha256=x6QPtt65hWeomw4IpJ_wQUesBl1M4WOLODbhOKyWMFg,55
119
+ txt2stix-1.0.6.dist-info/licenses/LICENSE,sha256=BK8Ppqlc4pdgnNzIxnxde0taoQ1BgicdyqmBvMiNYgY,11364
120
+ txt2stix-1.0.6.dist-info/RECORD,,