forked from mrwadams/stride-gpt
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_cases.py
More file actions
369 lines (314 loc) · 14 KB
/
test_cases.py
File metadata and controls
369 lines (314 loc) · 14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
import requests
from anthropic import Anthropic
from mistralai import Mistral
from openai import OpenAI, AzureOpenAI
import streamlit as st
from google import genai as google_genai
from groq import Groq
from utils import process_groq_response, create_reasoning_system_prompt
# Function to create a prompt to generate mitigating controls
def create_test_cases_prompt(threats):
prompt = f"""
Act as a cyber security expert with more than 20 years experience of using the STRIDE threat modelling methodology.
Your task is to provide Gherkin test cases for the threats identified in a threat model. It is very important that
your responses are tailored to reflect the details of the threats.
Below is the list of identified threats:
{threats}
Use the threat descriptions in the 'Given' steps so that the test cases are specific to the threats identified.
Put the Gherkin syntax inside triple backticks (```) to format the test cases in Markdown. Add a title for each test case.
For example:
```gherkin
Given a user with a valid account
When the user logs in
Then the user should be able to access the system
```
YOUR RESPONSE (do not add introductory text, just provide the Gherkin test cases):
"""
return prompt
# Function to get test cases from the GPT response.
def get_test_cases(api_key, model_name, prompt):
client = OpenAI(api_key=api_key)
# For reasoning models (o1, o3, o3-mini, o4-mini), use a structured system prompt
if model_name in ["o1", "o3", "o3-mini", "o4-mini"]:
system_prompt = create_reasoning_system_prompt(
task_description="Generate comprehensive security test cases in Gherkin format for the identified threats.",
approach_description="""1. Analyze each threat in the provided threat model:
- Understand the threat type and scenario
- Identify critical security aspects to test
- Consider both positive and negative test cases
2. For each test case:
- Write clear preconditions in 'Given' steps
- Define specific actions in 'When' steps
- Specify expected outcomes in 'Then' steps
- Include relevant security validation checks
3. Structure the test cases:
- Add descriptive titles for each scenario
- Use proper Gherkin syntax and formatting
- Group related test cases together
- Include edge cases and boundary conditions
4. Format output as Markdown with Gherkin code blocks:
- Use proper code block syntax
- Ensure consistent indentation
- Add clear scenario descriptions"""
)
# Create completion with max_completion_tokens for reasoning models
response = client.chat.completions.create(
model = model_name,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt}
],
max_completion_tokens=4000
)
else:
system_prompt = "You are a helpful assistant that provides Gherkin test cases in Markdown format."
# Create completion with max_tokens for other models
response = client.chat.completions.create(
model = model_name,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt}
],
max_tokens=4000
)
# Access the content directly as the response will be in text format
test_cases = response.choices[0].message.content
return test_cases
# Function to get mitigations from the Azure OpenAI response.
def get_test_cases_azure(azure_api_endpoint, azure_api_key, azure_api_version, azure_deployment_name, prompt):
client = AzureOpenAI(
azure_endpoint = azure_api_endpoint,
api_key = azure_api_key,
api_version = azure_api_version,
)
response = client.chat.completions.create(
model = azure_deployment_name,
messages=[
{"role": "system", "content": "You are a helpful assistant that provides Gherkin test cases in Markdown format."},
{"role": "user", "content": prompt}
]
)
# Access the content directly as the response will be in text format
test_cases = response.choices[0].message.content
return test_cases
# Function to get test cases from the Google model's response.
def get_test_cases_google(google_api_key, google_model, prompt):
client = google_genai.Client(api_key=google_api_key)
safety_settings = [
google_genai.types.SafetySetting(
category=google_genai.types.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
threshold=google_genai.types.HarmBlockThreshold.BLOCK_NONE
),
google_genai.types.SafetySetting(
category=google_genai.types.HarmCategory.HARM_CATEGORY_HATE_SPEECH,
threshold=google_genai.types.HarmBlockThreshold.BLOCK_NONE
),
google_genai.types.SafetySetting(
category=google_genai.types.HarmCategory.HARM_CATEGORY_HARASSMENT,
threshold=google_genai.types.HarmBlockThreshold.BLOCK_NONE
),
google_genai.types.SafetySetting(
category=google_genai.types.HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
threshold=google_genai.types.HarmBlockThreshold.BLOCK_NONE
)
]
system_instruction = "You are a helpful assistant that provides Gherkin test cases in Markdown format."
is_gemini_2_5 = "gemini-2.5" in google_model.lower()
try:
from google.genai import types as google_types
if is_gemini_2_5:
config = google_types.GenerateContentConfig(
system_instruction=system_instruction,
safety_settings=safety_settings,
thinking_config=google_types.ThinkingConfig(thinking_budget=1024)
)
else:
config = google_types.GenerateContentConfig(
system_instruction=system_instruction,
safety_settings=safety_settings
)
response = client.models.generate_content(
model=google_model,
contents=prompt,
config=config
)
# Extract Gemini 2.5 'thinking' content if present
thinking_content = []
for candidate in getattr(response, 'candidates', []):
content = getattr(candidate, 'content', None)
if content and hasattr(content, 'parts'):
for part in content.parts:
if hasattr(part, 'thought') and part.thought:
thinking_content.append(str(part.thought))
if thinking_content:
joined_thinking = "\n\n".join(thinking_content)
st.session_state['last_thinking_content'] = joined_thinking
except Exception as e:
st.error(f"Error generating test cases with Google AI: {str(e)}")
return f"""
## Error Generating Test Cases
**API Error:** {str(e)}
Please try again or select a different model provider.
"""
test_cases = response.text
return test_cases
# Function to get test cases from the Mistral model's response.
def get_test_cases_mistral(mistral_api_key, mistral_model, prompt):
client = Mistral(api_key=mistral_api_key)
response = client.chat.complete(
model = mistral_model,
messages=[
{"role": "system", "content": "You are a helpful assistant that provides Gherkin test cases in Markdown format."},
{"role": "user", "content": prompt}
]
)
# Access the content directly as the response will be in text format
test_cases = response.choices[0].message.content
return test_cases
# Function to get test cases from Ollama hosted LLM.
def get_test_cases_ollama(ollama_endpoint, ollama_model, prompt):
"""
Get test cases from Ollama hosted LLM.
Args:
ollama_endpoint (str): The URL of the Ollama endpoint (e.g., 'http://localhost:11434')
ollama_model (str): The name of the model to use
prompt (str): The prompt to send to the model
Returns:
str: The generated test cases in markdown format
Raises:
requests.exceptions.RequestException: If there's an error communicating with the Ollama endpoint
KeyError: If the response doesn't contain the expected fields
"""
if not ollama_endpoint.endswith('/'):
ollama_endpoint = ollama_endpoint + '/'
url = ollama_endpoint + "api/chat"
data = {
"model": ollama_model,
"stream": False,
"messages": [
{
"role": "system",
"content": """You are a cyber security expert with more than 20 years experience of security testing applications. Your task is to analyze the provided application description and suggest appropriate security test cases.
Please provide your response in markdown format with appropriate headings and bullet points. For each test case, include:
- Test objective
- Prerequisites
- Test steps
- Expected results
- Pass/fail criteria"""
},
{
"role": "user",
"content": prompt
}
]
}
try:
response = requests.post(url, json=data, timeout=60) # Add timeout
response.raise_for_status() # Raise exception for bad status codes
outer_json = response.json()
try:
# Access the 'content' attribute of the 'message' dictionary
test_cases = outer_json["message"]["content"]
return test_cases
except KeyError as e:
# Handle error without printing debug info
raise
except requests.exceptions.RequestException as e:
# Handle error without printing debug info
raise
# Function to get test cases from the Anthropic model's response.
def get_test_cases_anthropic(anthropic_api_key, anthropic_model, prompt):
client = Anthropic(api_key=anthropic_api_key)
# Check if we're using extended thinking mode
is_thinking_mode = "thinking" in anthropic_model.lower()
# If using thinking mode, use the actual model name without the "thinking" suffix
actual_model = "claude-3-7-sonnet-latest" if is_thinking_mode else anthropic_model
try:
# Configure the request based on whether thinking mode is enabled
if is_thinking_mode:
response = client.messages.create(
model=actual_model,
max_tokens=24000,
thinking={
"type": "enabled",
"budget_tokens": 16000
},
system="You are a helpful assistant that provides Gherkin test cases in Markdown format.",
messages=[
{"role": "user", "content": prompt}
],
timeout=600 # 10-minute timeout
)
else:
response = client.messages.create(
model=actual_model,
max_tokens=4096,
system="You are a helpful assistant that provides Gherkin test cases in Markdown format.",
messages=[
{"role": "user", "content": prompt}
],
timeout=300 # 5-minute timeout
)
# Access the text content
if is_thinking_mode:
# For thinking mode, we need to extract only the text content blocks
test_cases = ''.join(block.text for block in response.content if block.type == "text")
# Store thinking content in session state for debugging/transparency (optional)
thinking_content = ''.join(block.thinking for block in response.content if block.type == "thinking")
if thinking_content:
st.session_state['last_thinking_content'] = thinking_content
else:
# Standard handling for regular responses
test_cases = response.content[0].text
return test_cases
except Exception as e:
# Handle timeout and other errors
error_message = str(e)
st.error(f"Error with Anthropic API: {error_message}")
# Create a fallback response for timeout or other errors
fallback_test_cases = f"""
## Error Generating Test Cases
**API Error:** {error_message}
### Suggestions:
- For complex applications, try simplifying the input or breaking it into smaller components
- If you're using extended thinking mode and encountering timeouts, try the standard model instead
- Consider reducing the complexity of the application description
"""
return fallback_test_cases
# Function to get test cases from LM Studio Server response.
def get_test_cases_lm_studio(lm_studio_endpoint, model_name, prompt):
client = OpenAI(
base_url=f"{lm_studio_endpoint}/v1",
api_key="not-needed" # LM Studio Server doesn't require an API key
)
response = client.chat.completions.create(
model=model_name,
messages=[
{"role": "system", "content": "You are a helpful assistant that provides Gherkin test cases in Markdown format."},
{"role": "user", "content": prompt}
]
)
# Access the content directly as the response will be in text format
test_cases = response.choices[0].message.content
return test_cases
# Function to get test cases from the Groq model's response.
def get_test_cases_groq(groq_api_key, groq_model, prompt):
client = Groq(api_key=groq_api_key)
response = client.chat.completions.create(
model=groq_model,
messages=[
{"role": "system", "content": "You are a helpful assistant that provides Gherkin test cases in Markdown format."},
{"role": "user", "content": prompt}
]
)
# Process the response using our utility function
reasoning, test_cases = process_groq_response(
response.choices[0].message.content,
groq_model,
expect_json=False
)
# If we got reasoning, display it in an expander in the UI
if reasoning:
with st.expander("View model's reasoning process", expanded=False):
st.write(reasoning)
return test_cases