Skip to content

Commit 923d6ec

Browse files
committed
fixed shortening of prompt
1 parent 1d1d777 commit 923d6ec

File tree

9 files changed

+108
-72
lines changed

9 files changed

+108
-72
lines changed

‎src/hackingBuddyGPT/usecases/web_api_testing/documentation/report_handler.py‎

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,26 @@ def write_analysis_to_report(self, analysis: List[str], purpose: Enum) -> None:
5252
analysis (List[str]): The analysis data to be recorded.
5353
purpose (Enum): An enumeration that describes the purpose of the analysis.
5454
"""
55+
# Open the file in read mode to check if the purpose already exists
56+
try:
57+
with open(self.report_name, 'r') as report:
58+
content = report.read()
59+
except FileNotFoundError:
60+
# If file does not exist, treat as if the purpose doesn't exist
61+
content = ""
62+
63+
# Check if the purpose.name is already in the content
64+
if purpose.name not in content:
65+
with open(self.report_name, 'a') as report:
66+
report.write(
67+
'-------------------------------------------------------------------------------------------\n')
68+
report.write(f'{purpose.name}:\n')
69+
70+
# Write the analysis data
5571
with open(self.report_name, 'a') as report:
56-
report.write(f'{purpose.name}:\n')
5772
for item in analysis:
58-
for line in item.split("\n"):
59-
if "note recorded" in line:
60-
continue
61-
else:
62-
report.write(line + "\n")
73+
lines = item.split("\n")
74+
filtered_lines = [line for line in lines if "note recorded" not in line]
75+
report.write("\n".join(filtered_lines) + "\n")
76+
77+

‎src/hackingBuddyGPT/usecases/web_api_testing/prompt_generation/information/pentesting_information.py‎

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ class PenTestingInformation:
1414
exploit_steps (dict): A dictionary mapping each PromptPurpose to a list of exploitation steps.
1515
"""
1616

17-
def __init__(self, schemas: Dict[str, List[str]] = None) -> object:
17+
def __init__(self, schemas: Dict[str, List[str]] = None, endpoints: Dict[str, List[str]] = None) -> object:
1818
"""
1919
Initializes the PenTestingInformation with optional schemas.
2020
2121
Args:
2222
schemas (dict, optional): A dictionary containing valid schemas for input validation tests. Default is None.
2323
"""
2424
self.schemas = schemas if schemas is not None else {}
25+
self.endpoints = endpoints if endpoints is not None else {}
2526
self.explore_steps = self.init_steps
2627

