exploitfarm 1.7.5__tar.gz → 1.7.7__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 (47) hide show
  1. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/PKG-INFO +23 -9
  2. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/README.md +22 -8
  3. exploitfarm-1.7.7/exploitfarm/__init__.py +91 -0
  4. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/_init_exposed.py +53 -15
  5. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/flags.py +1 -0
  6. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/xfarm.py +18 -17
  7. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/xploit.py +0 -3
  8. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm.egg-info/PKG-INFO +23 -9
  9. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/setup.py +1 -1
  10. exploitfarm-1.7.5/exploitfarm/__init__.py +0 -29
  11. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/MANIFEST.in +0 -0
  12. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/__main__.py +0 -0
  13. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/__init__.py +0 -0
  14. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/client.py +0 -0
  15. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/dbtypes.py +0 -0
  16. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/enums.py +0 -0
  17. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/exploit.py +0 -0
  18. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/exploit_template.py +0 -0
  19. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/groups.py +0 -0
  20. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/response.py +0 -0
  21. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/service.py +0 -0
  22. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/submitter.py +0 -0
  23. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/teams.py +0 -0
  24. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/__init__.py +0 -0
  25. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/config.py +0 -0
  26. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/exploitdownload.py +0 -0
  27. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/exploitinit.py +0 -0
  28. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/group_prejoin.py +0 -0
  29. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/group_select.py +0 -0
  30. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/login.py +0 -0
  31. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/shared_attack_group.py +0 -0
  32. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/startxploit.py +0 -0
  33. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/template.py +0 -0
  34. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/utils/__init__.py +0 -0
  35. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/utils/cmd.py +0 -0
  36. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/utils/config.py +0 -0
  37. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/utils/exploit.py +0 -0
  38. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/utils/reqs.py +0 -0
  39. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/utils/templates.py +0 -0
  40. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/utils/windows_close_fix.py +0 -0
  41. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm.egg-info/SOURCES.txt +0 -0
  42. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm.egg-info/dependency_links.txt +0 -0
  43. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm.egg-info/requires.txt +0 -0
  44. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm.egg-info/top_level.txt +0 -0
  45. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/requirements.txt +0 -0
  46. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/setup.cfg +0 -0
  47. {exploitfarm-1.7.5 → exploitfarm-1.7.7}/xfarm +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: exploitfarm
3
- Version: 1.7.5
3
+ Version: 1.7.7
4
4
  Summary: Exploit Farm client
5
5
  Home-page: https://github.com/pwnzer0tt1/exploitfarm
6
6
  Author: Pwnzer0tt1
@@ -98,15 +98,18 @@ submit_flags(flags)
98
98
  #### **Store Class**
99
99
 
100
100
  The `Store` class provides a key-value storage system for exploits, allowing you to save and retrieve centralized data during execution.
101
- The key value store is different for each exploit id. You can use it to store some data you need to save and change for every exploit execution.
101
+ The key value store is by default isolated per exploit id.
102
+ If you need data shared between exploits you should use a custom context_id that allows to access the same data from different exploits. The context_id can be set also in the constructor of the Store class, and will be used as default context_id for all the calls with that store instance.
102
103
 
103
104
  ```python
104
105
  from exploitfarm import Store
105
106
 
106
- store = Store()
107
+ # Initialize with an optional default context
108
+ store = Store(default_context_id="my_global_context_id")
107
109
 
108
- # Set a value
110
+ # Set a value (context_id can be overridden per call)
109
111
  store.set("example_key", {"example": "data"})
112
+ store.set("other_key", "value", context_id="explicit_ctx")
110
113
 
111
114
  # Get a value (if not exists returns None)
112
115
  data = store.get("example_key")
@@ -115,17 +118,28 @@ print(data)
115
118
  # Delete a key
116
119
  store.delete("example_key")
117
120
 
118
- # List all keys
121
+ # List all keys in the current context
119
122
  keys = store.keys()
120
123
  print(keys)
124
+
125
+ # List all available contexts
126
+ contexts = store.contexts()
127
+ print(contexts)
128
+
129
+ # Delete an entire context
130
+ store.delete_context(context_id="explicit_ctx")
121
131
  ```
122
132
 
123
133
  **Methods**:
124
134
 
