mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-08 01:08:39 -10:00
316 lines
13 KiB
Python
316 lines
13 KiB
Python
# praisonai: skip=true
|
|
"""
|
|
SOLUTION: Domain Context Issue Fix using Existing PraisonAI Features
|
|
|
|
This solution fixes the issue where agents default to "example.com" instead of using
|
|
the specified domain "eenadu.net" by leveraging existing PraisonAI context features.
|
|
|
|
Key improvements:
|
|
1. Domain context passed through task context parameter
|
|
2. Agent instructions include domain-specific guidance
|
|
3. Custom tool wrappers that inject domain context
|
|
4. Shared memory for consistent domain context
|
|
|
|
No core SDK changes required - uses only existing framework features.
|
|
"""
|
|
|
|
import os
|
|
import requests
|
|
import json
|
|
import base64
|
|
from dotenv import load_dotenv
|
|
from qdrant_client import QdrantClient
|
|
from praisonaiagents import Agent, Task, AgentTeam, Memory
|
|
|
|
# Load environment variables from .env.local
|
|
if not load_dotenv(dotenv_path=".env.local"):
|
|
raise FileNotFoundError(".env.local file not found. Please ensure it exists in the working directory.")
|
|
|
|
# API Keys
|
|
api_keys = {
|
|
"WHOXY_API_KEY": os.getenv("WHOXY_API_KEY"),
|
|
"FOFA_API_KEY": os.getenv("FOFA_API_KEY"),
|
|
"API_NINJAS_KEY": os.getenv("API_NINJAS_KEY"),
|
|
"QDRANT_API_KEY": os.getenv("QDRANT_API_KEY"),
|
|
}
|
|
|
|
# Debug API Keys
|
|
for key, value in api_keys.items():
|
|
if not value:
|
|
raise ValueError(f"Missing API key: {key}. Ensure it is set in the environment variables or .env.local.")
|
|
|
|
# Qdrant Configuration
|
|
qdrant_url = "https://d32d17dc-6b1d-4e13-9bb5-94a4d9f4b457.europe-west3-0.gcp.cloud.qdrant.io:6333"
|
|
qdrant_client = QdrantClient(url=qdrant_url, api_key=api_keys["QDRANT_API_KEY"])
|
|
|
|
# Domain Configuration - This is the key to solving the context issue
|
|
class DomainContext:
|
|
def __init__(self, target_domain: str):
|
|
self.target_domain = target_domain
|
|
self.context_description = f"DOMAIN CONTEXT: All tool operations must focus exclusively on the domain '{target_domain}'. Never use example.com or any other domain."
|
|
|
|
def get_context(self):
|
|
return {
|
|
"domain": self.target_domain,
|
|
"instructions": self.context_description,
|
|
"constraints": [f"Only analyze {self.target_domain}", "No generic examples", "Domain-specific results only"]
|
|
}
|
|
|
|
# Initialize domain context
|
|
domain = "eenadu.net"
|
|
domain_context = DomainContext(domain)
|
|
|
|
# SOLUTION 1: Custom Tool Wrappers with Domain Context Injection
|
|
def create_domain_aware_tools(target_domain: str):
|
|
"""Create domain-aware versions of tools that inject the target domain"""
|
|
|
|
def query_fofa(query: str = None) -> dict:
|
|
# Use target domain if no specific query provided
|
|
if query is None or query == "example.com":
|
|
query = target_domain
|
|
# Ensure domain context is considered
|
|
enhanced_query = f"{target_domain} {query}" if target_domain not in query else query
|
|
|
|
base64_query = base64.b64encode(enhanced_query.encode("utf-8")).decode("utf-8")
|
|
response = requests.get(
|
|
f"https://fofa.info/api/v1/search/all?key={api_keys['FOFA_API_KEY']}&qbase64={base64_query}&size=100&fields=jarm,host,domain,title,ip,port,protocol,base_protocol,country,country_name,region,city,longitude,latitude,as_number,as_organization,os,server,icp,header,banner,cert,link,certs_issuer_org,certs_issuer_cn,certs_subject_org,certs_subject_cn,tls_ja3s,tls_version"
|
|
)
|
|
return response.json()
|
|
|
|
def query_crtsh(domain_param: str = None) -> list:
|
|
# Use target domain if no specific domain provided
|
|
if domain_param is None or domain_param == "example.com":
|
|
domain_param = target_domain
|
|
|
|
response = requests.get(f"https://crt.sh/?q={domain_param}&output=json")
|
|
return response.json()
|
|
|
|
def query_whoxy(email_or_org: str = None) -> dict:
|
|
# Use target domain if no specific parameter provided
|
|
if email_or_org is None or email_or_org == "example.com":
|
|
email_or_org = target_domain
|
|
|
|
response = requests.get(
|
|
f"https://api.whoxy.com/?key={api_keys['WHOXY_API_KEY']}&reverse=whois&search={email_or_org}"
|
|
)
|
|
return response.json()
|
|
|
|
def query_api_ninjas(domain_param: str = None) -> dict:
|
|
# Use target domain if no specific domain provided
|
|
if domain_param is None or domain_param == "example.com":
|
|
domain_param = target_domain
|
|
|
|
response = requests.get(
|
|
f"https://api.api-ninjas.com/v1/dnslookup?domain={domain_param}",
|
|
headers={"X-Api-Key": api_keys["API_NINJAS_KEY"]},
|
|
)
|
|
return response.json()
|
|
|
|
def query_networkcalc(domain_param: str = None) -> dict:
|
|
# Use target domain if no specific domain provided
|
|
if domain_param is None or domain_param == "example.com":
|
|
domain_param = target_domain
|
|
|
|
response = requests.get(f"https://networkcalc.com/api/dns/lookup/{domain_param}")
|
|
return response.json()
|
|
|
|
return [query_fofa, query_crtsh, query_whoxy, query_api_ninjas, query_networkcalc]
|
|
|
|
# Create domain-aware tools
|
|
domain_tools = create_domain_aware_tools(domain)
|
|
|
|
def perform_reverse_lookups(domain: str, manager_memory: dict):
|
|
"""Perform WHOXY lookups and store related domains."""
|
|
reverse_data = domain_tools[2](domain) # Use domain-aware whoxy tool
|
|
email = reverse_data.get("email_address", "")
|
|
organization = reverse_data.get("organization_name", "")
|
|
owner = reverse_data.get("owner_name", "")
|
|
|
|
# Perform additional reverse lookups
|
|
related_by_email = domain_tools[2](email) if email else {}
|
|
related_by_org = domain_tools[2](organization) if organization else {}
|
|
related_by_owner = domain_tools[2](owner) if owner else {}
|
|
|
|
# Filter out unrelated domains
|
|
in_scope_domains = set()
|
|
for lookup_data in [related_by_email, related_by_org, related_by_owner]:
|
|
for domain_info in lookup_data.get("searchResult", {}).get("domains", []):
|
|
if domain in domain_info or is_in_scope(domain_info):
|
|
in_scope_domains.add(domain_info)
|
|
|
|
# Store results in memory
|
|
manager_memory["related_domains"] = list(in_scope_domains)
|
|
|
|
def is_in_scope(domain: str) -> bool:
|
|
"""Filter domains based on scope criteria."""
|
|
out_of_scope_keywords = ["example.com", "google.com", "microsoft.com", "cloudflare.com"]
|
|
return not any(keyword in domain for keyword in out_of_scope_keywords)
|
|
|
|
# SOLUTION 2: Agents with Domain-Specific Instructions
|
|
manager_agent = Agent(
|
|
name="Manager Agent",
|
|
role="Coordinate data collection and refinement",
|
|
goal=f"Collect user input, oversee tasks, and perform WHOXY lookups for related domains specifically for {domain}.",
|
|
backstory=f"Strategically manages all agents and ensures focused execution on the domain {domain}.",
|
|
instructions=f"""
|
|
CRITICAL DOMAIN CONTEXT: You are working exclusively with the domain '{domain}'.
|
|
|
|
All operations, tool calls, and analysis must focus on this specific domain.
|
|
When coordinating with other agents, ensure they understand the domain context.
|
|
Never use example.com or generic examples - always use {domain}.
|
|
|
|
Your primary responsibility is to ensure all agents maintain focus on {domain} throughout the workflow.
|
|
""",
|
|
tools=[],
|
|
llm="gpt-4o-mini",
|
|
markdown=True,
|
|
memory={"short_term": {"target_domain": domain}, "long_term": qdrant_client},
|
|
)
|
|
|
|
tool_agent = Agent(
|
|
name="Tool Agent",
|
|
role="Run initial tool queries",
|
|
goal=f"Gather subdomains, DNS records, reverse WHOIS data, and FOFA data specifically for {domain}.",
|
|
backstory=f"Tool specialist focused exclusively on analyzing the domain {domain}. Expert in running information-gathering queries using all available tools.",
|
|
instructions=f"""
|
|
CRITICAL DOMAIN CONTEXT: Your exclusive focus is the domain '{domain}'.
|
|
|
|
When using any tool:
|
|
1. Always use '{domain}' as the target domain
|
|
2. Never use example.com or any other domain
|
|
3. If a tool asks for a domain parameter, provide '{domain}'
|
|
4. If a tool asks for a query, include '{domain}' in the query
|
|
5. All searches and analysis must be domain-specific to '{domain}'
|
|
|
|
Remember: You are a specialist for {domain} only. No generic examples or other domains.
|
|
""",
|
|
tools=domain_tools, # Use domain-aware tools
|
|
llm="gpt-4o-mini",
|
|
markdown=True,
|
|
memory={"short_term": {"target_domain": domain}, "long_term": qdrant_client},
|
|
)
|
|
|
|
sr_pentester_agent = Agent(
|
|
name="Senior Pentester",
|
|
role="Analyze tool results for vulnerabilities",
|
|
goal=f"Investigate vulnerabilities and organize data for deep dives specifically for {domain}.",
|
|
backstory=f"An expert in penetration testing and data correlation with deep expertise in analyzing {domain}.",
|
|
instructions=f"""
|
|
CRITICAL DOMAIN CONTEXT: You are analyzing security findings for the domain '{domain}' exclusively.
|
|
|
|
When reviewing tool results:
|
|
1. Focus only on findings related to '{domain}'
|
|
2. Filter out any results from other domains
|
|
3. Prioritize vulnerabilities specific to '{domain}'
|
|
4. Organize findings with '{domain}' as the central focus
|
|
|
|
Your expertise is domain-specific to {domain}. All analysis must be relevant to this domain only.
|
|
""",
|
|
tools=[],
|
|
llm="gpt-4o-mini",
|
|
markdown=True,
|
|
memory={"short_term": {"target_domain": domain}, "long_term": qdrant_client},
|
|
)
|
|
|
|
ciso_agent = Agent(
|
|
name="CISO Agent",
|
|
role="Executive Security Analysis",
|
|
goal=f"Review findings and provide high-level recommendations specifically for {domain}.",
|
|
backstory=f"Provides strategic insights and ensures alignment with organizational goals for {domain} security posture.",
|
|
instructions=f"""
|
|
CRITICAL DOMAIN CONTEXT: You are providing executive security analysis for the domain '{domain}' exclusively.
|
|
|
|
In your strategic recommendations:
|
|
1. Focus on security posture of '{domain}' only
|
|
2. Provide recommendations specific to '{domain}'
|
|
3. Consider business impact on '{domain}' operations
|
|
4. Align security strategy with '{domain}' organizational goals
|
|
|
|
Your strategic insights must be domain-specific to {domain}. No generic recommendations.
|
|
""",
|
|
tools=[],
|
|
llm="gpt-4o-mini",
|
|
markdown=True,
|
|
memory={"short_term": {"target_domain": domain}, "long_term": qdrant_client},
|
|
)
|
|
|
|
# SOLUTION 3: Tasks with Domain Context Parameter
|
|
tool_query_task = Task(
|
|
name="tool_query_task",
|
|
description=f"Run all tools to gather initial domain data for {domain}. Use the domain {domain} exclusively for all tool operations.",
|
|
expected_output=f"Raw data from all tools for the domain {domain} stored in Qdrant.",
|
|
agent=tool_agent,
|
|
context=[domain_context.get_context()] # Pass domain context to task
|
|
)
|
|
|
|
pentester_analysis_task = Task(
|
|
name="pentester_analysis_task",
|
|
description=f"Analyze tool output for vulnerabilities and prioritize areas of focus for the domain {domain}.",
|
|
expected_output=f"Organized and prioritized security findings for {domain} stored in Qdrant.",
|
|
agent=sr_pentester_agent,
|
|
context=[domain_context.get_context(), tool_query_task] # Include domain context and previous task
|
|
)
|
|
|
|
ciso_review_task = Task(
|
|
name="ciso_review_task",
|
|
description=f"Review pentester findings and provide strategic recommendations for the domain {domain}.",
|
|
expected_output=f"High-level executive security recommendations for {domain} stored in Qdrant.",
|
|
agent=ciso_agent,
|
|
context=[domain_context.get_context(), pentester_analysis_task] # Include domain context and previous task
|
|
)
|
|
|
|
# SOLUTION 4: Agents with Shared Memory Configuration
|
|
agents = AgentTeam(
|
|
agents=[manager_agent, tool_agent, sr_pentester_agent, ciso_agent],
|
|
tasks=[tool_query_task, pentester_analysis_task, ciso_review_task],
|
|
process="hierarchical",
|
|
manager_llm="gpt-4o-mini",
|
|
memory={
|
|
"backend": "sqlite",
|
|
"config": {
|
|
"collection_name": f"{domain.replace('.', '_')}_security_analysis"
|
|
},
|
|
},
|
|
)
|
|
|
|
# Main Execution Workflow
|
|
def main():
|
|
print(f"Welcome to the Hierarchical Pentesting Workflow for {domain}!")
|
|
|
|
# Store domain context in memory for all agents
|
|
domain_info = f"Target domain for analysis: {domain}. All operations must focus on this domain exclusively."
|
|
|
|
# Initialize shared context for all agents
|
|
for agent in [manager_agent, tool_agent, sr_pentester_agent, ciso_agent]:
|
|
agent.memory["short_term"]["domain_context"] = domain_context.get_context()
|
|
agent.memory["short_term"]["target_domain"] = domain
|
|
|
|
print(f"Domain context initialized: {domain}")
|
|
print(f"All agents configured to focus exclusively on: {domain}")
|
|
|
|
# Execute the workflow
|
|
result = agents.start()
|
|
|
|
# Serialize TaskOutput to JSON-serializable format
|
|
serialized_result = []
|
|
for task in agents.tasks:
|
|
if hasattr(task, "name") and hasattr(task, "output"):
|
|
serialized_result.append({
|
|
"task_name": task.name,
|
|
"domain": domain,
|
|
"output": task.output.to_dict() if hasattr(task.output, "to_dict") else str(task.output),
|
|
})
|
|
else:
|
|
print(f"Skipping task: {task} due to missing attributes.")
|
|
|
|
# Output results
|
|
print(f"Workflow Results for {domain}:")
|
|
print(json.dumps(serialized_result, indent=4))
|
|
|
|
# Verify domain context was maintained
|
|
print(f"\nDomain Context Verification:")
|
|
print(f"Target Domain: {domain}")
|
|
print(f"Context properly maintained: {'YES' if all('target_domain' in agent.memory['short_term'] for agent in agents.agents) else 'NO'}")
|
|
|
|
if __name__ == "__main__":
|
|
main() |