Skip to content

Commit 16a013d

Browse files
authored
Merge pull request #185 from thorinaboenke/fix_pre_commit_errors
pre commit fix
2 parents 96ddbe2 + fe03c73 commit 16a013d

32 files changed

Lines changed: 178 additions & 177 deletions

.pre-commit-config.yaml

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,36 @@ repos:
2020
- id: mypy
2121
additional_dependencies: [pydantic, types-PyYAML, types-requests, types-paramiko, types-tabulate]
2222

23+
- repo: https://github.com/myint/autoflake
24+
rev: 'v2.3.1'
25+
hooks:
26+
- id: autoflake
27+
args: [
28+
--in-place,
29+
--remove-unused-variables,
30+
--remove-all-unused-imports,
31+
--recursive
32+
]
33+
2334
- repo: https://github.com/pycqa/flake8
2435
rev: '7.1.0' # pick a git hash / tag to point to
2536
hooks:
2637
- id: flake8
38+
args: [
39+
--max-line-length=110,
40+
]
2741

2842
- repo: https://github.com/hhatto/autopep8
2943
rev: 'v2.3.1'
3044
hooks:
3145
- id: autopep8
32-
args: [--max-line-length=110, --diff]
33-
46+
args: [
47+
--in-place,
48+
--aggressive,
49+
--aggressive,
50+
--max-line-length=110,
51+
--recursive
52+
]
3453

3554
# PROBLEMS WITH IMPORTS IN PYLINT!!!
3655
#- repo: https://github.com/PyCQA/prospector

docs/source/developing/command.rst

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
Adding a New Command
55
======================
66

7-
AttackMate supports extending its functionality by adding new commands.
7+
AttackMate supports extending its functionality by adding new commands.
88
This section details the steps required to integrate a new command.
99

1010

1111
1. Define the Command Schema
1212
=============================
1313

14-
All Commands in AttackMate inherit from ``BaseCommand``.
14+
All Commands in AttackMate inherit from ``BaseCommand``.
1515
To create a new command, define a class in `/src/attackmate/schemas` and register it using the ``@CommandRegistry.register('<command_type>')`` decorator.
1616

1717
For example, to add a ``debug`` command:
@@ -21,7 +21,7 @@ For example, to add a ``debug`` command:
2121
from typing import Literal
2222
from .base import BaseCommand
2323
from attackmate.command import CommandRegistry
24-
24+
2525
@CommandRegistry.register('debug')
2626
class DebugCommand(BaseCommand):
2727
type: Literal['debug']
@@ -30,7 +30,7 @@ For example, to add a ``debug`` command:
3030
wait_for_key: bool = False
3131
cmd: str = ''
3232

33-
Registering the command in the ``CommandRegistry`` allows the command to be also instantiated dynamically using the ``Command.create()`` method and is essential to
33+
Registering the command in the ``CommandRegistry`` allows the command to be also instantiated dynamically using the ``Command.create()`` method and is essential to
3434
make them usable in external python scripts.
3535

3636