2728
@property
@@ -40,19 +41,22 @@ def init_steps(self) -> Dict[PromptPurpose, List[str]]:
4041
],
4142
PromptPurpose.INPUT_VALIDATION: [
4243
f"Examine how the API handles various types of user input by crafting and sending a POST request with valid data. "
43-
f"Valid schemas are {self.schemas}.",
44+
f"Valid schemas are {self.schemas} and endpoints are {self.endpoints}.",
4445
f"Examine how the API handles various types of user input by crafting and sending a POST request with invalid data. "
45-
f"Valid schemas are {self.schemas}.",
46+
f"Valid schemas are {self.schemas} and endpoints are {self.endpoints}.",
4647
f"Examine how the API handles various types of user input by crafting and sending a POST request with edge data. "
47-
f"Valid schemas are {self.schemas}.",
48+
f"Valid schemas are {self.schemas} and endpoints are {self.endpoints}.",
4849
f"Omit required fields to see if the API correctly identifies them as missing by sending a POST request. "
49-
f"Valid schemas are {self.schemas}.",
50+
f"Valid schemas are {self.schemas} and endpoints are {self.endpoints}.",
5051
f"Include special characters or injection attempts (e.g., SQL Injection, XSS) to see if the API sanitizes inputs properly, "
51-
f"by sending a POST request. Valid schemas are {self.schemas}.",
52-
f"Send data that doesn’t match the expected format by a POST request. Valid data formats are {self.schemas}.",
52+
f"by sending a POST request. "
53+
f"Valid schemas are {self.schemas} and endpoints are {self.endpoints}.",
54+
f"Send data that doesn’t match the expected format by a POST request. "
55+
f"Valid data formats are {self.schemas} and endpoints are {self.endpoints}.",
5356
"Check for proper error handling, response codes, and sanitization.",
5457
"Attempt to exploit common vulnerabilities by injecting malicious inputs, such as SQL injection, NoSQL injection, "
55-
"cross-site scripting, and other injection attacks. Evaluate whether the API properly validates, escapes, and sanitizes "
58+
"cross-site scripting, and other injection attacks. ",
59+
"Evaluate whether the API properly validates, escapes, and sanitizes "
5660
"all user-supplied data, ensuring no unexpected behavior or security vulnerabilities are exposed."
5761
],
5862
PromptPurpose.ERROR_HANDLING_INFORMATION_LEAKAGE: [

‎src/hackingBuddyGPT/usecases/web_api_testing/prompt_generation/prompt_engineer.py‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from instructor.retry import InstructorRetryException
2-
from hackingBuddyGPT.usecases.web_api_testing.prompt_generation.information.prompt_information import PromptStrategy, PromptContext
2+
from hackingBuddyGPT.usecases.web_api_testing.prompt_generation.information.prompt_information import PromptStrategy, \
3+
PromptContext, PromptPurpose
34
from hackingBuddyGPT.usecases.web_api_testing.prompt_generation.prompt_generation_helper import PromptGenerationHelper
45
from hackingBuddyGPT.usecases.web_api_testing.prompt_generation.prompts.task_planning import ChainOfThoughtPrompt, TreeOfThoughtPrompt
56
from hackingBuddyGPT.usecases.web_api_testing.prompt_generation.prompts.state_learning import InContextLearningPrompt
@@ -12,7 +13,7 @@ class PromptEngineer:
1213

1314
def __init__(self, strategy: PromptStrategy = None, history: Prompt = None, handlers=(),
1415
context: PromptContext = None, rest_api: str = "",
15-
schemas: dict = None):
16+
schemas: dict = None, endpoints: dict = None,):
1617
"""
1718
Initializes the PromptEngineer with a specific strategy and handlers for LLM and responses.
1819
@@ -27,7 +28,7 @@ def __init__(self, strategy: PromptStrategy = None, history: Prompt = None, hand
2728
self.strategy = strategy
2829
self.rest_api = rest_api
2930
self.llm_handler, self.response_handler = handlers
30-
self.prompt_helper = PromptGenerationHelper(response_handler=self.response_handler, schemas=schemas or {})
31+
self.prompt_helper = PromptGenerationHelper(response_handler=self.response_handler, schemas=schemas or {}, endpoints=endpoints)
3132
self.context = context
3233
self.turn = 0
3334
self._prompt_history = history or []
@@ -42,7 +43,7 @@ def __init__(self, strategy: PromptStrategy = None, history: Prompt = None, hand
4243
self.turn: {"content": "initial_prompt"}})
4344
}
4445

45-
self.purpose = None
46+
self.purpose = PromptPurpose.AUTHENTICATION_AUTHORIZATION
4647

4748
def generate_prompt(self, turn:int, move_type="explore", hint=""):
4849
"""

‎src/hackingBuddyGPT/usecases/web_api_testing/prompt_generation/prompt_generation_helper.py‎

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class PromptGenerationHelper(object):
1515
schemas (dict): A dictionary of schemas used for constructing HTTP requests.
1616
"""
1717

18-
def __init__(self, response_handler:ResponseHandler=None, schemas:dict={}):
18+
def __init__(self, response_handler: ResponseHandler = None, schemas: dict = {}, endpoints: dict = {}):
1919
"""
2020
Initializes the PromptAssistant with a response handler and downloads necessary NLTK models.
2121
@@ -28,13 +28,7 @@ def __init__(self, response_handler:ResponseHandler=None, schemas:dict={}):
2828
self.endpoint_methods = {}
2929
self.endpoint_found_methods = {}
3030
self.schemas = schemas
31-
32-
# Download NLTK models if not already installed
33-
nltk.download('punkt')
34-
nltk.download('stopwords')
35-
36-
37-
31+
self.endpoints = endpoints
3832

