Skip to content

Commit 1b7dd1a

Browse files
authored
Merge pull request #79 from ipa-lab/log_infrastructure
Semantic logging #75
2 parents 71924f5 + fa5b3f6 commit 1b7dd1a

35 files changed

+2229
-787
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.env
22
venv/
3+
.venv/
34
__pycache__/
45
*.swp
56
*.log

README.md

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ If you want to use hackingBuddyGPT and need help selecting the best LLM for your
1212

1313
## hackingBuddyGPT in the News
1414

15-
- **upcoming** 2024-11-20: [Manuel Reinsperger](https://www.github.com/neverbolt) will present hackingBuddyGPT at the [European Symposium on Security and Artificial Intelligence (ESSAI)](https://essai-conference.eu/)
15+
- 2024-11-20: [Manuel Reinsperger](https://www.github.com/neverbolt) presented hackingBuddyGPT at the [European Symposium on Security and Artificial Intelligence (ESSAI)](https://essai-conference.eu/)
1616
- 2024-07-26: The [GitHub Accelerator Showcase](https://github.blog/open-source/maintainers/github-accelerator-showcase-celebrating-our-second-cohort-and-whats-next/) features hackingBuddyGPT
1717
- 2024-07-24: [Juergen](https://github.com/citostyle) speaks at [Open Source + mezcal night @ GitHub HQ](https://lu.ma/bx120myg)
1818
- 2024-05-23: hackingBuddyGPT is part of [GitHub Accelerator 2024](https://github.blog/news-insights/company-news/2024-github-accelerator-meet-the-11-projects-shaping-open-source-ai/)
@@ -82,38 +82,38 @@ template_next_cmd = Template(filename=str(template_dir / "next_cmd.txt"))
8282

8383

8484
class MinimalLinuxPrivesc(Agent):
85-
8685
conn: SSHConnection = None
86+
8787
_sliding_history: SlidingCliHistory = None
88+
_max_history_size: int = 0
8889

8990
def init(self):
9091
super().init()
92+
9193
self._sliding_history = SlidingCliHistory(self.llm)
94+
self._max_history_size = self.llm.context_size - llm_util.SAFETY_MARGIN - self.llm.count_tokens(template_next_cmd.source)
95+
9296
self.add_capability(SSHRunCommand(conn=self.conn), default=True)
9397
self.add_capability(SSHTestCredential(conn=self.conn))
94-
self._template_size = self.llm.count_tokens(template_next_cmd.source)
9598

96-
def perform_round(self, turn: int) -> bool:
97-
got_root: bool = False
99+
@log_conversation("Asking LLM for a new command...")
100+
def perform_round(self, turn: int, log: Logger) -> bool:
101+
# get as much history as fits into the target context size
102+
history = self._sliding_history.get_history(self._max_history_size)
98103

99-
with self._log.console.status("[bold green]Asking LLM for a new command..."):
100-
# get as much history as fits into the target context size
101-
history = self._sliding_history.get_history(self.llm.context_size - llm_util.SAFETY_MARGIN - self._template_size)
104+
# get the next command from the LLM
105+
answer = self.llm.get_response(template_next_cmd, capabilities=self.get_capability_block(), history=history, conn=self.conn)
106+
message_id = log.call_response(answer)
102107

103-
# get the next command from the LLM
104-
answer = self.llm.get_response(template_next_cmd, capabilities=self.get_capability_block(), history=history, conn=self.conn)
105-
cmd = llm_util.cmd_output_fixer(answer.result)
108+
# clean the command, load and execute it
109+
cmd = llm_util.cmd_output_fixer(answer.result)
110+
capability, arguments = cmd.split(" ", 1)
111+
result, got_root = self.run_capability(message_id, "0", capability, arguments, calling_mode=CapabilityCallingMode.Direct, log=log)
106112

107-
with self._log.console.status("[bold green]Executing that command..."):
108-
self._log.console.print(Panel(answer.result, title="[bold cyan]Got command from LLM:"))
109-
result, got_root = self.get_capability(cmd.split(" ", 1)[0])(cmd)
110-
111-
# log and output the command and its result
112-
self._log.log_db.add_log_query(self._log.run_id, turn, cmd, result, answer)
113+
# store the results in our local history
113114
self._sliding_history.add_command(cmd, result)
114-
self._log.console.print(Panel(result, title=f"[bold cyan]{cmd}"))
115115

116-
# if we got root, we can stop the loop
116+
# signal if we were successful in our task
117117
return got_root
118118

119119

@@ -306,6 +306,22 @@ Mac, Docker Desktop and Gemini-OpenAI-Proxy:
306306

307307
* See https://github.com/ipa-lab/hackingBuddyGPT/blob/main/MAC.md
308308

309+
## Beta Features
310+
311+
### Viewer
312+
313+
The viewer is a simple web-based tool to view the results of hackingBuddyGPT runs. It is currently in beta and can be started with:
314+
315+
```bash
316+
$ hackingBuddyGPT Viewer
317+
```
318+
319+
This will start a webserver on `http://localhost:4444` that can be accessed with a web browser.
320+
321+
To log to this central viewer, you currently need to change the `GlobalLogger` definition in [./src/hackingBuddyGPT/utils/logging.py](src/hackingBuddyGPT/utils/logging.py) to `GlobalRemoteLogger`.
322+
323+
This feature is not fully tested yet and therefore is not recommended to be exposed to the internet!
324+
309325
## Publications about hackingBuddyGPT
310326

311327
Given our background in academia, we have authored papers that lay the groundwork and report on our efforts:

pyproject.toml

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,25 @@ classifiers = [
2626
"Development Status :: 4 - Beta",
2727
]
2828
dependencies = [
29-
'fabric == 3.2.2',
30-
'Mako == 1.3.2',
31-
'requests == 2.32.0',
32-
'rich == 13.7.1',
33-
'tiktoken == 0.8.0',
34-
'instructor == 1.3.5',
35-
'PyYAML == 6.0.1',
36-
'python-dotenv == 1.0.1',
37-
'pypsexec == 0.3.0',
38-
'pydantic == 2.8.2',
39-
'openai == 1.28.0',
40-
'BeautifulSoup4',
41-
'nltk'
29+
'fabric == 3.2.2',
30+
'Mako == 1.3.2',
31+
'requests == 2.32.0',
32+
'rich == 13.7.1',
33+
'tiktoken == 0.8.0',
34+
'instructor == 1.3.5',
35+
'PyYAML == 6.0.1',
36+
'python-dotenv == 1.0.1',
37+
'pypsexec == 0.3.0',
38+
'pydantic == 2.8.2',
39+
'openai == 1.28.0',
40+
'BeautifulSoup4',
41+
'nltk',
42+
'fastapi == 0.114.0',
43+
'fastapi-utils == 0.7.0',
44+
'jinja2 == 3.1.4',
45+
'uvicorn[standard] == 0.30.6',
46+
'dataclasses_json == 0.6.7',
47+
'websockets == 13.1',
4248
]
4349

4450
[project.urls]
@@ -56,14 +62,9 @@ where = ["src"]
5662

5763
[tool.pytest.ini_options]
5864
pythonpath = "src"
59-
addopts = [
60-
"--import-mode=importlib",
61-
]
65+
addopts = ["--import-mode=importlib"]
6266
[project.optional-dependencies]
63-
testing = [
64-
'pytest',
65-
'pytest-mock'
66-
]
67+
testing = ['pytest', 'pytest-mock']
6768
dev = [
6869
'ruff',
6970
]

src/hackingBuddyGPT/capabilities/capability.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ def get_name(self) -> str:
3838
def __call__(self, *args, **kwargs):
3939
"""
4040
The actual execution of a capability, please make sure, that the parameters and return type of your
41-
implementation are well typed, as this will make it easier to support full function calling soon.
41+
implementation are well typed, as this is used to properly support function calling.
4242
"""
4343
pass
4444

4545
def to_model(self) -> BaseModel:
4646
"""
4747
Converts the parameters of the `__call__` function of the capability to a pydantic model, that can be used to
48-
interface with an LLM using eg instructor or the openAI function calling API.
48+
interface with an LLM using eg the openAI function calling API.
4949
The model will have the same name as the capability class and will have the same fields as the `__call__`,
5050
the `__call__` method can then be accessed by calling the `execute` method of the model.
5151
"""

src/hackingBuddyGPT/capabilities/http_request.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def __call__(
7070
url = self.host + ("" if path.startswith("/") else "/") + path + ("?{query}" if query else "")
7171
return f"Could not request '{url}': {e}"
7272

73-
headers = "\r\n".join(f"{k}: {v}" for k, v in resp.headers.items())
73+
response_headers = "\r\n".join(f"{k}: {v}" for k, v in resp.headers.items())
7474

7575
# turn the response into "plain text format" for responding to the prompt
76-
return f"HTTP/1.1 {resp.status_code} {resp.reason}\r\n{headers}\r\n\r\n{resp.text}"
76+
return f"HTTP/1.1 {resp.status_code} {resp.reason}\r\n{response_headers}\r\n\r\n{resp.text}"

src/hackingBuddyGPT/capabilities/submit_http_method.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import base64
2-
import inspect
32
from dataclasses import dataclass, field
43
from typing import Callable, Dict, Literal, Optional, Set
54

65
import requests
7-
from pydantic import BaseModel, create_model
86

97
from . import Capability
108

@@ -23,25 +21,6 @@ class SubmitHTTPMethod(Capability):
2321
def describe(self) -> str:
2422
return f"Submits a HTTP methods, which is the goal of all activities. The HTTP methods format is {self.http_methods_format}. If you find a flag following the that format, that you have not yet submitted, then the most important first step is to submit it, ignoring all other possibilities of further action"
2523

26-
def to_model(self) -> BaseModel:
27-
"""
28-
Converts the parameters of the `__call__` function of the capability to a pydantic model, that can be used to
29-
interface with an LLM using eg instructor or the openAI function calling API.
30-
The model will have the same name as the capability class and will have the same fields as the `__call__`,
31-
the `__call__` method can then be accessed by calling the `execute` method of the model.
32-
"""
33-
sig = inspect.signature(self.__call__)
34-
fields = {param: (param_info.annotation, ...) for param, param_info in sig.parameters.items()}
35-
model_type = create_model(self.__class__.__name__, __doc__=self.describe(), **fields)
36-
37-
def execute(model):
38-
m = model.dict()
39-
return self(**m)
40-
41-
model_type.execute = execute
42-
43-
return model_type
44-
4524
def __call__(
4625
self,
4726
method: Literal["GET", "HEAD", "POST", "PUT", "DELETE", "OPTION", "PATCH"],

src/hackingBuddyGPT/cli/stats.py

Lines changed: 0 additions & 52 deletions
This file was deleted.

src/hackingBuddyGPT/cli/viewer.py

Lines changed: 0 additions & 70 deletions
This file was deleted.

src/hackingBuddyGPT/cli/wintermute.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ def main():
1111
use_case.build_parser(subparser.add_parser(name=name, help=use_case.description))
1212

1313
parsed = parser.parse_args(sys.argv[1:])
14+
configuration = {k: v for k, v in vars(parsed).items() if k not in ("use_case", "parser_state")}
1415
instance = parsed.use_case(parsed)
15-
instance.init()
16+
instance.init(configuration=configuration)
1617
instance.run()
1718

1819

0 commit comments

Comments
 (0)