@@ -53,16 +53,16 @@ The new command should be handled by an executor in `src/attackmate/executors``
5353

5454
3. Ensure the Executor Handles the New Command
5555
==============================================
56-
57-
The ``ExecutorFactory`` class manages and creates executor instances based on command types.
56+
57+
The ``ExecutorFactory`` class manages and creates executor instances based on command types.
5858
It maintains a registry (``_executors``) that maps command type strings to executor classes, allowing for dynamic execution of different command types.
59-
Executors are registered using the ``register_executor`` method, which provides a decorator to associate a command type with a class.
59+
Executors are registered using the ``register_executor`` method, which provides a decorator to associate a command type with a class.
6060
When a command is executed, the ``create_executor`` method retrieves the corresponding executor class, filters the constructor arguments based on the class's signature, and then creates an instance.
6161

62-
Accordingly, executors must be registered using the ``@executor_factory.register_executor('<command_type>')`` decorator.
62+
Accordingly, executors must be registered using the ``@executor_factory.register_executor('<command_type>')`` decorator.
6363

64-
If the new executor class requires additional initialization arguments, these must be added to the ``_get_executor_config`` method in ``attackmate.py``.
65-
All configurations are always passed to the ``ExecutorFactory``.
64+
If the new executor class requires additional initialization arguments, these must be added to the ``_get_executor_config`` method in ``attackmate.py``.
65+
All configurations are always passed to the ``ExecutorFactory``.
6666
The factory filters the provided configurations based on the class constructor signature, ensuring that only the required parameters are used.
6767

6868
::
@@ -93,7 +93,7 @@ Update the ``LoopCommand`` schema to include the new command.
9393
DebugCommand, # Newly added command
9494
# ... other command classes ...
9595
]
96-
96+
9797

9898
5. Modify playbook.py to Include the New Command
9999
=====================================================
@@ -116,10 +116,3 @@ Once these steps are completed, the new command will be fully integrated into At
116116
=====================
117117

118118
Finally, update the documentation in `docs/source/playbook/commands` to include the new command.
119-
120-
121-
122-
123-
124-
125-

docs/source/developing/integration.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,4 @@ The return code can be used to determine if the command was successful:
101101
if result.returncode == 0:
102102
print("Command executed successfully.")
103103
else:
104-
print(f"Command failed with return code {result.returncode}")
104+
print(f"Command failed with return code {result.returncode}")

docs/source/installation/uv.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,4 @@ Installation Steps
5959
.. warning::
6060

6161
Please note that you need to :ref:`sliver-fix` if you want
62-
to use the sliver commands!
62+
to use the sliver commands!

docs/source/playbook/commands/vnc.rst

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,3 @@ Execute commands on a remote server via VNC. Uses the `vncdotool <https://github
151151
.. note::
152152

153153
The vnc connection needs to be closed with the command ``close`` explicitely, otherwise attackmate will keep running.
154-
155-
156-
157-
158-

examples/regex.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,3 @@ commands:
6060

6161
- type: debug
6262
cmd: "Result string: $SUBSTITUTED"
63-
64-
65-

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,6 @@ where = ["src"]
5252

5353
[tool.setuptools.dynamic]
5454
version = {attr = "attackmate.metadata.__version__"}
55+
56+
[tool.mypy]
57+
explicit_package_bases = true

remote_rest/auth_utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def verify_password(plain_password: str, hashed_password: str) -> bool:
3232

3333
def get_user_hash(username: str) -> Optional[str]:
3434
"""Fetches the hashed password from environment variables."""
35-
env_var_name = f"USER_{username.upper()}_HASH"
35+
env_var_name = f'USER_{username.upper()}_HASH'
3636
return os.getenv(env_var_name)
3737

3838

@@ -84,7 +84,7 @@ async def get_current_user(token: str = Depends(api_key_header_scheme)) -> str:
8484

8585
token_data = ACTIVE_TOKENS.get(token)
8686
if not token_data:
87-
logger.warning(f"Token not found: {token[:5]}...")
87+
logger.warning(f'Token not found: {token[:5]}...')
8888
raise credentials_exception
8989

9090
username: str = token_data['username']
@@ -99,5 +99,5 @@ async def get_current_user(token: str = Depends(api_key_header_scheme)) -> str:
9999

100100
renew_token_expiry(token)
101101

102-
logger.debug(f"Token validated successfully for user: {username}")
102+
logger.debug(f'Token validated successfully for user: {username}')
103103
return username

remote_rest/client.py

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def save_token(token: Optional[str]):
3636
if token:
3737
# This is pretty hacky, client mainly for testing purposes
3838
logger.info('updating env var')
39-
logger.info(f"run in your shell: export ATTACKMATE_API_TOKEN={token}")
39+
logger.info(f'run in your shell: export ATTACKMATE_API_TOKEN={token}')
4040
else:
4141
os.environ.pop(TOKEN_ENV_VAR, None)
4242

@@ -67,14 +67,14 @@ def parse_key_value_pairs(items: List[str] | None) -> Dict[str, str]:
6767
key, value = item.split('=', 1)
6868
result[key.strip()] = value.strip()
6969
else:
70-
logging.warning(f"Skipping malformed pair: {item}")
70+
logging.warning(f'Skipping malformed pair: {item}')
7171
return result
7272

7373

