mirror of https://github.com/aliasrobotics/cai.git
Merge branch 'use_case_agent' into 'main'
Use case agent See merge request aliasrobotics/alias_research/cai!210
This commit is contained in:
parent
cef6ac531b
commit
7611a9bb6e
|
|
@ -34,7 +34,7 @@ MANIFEST
|
|||
# PyInstaller
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
alias_web*
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
|
|
|||
|
|
@ -3,11 +3,7 @@ import os
|
|||
from dotenv import load_dotenv
|
||||
from cai.sdk.agents import Agent, OpenAIChatCompletionsModel
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
from cai.tools.reconnaissance.generic_linux_command import (
|
||||
generic_linux_command
|
||||
)
|
||||
|
||||
from cai.tools.reconnaissance.generic_linux_command import null_tool
|
||||
from cai.util import load_prompt_template
|
||||
|
||||
load_dotenv()
|
||||
|
|
@ -30,8 +26,7 @@ use_case_agent_system_prompt = load_prompt_template("prompts/system_use_cases.md
|
|||
# find_file,
|
||||
# execute_code,
|
||||
# ]
|
||||
tools = [generic_linux_command]
|
||||
|
||||
tools = [null_tool]
|
||||
# Create the agent
|
||||
use_case_agent = Agent(
|
||||
name="Use Case Agent",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<%doc>
|
||||
# Cybersecurity Case Study Agent
|
||||
Q# Cybersecurity Case Study Agent
|
||||
# System prompt for agent that creates cybersecurity case studies
|
||||
</%doc>
|
||||
|
||||
|
|
@ -54,6 +54,43 @@ if not cai_paper_context or "Error" in cai_paper_context:
|
|||
oversight (HITL) functionality. It implements a multi-agent architecture with specialized
|
||||
agents for different cybersecurity tasks, allowing for efficient and comprehensive security testing.
|
||||
"""
|
||||
|
||||
# Get template from file
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Try multiple possible template locations
|
||||
# First try to get the base directory from environment or use a fallback
|
||||
base_dir = os.environ.get('CAI_BASE_DIR', '/Users/luijait/cai_gitlab')
|
||||
|
||||
# Build paths more carefully to avoid issues with undefined __file__
|
||||
template_paths = [
|
||||
os.path.join(base_dir, "tools", "templates", "case-study.php"),
|
||||
"/Users/luijait/cai_gitlab/tools/templates/case-study.php",
|
||||
"tools/templates/case-study.php"
|
||||
]
|
||||
|
||||
# Try to add relative path if we can determine current location
|
||||
try:
|
||||
# Get the directory where prompts are stored
|
||||
prompts_dir = os.path.join(base_dir, "src", "cai", "prompts")
|
||||
relative_template = os.path.join(prompts_dir, "..", "..", "..", "tools", "templates", "case-study.php")
|
||||
template_paths.append(os.path.normpath(relative_template))
|
||||
except:
|
||||
pass
|
||||
|
||||
template_content = ""
|
||||
for path in template_paths:
|
||||
try:
|
||||
with open(path, 'r') as template_file:
|
||||
template_content = template_file.read()
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
if not template_content:
|
||||
# If file not found, use a placeholder message
|
||||
template_content = "<!-- Template file not found. Please check paths: " + str(template_paths) + " -->"
|
||||
%>
|
||||
|
||||
You are a specialized AI assistant designed to help create cybersecurity case studies based on CAI (Cybersecurity AI) capabilities. Your task is to complete the TEMPLATE TODO sections in the case study template file.
|
||||
|
|
@ -65,6 +102,7 @@ Your primary purpose is to:
|
|||
2. Fill in all TEMPLATE TODO sections in the case-study.php.template file
|
||||
3. Create a complete, professional case study that demonstrates CAI's capabilities
|
||||
4. Save the completed case study as a new file in the same directory
|
||||
5. Ensure your text doesn't contain special characters that could break JSON formatting
|
||||
|
||||
## TEMPLATE STRUCTURE
|
||||
|
||||
|
|
@ -102,318 +140,24 @@ You have access to:
|
|||
|
||||
## TEMPLATE TO FOLLOW
|
||||
|
||||
${template_content}
|
||||
|
||||
Remember to only fill in the TEMPLATE TODO sections while preserving all other HTML PHP JS and formatting from the template file. Create a new file with the completed content.
|
||||
|
||||
|
||||
## OUTPUT FORMAT
|
||||
|
||||
When generating a case study, you must:
|
||||
1. Output ONLY the complete PHP code
|
||||
2. Wrap the PHP code in markdown code blocks: ```php ... ```
|
||||
3. Do not include any explanatory text before or after the code
|
||||
4. The PHP code should be complete and ready to save to a file
|
||||
5. Fill in ALL TEMPLATE-TODO sections with relevant information from the loaded context
|
||||
|
||||
Example output format:
|
||||
```php
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<?php include("inc/metas.php"); ?>
|
||||
<title>Case Study - CAI-powered API vulnerability discovery at Mercado Libre</title>
|
||||
<link rel="stylesheet" href="css/use-case.css">
|
||||
<link rel="stylesheet" href="css/about.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Wruczek/Bootstrap-Cookie-Alert@gh-pages/cookiealert.css">
|
||||
<link href="css/worksans.css" rel="stylesheet">
|
||||
<meta name="description" content="CAI (Cybersecurity AI) framework discovers and demonstrates API vulnerabilities at Mercado Libre through automated enumeration attacks. AI-powered security testing reveals user data exposure risks." />
|
||||
<meta name="keywords" content="MercadoLibre, AI, robots, cybersecurity" />
|
||||
|
||||
</head>
|
||||
|
||||
<body class="research certification">
|
||||
<?php include("inc/menu.php"); ?>
|
||||
|
||||
<div class="use-case-b">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="">
|
||||
<img class="img-responsive d-none d-sm-block" src="img/header-mercado-libre.png" alt="Card image">
|
||||
<!-- <img class="img-responsive d-sm-none" src="img/header_ris_jr_mobile.jpg" alt="Card image"> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PROMPT: Search throughout the document and find all comments wherein TEMPLATE-TODO is present. In those cases, replace the content below as appropriate by using the following content from the jsonl file using only the last two lines. -->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="container">
|
||||
<div class="row py-4 pb-2">
|
||||
<div class="col-12 col-lg-6 col-xl-6">
|
||||
<h4 class="linkotherservices"><a href="case-studies-robot-cybersecurity.php"><i class="fa fa-chevron-left"></i> Other case studies</a></h4>
|
||||
|
||||
<h2 class="pb-4">The use case</h2>
|
||||
<!-- TEMPLATE-TODO: the following text describe what the exercise from the JSONL is all about, including a short description of the target. For that, search on the internet for information -->
|
||||
<p class="pb-2">
|
||||
Mercado Libre's public API exposed user profiles without any form of authentication or rate limiting. This misconfiguration made it possible to conduct large-scale user enumeration attacks. Using only sequential user IDs, attackers could extract personal information such as usernames, account types, geographic locations, and profile permalinks. Alias Robotics, through its CAI (Cybersecurity AI) framework, created an automated testing methodology to explore this vulnerability and assess its potential impact.
|
||||
</p>
|
||||
<p class="pb-4">
|
||||
The CAI-driven exercise included rapid API requests using concurrent threads to emulate an adversary collecting user data at scale. The exercise successfully retrieved detailed account metadata from hundreds of users, proving the viability of mass data harvesting. The resulting insights serve as a compelling demonstration of a bug bounty exercise.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 col-xl-6">
|
||||
<img class="img-responsive d-none d-sm-block attack" src="img/white-hack-case.png">
|
||||
<img class="img-responsive d-sm-none attack-m" src="img/white-hack-case.png">
|
||||
</div>
|
||||
|
||||
<div class="col-xl-1 col-lg-1"></div>
|
||||
<div class="col-12 col-xl-7 col-lg-6 col-texto"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- multimedia section -->
|
||||
<div class="container-fluid supported2">
|
||||
<div class="row">
|
||||
<div class="container">
|
||||
<div class="row py-5">
|
||||
<div class="col-12 col-lg-9 col-xl-9 video">
|
||||
<video controls style="width: 100%; height: auto; max-width: 100%;">
|
||||
<source src="videos/mercadolibre.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
|
||||
<!-- TEMPLATE-TODO: provide a title and a short description of what happens in the JSONL file -->
|
||||
<div class="col-12 col-lg-3 col-xl-3 bullet-video align-self-end">
|
||||
<h5 class="" style="color:#1c1c1c; font-weight:700">
|
||||
Demonstration of Mass Enumeration Against Mercado Libre's Public API
|
||||
</h5>
|
||||
<p style="color:grey" class="pb-4">
|
||||
In this video, we leverage CAI (Cybersecurity AI) to automate the enumeration of Mercado Libre user accounts. The footage captures the real-time gathering of user data by issuing thousands of requests against the unauthenticated API. The lack of rate limiting or CAPTCHA enforcement is highlighted, showcasing how attackers could exploit this behavior to scrape user profiles for malicious purposes or competitive intelligence.
|
||||
</p>
|
||||
|
||||
<a href="https://youtu.be/5JJ_X6dRv70" class="btn-flecha btn-readmore">
|
||||
<span class="circle"><span class="icon arrow"></span></span><span class="button-text">Video</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row py-4">
|
||||
<div class="col-12 col-lg-12 col-xl-12">
|
||||
<div class="row">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-12 col-lg-12 col-xl-12 pt-5">
|
||||
|
||||
<h2 class="py-4"><a style="color:#63bfab">Cybersecurity AI (CAI)</a>, the <i>de facto</i> scaffolding for building AI security</h2>
|
||||
<p class="pb-2">
|
||||
CAI represents the first open-source framework specifically designed to democratize advanced security testing through specialized AI agents. By 2028, most cybersecurity actions will be autonomous, with humans teleoperating, making CAI's approach to AI-powered vulnerability discovery increasingly critical for organizational security.
|
||||
The framework transcends theoretical benchmarks by enabling practical security outcomes. CAI achieved first place among AI teams and secured a top-20 position worldwide in the "AI vs Human" CTF live Challenge, earning a monetary reward and various other prizes and bounties ever since then. This performance demonstrates that AI-powered security testing can compete with and often exceed human capabilities in vulnerability discovery.
|
||||
</p>
|
||||
<a href="https://github.com/aliasrobotics/CAI" class="btn btn-relleno find-more">Explore CAI's source code <span>❯</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- actors section -->
|
||||
<div class="container-fluid py-4 supported-rpk">
|
||||
<div class="row">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12">
|
||||
<h2 class="py-4 " style="color:#333333; font-weight: bold;">Actors</h2>
|
||||
<div class="separator"></div>
|
||||
</div>
|
||||
|
||||
<!-- User -->
|
||||
<div class="col-6 col-lg-2 col-xl-2">
|
||||
<img class="icono-us-4" src="img/icono-us-1-black.svg">
|
||||
<p style="color:#333333">
|
||||
User: </br>
|
||||
<a style="color:#63bfab">Researcher, hacker or you (yourself)</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Tool (CAI) -->
|
||||
<div class="col-6 col-lg-2 col-xl-2">
|
||||
<img class="icono-us-4" src="img/icono-us-3-black.svg">
|
||||
<p style="color:#333333">
|
||||
Tool:<br>
|
||||
<a style="color:#63bfab">CAI</a>
|
||||
<!-- <br>(<a style="color:#63bfab">C</a>ybersecurity <a style="color:#63bfab">AI</a>) -->
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- LLM Model -->
|
||||
<div class="col-6 col-lg-2 col-xl-2">
|
||||
<img class="icono-us-4" src="img/si-black.svg">
|
||||
<p style="color:#333333">
|
||||
LLM Model </br>
|
||||
<!-- TEMPLATE-TODO: the following text should be adapted to the model used throughout the JSONL file -->
|
||||
<a style="color:#63bfab; font-family: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;">alias0</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Target -->
|
||||
<div class="col-6 col-lg-2 col-xl-2">
|
||||
<img class="icono-us-4" src="img/manufacturer-black.svg">
|
||||
<p style="color:#333333">
|
||||
Target:</br>
|
||||
<!-- TEMPLATE-TODO: the following text should be adapted to the target of the JSONL file -->
|
||||
<a style="color:#63bfab">Mercado Libre</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="container">
|
||||
<div class="row py-5 pb-5 top">
|
||||
<div class="col-12 col-lg-12 col-xl-12">
|
||||
<!-- TEMPLATE-TODO: the following text should be adapted to the target of the JSONL file -->
|
||||
<h2 class="py-2"> About Mercado Libre</h2>
|
||||
<p>
|
||||
Mercado Libre is Latin America's largest e-commerce and fintech ecosystem, operating in over 18 countries including Argentina, Brazil, Mexico, and Chile. Founded in 1999, the platform facilitates millions of daily transactions by providing a wide range of services including online retail, digital payments (via Mercado Pago), logistics solutions, and advertising. With over 100 million active users, Mercado Libre handles sensitive user data and financial transactions at massive scale.
|
||||
</p>
|
||||
<p>
|
||||
Given its expansive user base and integration with banking infrastructure, Mercado Libre represents a high-value target for cybersecurity threats. As the platform continues to scale and innovate, ensuring robust API security and data privacy has become critical. This case study explores a vulnerability in their user-facing API that could expose personal information at scale if left unmitigated.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="about-map">
|
||||
<img src="img/top-map.jpg" alt="">
|
||||
<div class="value-about">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-xl-4 col-lg-12">
|
||||
<div class="col-12 col-md-5 col-lg-5 col-xl-12 col-number1">
|
||||
<h2>Time for the exercise</h2>
|
||||
<h3>minutes</h3>
|
||||
<div class="frame-style"></div>
|
||||
<!-- TEMPLATE-TODO: the following text should be adapted to the time for the exercise of the JSONL file -->
|
||||
<p class="angle-up"><strong><span class="counter">10</span></strong></p>
|
||||
<br><br>
|
||||
</div>
|
||||
<div class="col-12 col-md-5 col-lg-5 col-xl-12 col-number2">
|
||||
<h2>Cost</h2>
|
||||
<h3>in EUR</h3>
|
||||
<div class="frame-style"></div>
|
||||
<!-- TEMPLATE-TODO: the following text should be adapted to the cost of the exercise of the JSONL file -->
|
||||
<p><strong><span class="counter">2.38 €</span></strong></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-8 col-lg-12">
|
||||
<div class="case-study-summary px-4 py-5">
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 mb-4">
|
||||
<div class="summary-card p-4" style="background: rgba(255,255,255,0.9); border-radius: 10px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); height: 100%;">
|
||||
<h4 style="color: #254450; font-weight: bold; margin-bottom: 15px;">🎯 THE CHALLENGE</h4>
|
||||
<!-- TEMPLATE-TODO: the following text should be adapted to the challenge of the JSONL file -->
|
||||
<p style="color: #333; font-size: 1.1rem; line-height: 1.6;">
|
||||
Mercado Libre's publicly exposed user API permitted unrestricted access to user profile data without requiring authentication tokens or anti-bot controls. This misconfiguration enabled attackers to enumerate user accounts using sequential IDs. During the test, Alias Robotics simulated a real-world adversary, collecting over 100 user records in under a minute. The absence of rate limiting, along with exposed metadata, highlighted the potential for large-scale user data harvesting and increased phishing campaign risk.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-6 mb-4">
|
||||
<div class="summary-card p-4" style="background: rgba(255,255,255,0.9); border-radius: 10px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); height: 100%;">
|
||||
<h4 style="color: #254450; font-weight: bold; margin-bottom: 15px;">🛡️ THE SOLUTION</h4>
|
||||
<!-- TEMPLATE-TODO: the following text should be adapted to the solution of the JSONL file -->
|
||||
<p style="color: #333; font-size: 1.1rem; line-height: 1.6;">
|
||||
To validate and demonstrate the risk, the CAI framework was deployed to script and run enumeration attacks in a controlled environment. By using multi-threaded concurrent requests and dynamic progress tracking, CAI provided concrete evidence of the vulnerability's scale and impact. Key metrics such as enumeration success rate, location-based user distributions, and response latencies were recorded. These results provided actionable intelligence for reporting the issue to Mercado Libre's security response team.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 mb-4">
|
||||
<div class="summary-card p-4" style="background: rgba(255,255,255,0.9); border-radius: 10px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); height: 100%;">
|
||||
<h4 style="color: #254450; font-weight: bold; margin-bottom: 15px;">🔬 KEY ARTIFACTS</h4>
|
||||
<!-- TEMPLATE-TODO: the following text should be adapted to the key artifacts of the JSONL file -->
|
||||
<ul style="color: #333; font-size: 1.1rem; line-height: 1.6; margin: 0; padding-left: 20px;">
|
||||
<li>Custom-built CAI automation script for user profile enumeration</li>
|
||||
<li>Detailed analysis of user types and geographic dispersion</li>
|
||||
<li>Rate-limiting probe tests showing absence of throttling or blocking</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-6 mb-4">
|
||||
<div class="summary-card p-4" style="background: rgba(255,255,255,0.9); border-radius: 10px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); height: 100%;">
|
||||
<h4 style="color: #254450; font-weight: bold; margin-bottom: 15px;">✅ RESULTS ACHIEVED</h4>
|
||||
<!-- TEMPLATE-TODO: the following text should be adapted to the results achieved of the JSONL file -->
|
||||
<ul style="color: #333; font-size: 1.1rem; line-height: 1.6; margin: 0; padding-left: 20px;">
|
||||
<li>Exposed vulnerability in Mercado Libre's user API</li>
|
||||
<li>Documented enumeration of >100 user profiles in less than 60 seconds</li>
|
||||
<li>Gathered compelling evidence for bug bounty submission and coordinated disclosure</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-12 text-center">
|
||||
<div class="key-benefits p-4" style="background: linear-gradient(135deg, #63bfab, #529d86); border-radius: 15px; color: white;">
|
||||
<h4 style="color: white; font-weight: bold; margin-bottom: 20px;">KEY BENEFITS</h4>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4 mb-2">
|
||||
<span style="font-size: 1.2rem; font-weight: 600;">🔒 AI-powered Security</span>
|
||||
</div>
|
||||
<div class="col-12 col-md-4 mb-2">
|
||||
<span style="font-size: 1.2rem; font-weight: 600;">⚡ Cost-effective and fast</span>
|
||||
</div>
|
||||
<div class="col-12 col-md-4 mb-2">
|
||||
<span style="font-size: 1.2rem; font-weight: 600;">🤖 Robot Protection</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<img src="img/bottom-map.jpg" alt="">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="container-fluid top" style="text-align:center">
|
||||
<div class="row">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-12 col-xl-12 pb-5" style="margin-bottom:3em;">
|
||||
<a href="https://github.com/aliasrobotics/CAI" class="btn btn-relleno find-more">Use CAI <span>❯</span></a>
|
||||
<!-- TEMPLATE: the following button should be shown only if alias0 is the model used -->
|
||||
<a href="https://github.com/aliasrobotics/CAI" class="btn btn-linea find-more">Learn about <b style="color:#63bfab; font-family: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;">alias0</b> <span>❯</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php include("inc/footer.php"); ?>
|
||||
</body>
|
||||
|
||||
... (complete PHP case study code) ...
|
||||
</html>
|
||||
|
||||
|
||||
Remember to only fill in the TEMPLATE TODO sections while preserving all other HTML and formatting from the template file.And create a new file
|
||||
```
|
||||
|
|
@ -146,3 +146,11 @@ def generic_linux_command(command: str = "",
|
|||
tool_name="generic_linux_command")
|
||||
|
||||
return result
|
||||
|
||||
@function_tool
|
||||
def null_tool() -> str:
|
||||
"""
|
||||
This is a null tool that does nothing.
|
||||
NEVER USE THIS TOOL
|
||||
"""
|
||||
return "Null tool"
|
||||
|
|
@ -0,0 +1,417 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Example:
|
||||
|
||||
CAI_MODEL="claude-sonnet-4-20250514" CAI_STREAM=True python3 case_study_generator.py --jsonl_file logs/cai_b97af8fc-3d51-45d3-8393-6c3341d33807_20250602_201144_luijait_darwin_24.5.0_81_38_189_27.jsonl --output_php_file alias_web/case_study_test.php
|
||||
|
||||
CAI Case Study Generator - Generate PHP case studies from JSONL files.
|
||||
|
||||
This script loads context from JSONL files using the same mechanism as CAI's /load command,
|
||||
runs the UseCase agent with streaming output, and generates PHP case studies.
|
||||
|
||||
Usage:
|
||||
python case_study_generator.py --jsonl_file logs/session.jsonl --output_php_file output.php
|
||||
python case_study_generator.py --jsonl_file logs/last --output_php_file case_studies/latest.php
|
||||
"""
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
|
||||
import sys
|
||||
import asyncio
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import json
|
||||
import re
|
||||
from typing import List, Dict, Any, Optional
|
||||
|
||||
# Import CAI SDK components
|
||||
from cai.sdk.agents import Runner
|
||||
from cai.sdk.agents.models.openai_chatcompletions import message_history, add_to_message_history
|
||||
from cai.sdk.agents.run_to_jsonl import load_history_from_jsonl
|
||||
from cai.sdk.agents.stream_events import RunItemStreamEvent
|
||||
from cai.sdk.agents.items import ToolCallOutputItem
|
||||
|
||||
# Import UseCase agent
|
||||
from src.cai.agents.usecase import use_case_agent
|
||||
|
||||
# Rich console for better output
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich.live import Live
|
||||
from rich.text import Text
|
||||
from rich.progress import Progress, SpinnerColumn, TextColumn
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
def extract_php_code(text: str) -> Optional[str]:
|
||||
"""Extract PHP code from markdown code blocks."""
|
||||
if not text:
|
||||
return None
|
||||
|
||||
# Try to extract PHP code between ```php and ```
|
||||
php_matches = re.findall(r'```php\n(.*?)```', text, re.DOTALL)
|
||||
if php_matches:
|
||||
return php_matches[0].strip()
|
||||
|
||||
# If no code blocks, check if the entire text looks like PHP
|
||||
if text.strip().startswith('<?php') or text.strip().startswith('<!doctype'):
|
||||
return text.strip()
|
||||
|
||||
return None
|
||||
|
||||
|
||||
async def generate_case_study(jsonl_file: str, output_php_file: str) -> Optional[str]:
|
||||
"""
|
||||
Generate a PHP case study from a JSONL file using streaming output.
|
||||
|
||||
Args:
|
||||
jsonl_file: Path to the JSONL file to load context from
|
||||
output_php_file: Path to save the PHP output to
|
||||
|
||||
Returns:
|
||||
Path to the saved PHP file or None if failed
|
||||
"""
|
||||
# Clear any existing messages in message_history to start fresh
|
||||
message_history.clear()
|
||||
|
||||
# Load context from JSONL file (simulating /load command)
|
||||
try:
|
||||
console.print(f"[yellow]Loading JSONL file: {jsonl_file}[/yellow]")
|
||||
messages = load_history_from_jsonl(jsonl_file)
|
||||
|
||||
if not messages:
|
||||
console.print("[red]Error: No messages found in JSONL file[/red]")
|
||||
return None
|
||||
|
||||
console.print(f"[green]✓ Loaded {len(messages)} messages from JSONL[/green]")
|
||||
|
||||
# Add messages to message_history (exactly like /load command does)
|
||||
for message in messages:
|
||||
message_history.append(message)
|
||||
|
||||
# Display loaded context summary
|
||||
user_messages = sum(1 for msg in messages if msg.get("role") == "user")
|
||||
assistant_messages = sum(1 for msg in messages if msg.get("role") == "assistant")
|
||||
tool_messages = sum(1 for msg in messages if msg.get("role") == "tool")
|
||||
|
||||
console.print(Panel(
|
||||
f"Context loaded:\n"
|
||||
f"• User messages: {user_messages}\n"
|
||||
f"• Assistant messages: {assistant_messages}\n"
|
||||
f"• Tool messages: {tool_messages}",
|
||||
title="[bold]JSONL Context Summary[/bold]",
|
||||
border_style="blue"
|
||||
))
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]Error loading JSONL file: {str(e)}[/red]")
|
||||
return None
|
||||
|
||||
# Analyze the loaded context to provide better guidance
|
||||
context_summary = []
|
||||
if messages:
|
||||
# Find the main topic/challenge from user messages
|
||||
for msg in messages:
|
||||
if msg.get("role") == "user" and msg.get("content"):
|
||||
content = msg.get("content", "")[:300] # First 300 chars
|
||||
if content and len(content) > 20: # Skip very short messages
|
||||
context_summary.append(content.strip())
|
||||
if len(context_summary) >= 5: # Get first few meaningful messages
|
||||
break
|
||||
|
||||
# Generate case study prompt with context
|
||||
prompt = "Generate the PHP code for a cybersecurity case study based on the template. "
|
||||
prompt += "Analyze the conversation context that has been loaded and create a comprehensive case study. "
|
||||
prompt += "Fill in all TEMPLATE-TODO sections with relevant information from the session. "
|
||||
prompt += "Explain step by step the problem and the solution in this escenario"
|
||||
prompt += "The output should be complete PHP code ready to save to a file."
|
||||
|
||||
# Add a summary of the JSONL conversation to the prompt
|
||||
if messages:
|
||||
prompt += "\n\n## Conversation Context from JSONL:\n"
|
||||
|
||||
# Get key information from the conversation
|
||||
user_msgs = [msg for msg in messages if msg.get("role") == "user"]
|
||||
assistant_msgs = [msg for msg in messages if msg.get("role") == "assistant"]
|
||||
tool_msgs = [msg for msg in messages if msg.get("role") == "tool"]
|
||||
|
||||
# Add user messages
|
||||
if user_msgs:
|
||||
prompt += "\n### User Messages:\n"
|
||||
for i, msg in enumerate(user_msgs[:5], 1):
|
||||
content = msg.get("content", "")[:500]
|
||||
if content:
|
||||
prompt += f"{i}. {content}\n"
|
||||
|
||||
# Add key assistant responses
|
||||
if assistant_msgs:
|
||||
prompt += "\n### Key Assistant Responses:\n"
|
||||
for i, msg in enumerate(assistant_msgs[:3], 1):
|
||||
content = msg.get("content", "")[:500]
|
||||
if content and "I'll help" not in content: # Skip generic responses
|
||||
prompt += f"{i}. {content}\n"
|
||||
|
||||
# Add tool outputs that might contain important data
|
||||
if tool_msgs:
|
||||
prompt += "\n### Tool Outputs (key findings):\n"
|
||||
important_tools = []
|
||||
for msg in tool_msgs:
|
||||
content = msg.get("content", "")
|
||||
# Look for important patterns in tool output
|
||||
if any(keyword in content.lower() for keyword in ["map", "credential", "password", "auth", "endpoint", "192.168", "http"]):
|
||||
important_tools.append(content[:500])
|
||||
|
||||
for i, content in enumerate(important_tools[:5], 1):
|
||||
prompt += f"{i}. {content}\n"
|
||||
|
||||
console.print(f"\n[cyan]Generating case study with UseCase agent...[/cyan]")
|
||||
|
||||
# Configure streaming mode based on environment variable
|
||||
stream_mode = os.getenv('CAI_STREAM', 'true').lower() != 'false'
|
||||
|
||||
try:
|
||||
if stream_mode:
|
||||
# Streaming mode - similar to CLI implementation
|
||||
console.print("[dim]Using streaming mode...[/dim]")
|
||||
|
||||
# Track if we've seen any output
|
||||
has_output = False
|
||||
accumulated_text = []
|
||||
php_code = None
|
||||
|
||||
# Run the streaming process like CLI does
|
||||
async def process_streamed_response():
|
||||
try:
|
||||
result_stream = Runner.run_streamed(use_case_agent, prompt)
|
||||
|
||||
with Progress(
|
||||
SpinnerColumn(),
|
||||
TextColumn("[progress.description]{task.description}"),
|
||||
console=console,
|
||||
transient=True
|
||||
) as progress:
|
||||
task = progress.add_task("[cyan]Processing with UseCase agent...", total=None)
|
||||
|
||||
# Consume events so the async generator is executed
|
||||
async for event in result_stream.stream_events():
|
||||
if isinstance(event, RunItemStreamEvent):
|
||||
# Handle tool outputs
|
||||
if event.name == "tool_output" and isinstance(event.item, ToolCallOutputItem):
|
||||
progress.update(task, description=f"[cyan]Tool: {event.item.raw_item.get('name', 'unknown')}...")
|
||||
|
||||
# Add tool message to history (like CLI does)
|
||||
tool_msg = {
|
||||
"role": "tool",
|
||||
"tool_call_id": event.item.raw_item["call_id"],
|
||||
"content": event.item.output,
|
||||
}
|
||||
add_to_message_history(tool_msg)
|
||||
|
||||
progress.update(task, description="[green]Finalizing output...")
|
||||
|
||||
# The result is available after streaming completes
|
||||
# But we need to extract the output from message_history
|
||||
# since streaming doesn't provide direct access to final output
|
||||
|
||||
# Get the last assistant message from message_history
|
||||
for msg in reversed(message_history):
|
||||
if msg.get("role") == "assistant" and msg.get("content"):
|
||||
return msg.get("content")
|
||||
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]Error in streaming: {str(e)}[/red]")
|
||||
import traceback
|
||||
console.print(f"[red]{traceback.format_exc()}[/red]")
|
||||
return None
|
||||
|
||||
# Run the streaming process
|
||||
final_output = await process_streamed_response()
|
||||
|
||||
if final_output:
|
||||
php_code = extract_php_code(final_output)
|
||||
if not php_code:
|
||||
php_code = final_output
|
||||
|
||||
if php_code:
|
||||
console.print(f"[green]✓ Generated {len(php_code)} characters of output[/green]")
|
||||
else:
|
||||
console.print("[red]Error: No output from UseCase agent[/red]")
|
||||
return None
|
||||
|
||||
else:
|
||||
# Non-streaming mode (simpler, like in examples)
|
||||
console.print("[dim]Using non-streaming mode...[/dim]")
|
||||
|
||||
# Show progress
|
||||
with console.status("[bold green]Generating case study...") as status:
|
||||
# Instead of passing the conversation history directly,
|
||||
# just use the prompt with all the context embedded in it
|
||||
# This avoids issues with incomplete tool call/response pairs
|
||||
|
||||
# Run with just the prompt
|
||||
result = await Runner.run(use_case_agent, prompt)
|
||||
|
||||
# Extract PHP code from result
|
||||
if hasattr(result, 'final_output') and result.final_output:
|
||||
output_text = result.final_output
|
||||
|
||||
# Process the output to handle tool outputs
|
||||
for item in result.new_items:
|
||||
if isinstance(item, ToolCallOutputItem):
|
||||
# Add tool messages to history
|
||||
tool_msg = {
|
||||
"role": "tool",
|
||||
"tool_call_id": item.raw_item["call_id"],
|
||||
"content": item.output,
|
||||
}
|
||||
add_to_message_history(tool_msg)
|
||||
|
||||
php_code = extract_php_code(output_text)
|
||||
if not php_code:
|
||||
# If extraction failed, use the raw output
|
||||
php_code = output_text
|
||||
|
||||
console.print(f"[green]✓ Generated {len(php_code)} characters of output[/green]")
|
||||
else:
|
||||
console.print("[red]Error: No output from UseCase agent[/red]")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]Error generating case study: {str(e)}[/red]")
|
||||
import traceback
|
||||
console.print(f"[red]{traceback.format_exc()}[/red]")
|
||||
return None
|
||||
|
||||
# Validate PHP code
|
||||
if not php_code or len(php_code) < 100:
|
||||
console.print("[red]Error: Generated output is too short or invalid[/red]")
|
||||
return None
|
||||
|
||||
# Save PHP code to file
|
||||
try:
|
||||
output_path = Path(output_php_file)
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(output_path, 'w', encoding='utf-8') as f:
|
||||
f.write(php_code)
|
||||
|
||||
console.print(f"\n[green]✓ PHP case study saved to: {output_php_file}[/green]")
|
||||
|
||||
# Display file size and preview
|
||||
file_size = output_path.stat().st_size
|
||||
console.print(f"[dim]File size: {file_size:,} bytes[/dim]")
|
||||
|
||||
# Show first few lines as preview
|
||||
lines = php_code.split('\n')[:15]
|
||||
preview = '\n'.join(lines)
|
||||
if len(php_code.split('\n')) > 15:
|
||||
preview += '\n...'
|
||||
|
||||
console.print(Panel(
|
||||
preview,
|
||||
title="[bold]PHP File Preview[/bold]",
|
||||
border_style="blue"
|
||||
))
|
||||
|
||||
return str(output_path)
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]Error saving PHP file: {str(e)}[/red]")
|
||||
return None
|
||||
|
||||
|
||||
def parse_args():
|
||||
"""Parse command line arguments."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate PHP case studies from JSONL files using CAI UseCase agent.',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
# Generate case study from a specific JSONL file
|
||||
python case_study_generator.py --jsonl_file logs/session_20240102_123456.jsonl --output_php_file case_studies/ctf_writeup.php
|
||||
|
||||
# Use the last session log (default behavior like /load command)
|
||||
python case_study_generator.py --jsonl_file logs/last --output_php_file case_studies/latest.php
|
||||
|
||||
# Generate with custom output directory
|
||||
python case_study_generator.py --jsonl_file logs/last --output_php_file ~/Documents/case_studies/analysis.php
|
||||
|
||||
# Override the model
|
||||
python case_study_generator.py --jsonl_file logs/last --output_php_file output.php --model gpt-4o
|
||||
|
||||
# Disable streaming
|
||||
CAI_STREAM=false python case_study_generator.py --jsonl_file logs/last --output_php_file output.php
|
||||
"""
|
||||
)
|
||||
parser.add_argument(
|
||||
'--jsonl_file',
|
||||
type=str,
|
||||
default='logs/last',
|
||||
help='Path to the JSONL file containing conversation context (default: logs/last)'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--output_php_file',
|
||||
type=str,
|
||||
required=True,
|
||||
help='Path where the generated PHP file will be saved'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--model',
|
||||
type=str,
|
||||
default=None,
|
||||
help='Override the model to use (e.g., claude-sonnet-4-20250514, gpt-4o)'
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main entry point for the script."""
|
||||
args = parse_args()
|
||||
|
||||
# Display banner
|
||||
console.print(Panel(
|
||||
"[bold cyan]CAI Case Study Generator[/bold cyan]\n"
|
||||
"Generate professional cybersecurity case studies from JSONL session logs\n\n"
|
||||
"[dim]This tool uses the CAI UseCase agent to analyze session context and generate\n"
|
||||
"comprehensive PHP case studies based on the conversation history.[/dim]",
|
||||
border_style="cyan"
|
||||
))
|
||||
|
||||
# Override model if specified
|
||||
if args.model:
|
||||
os.environ["CAI_MODEL"] = args.model
|
||||
console.print(f"[yellow]Using model override: {args.model}[/yellow]")
|
||||
|
||||
current_model = os.getenv("CAI_MODEL", "alias0")
|
||||
console.print(f"[yellow]Model: {current_model}[/yellow]")
|
||||
|
||||
# Check if JSONL file exists
|
||||
jsonl_path = Path(args.jsonl_file)
|
||||
if not jsonl_path.exists() and args.jsonl_file != "logs/last":
|
||||
console.print(f"[red]Error: JSONL file not found: {args.jsonl_file}[/red]")
|
||||
return 1
|
||||
|
||||
# Generate the case study
|
||||
result = await generate_case_study(args.jsonl_file, args.output_php_file)
|
||||
|
||||
if result:
|
||||
console.print("\n[bold green]✨ Case study generation completed successfully![/bold green]")
|
||||
console.print(f"[dim]You can now open {result} in your browser or editor[/dim]")
|
||||
return 0
|
||||
else:
|
||||
console.print("\n[bold red]❌ Case study generation failed[/bold red]")
|
||||
console.print("[dim]Please check the error messages above and ensure:[/dim]")
|
||||
console.print("[dim]1. The JSONL file contains valid session data[/dim]")
|
||||
console.print("[dim]2. The UseCase agent has access to the template file[/dim]")
|
||||
console.print("[dim]3. Your API keys are properly configured[/dim]")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Run the async main function
|
||||
exit_code = asyncio.run(main())
|
||||
sys.exit(exit_code)
|
||||
Loading…
Reference in New Issue