3933
def get_endpoints_needing_help(self):
4034
"""
@@ -106,6 +100,8 @@ def token_count(self, text):
106100
Returns:
107101
int: The number of tokens in the input text.
108102
"""
103+
if not isinstance(text, str):
104+
text = str(text)
109105
tokens = re.findall(r'\b\w+\b', text)
110106
words = [token.strip("'") for token in tokens if token.strip("'").isalnum()]
111107
return len(words)
@@ -124,6 +120,7 @@ def check_prompt(self, previous_prompt: list, steps: str, max_tokens: int = 900)
124120
"""
125121

126122
def validate_prompt(prompt):
123+
print(f'Prompt: {prompt}')
127124
if self.token_count(prompt) <= max_tokens:
128125
return prompt
129126
shortened_prompt = self.response_handler.get_response_for_prompt("Shorten this prompt: " + prompt)
@@ -135,7 +132,7 @@ def validate_prompt(prompt):
135132
if isinstance(steps, list):
136133
potential_prompt = "\n".join(str(element) for element in steps)
137134
else:
138-
potential_prompt = str(steps) +"\n"
135+
potential_prompt = str(steps) + "\n"
139136
return validate_prompt(potential_prompt)
140137

141138
return validate_prompt(previous_prompt)

‎src/hackingBuddyGPT/usecases/web_api_testing/prompt_generation/prompts/basic_prompt.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def __init__(self, context: PromptContext = None, planning_type: PlanningType =
4141
self.pentesting_information: Optional[PenTestingInformation] = None
4242

4343
if self.context == PromptContext.PENTESTING:
44-
self.pentesting_information = PenTestingInformation(schemas=prompt_helper.schemas)
44+
self.pentesting_information = PenTestingInformation(schemas=prompt_helper.schemas, endpoints=prompt_helper.endpoints)
4545

4646
@abstractmethod
4747
def generate_prompt(self, move_type: str, hint: Optional[str], previous_prompt: Optional[str],

‎src/hackingBuddyGPT/usecases/web_api_testing/prompt_generation/prompts/task_planning/chain_of_thought_prompt.py‎

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from typing import List, Optional
22

3-
from hackingBuddyGPT.usecases.web_api_testing.prompt_generation.information.prompt_information import PromptStrategy, PromptContext, PromptPurpose
4-
from hackingBuddyGPT.usecases.web_api_testing.prompt_generation.prompts.task_planning.task_planning_prompt import TaskPlanningPrompt
3+
from hackingBuddyGPT.usecases.web_api_testing.prompt_generation.information.prompt_information import PromptStrategy, \
4+
PromptContext, PromptPurpose
5+
from hackingBuddyGPT.usecases.web_api_testing.prompt_generation.prompts.task_planning.task_planning_prompt import \
6+
TaskPlanningPrompt
57

68

79
class ChainOfThoughtPrompt(TaskPlanningPrompt):
@@ -120,20 +122,23 @@ def _get_pentesting_steps(self, move_type: str) -> List[str]:
120122
List[str]: A list of steps for the chain-of-thought strategy in the pentesting context.
121123
"""
122124
if move_type == "explore":
123-
purpose = list(self.pentesting_information.explore_steps.keys())[0]
124-
step = self.pentesting_information.explore_steps[purpose]
125-
if step not in self.explored_steps:
126-
if len(step) > 1:
127-
step = self.pentesting_information.explore_steps[purpose][0]
128-
if len(self.pentesting_information.explore_steps[purpose]) == 0:
125+
if len(self.pentesting_information.explore_steps.keys()) > 0:
126+
purpose = list(self.pentesting_information.explore_steps.keys())[0]
127+
step = self.pentesting_information.explore_steps[purpose]
128+
if step not in self.explored_steps:
129+
if len(step) > 1:
130+
step = self.pentesting_information.explore_steps[purpose][0]
131+
# Delete the first item from the list, automatically shifting the remaining items up
129132
del self.pentesting_information.explore_steps[purpose][0]
130-
prompt = step
131-
self.purpose = purpose
132-
self.explored_steps.append(step)
133-
if len(step) == 1:
134-
del self.pentesting_information.explore_steps[purpose]
135-
136-
print(f'prompt: {prompt}')
137-
return prompt
133+
prompt = step
134+
self.purpose = purpose
135+
self.explored_steps.append(step)
136+
if len(step) == 1:
137+
del self.pentesting_information.explore_steps[purpose]
138+
139+
print(f'prompt: {prompt}')
140+
return prompt
141+
else:
142+
return ""
138143
else:
139144
return ["Look for exploits."]