7474
# Login
7575
def login(client: httpx.Client, base_url: str, username: str, password: str):
7676
"""Logs in and saves the token."""
77-
url = f"{base_url}/login"
77+
url = f'{base_url}/login'
7878
logger.info(f"Attempting login for user '{username}' at {url}...")
7979
try:
8080
# standard form encoding for OAuth2PasswordRequestForm -> expected bei Fastapi
@@ -84,46 +84,46 @@ def login(client: httpx.Client, base_url: str, username: str, password: str):
8484
token = data.get('access_token')
8585
if token:
8686
save_token(token) # workaround, export to env var in shell
87-
print(f"Login successful. Token received: {token[:5]}...")
87+
print(f'Login successful. Token received: {token[:5]}...')
8888
else:
8989
logger.error(' No access token received in response.')
9090
sys.exit(1)
9191
except httpx.RequestError as e:
92-
logger.error(f"HTTP Request Error during login: {e}")
92+
logger.error(f'HTTP Request Error during login: {e}')
9393
sys.exit(1)
9494
except httpx.HTTPStatusError as e:
95-
logger.error(f"Login failed: {e.response.status_code}")
95+
logger.error(f'Login failed: {e.response.status_code}')
9696
sys.exit(1)
9797
except Exception as e:
98-
logger.error(f"Unexpected error during login: {e}", exc_info=True)
98+
logger.error(f'Unexpected error during login: {e}', exc_info=True)
9999
sys.exit(1)
100100

101101

102102
def get_instance_state_from_server(client: httpx.Client, base_url: str, instance_id: str):
103103
"""Requests the state of a specific instance."""
104-
url = f"{base_url}/instances/{instance_id}/state"
105-
logger.info(f"Requesting state for instance {instance_id} at {url}...")
104+
url = f'{base_url}/instances/{instance_id}/state'
105+
logger.info(f'Requesting state for instance {instance_id} at {url}...')
106106
try:
107107
response = client.get(url, headers=get_auth_headers())
108108
response.raise_for_status()
109109
data = response.json()
110110
update_token_from_response(data)
111-
print(f"\n State for Instance {instance_id} ")
111+
print(f'\n State for Instance {instance_id} ')
112112
print(yaml.dump(data.get('variables', {}), indent=2))
113113
except httpx.RequestError as e:
114-
logger.error(f"HTTP Request Error getting state: {e}")
114+
logger.error(f'HTTP Request Error getting state: {e}')
115115
except httpx.HTTPStatusError as e:
116-
logger.error(f"HTTP Status Error getting state: {e.response.status_code} - {e.response.text}")
116+
logger.error(f'HTTP Status Error getting state: {e.response.status_code} - {e.response.text}')
117117
except Exception as e:
118-
logger.error(f"Unexpected error getting state: {e}", exc_info=True)
118+
logger.error(f'Unexpected error getting state: {e}', exc_info=True)
119119

120120

