mirror of
https://github.com/FuzzingLabs/fuzzforge_ai.git
synced 2026-02-13 11:12:46 +00:00
Compare commits
2 Commits
ci/worker-
...
fix/a2a-ag
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
574e0cbce7 | ||
|
|
f6cdb1ae2e |
79
.github/pull_request_template.md
vendored
79
.github/pull_request_template.md
vendored
@@ -1,79 +0,0 @@
|
||||
## Description
|
||||
|
||||
<!-- Provide a brief description of the changes in this PR -->
|
||||
|
||||
## Type of Change
|
||||
|
||||
<!-- Mark the appropriate option with an 'x' -->
|
||||
|
||||
- [ ] 🐛 Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] ✨ New feature (non-breaking change which adds functionality)
|
||||
- [ ] 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] 📝 Documentation update
|
||||
- [ ] 🔧 Configuration change
|
||||
- [ ] ♻️ Refactoring (no functional changes)
|
||||
- [ ] 🎨 Style/formatting changes
|
||||
- [ ] ✅ Test additions or updates
|
||||
|
||||
## Related Issues
|
||||
|
||||
<!-- Link to related issues using #issue_number -->
|
||||
<!-- Example: Closes #123, Relates to #456 -->
|
||||
|
||||
## Changes Made
|
||||
|
||||
<!-- List the specific changes made in this PR -->
|
||||
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
## Testing
|
||||
|
||||
<!-- Describe the tests you ran to verify your changes -->
|
||||
|
||||
### Tested Locally
|
||||
|
||||
- [ ] All tests pass (`pytest`, `uv build`, etc.)
|
||||
- [ ] Linting passes (`ruff check`)
|
||||
- [ ] Code builds successfully
|
||||
|
||||
### Worker Changes (if applicable)
|
||||
|
||||
- [ ] Docker images build successfully (`docker compose build`)
|
||||
- [ ] Worker containers start correctly
|
||||
- [ ] Tested with actual workflow execution
|
||||
|
||||
### Documentation
|
||||
|
||||
- [ ] Documentation updated (if needed)
|
||||
- [ ] README updated (if needed)
|
||||
- [ ] CHANGELOG.md updated (if user-facing changes)
|
||||
|
||||
## Pre-Merge Checklist
|
||||
|
||||
<!-- Ensure all items are completed before requesting review -->
|
||||
|
||||
- [ ] My code follows the project's coding standards
|
||||
- [ ] I have performed a self-review of my code
|
||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||
- [ ] I have made corresponding changes to the documentation
|
||||
- [ ] My changes generate no new warnings
|
||||
- [ ] I have added tests that prove my fix is effective or that my feature works
|
||||
- [ ] New and existing unit tests pass locally with my changes
|
||||
- [ ] Any dependent changes have been merged and published
|
||||
|
||||
### Worker-Specific Checks (if workers/ modified)
|
||||
|
||||
- [ ] All worker files properly tracked by git (not gitignored)
|
||||
- [ ] Worker validation script passes (`.github/scripts/validate-workers.sh`)
|
||||
- [ ] Docker images build without errors
|
||||
- [ ] Worker configuration updated in `docker-compose.yml` (if needed)
|
||||
|
||||
## Screenshots (if applicable)
|
||||
|
||||
<!-- Add screenshots to help explain your changes -->
|
||||
|
||||
## Additional Notes
|
||||
|
||||
<!-- Any additional information that reviewers should know -->
|
||||
95
.github/scripts/validate-workers.sh
vendored
95
.github/scripts/validate-workers.sh
vendored
@@ -1,95 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Worker Validation Script
|
||||
# Ensures all workers defined in docker-compose.yml exist in the repository
|
||||
# and are properly tracked by git.
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔍 Validating worker completeness..."
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
ERRORS=0
|
||||
WARNINGS=0
|
||||
|
||||
# Extract worker service names from docker-compose.yml
|
||||
echo ""
|
||||
echo "📋 Checking workers defined in docker-compose.yml..."
|
||||
WORKERS=$(grep -E "^\s+worker-" docker-compose.yml | grep -v "#" | cut -d: -f1 | tr -d ' ' | sort -u)
|
||||
|
||||
if [ -z "$WORKERS" ]; then
|
||||
echo -e "${RED}❌ No workers found in docker-compose.yml${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Found workers:"
|
||||
for worker in $WORKERS; do
|
||||
echo " - $worker"
|
||||
done
|
||||
|
||||
# Check each worker
|
||||
echo ""
|
||||
echo "🔎 Validating worker files..."
|
||||
for worker in $WORKERS; do
|
||||
WORKER_DIR="workers/${worker#worker-}"
|
||||
|
||||
echo ""
|
||||
echo "Checking $worker ($WORKER_DIR)..."
|
||||
|
||||
# Check if directory exists
|
||||
if [ ! -d "$WORKER_DIR" ]; then
|
||||
echo -e "${RED} ❌ Directory not found: $WORKER_DIR${NC}"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check required files
|
||||
REQUIRED_FILES=("Dockerfile" "requirements.txt" "worker.py")
|
||||
for file in "${REQUIRED_FILES[@]}"; do
|
||||
FILE_PATH="$WORKER_DIR/$file"
|
||||
|
||||
if [ ! -f "$FILE_PATH" ]; then
|
||||
echo -e "${RED} ❌ Missing file: $FILE_PATH${NC}"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
# Check if file is tracked by git
|
||||
if ! git ls-files --error-unmatch "$FILE_PATH" &> /dev/null; then
|
||||
echo -e "${RED} ❌ File not tracked by git: $FILE_PATH${NC}"
|
||||
echo -e "${YELLOW} Check .gitignore patterns!${NC}"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo -e "${GREEN} ✓ $file (tracked)${NC}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Check for any ignored worker files
|
||||
echo ""
|
||||
echo "🚫 Checking for gitignored worker files..."
|
||||
IGNORED_FILES=$(git check-ignore workers/*/* 2>/dev/null || true)
|
||||
if [ -n "$IGNORED_FILES" ]; then
|
||||
echo -e "${YELLOW}⚠️ Warning: Some worker files are being ignored:${NC}"
|
||||
echo "$IGNORED_FILES" | while read -r file; do
|
||||
echo -e "${YELLOW} - $file${NC}"
|
||||
done
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then
|
||||
echo -e "${GREEN}✅ All workers validated successfully!${NC}"
|
||||
exit 0
|
||||
elif [ $ERRORS -eq 0 ]; then
|
||||
echo -e "${YELLOW}⚠️ Validation passed with $WARNINGS warning(s)${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}❌ Validation failed with $ERRORS error(s) and $WARNINGS warning(s)${NC}"
|
||||
exit 1
|
||||
fi
|
||||
36
.github/workflows/test.yml
vendored
36
.github/workflows/test.yml
vendored
@@ -7,36 +7,6 @@ on:
|
||||
branches: [ main, master, develop ]
|
||||
|
||||
jobs:
|
||||
validate-workers:
|
||||
name: Validate Workers
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run worker validation
|
||||
run: |
|
||||
chmod +x .github/scripts/validate-workers.sh
|
||||
.github/scripts/validate-workers.sh
|
||||
|
||||
build-workers:
|
||||
name: Build Worker Docker Images
|
||||
runs-on: ubuntu-latest
|
||||
# Only run if workers directory is modified
|
||||
if: |
|
||||
github.event_name == 'pull_request' &&
|
||||
contains(github.event.pull_request.changed_files, 'workers/')
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build worker images
|
||||
run: |
|
||||
echo "Building worker Docker images..."
|
||||
docker compose build worker-python worker-secrets worker-rust worker-android worker-ossfuzz --no-cache
|
||||
continue-on-error: false
|
||||
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
@@ -173,15 +143,11 @@ jobs:
|
||||
test-summary:
|
||||
name: Test Summary
|
||||
runs-on: ubuntu-latest
|
||||
needs: [validate-workers, lint, unit-tests]
|
||||
needs: [lint, unit-tests]
|
||||
if: always()
|
||||
steps:
|
||||
- name: Check test results
|
||||
run: |
|
||||
if [ "${{ needs.validate-workers.result }}" != "success" ]; then
|
||||
echo "Worker validation failed"
|
||||
exit 1
|
||||
fi
|
||||
if [ "${{ needs.unit-tests.result }}" != "success" ]; then
|
||||
echo "Unit tests failed"
|
||||
exit 1
|
||||
|
||||
@@ -26,27 +26,50 @@ class RemoteAgentConnection:
|
||||
"""Initialize connection to a remote agent"""
|
||||
self.url = url.rstrip('/')
|
||||
self.agent_card = None
|
||||
self.client = httpx.AsyncClient(timeout=120.0)
|
||||
self.client = httpx.AsyncClient(timeout=120.0, follow_redirects=True)
|
||||
self.context_id = None
|
||||
|
||||
async def get_agent_card(self) -> Optional[Dict[str, Any]]:
|
||||
"""Get the agent card from the remote agent"""
|
||||
try:
|
||||
# Try new path first (A2A 0.3.0+)
|
||||
response = await self.client.get(f"{self.url}/.well-known/agent-card.json")
|
||||
response.raise_for_status()
|
||||
self.agent_card = response.json()
|
||||
return self.agent_card
|
||||
except Exception:
|
||||
# Try old path for compatibility
|
||||
# If URL already points to a .json file, fetch it directly
|
||||
if self.url.endswith('.json'):
|
||||
try:
|
||||
response = await self.client.get(f"{self.url}/.well-known/agent.json")
|
||||
response = await self.client.get(self.url)
|
||||
response.raise_for_status()
|
||||
self.agent_card = response.json()
|
||||
|
||||
# Use canonical URL from agent card if provided
|
||||
if isinstance(self.agent_card, dict) and "url" in self.agent_card:
|
||||
self.url = self.agent_card["url"].rstrip('/')
|
||||
|
||||
return self.agent_card
|
||||
except Exception as e:
|
||||
print(f"Failed to get agent card from {self.url}: {e}")
|
||||
return None
|
||||
|
||||
# Try both agent-card.json (A2A 0.3.0+) and agent.json (legacy)
|
||||
well_known_paths = [
|
||||
"/.well-known/agent-card.json",
|
||||
"/.well-known/agent.json",
|
||||
]
|
||||
|
||||
for path in well_known_paths:
|
||||
try:
|
||||
response = await self.client.get(f"{self.url}{path}")
|
||||
response.raise_for_status()
|
||||
self.agent_card = response.json()
|
||||
|
||||
# Use canonical URL from agent card if provided
|
||||
if isinstance(self.agent_card, dict) and "url" in self.agent_card:
|
||||
self.url = self.agent_card["url"].rstrip('/')
|
||||
|
||||
return self.agent_card
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
print(f"Failed to get agent card from {self.url}")
|
||||
print("Tip: If agent is at /a2a/something, use full URL: /register http://host:port/a2a/something")
|
||||
return None
|
||||
|
||||
async def send_message(self, message: str | Dict[str, Any] | List[Dict[str, Any]]) -> str:
|
||||
"""Send a message to the remote agent using A2A protocol"""
|
||||
@@ -93,7 +116,7 @@ class RemoteAgentConnection:
|
||||
payload["params"]["contextId"] = self.context_id
|
||||
|
||||
# Send to root endpoint per A2A protocol
|
||||
response = await self.client.post(f"{self.url}/", json=payload)
|
||||
response = await self.client.post(self.url, json=payload)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ fuzzforge workflows list
|
||||
fuzzforge workflows info security_assessment
|
||||
|
||||
# Submit a workflow for analysis
|
||||
fuzzforge workflow security_assessment /path/to/your/code
|
||||
fuzzforge workflow run security_assessment /path/to/your/code
|
||||
|
||||
|
||||
# View findings when complete
|
||||
@@ -150,24 +150,24 @@ fuzzforge workflows parameters security_assessment --no-interactive
|
||||
|
||||
### Workflow Execution
|
||||
|
||||
#### `fuzzforge workflow <workflow> <target-path>`
|
||||
#### `fuzzforge workflow run <workflow> <target-path>`
|
||||
Execute a security testing workflow with **automatic file upload**.
|
||||
|
||||
```bash
|
||||
# Basic execution - CLI automatically detects local files and uploads them
|
||||
fuzzforge workflow security_assessment /path/to/code
|
||||
fuzzforge workflow run security_assessment /path/to/code
|
||||
|
||||
# With parameters
|
||||
fuzzforge workflow security_assessment /path/to/binary \
|
||||
fuzzforge workflow run security_assessment /path/to/binary \
|
||||
--param timeout=3600 \
|
||||
--param iterations=10000
|
||||
|
||||
# With parameter file
|
||||
fuzzforge workflow security_assessment /path/to/code \
|
||||
fuzzforge workflow run security_assessment /path/to/code \
|
||||
--param-file my-params.json
|
||||
|
||||
# Wait for completion
|
||||
fuzzforge workflow security_assessment /path/to/code --wait
|
||||
fuzzforge workflow run security_assessment /path/to/code --wait
|
||||
```
|
||||
|
||||
**Automatic File Upload Behavior:**
|
||||
|
||||
@@ -141,7 +141,7 @@ FuzzForge security testing project.
|
||||
fuzzforge workflows
|
||||
|
||||
# Submit a workflow for analysis
|
||||
fuzzforge workflow <workflow-name> /path/to/target
|
||||
fuzzforge workflow run <workflow-name> /path/to/target
|
||||
|
||||
# View findings
|
||||
fuzzforge finding <run-id>
|
||||
|
||||
@@ -438,7 +438,7 @@ def execute_workflow(
|
||||
|
||||
# Suggest --live for fuzzing workflows
|
||||
if not live and not wait and "fuzzing" in workflow.lower():
|
||||
console.print(f"💡 Next time try: [bold cyan]fuzzforge workflow {workflow} {target_path} --live[/bold cyan] for real-time monitoring", style="dim")
|
||||
console.print(f"💡 Next time try: [bold cyan]fuzzforge workflow run {workflow} {target_path} --live[/bold cyan] for real-time monitoring", style="dim")
|
||||
|
||||
# Start live monitoring if requested
|
||||
if live:
|
||||
|
||||
@@ -251,7 +251,7 @@ def workflow_main():
|
||||
Execute workflows and manage workflow executions
|
||||
|
||||
Examples:
|
||||
fuzzforge workflow security_assessment ./target # Execute workflow
|
||||
fuzzforge workflow run security_assessment ./target # Execute workflow
|
||||
fuzzforge workflow status # Check latest status
|
||||
fuzzforge workflow history # Show execution history
|
||||
"""
|
||||
|
||||
@@ -9,7 +9,7 @@ FuzzForge security testing project.
|
||||
fuzzforge workflows
|
||||
|
||||
# Submit a workflow for analysis
|
||||
fuzzforge workflow <workflow-name> /path/to/target
|
||||
fuzzforge workflow run <workflow-name> /path/to/target
|
||||
|
||||
# View findings
|
||||
fuzzforge finding <run-id>
|
||||
|
||||
Reference in New Issue
Block a user