125
- - `get(key: str, timeout: int = HTTP_TIMEOUT) -> bytes`: Retrieve a value by key.
126
- - `set(key: str, value: bytes, timeout: int = HTTP_TIMEOUT)`: Store a value by key.
127
- - `delete(key: str, timeout: int = HTTP_TIMEOUT)`: Delete a key.
128
- - `keys(timeout: int = HTTP_TIMEOUT) -> list[str]`: List all stored keys.
135
+ - `get(key: str, default=None, context_id: str | None = None, timeout: int = HTTP_TIMEOUT) -> any`: Retrieve and deserialize a value by key.
136
+ - `get_bytes(key: str, default=None, context_id: str | None = None, timeout: int = HTTP_TIMEOUT) -> bytes`: Retrieve a raw byte value.
137
+ - `set(key: str, value: any, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Serialize and store a value by key.
138
+ - `set_bytes(key: str, value: bytes, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Store a raw byte value.
139
+ - `delete(key: str, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Delete a key.
140
+ - `keys(timeout: int = HTTP_TIMEOUT, context_id: str | None = None) -> list[str]`: List all stored keys in a context.
141
+ - `contexts(timeout: int = HTTP_TIMEOUT) -> list[str]`: List all available contexts.
142
+ - `delete_context(context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Delete an entire context and its keys.
129
143
 
130
144
  **Environment Variables Required**:
131
145
 
@@ -61,15 +61,18 @@ submit_flags(flags)
61
61
  #### **Store Class**
62
62
 
63
63
  The `Store` class provides a key-value storage system for exploits, allowing you to save and retrieve centralized data during execution.
64
- The key value store is different for each exploit id. You can use it to store some data you need to save and change for every exploit execution.
64
+ The key value store is by default isolated per exploit id.
65
+ If you need data shared between exploits you should use a custom context_id that allows to access the same data from different exploits. The context_id can be set also in the constructor of the Store class, and will be used as default context_id for all the calls with that store instance.
65
66
 
66
67
  ```python
67
68
  from exploitfarm import Store
68
69
 
69
- store = Store()
70
+ # Initialize with an optional default context
71
+ store = Store(default_context_id="my_global_context_id")
70
72
 
71
- # Set a value
73
+ # Set a value (context_id can be overridden per call)
72
74
  store.set("example_key", {"example": "data"})
75
+ store.set("other_key", "value", context_id="explicit_ctx")
73
76
 
74
77
  # Get a value (if not exists returns None)
75
78
  data = store.get("example_key")
@@ -78,17 +81,28 @@ print(data)
78
81
  # Delete a key
79
82
  store.delete("example_key")
80
83
 
81
- # List all keys
84
+ # List all keys in the current context
82
85
  keys = store.keys()
83
86
  print(keys)
87
+
88
+ # List all available contexts
89
+ contexts = store.contexts()
90
+ print(contexts)
91
+
92
+ # Delete an entire context
93
+ store.delete_context(context_id="explicit_ctx")
84
94
  ```
85
95
 
86
96
  **Methods**:
87
97
 
88
- - `get(key: str, timeout: int = HTTP_TIMEOUT) -> bytes`: Retrieve a value by key.
89
- - `set(key: str, value: bytes, timeout: int = HTTP_TIMEOUT)`: Store a value by key.
90
- - `delete(key: str, timeout: int = HTTP_TIMEOUT)`: Delete a key.
91
- - `keys(timeout: int = HTTP_TIMEOUT) -> list[str]`: List all stored keys.
98
+ - `get(key: str, default=None, context_id: str | None = None, timeout: int = HTTP_TIMEOUT) -> any`: Retrieve and deserialize a value by key.
99
+ - `get_bytes(key: str, default=None, context_id: str | None = None, timeout: int = HTTP_TIMEOUT) -> bytes`: Retrieve a raw byte value.
100
+ - `set(key: str, value: any, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Serialize and store a value by key.
101
+ - `set_bytes(key: str, value: bytes, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Store a raw byte value.
102
+ - `delete(key: str, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Delete a key.
103
+ - `keys(timeout: int = HTTP_TIMEOUT, context_id: str | None = None) -> list[str]`: List all stored keys in a context.
104
+ - `contexts(timeout: int = HTTP_TIMEOUT) -> list[str]`: List all available contexts.
105
+ - `delete_context(context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Delete an entire context and its keys.
92
106
 
93
107
  **Environment Variables Required**:
94
108
 
@@ -0,0 +1,91 @@
1
+ def _get_version():
2
+ v = "1.7.7" if "{" not in "1.7.7" else "0.0.0"
3
+ if v == "0.0.0":
4
+ import os
5
+ env_v = os.getenv("EXPLOITFARM_VERSION")
6
+ if env_v:
7
+ return env_v
8
+ import re
9
+
10
+ def parse_version(ver):
11
+ ver = ver.lstrip("v")
12
+ parts = re.split(r'[^0-9]+', ver)
13
+ return tuple(int(p) if p.isdigit() else 0 for p in parts if p)
14
+
15
+ def find_git_dir(start_path):
16
+ current = start_path
17
+ while current and current != "/" and current != "":
18
+ gd = os.path.join(current, ".git")
19
+ if os.path.isdir(gd):
20
+ return gd
21
+ parent = os.path.dirname(current)
22
+ if parent == current:
23
+ break
24
+ current = parent
25
+ return None
26
+
27
+ try:
28
+ git_dir = find_git_dir(os.path.abspath(os.path.dirname(__file__)))
29
+ if not git_dir:
30
+ return v
31
+
32
+ branch = None
33
+ with open(os.path.join(git_dir, "HEAD"), "r") as f:
34
+ head = f.read().strip()
35
+ if head.startswith("ref: refs/heads/"):
36
+ branch = head.split("ref: refs/heads/")[1]
37
+
38
+ if branch != "main":
39
+ return v
40
+
41
+ tags = []
42
+ try:
43
+ with open(os.path.join(git_dir, "packed-refs"), "r") as f:
44
+ for line in f:
45
+ if " refs/tags/" in line:
46
+ tags.append(line.strip().split(" refs/tags/")[1])
47
+ except Exception:
48
+ pass
49
+
50
+ tags_dir = os.path.join(git_dir, "refs", "tags")
51
+ try:
52
+ for root, _, files in os.walk(tags_dir):
53
+ for file in files:
54
+ tags.append(os.path.relpath(os.path.join(root, file), tags_dir).replace(os.path.sep, "/"))
55
+ except Exception:
56
+ pass
57
+
58
+ version_tags = [t for t in set(tags) if any(c.isdigit() for c in t)]
59
+ if version_tags:
60
+ return max(version_tags, key=parse_version)
61
+ except Exception:
62
+ pass
63
+ return v
64
+
65
+ __version__ = _get_version()
66
+
67
+ from exploitfarm.utils import try_tcp_connection
68
+ from exploitfarm.models.enums import AttackMode, SetupStatus, FlagStatus
69
+ from exploitfarm.utils import DEV_MODE
70
+ from exploitfarm._init_exposed import get_host, Prio
71
+ from exploitfarm._init_exposed import nicenessify, get_config, random_str, print, Store
72
+ from exploitfarm._init_exposed import os_unix, os_windows, session
73
+
74
+ # Exported functions
75
+ __all__ = [
76
+ "try_tcp_connection",
77
+ "AttackMode",
78
+ "SetupStatus",
79
+ "get_host",
80
+ "get_config",
81
+ "random_str",
82
+ "session",
83
+ "print",
84
+ "nicenessify",
85
+ "Prio",
86
+ "FlagStatus",
87
+ "Store",
88
+ "DEV_MODE",
89
+ "os_unix",
90
+ "os_windows",
91
+ ]
@@ -147,7 +147,7 @@ print = functools.partial(print, flush=True)
147
147
 
148
148
 
149
149
  class Store:
150
- def __init__(self):
150
+ def __init__(self, default_context_id: str | None = None):
151
151
  self.base_url = os.getenv("XFARM_REMOTE_URL", None)
152
152
  if not self.base_url:
153
153
  raise ValueError("this exploit has to be run with xfarm")
@@ -156,62 +156,100 @@ class Store:
156
156
  raise ValueError("this exploit has to be run with xfarm")
157
157
  self.login_token = os.getenv("XFARM_LOGIN_TOKEN", None)
158
158
  self.session: Session = get_session(self.login_token)
159
-
160
- def _keycheck(self, key: str):
159
+ if default_context_id is not None:
160
+ self._keycheck(default_context_id, "default_context_id")
161
+ self.default_context_id = default_context_id
162
+
163
+ def _get_context_id(self, context_id: str | None = None) -> str | None:
164
+ if context_id is not None:
165
+ self._keycheck(context_id, "context_id")
166
+ return context_id
167
+ if self.default_context_id is not None:
168
+ return self.default_context_id
169
+ return None
170
+
171
+ def _keycheck(self, key: str, what: str = "key"):
161
172
  if not isinstance(key, str):
162
- raise ValueError("key must be a string")
173
+ raise ValueError(f"{what} must be a string")
163
174
  if len(key) > 255:
164
- raise ValueError("key must be less than 255 characters")
175
+ raise ValueError(f"{what} must be less than 255 characters")
165
176
  if not re.match(r"^[A-Za-z0-9\-\._~()'!*:@,;]+$", key):
166
- raise ValueError(r"key must match this regex: ^[A-Za-z0-9\-\._~()'!*:@,;]+$")
177
+ raise ValueError(rf"{what} must match this regex: ^[A-Za-z0-9\-\._~()'!*:@,;$]+$")
167
178
 
168
- def get_bytes(self, key: str, default=None, timeout: int = HTTP_TIMEOUT) -> bytes:
179
+ def get_bytes(self, key: str, default=None, context_id: str | None = None, timeout: int = HTTP_TIMEOUT) -> bytes:
180
+ context_id = self._get_context_id(context_id)
169
181
  self._keycheck(key)
170
182
  r = self.session.get(
171
183
  urljoin(self.base_url, f"exploits/{self.exploit_id}/store/key/{key}"),
184
+ params={"context_id": context_id} if context_id else None,
172
185
  timeout=timeout,
173
186
  )
174
187
  if r.status_code == 404:
175
188
  return default
176
189
  return check_req(r).content
177
190
 
178
- def get(self, key: str, default=None, timeout: int = HTTP_TIMEOUT) -> bytes:
191
+ def get(self, key: str, default=None, context_id: str | None = None, timeout: int = HTTP_TIMEOUT) -> bytes:
179
192
  class TmpNotFound:
180
193
  pass
181
-
182
- data = self.get_bytes(key, default=TmpNotFound, timeout=timeout)
194
+ data = self.get_bytes(key, context_id=context_id, default=TmpNotFound, timeout=timeout)
183
195
  if data == TmpNotFound:
184
196
  return default
185
197
  return pickle.loads(data)
186
198
 
187
- def set_bytes(self, key: str, value: bytes, timeout: int = HTTP_TIMEOUT):
199
+ def set_bytes(self, key: str, value: bytes, context_id: str | None = None, timeout: int = HTTP_TIMEOUT):
188
200
  self._keycheck(key)
201
+ context_id = self._get_context_id(context_id)
189
202
  return requests_check_json(
190
203
  self.session.put(
191
204
  urljoin(self.base_url, f"exploits/{self.exploit_id}/store/key/{key}"),
192
205
  data=value,
193
206
  timeout=timeout,
194
207
  headers={"Content-Type": "application/octet-stream"},
208
+ params={"context_id": context_id} if context_id else None,
195
209
  )
196
210
  )
197
211
 
198
- def set(self, key: str, value, timeout: int = HTTP_TIMEOUT):
212
+ def set(self, key: str, value, context_id: str | None = None, timeout: int = HTTP_TIMEOUT):
199
213
  value = pickle.dumps(value)
200
- return self.set_bytes(key, value, timeout)
214
+ return self.set_bytes(key, value, context_id=context_id, timeout=timeout)
201
215
 
202
- def delete(self, key: str, timeout: int = HTTP_TIMEOUT):
216
+ def delete(self, key: str, context_id: str | None = None, timeout: int = HTTP_TIMEOUT):
203
217
  self._keycheck(key)
218
+ context_id = self._get_context_id(context_id)
204
219
  return requests_check_json(
205
220
  self.session.delete(
206
221
  urljoin(self.base_url, f"exploits/{self.exploit_id}/store/key/{key}"),
222
+ params={"context_id": context_id} if context_id else None,
207
223
  timeout=timeout,
208
224
  )
209
225
  )
210
226
 
211
- def keys(self, timeout: int = HTTP_TIMEOUT) -> list[str]:
227
+ def keys(self, timeout: int = HTTP_TIMEOUT, context_id: str | None = None) -> list[str]:
228
+ context_id = self._get_context_id(context_id)
212
229
  return requests_check_json(
213
230
  self.session.get(
214
231
  urljoin(self.base_url, f"exploits/{self.exploit_id}/store/keys"),
232
+ params={ "context_id": context_id } if context_id else None,
233
+ timeout=timeout,
234
+ )
235
+ )
236
+
237
+ def contexts(self, timeout: int = HTTP_TIMEOUT) -> list[str]:
238
+ return requests_check_json(
239
+ self.session.get(
240
+ urljoin(self.base_url, f"exploits/{self.exploit_id}/store/contexts"),
241
+ timeout=timeout,
242
+ )
243
+ )
244
+
245
+ def delete_context(self, context_id: str | None = None, timeout: int = HTTP_TIMEOUT):
246
+ context_id = self._get_context_id(context_id)
247
+ if not context_id:
248
+ raise ValueError("context_id must be provided to delete it")
249
+ self._keycheck(context_id, "context_id")
250
+ return requests_check_json(
251
+ self.session.delete(
252
+ urljoin(self.base_url, f"exploits/{self.exploit_id}/store/context/{context_id}"),
215
253
  timeout=timeout,
216
254
  )
217
255
  )
@@ -21,6 +21,7 @@ class AttackExecutionDTO(BaseModel):
21
21
  executed_by: ClientID|None = Field(None, validation_alias=AliasChoices('executed_by_id'))
22
22
  executed_by_group: AttackGroupID|None = Field(None, validation_alias=AliasChoices('executed_by_group_id'))
23
23
  flags: list[FlagDTOSmall]
24
+ duplicate_flags: list[str] = []
24
25
  exploit_source: ExploitSourceID|None = Field(None, validation_alias=AliasChoices('exploit_source_id'))
25
26
 
26
27
  class FlagDTOAttackDetails(BaseModel):
@@ -123,28 +123,29 @@ def fast_check_exploit_conf_service(exploit_config: ExploitConfig) -> ExploitCon
123
123
 
124
124
 
125
125
  def auto_push_is_safe(exploit_config: ExploitConfig):
126
- no_latest_groups = (
127
- len(
128
- [
129
- g
130
- for g in g.config.reqs.groups()
131
- if g["exploit"] == str(exploit_config.uuid) and g["commit"] == "latest"
132
- ]
133
- )
134
- == 0
135
- )
136
- if no_latest_groups:
126
+ # Since groups always use the latest version, check if there are ANY groups using this exploit
127
+ active_groups = [
128
+ gr
129
+ for gr in g.config.reqs.groups()
130
+ if gr.get("exploit") == str(exploit_config.uuid)
131
+ ]
132
+
133
+ if len(active_groups) == 0:
137
134
  return True
138
135
  else:
139
136
  current_hash = exploit_config.hash()
140
- if current_hash in [
141
- ele["hash"] for ele in g.config.reqs.exploit_source_log(exploit_config.uuid)
142
- ]: # If the current commit is pushed
143
- return True
137
+ try:
138
+ # If the exact source version is already pushed, it's safe
139
+ pushed_hashes = [ele["hash"] for ele in g.config.reqs.exploit_source_log(exploit_config.uuid)]
140
+ if current_hash in pushed_hashes:
141
+ return True
142
+ except Exception:
143
+ pass
144
+
144
145
  if g.interactive:
145
146
  return typer.confirm(
146
- "There are attack groups that are using the latest commit, "
147
- "this command is trying to push a new version of the exploit. "
147
+ "There are active attack groups using this exploit. "
148
+ "Pushing a new version will update them to the new code. "
148
149
  "Do you want to continue?",
149
150
  default=False,
150
151
  )
@@ -55,7 +55,6 @@ class g:
55
55
  server_id = None
56
56
  thread_pool: ThreadPoolExecutor = None
57
57
  tot_teams: int = 0
58
- flag_cache: set = set()
59
58
  attack_round_deadline = dt_now()
60
59
  exploit_hash: str | None = None
61
60
  virtual_time_available = 0
@@ -198,8 +197,6 @@ class AttackStorage:
198
197
  stats: bool = True,
199
198
  ):
200
199
  with self._lock:
201
- flags = [flag for flag in flags if flag not in g.flag_cache]
202
- g.flag_cache.update(flags)
203
200
  execution_submit = {
204
201
  "start_time": start_time,
205
202
  "end_time": end_time,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: exploitfarm
3
- Version: 1.7.5
3
+ Version: 1.7.7
4
4
  Summary: Exploit Farm client
5
5
  Home-page: https://github.com/pwnzer0tt1/exploitfarm
6
6
  Author: Pwnzer0tt1
@@ -98,15 +98,18 @@ submit_flags(flags)
98
98
  #### **Store Class**
99
99
 
100
100
  The `Store` class provides a key-value storage system for exploits, allowing you to save and retrieve centralized data during execution.
101
- The key value store is different for each exploit id. You can use it to store some data you need to save and change for every exploit execution.
101
+ The key value store is by default isolated per exploit id.
102
+ If you need data shared between exploits you should use a custom context_id that allows to access the same data from different exploits. The context_id can be set also in the constructor of the Store class, and will be used as default context_id for all the calls with that store instance.
102
103
 
103
104
  ```python
104
105
  from exploitfarm import Store
105
106
 
106
- store = Store()
107
+ # Initialize with an optional default context
108
+ store = Store(default_context_id="my_global_context_id")
107
109
 
108
- # Set a value
110
+ # Set a value (context_id can be overridden per call)
109
111
  store.set("example_key", {"example": "data"})
112
+ store.set("other_key", "value", context_id="explicit_ctx")
110
113
 
111
114
  # Get a value (if not exists returns None)
112
115
  data = store.get("example_key")
@@ -115,17 +118,28 @@ print(data)
115
118
  # Delete a key
116
119
  store.delete("example_key")
117
120
 
118
- # List all keys
121
+ # List all keys in the current context
119
122
  keys = store.keys()
120
123
  print(keys)
124
+
125
+ # List all available contexts
126
+ contexts = store.contexts()
127
+ print(contexts)
128
+
129
+ # Delete an entire context
130
+ store.delete_context(context_id="explicit_ctx")
121
131
  ```
122
132
 
123
133
  **Methods**:
124
134
 
125
- - `get(key: str, timeout: int = HTTP_TIMEOUT) -> bytes`: Retrieve a value by key.
126
- - `set(key: str, value: bytes, timeout: int = HTTP_TIMEOUT)`: Store a value by key.
127
- - `delete(key: str, timeout: int = HTTP_TIMEOUT)`: Delete a key.
128
- - `keys(timeout: int = HTTP_TIMEOUT) -> list[str]`: List all stored keys.
135
+ - `get(key: str, default=None, context_id: str | None = None, timeout: int = HTTP_TIMEOUT) -> any`: Retrieve and deserialize a value by key.
136
+ - `get_bytes(key: str, default=None, context_id: str | None = None, timeout: int = HTTP_TIMEOUT) -> bytes`: Retrieve a raw byte value.
137
+ - `set(key: str, value: any, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Serialize and store a value by key.
138
+ - `set_bytes(key: str, value: bytes, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Store a raw byte value.
139
+ - `delete(key: str, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Delete a key.
140
+ - `keys(timeout: int = HTTP_TIMEOUT, context_id: str | None = None) -> list[str]`: List all stored keys in a context.
141
+ - `contexts(timeout: int = HTTP_TIMEOUT) -> list[str]`: List all available contexts.
142
+ - `delete_context(context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Delete an entire context and its keys.
129
143
 
130
144
  **Environment Variables Required**:
131
145
 
@@ -10,7 +10,7 @@ with open("requirements.txt", "r", encoding="utf-8") as f:
10
10
  if not ele.strip().startswith("#") and ele.strip() != ""
11
11
  ]
12
12
 
13
- VERSION = "1.7.5"
13
+ VERSION = "1.7.7"
14
14
 
15
15
  setuptools.setup(
16
16
  name="exploitfarm",
@@ -1,29 +0,0 @@
1
- __version__ = (
2
- "1.7.5" if "{" not in "1.7.5" else "0.0.0"
3
- )
4
-
5
- from exploitfarm.utils import try_tcp_connection
6
- from exploitfarm.models.enums import AttackMode, SetupStatus, FlagStatus
7
- from exploitfarm.utils import DEV_MODE
8
- from exploitfarm._init_exposed import get_host, Prio
9
- from exploitfarm._init_exposed import nicenessify, get_config, random_str, print, Store
10
- from exploitfarm._init_exposed import os_unix, os_windows, session
11
-
12
- # Exported functions
13
- __all__ = [
14
- "try_tcp_connection",
15
- "AttackMode",
16
- "SetupStatus",
17
- "get_host",
18
- "get_config",
19
- "random_str",
20
- "session",
21
- "print",
22
- "nicenessify",
23
- "Prio",
24
- "FlagStatus",
25
- "Store",
26
- "DEV_MODE",
27
- "os_unix",
28
- "os_windows",
29
- ]
File without changes
File without changes
File without changes