121121
def run_playbook_yaml(
122122
client: httpx.Client, base_url: str, playbook_file: str, debug: bool = False
123123
):
124124
"""Sends playbook YAML content to the server."""
125-
url = f"{base_url}/playbooks/execute/yaml"
126-
logger.info(f"Attempting to execute playbook from local file: {playbook_file}")
125+
url = f'{base_url}/playbooks/execute/yaml'
126+
logger.info(f'Attempting to execute playbook from local file: {playbook_file}')
127127
try:
128128
with open(playbook_file, 'r') as f:
129129
playbook_yaml_content = f.read()
@@ -147,13 +147,13 @@ def run_playbook_yaml(
147147
if not data.get('success'):
148148
sys.exit(1)
149149
except httpx.RequestError as e:
150-
logger.error(f"HTTP Request Error executing playbook YAML: {e}")
150+
logger.error(f'HTTP Request Error executing playbook YAML: {e}')
151151
sys.exit(1)
152152
except httpx.HTTPStatusError as e:
153-
logger.error(f"HTTP Status Error (YAML): {e.response.status_code} - {e.response.text}")
153+
logger.error(f'HTTP Status Error (YAML): {e.response.status_code} - {e.response.text}')
154154
sys.exit(1)
155155
except Exception as e:
156-
logger.error(f"Unexpected error (YAML): {e}", exc_info=True)
156+
logger.error(f'Unexpected error (YAML): {e}', exc_info=True)
157157
sys.exit(1)
158158

159159

@@ -164,8 +164,8 @@ def run_playbook_file(
164164
debug: bool = False
165165
):
166166
"""Requests server to execute a playbook from local path."""
167-
url = f"{base_url}/playbooks/execute/file"
168-
logger.info(f"Requesting server execute playbook file: {playbook_file_path_on_server}")
167+
url = f'{base_url}/playbooks/execute/file'
168+
logger.info(f'Requesting server execute playbook file: {playbook_file_path_on_server}')
169169
payload = {'file_path': playbook_file_path_on_server}
170170
try:
171171
params = {'debug': True} if debug else {}
@@ -183,13 +183,13 @@ def run_playbook_file(
183183
if not data.get('success'):
184184
sys.exit(1)
185185
except httpx.RequestError as e:
186-
logger.error(f"HTTP Request Error executing playbook file: {e}")
186+
logger.error(f'HTTP Request Error executing playbook file: {e}')
187187
sys.exit(1)
188188
except httpx.HTTPStatusError as e:
189-
logger.error(f"HTTP Status Error (File): {e.response.status_code} - {e.response.text}")
189+
logger.error(f'HTTP Status Error (File): {e.response.status_code} - {e.response.text}')
190190
sys.exit(1)
191191
except Exception as e:
192-
logger.error(f"Unexpected error (File): {e}", exc_info=True)
192+
logger.error(f'Unexpected error (File): {e}', exc_info=True)
193193
sys.exit(1)
194194

195195

@@ -219,14 +219,14 @@ def run_command(client: httpx.Client, base_url: str, args):
219219
body_dict[pydantic_field_name] = arg_value
220220

221221
try:
222-
logger.debug(f"Sending POST to {url}")
223-
logger.debug(f"Request Body: {json.dumps(body_dict, indent=2)}")
222+
logger.debug(f'Sending POST to {url}')
223+
logger.debug(f'Request Body: {json.dumps(body_dict, indent=2)}')
224224
response = client.post(url, json=body_dict, headers=get_auth_headers())
225225
response.raise_for_status()
226226
data = response.json()
227227
update_token_from_response(data)
228-
logger.info(f"Received response from /{type} endpoint.")
229-
logger.debug(f"Response data: {data}")
228+
logger.info(f'Received response from /{type} endpoint.')
229+
logger.debug(f'Response data: {data}')
230230

231231
result = data.get('result', {})
232232
state = data.get('state', {}).get('variables', {})
@@ -246,13 +246,13 @@ def run_command(client: httpx.Client, base_url: str, args):
246246
sys.exit(1)
247247

248248
except httpx.RequestError as e:
249-
logger.error(f"HTTP Request Error executing command: {e}")
249+
logger.error(f'HTTP Request Error executing command: {e}')
250250
sys.exit(1)
251251
except httpx.HTTPStatusError as e:
252-
logger.error(f"HTTP Status Error ({url}): {e.response.status_code} - {e.response.text}")
252+
logger.error(f'HTTP Status Error ({url}): {e.response.status_code} - {e.response.text}')
253253
sys.exit(1)
254254
except Exception as e:
255-
logger.error(f"Unexpected error executing command: {e}", exc_info=True)
255+
logger.error(f'Unexpected error executing command: {e}', exc_info=True)
256256
sys.exit(1)
257257

258258

@@ -362,9 +362,9 @@ def main():
362362
if args.cacert:
363363
cert_path = os.path.abspath(args.cacert) # Ensure absolute path
364364
if os.path.exists(cert_path):
365-
logger.info(f"Configured httpx to verify using CA cert: {cert_path}")
365+
logger.info(f'Configured httpx to verify using CA cert: {cert_path}')
366366
else:
367-
logger.error(f"CA certificate file not found at specified path: {cert_path}")
367+
logger.error(f'CA certificate file not found at specified path: {cert_path}')
368368
sys.exit(1)
369369

370370
# Create HTTP Client
@@ -388,12 +388,12 @@ def main():
388388
sys.exit(1)
389389
except httpx.ConnectError as e:
390390
logger.error(
391-
f"Connection Error: Could not connect to {args.base_url}. "
392-
f"Is the server running with HTTPS? Did you provide cert? Details: {e}"
391+
f'Connection Error: Could not connect to {args.base_url}. '
392+
f'Is the server running with HTTPS? Did you provide cert? Details: {e}'
393393
)
394394
sys.exit(1)
395395
except Exception as main_err:
396-
logger.error(f"Client execution failed: {main_err}", exc_info=True)
396+
logger.error(f'Client execution failed: {main_err}', exc_info=True)
397397
sys.exit(1)
398398

399399
logger.info('Client finished.')

remote_rest/create_hashes.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import os
21

32
from passlib.context import CryptContext
43

0 commit comments

Comments
 (0)