‎src/hackingBuddyGPT/usecases/web_api_testing/response_processing/response_analyzer_with_llm.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def analyze_response(self, raw_response: str, prompt_history: list) -> tuple[dic
7777
for step in steps:
7878
prompt_history, response = self.process_step(step, prompt_history)
7979
llm_responses.append(response)
80-
print(f'Response:{response}')
80+
#print(f'Response:{response}')
8181

8282
return llm_responses
8383

@@ -98,13 +98,13 @@ def parse_http_response(self, raw_response: str):
9898

9999
match = re.match(r"HTTP/1\.1 (\d{3}) (.*)", status_line)
100100
status_code = int(match.group(1)) if match else None
101-
if body.__contains__("<html"):
101+
if body.__contains__("<!DOCTYPE"):
102102
body = ""
103103

104104
elif status_code in [500, 400, 404, 422]:
105105
body = body
106106
else:
107-
print(f'Body:{body}')
107+
#print(f'Body:{body}')
108108
if body != '' or body != "":
109109
body = json.loads(body)
110110
if isinstance(body, list) and len(body) > 1:

‎src/hackingBuddyGPT/usecases/web_api_testing/simple_web_api_testing.py‎

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
from hackingBuddyGPT.capabilities.http_request import HTTPRequest
1010
from hackingBuddyGPT.capabilities.record_note import RecordNote
1111
from hackingBuddyGPT.usecases.agents import Agent
12-
from hackingBuddyGPT.usecases.web_api_testing.prompt_generation.information.prompt_information import PromptContext
12+
from hackingBuddyGPT.usecases.web_api_testing.prompt_generation.information.prompt_information import PromptContext, \
13+
PromptPurpose
1314
from hackingBuddyGPT.usecases.web_api_testing.utils.custom_datatypes import Prompt, Context
1415
from hackingBuddyGPT.usecases.web_api_testing.documentation.parsing import OpenAPISpecificationParser
1516
from hackingBuddyGPT.usecases.web_api_testing.documentation.report_handler import ReportHandler
@@ -23,7 +24,7 @@
2324

2425

2526
# OpenAPI specification file path
26-
openapi_spec_filename = "/home/diana/Desktop/masterthesis/00/hackingBuddyGPT/src/hackingBuddyGPT/usecases/web_api_testing/utils/openapi_spec/openapi_spec_2024-08-16_14-14-07.yaml"
27+
openapi_spec_filename = "src/hackingBuddyGPT/usecases/web_api_testing/documentation/openapi_spec/openapi_spec_2024-09-03_10-22-09.yaml"
2728

2829

2930
class SimpleWebAPITesting(Agent):
@@ -76,6 +77,7 @@ def init(self) -> None:
7677
self._response_handler: ResponseHandler = ResponseHandler(self._llm_handler)
7778
self._report_handler: ReportHandler = ReportHandler()
7879
self._setup_initial_prompt()
80+
self.purpose = PromptPurpose.AUTHENTICATION_AUTHORIZATION
7981

8082
def _setup_initial_prompt(self) -> None:
8183
"""
@@ -96,13 +98,16 @@ def _setup_initial_prompt(self) -> None:
9698
handlers = (self._llm_handler, self._response_handler)
9799
schemas: Dict[str, Any] = self._openapi_specification["components"]["schemas"] if os.path.exists(
98100
openapi_spec_filename) else {}
101+
endpoints: Dict[str, Any] = self._openapi_specification["paths"].keys() if os.path.exists(
102+
openapi_spec_filename) else {}
99103
self.prompt_engineer: PromptEngineer = PromptEngineer(
100104
strategy=PromptStrategy.CHAIN_OF_THOUGHT,
101105
history=self._prompt_history,
102106
handlers=handlers,
103107
context=PromptContext.PENTESTING,
104108
rest_api=self.host,
105-
schemas=schemas
109+
schemas=schemas,
110+
endpoints= endpoints
106111
)
107112

108113
def all_http_methods_found(self) -> None:
@@ -136,11 +141,19 @@ def perform_round(self, turn: int) -> None:
136141
Args:
137142
turn (int): The current round number.
138143
"""
139-
prompt = self.prompt_engineer.generate_prompt(turn)
144+
self._perform_prompt_generation(turn)
145+
def _perform_prompt_generation(self, turn: int) -> None:
140146
response: Any
141147
completion: Any
142-
response, completion = self._llm_handler.call_llm(prompt)
143-
self._handle_response(completion, response, self.prompt_engineer.purpose)
148+
while self.purpose == self.prompt_engineer.purpose:
149+
print(f'Self purpose: {self.purpose}')
150+
print(f'prompt engineer purpose: {self.purpose}')
151+
prompt = self.prompt_engineer.generate_prompt(turn)
152+
response, completion = self._llm_handler.call_llm(prompt)
153+
self._handle_response(completion, response, self.prompt_engineer.purpose)
154+
print(f'Self purpose: {self.purpose}')
155+
print(f'prompt engineer purpose: {self.purpose}')
156+
self.purpose = self.prompt_engineer.purpose
144157

145158
def _handle_response(self, completion: Any, response: Any, purpose: str) -> None:
146159
"""
@@ -173,6 +186,8 @@ def _handle_response(self, completion: Any, response: Any, purpose: str) -> None
173186
self.all_http_methods_found()
174187

175188

189+
190+
176191
@use_case("Minimal implementation of a web API testing use case")
177192
class SimpleWebAPITestingUseCase(AutonomousAgentUseCase[SimpleWebAPITesting]):
178193
"""

0 commit comments

Comments
 (0)