diff --git a/Sources/FatherOfGeorge/BaseAgent.py b/Sources/FatherOfGeorge/BaseAgent.py new file mode 100644 index 000000000..ff1976621 --- /dev/null +++ b/Sources/FatherOfGeorge/BaseAgent.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 + +import threading +import time +from abc import ABC, abstractmethod +from pathlib import Path + +from smolagents import LiteLLMModel, ToolCallingAgent + + +class Agent(ABC): + """Base class for AI agents.""" + + def __init__(self, model: LiteLLMModel, api_key: str = None, anthropic_api_key: str = None): + self.model = model + self.api_key = api_key + self.anthropic_api_key = anthropic_api_key + self.agents = {} + self.setup_agents() + + # Rate limiting configuration + self.min_request_interval = 1.0 # Minimum seconds between requests + self.last_request_time = None + self.request_lock = threading.Lock() + + def wait_for_rate_limit(self): + """Ensure minimum time between requests.""" + with self.request_lock: + if self.last_request_time: + elapsed = time.time() - self.last_request_time + if elapsed < self.min_request_interval: + sleep_time = self.min_request_interval - elapsed + print(f"Rate limiting: waiting {sleep_time:.2f} seconds...") + time.sleep(sleep_time) + self.last_request_time = time.time() + + def get_prompt(self, prompt_name: str) -> str: + """Load prompt template from file.""" + prompt_path = Path(__file__).parent / "prompts" / prompt_name + if prompt_path.exists(): + with open(prompt_path, 'r') as f: + return f.read() + return "" + + @abstractmethod + def setup_agents(self): + """Setup agents specific to this agent.""" + pass + + def get_manager_agent(self) -> ToolCallingAgent: + """Get the main manager agent for this system.""" + # Try to find father_of_george first + if 'father_of_george' in self.agents: + return self.agents['father_of_george'] + + # Look for agents with 'Manager' in their description or name + for key, agent in self.agents.items(): + if hasattr(agent, 'description') and 'Manager' in agent.description: + return agent + if hasattr(agent, 'name') and 'Manager' in agent.name: + return agent + + # Fallback: return the first agent if no manager found + if self.agents: + return list(self.agents.values())[0] + + return None + + def get_all_agents(self) -> list: + """Get all agents in this system.""" + return list(self.agents.values()) + + def run_task(self, task_description: str, context: dict = None) -> dict: + """Run a task using this system's agents.""" + results = { + "task_description": task_description, + "completed": False, + "output": None, + "error": None, + } + + try: + self.wait_for_rate_limit() + + # Get the manager agent + manager_agent = self.get_manager_agent() + if not manager_agent: + results["error"] = "No manager agent found for this system" + return results + + # Create task prompt + prompt = self._create_task_prompt(task_description, context) + + print(f"Using {manager_agent.name} for task: {task_description}") + + # Run the agent + agent_output = manager_agent.run(prompt) + results["output"] = str(agent_output) + results["completed"] = True + + except Exception as e: + results["error"] = str(e) + print(f"Error running task: {e}") + + return results + + def _create_task_prompt(self, task_description: str, context: dict = None) -> str: + """Create a prompt for the given task.""" + prompt = f"Task: {task_description}\n\n" + + if context: + prompt += "Context:\n" + for key, value in context.items(): + prompt += f"- {key}: {value}\n" + prompt += "\n" + + return prompt + + +# Tool definitions for different agent types +def get_manager_tools(): + """Tools available to manager agents.""" + return [ + # TODO: Add manager-specific tools + # - orchestrate_subagents + # - collect_results + # - make_decisions + # - validate_outputs + ] + +def get_code_analysis_tools(): + """Tools available to code analysis agents.""" + return [ + # TODO: Add code analysis tools + # - analyze_syntax + # - analyze_semantics + # - extract_patterns + # - identify_vulnerabilities + ] + +def get_retrieval_tools(): + """Tools available to retrieval agents.""" + return [ + # TODO: Add retrieval tools + # - query_rag_db + # - search_vector_db + # - retrieve_context + # - validate_information + ] + +def get_v8_search_tools(): + """Tools available to V8 search agents.""" + return [ + # TODO: Add V8 search tools + # - fuzzy_find + # - regex_search + # - compile_with_clang + # - test_with_python + # - view_call_graph + # - web_search + ] + +def get_program_builder_tools(): + """Tools available to program builder agents.""" + return [ + # TODO: Add program builder tools + # - query_postgres_db + # - generate_seed_program + # - combine_contexts + # - validate_syntax + ] + +def get_corpus_generation_tools(): + """Tools available to corpus generation agents.""" + return [ + # TODO: Add corpus generation tools + # - validate_syntax + # - validate_semantics + # - test_program + # - evaluate_interestingness + ] + +def get_runtime_analysis_tools(): + """Tools available to runtime analysis agents.""" + return [ + # TODO: Add runtime analysis tools + # - analyze_execution_state + # - check_coverage + # - evaluate_flags + # - determine_seed_quality + ] + +def get_validation_tools(): + """Tools available to validation agents.""" + return [ + # TODO: Add validation tools + # - validate_corpus + # - check_db_integrity + # - verify_results + # - quality_assurance + ] diff --git a/Sources/FatherOfGeorge/FoG.py b/Sources/FatherOfGeorge/FoG.py new file mode 100644 index 000000000..742e0d70f --- /dev/null +++ b/Sources/FatherOfGeorge/FoG.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +""" +Father of George, and thus George the negative First. +L0 Root Manager Agent - Orchestrates Code Analysis and Program Building +""" + +from smolagents import LiteLLMModel, ToolCallingAgent +from BaseAgent import Agent, get_manager_tools, get_code_analysis_tools, get_program_builder_tools, get_retrieval_tools, get_v8_search_tools +from GeorgeForeman import George + + +class Father(Agent): + """Init code analysis and program builder.""" + + def setup_agents(self): + # L2 Worker: George Foreman + self.agents['george_foreman'] = George( + model=self.model, + api_key=self.api_key, + anthropic_api_key=self.anthropic_api_key + ) + + # L2 Worker: Retriever of Code (under CodeAnalyzer) + self.agents['retriever_of_code'] = ToolCallingAgent( + name="RetrieverOfCode", + description="L2 Worker responsible for retrieving code from various sources using RAG database", + tools=get_retrieval_tools(), + model=LiteLLMModel(model_id="gpt-5", api_key=self.api_key), + managed_agents=[ + self.agents['george_foreman'] + ], + max_steps=10, + planning_interval=None, + ) + + # L2 Worker: V8 Search (under CodeAnalyzer) + self.agents['v8_search'] = ToolCallingAgent( + name="V8Search", + description="L2 Worker responsible for searching V8 source code using fuzzy find, regex, and compilation tools", + tools=get_v8_search_tools(), + model=LiteLLMModel(model_id="gpt-5", api_key=self.api_key), + managed_agents=[], + max_steps=10, + planning_interval=None, + ) + + # L1 Manager: Code Analysis Agent + self.agents['code_analyzer'] = ToolCallingAgent( + name="CodeAnalyzer", + description="L1 Manager responsible for analyzing code and coordinating retrieval and V8 search operations", + tools=get_code_analysis_tools(), + model=LiteLLMModel(model_id="gpt-5", api_key=self.api_key), + managed_agents=[ + self.agents['retriever_of_code'], + self.agents['v8_search'] + ], + max_steps=15, + planning_interval=None, + ) + + # L1 Manager: Program Builder Agent + self.agents['program_builder'] = ToolCallingAgent( + name="ProgramBuilder", + description="L1 Manager responsible for building program templates using corpus and context", + tools=get_program_builder_tools(), + model=LiteLLMModel(model_id="gpt-5", api_key=self.api_key), + managed_agents=[ + self.agents['george_foreman'] + ], + max_steps=15, + planning_interval=None, + ) + + # L0 Root Manager Agent + self.agents['father_of_george'] = ToolCallingAgent( + name="FatherOfGeorge", + description="L0 Root Manager responsible for orchestrating code analysis and program building operations", + tools=get_manager_tools(), + model=LiteLLMModel(model_id="gpt-5-mini", api_key=self.api_key), + managed_agents=[ + self.agents['code_analyzer'], + self.agents['program_builder'] + ], + max_steps=20, + planning_interval=None, + ) + + + def run_task(self, task_description: str, context: dict = None) -> dict: + results = { + "task_description": task_description, + "completed": False, + "output": None, + "error": None, + } + return results + + +def main(): + # Init model + model = LiteLLMModel( + model_id="gpt-5-mini", + api_key="" + ) + + system = Father(model) + + # run task + result = system.run_task( + task_description="Initialize corpus generation for V8 fuzzing", + context={ + "CodeAnalyzer": "Analyze V8 source code for patterns. vulnerabilities. specifc components, etc...", + "ProgramBuilder": "Build JavaScript programs using corpus and context" + } + ) + + print("Task Result:") + print(f"Completed: {result['completed']}") + print(f"Output: {result['output']}") + if result['error']: + print(f"Error: {result['error']}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Sources/FatherOfGeorge/GeorgeForeman.py b/Sources/FatherOfGeorge/GeorgeForeman.py new file mode 100644 index 000000000..055e408b8 --- /dev/null +++ b/Sources/FatherOfGeorge/GeorgeForeman.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +''' +Child of the Father of George. Thus, George. +L1 Manager Agent - Verification and Testing Coordinator +''' + +from smolagents import LiteLLMModel, ToolCallingAgent +from BaseAgent import Agent, get_manager_tools, get_corpus_generation_tools, get_runtime_analysis_tools, get_validation_tools, get_code_analysis_tools + + +class George(Agent): + """Verify and test seeds.""" + + def setup_agents(self): + # L3 Worker: Code Analyzer (under RuntimeAnalyzer) + self.agents['code_analyzer'] = ToolCallingAgent( + name="CodeAnalyzer", + description="L3 Worker responsible for analyzing code patterns, vulnerabilities, and specific components for runtime analysis", + tools=get_code_analysis_tools(), + model=LiteLLMModel(model_id="gpt-5", api_key=self.api_key), + managed_agents=[], + max_steps=8, # Fewer steps than L1 CodeAnalyzer + planning_interval=None, + ) + + # L2 Worker: Corpus Generator (under George) + self.agents['corpus_generator'] = ToolCallingAgent( + name="CorpusGenerator", + description="L2 Worker responsible for generating seeds from the corpus and validating syntax/semantics", + tools=get_corpus_generation_tools(), + model=LiteLLMModel(model_id="gpt-5", api_key=self.api_key), + managed_agents=[], + max_steps=10, + planning_interval=None, + ) + + # L2 Worker: Runtime Analyzer (under George) + self.agents['runtime_analyzer'] = ToolCallingAgent( + name="RuntimeAnalyzer", + description="L2 Manager responsible for analyzing program runtime, coverage, and execution state", + tools=get_runtime_analysis_tools(), + model=LiteLLMModel(model_id="gpt-5", api_key=self.api_key), + managed_agents=[ + self.agents['code_analyzer'] + ], + max_steps=10, + planning_interval=None, + ) + + # L2 Worker: Corpus Validator (under George) + self.agents['corpus_validator'] = ToolCallingAgent( + name="CorpusValidator", + description="L2 Worker responsible for validating corpus integrity and quality", + tools=get_validation_tools(), + model=LiteLLMModel(model_id="gpt-5", api_key=self.api_key), + managed_agents=[], + max_steps=8, + planning_interval=None, + ) + + # L2 Worker: DB Analyzer (under George) + self.agents['db_analyzer'] = ToolCallingAgent( + name="DBAnalyzer", + description="L2 Worker responsible for analyzing PostgreSQL database for corpus, flags, coverage, and execution state", + tools=get_validation_tools(), # Reusing validation tools for DB analysis + model=LiteLLMModel(model_id="gpt-5", api_key=self.api_key), + managed_agents=[], + max_steps=8, + planning_interval=None, + ) + + # L1 Manager Agent: George Foreman + self.agents['george_foreman'] = ToolCallingAgent( + name="GeorgeForeman", + description="L1 Manager responsible for verifying JavaScript programs for correctness and testing them to evaluate interestingness", + tools=get_manager_tools(), + model=LiteLLMModel(model_id="gpt-5-mini", api_key=self.api_key), + managed_agents=[ + self.agents['corpus_generator'], + self.agents['runtime_analyzer'], + self.agents['corpus_validator'], + self.agents['db_analyzer'] + ], + max_steps=15, + planning_interval=None, + ) + + def run_task(self, task_description: str, context: dict = None) -> dict: + results = { + "task_description": task_description, + "completed": False, + "output": None, + "error": None, + } + return results + + +def main(): + # Init model + model = LiteLLMModel( + model_id="gpt-5-mini", + api_key="" + ) + + system = George(model) + + # run task + result = system.run_task( + task_description="Verify and test JavaScript program seeds", + context={ + "CorpusGenerator": "Generate and validate seeds from corpus", + "RuntimeAnalyzer": "Analyze program execution and coverage", + "CorpusValidator": "Validate corpus quality and integrity", + "DBAnalyzer": "Analyze database for execution information" + } + ) + + print("Task Result:") + print(f"Completed: {result['completed']}") + print(f"Output: {result['output']}") + if result['error']: + print(f"Error: {result['error']}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Sources/FatherOfGeorge/README.md b/Sources/FatherOfGeorge/README.md new file mode 100644 index 000000000..d2bd400ad --- /dev/null +++ b/Sources/FatherOfGeorge/README.md @@ -0,0 +1,30 @@ +### `Father of George (FoG)` is the Initialization agent for our program template builder. Execution will start in the L0 root/manager agent which is in charge of 2 main agents: `Code analysis` agent and `Program(Template) Builder` agent. +* ##### Starting with the `Code Analysis` agent: it has access to 2 sub-agents: `Retriever of Code`(RoC) and `V8 Search`. RoC has access to a vector RAG database that it can query for information from sources like cppdev, gpz, v8.dev, mdn, various whitepapers, as well as another sub agent `George Foreman` that can verify the semantics/logic of the information RoC wants to pass along. The other L2 sub agent, V8 Search, does exactly what it says. It uses tool calls like fuzzyfind(fzf) and regex(grep) to analyze specific parts of V8 src for context and seed gen. Also has the ability to compiler with clang and test with python as well as view the V8 call graph up to the point where a program would be analyzed. Also will have access to the internet so that it can quickly look up things it needs to know. +* ##### - The other L1 agent is the `Program Template Builder`. The most important part of this agent is it's access to the PostgreSQL database that will contain our existing corpus- unique and interesting javascript(/FuzzIL) programs. Ideally it will use these existing programs to make a new-interesting programs by combining the context garnered from code analysis agent as well as from a RAG db of various JS PoCs and program templates. After building an initial seed program it will be analyzed by the George Foreman agent for correctness and added to the PostgreSQL db to start the fuzzing instance. + + +#### `George Foreman` is the verification agent that has 2 main l1 agents (given it's context): `Corpus Generation` and `Runtime Analysis`. +Corpus Gen is responsible for taking in test inputs/seeds from the PostgreSQL db and PoCs from the RAG db. Here it will validate the Syntactical and Semantical correctness of the seeds for which it will then call the `test` tool- this is where `Runtime Analysis` comes in. +It has access to the PostgreSQL db which besides the corpus, contains information such as what flags the program was run with, what sort of coverage it hit, and ideally it's execution state. (also a 'list tree'. don't know what this does.) After analyzing the seed and it's corresponding execution information, it can determine whether or not it was a good seed and if it should be added back/taken out of the db. + +``` +FatherOfGeorge (L0 Manager) +├── CodeAnalyzer (L1 Manager) +│ ├── RetrieverOfCode (L2 Worker) → GeorgeForeman +│ └── V8Search (L2 Worker) +└── ProgramBuilder (L1 Manager) + └── GeorgeForeman (L1 Manager) + ├── CorpusGenerator (L2 Worker) + ├── RuntimeAnalyzer (L2 Manager) + │ └── CodeAnalyzer (L3 Worker) + ├── CorpusValidator (L2 Worker) + └── DBAnalyzer (L2 Worker) +``` + +--- + +``` +This is all my interpretation of the agentic system, and I'm sure my understanding of the George Foreman agent is skewed- feel free to correct my mistakes. + +I'd like to clarify this agent is specifically for corpus initialization- at the start of a fuzzing campaign. Yes we can expect it to be slower due to it's various API and MCP calls but that will resolve itself after the initialization phase has passed. +``` \ No newline at end of file