mirror of
https://github.com/FuzzingLabs/fuzzforge_ai.git
synced 2026-05-25 17:27:49 +02:00
Merge pull request #56 from FuzzingLabs/feat/rename-secpipe
rename: FuzzForge → SecPipe
This commit is contained in:
+15
-15
@@ -25,37 +25,37 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: uv sync
|
||||
|
||||
- name: Ruff check (fuzzforge-cli)
|
||||
- name: Ruff check (secpipe-cli)
|
||||
run: |
|
||||
cd fuzzforge-cli
|
||||
cd secpipe-cli
|
||||
uv run --extra lints ruff check src/
|
||||
|
||||
- name: Ruff check (fuzzforge-mcp)
|
||||
- name: Ruff check (secpipe-mcp)
|
||||
run: |
|
||||
cd fuzzforge-mcp
|
||||
cd secpipe-mcp
|
||||
uv run --extra lints ruff check src/
|
||||
|
||||
- name: Ruff check (fuzzforge-common)
|
||||
- name: Ruff check (secpipe-common)
|
||||
run: |
|
||||
cd fuzzforge-common
|
||||
cd secpipe-common
|
||||
uv run --extra lints ruff check src/
|
||||
|
||||
- name: Mypy type check (fuzzforge-cli)
|
||||
- name: Mypy type check (secpipe-cli)
|
||||
run: |
|
||||
cd fuzzforge-cli
|
||||
cd secpipe-cli
|
||||
uv run --extra lints mypy src/
|
||||
|
||||
- name: Mypy type check (fuzzforge-mcp)
|
||||
- name: Mypy type check (secpipe-mcp)
|
||||
run: |
|
||||
cd fuzzforge-mcp
|
||||
cd secpipe-mcp
|
||||
uv run --extra lints mypy src/
|
||||
|
||||
# NOTE: Mypy check for fuzzforge-common temporarily disabled
|
||||
# NOTE: Mypy check for secpipe-common temporarily disabled
|
||||
# due to 37 pre-existing type errors in legacy code.
|
||||
# TODO: Fix type errors and re-enable strict checking
|
||||
#- name: Mypy type check (fuzzforge-common)
|
||||
#- name: Mypy type check (secpipe-common)
|
||||
# run: |
|
||||
# cd fuzzforge-common
|
||||
# cd secpipe-common
|
||||
# uv run --extra lints mypy src/
|
||||
|
||||
test:
|
||||
@@ -77,10 +77,10 @@ jobs:
|
||||
|
||||
- name: Run MCP tests
|
||||
run: |
|
||||
cd fuzzforge-mcp
|
||||
cd secpipe-mcp
|
||||
uv run --extra tests pytest -v
|
||||
|
||||
- name: Run common tests
|
||||
run: |
|
||||
cd fuzzforge-common
|
||||
cd secpipe-common
|
||||
uv run --extra tests pytest -v
|
||||
|
||||
@@ -27,23 +27,23 @@ jobs:
|
||||
|
||||
- name: Start MCP server in background
|
||||
run: |
|
||||
cd fuzzforge-mcp
|
||||
nohup uv run python -m fuzzforge_mcp.server > server.log 2>&1 &
|
||||
cd secpipe-mcp
|
||||
nohup uv run uvicorn secpipe_mcp.application:app --host 127.0.0.1 --port 8000 > server.log 2>&1 &
|
||||
echo $! > server.pid
|
||||
sleep 3
|
||||
|
||||
- name: Run MCP tool tests
|
||||
run: |
|
||||
cd fuzzforge-mcp
|
||||
cd secpipe-mcp
|
||||
uv run --extra tests pytest tests/test_resources.py -v
|
||||
|
||||
- name: Stop MCP server
|
||||
if: always()
|
||||
run: |
|
||||
if [ -f fuzzforge-mcp/server.pid ]; then
|
||||
kill $(cat fuzzforge-mcp/server.pid) || true
|
||||
if [ -f secpipe-mcp/server.pid ]; then
|
||||
kill $(cat secpipe-mcp/server.pid) || true
|
||||
fi
|
||||
|
||||
- name: Show server logs
|
||||
if: failure()
|
||||
run: cat fuzzforge-mcp/server.log || true
|
||||
run: cat secpipe-mcp/server.log || true
|
||||
|
||||
@@ -13,3 +13,4 @@ __pycache__
|
||||
|
||||
# User-specific hub config (generated at runtime)
|
||||
hub-config.json
|
||||
*.egg-info/
|
||||
|
||||
+35
-35
@@ -71,13 +71,13 @@ test(runner): add container execution tests
|
||||
3. **Test Your Changes**
|
||||
```bash
|
||||
# Test modules
|
||||
FUZZFORGE_MODULES_PATH=./fuzzforge-modules uv run fuzzforge modules list
|
||||
SECPIPE_MODULES_PATH=./secpipe-modules uv run secpipe modules list
|
||||
|
||||
# Run a module
|
||||
uv run fuzzforge modules run your-module --assets ./test-assets
|
||||
uv run secpipe modules run your-module --assets ./test-assets
|
||||
|
||||
# Test MCP integration (if applicable)
|
||||
uv run fuzzforge mcp status
|
||||
uv run secpipe mcp status
|
||||
```
|
||||
|
||||
4. **Submit Pull Request**
|
||||
@@ -88,11 +88,11 @@ test(runner): add container execution tests
|
||||
|
||||
## Module Development
|
||||
|
||||
SecPipe uses a modular architecture where security tools run as isolated containers. The `fuzzforge-modules-sdk` provides everything you need to create new modules.
|
||||
SecPipe uses a modular architecture where security tools run as isolated containers. The `secpipe-modules-sdk` provides everything you need to create new modules.
|
||||
|
||||
**Documentation:**
|
||||
- [Module SDK Documentation](fuzzforge-modules/fuzzforge-modules-sdk/README.md) - Complete SDK reference
|
||||
- [Module Template](fuzzforge-modules/fuzzforge-module-template/) - Starting point for new modules
|
||||
- [Module SDK Documentation](secpipe-modules/secpipe-modules-sdk/README.md) - Complete SDK reference
|
||||
- [Module Template](secpipe-modules/secpipe-module-template/) - Starting point for new modules
|
||||
- [USAGE Guide](USAGE.md) - Setup and installation instructions
|
||||
|
||||
### Creating a New Module
|
||||
@@ -100,8 +100,8 @@ SecPipe uses a modular architecture where security tools run as isolated contain
|
||||
1. **Use the Module Template**
|
||||
```bash
|
||||
# Generate a new module from template
|
||||
cd fuzzforge-modules/
|
||||
cp -r fuzzforge-module-template my-new-module
|
||||
cd secpipe-modules/
|
||||
cp -r secpipe-module-template my-new-module
|
||||
cd my-new-module
|
||||
```
|
||||
|
||||
@@ -127,8 +127,8 @@ SecPipe uses a modular architecture where security tools run as isolated contain
|
||||
|
||||
Edit `src/module/mod.py`:
|
||||
```python
|
||||
from fuzzforge_modules_sdk.api.modules import BaseModule
|
||||
from fuzzforge_modules_sdk.api.models import ModuleResult
|
||||
from secpipe_modules_sdk.api.modules import BaseModule
|
||||
from secpipe_modules_sdk.api.models import ModuleResult
|
||||
from .models import MyModuleConfig, MyModuleOutput
|
||||
|
||||
class MyModule(BaseModule[MyModuleConfig, MyModuleOutput]):
|
||||
@@ -157,7 +157,7 @@ SecPipe uses a modular architecture where security tools run as isolated contain
|
||||
Edit `src/module/models.py`:
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
from fuzzforge_modules_sdk.api.models import BaseModuleConfig, BaseModuleOutput
|
||||
from secpipe_modules_sdk.api.models import BaseModuleConfig, BaseModuleOutput
|
||||
|
||||
class MyModuleConfig(BaseModuleConfig):
|
||||
"""Configuration for your module."""
|
||||
@@ -173,31 +173,31 @@ SecPipe uses a modular architecture where security tools run as isolated contain
|
||||
5. **Build Your Module**
|
||||
```bash
|
||||
# Build the SDK first (if not already done)
|
||||
cd ../fuzzforge-modules-sdk
|
||||
cd ../secpipe-modules-sdk
|
||||
uv build
|
||||
mkdir -p .wheels
|
||||
cp ../../dist/fuzzforge_modules_sdk-*.whl .wheels/
|
||||
cp ../../dist/secpipe_modules_sdk-*.whl .wheels/
|
||||
cd ../..
|
||||
docker build -t localhost/fuzzforge-modules-sdk:0.1.0 fuzzforge-modules/fuzzforge-modules-sdk/
|
||||
docker build -t localhost/secpipe-modules-sdk:0.1.0 secpipe-modules/secpipe-modules-sdk/
|
||||
|
||||
# Build your module
|
||||
cd fuzzforge-modules/my-new-module
|
||||
docker build -t fuzzforge-my-new-module:0.1.0 .
|
||||
cd secpipe-modules/my-new-module
|
||||
docker build -t secpipe-my-new-module:0.1.0 .
|
||||
```
|
||||
|
||||
6. **Test Your Module**
|
||||
```bash
|
||||
# Run with test assets
|
||||
uv run fuzzforge modules run my-new-module --assets ./test-assets
|
||||
uv run secpipe modules run my-new-module --assets ./test-assets
|
||||
|
||||
# Check module info
|
||||
uv run fuzzforge modules info my-new-module
|
||||
uv run secpipe modules info my-new-module
|
||||
```
|
||||
|
||||
### Module Development Guidelines
|
||||
|
||||
**Important Conventions:**
|
||||
- **Input/Output**: Use `/fuzzforge/input` for assets and `/fuzzforge/output` for results
|
||||
- **Input/Output**: Use `/secpipe/input` for assets and `/secpipe/output` for results
|
||||
- **Configuration**: Support JSON configuration via stdin or file
|
||||
- **Logging**: Use structured logging (structlog is pre-configured)
|
||||
- **Error Handling**: Return proper exit codes and error messages
|
||||
@@ -206,7 +206,7 @@ SecPipe uses a modular architecture where security tools run as isolated contain
|
||||
- **Dependencies**: Minimize container size, use multi-stage builds
|
||||
|
||||
**See also:**
|
||||
- [Module SDK API Reference](fuzzforge-modules/fuzzforge-modules-sdk/src/fuzzforge_modules_sdk/api/)
|
||||
- [Module SDK API Reference](secpipe-modules/secpipe-modules-sdk/src/secpipe_modules_sdk/api/)
|
||||
- [Dockerfile Best Practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)
|
||||
|
||||
### Module Types
|
||||
@@ -273,8 +273,8 @@ SecPipe is designed to support modules across **all cybersecurity domains**. The
|
||||
```python
|
||||
# src/module/mod.py
|
||||
from pathlib import Path
|
||||
from fuzzforge_modules_sdk.api.modules import BaseModule
|
||||
from fuzzforge_modules_sdk.api.models import ModuleResult
|
||||
from secpipe_modules_sdk.api.modules import BaseModule
|
||||
from secpipe_modules_sdk.api.models import ModuleResult
|
||||
from .models import ScannerConfig, ScannerOutput
|
||||
|
||||
class SecurityScanner(BaseModule[ScannerConfig, ScannerOutput]):
|
||||
@@ -350,18 +350,18 @@ Beyond modules, you can contribute to SecPipe's core components.
|
||||
|
||||
### Core Components
|
||||
|
||||
- **fuzzforge-mcp** - MCP server for AI agent integration
|
||||
- **fuzzforge-runner** - Module execution engine
|
||||
- **fuzzforge-cli** - Command-line interface
|
||||
- **fuzzforge-common** - Shared utilities and sandbox engines
|
||||
- **fuzzforge-types** - Type definitions and schemas
|
||||
- **secpipe-mcp** - MCP server for AI agent integration
|
||||
- **secpipe-runner** - Module execution engine
|
||||
- **secpipe-cli** - Command-line interface
|
||||
- **secpipe-common** - Shared utilities and sandbox engines
|
||||
- **secpipe-types** - Type definitions and schemas
|
||||
|
||||
### Development Setup
|
||||
|
||||
1. **Clone and Install**
|
||||
```bash
|
||||
git clone https://github.com/FuzzingLabs/fuzzforge_ai.git
|
||||
cd fuzzforge_ai
|
||||
git clone https://github.com/FuzzingLabs/secpipe_ai.git
|
||||
cd secpipe_ai
|
||||
uv sync --all-extras
|
||||
```
|
||||
|
||||
@@ -371,7 +371,7 @@ Beyond modules, you can contribute to SecPipe's core components.
|
||||
make test
|
||||
|
||||
# Run specific package tests
|
||||
cd fuzzforge-mcp
|
||||
cd secpipe-mcp
|
||||
uv run pytest
|
||||
```
|
||||
|
||||
@@ -381,7 +381,7 @@ Beyond modules, you can contribute to SecPipe's core components.
|
||||
make typecheck
|
||||
|
||||
# Type check specific package
|
||||
cd fuzzforge-runner
|
||||
cd secpipe-runner
|
||||
uv run mypy .
|
||||
```
|
||||
|
||||
@@ -399,7 +399,7 @@ Beyond modules, you can contribute to SecPipe's core components.
|
||||
When reporting bugs, please include:
|
||||
|
||||
- **Environment**: OS, Python version, Docker version, uv version
|
||||
- **SecPipe Version**: Output of `uv run fuzzforge --version`
|
||||
- **SecPipe Version**: Output of `uv run secpipe --version`
|
||||
- **Module**: Which module or component is affected
|
||||
- **Steps to Reproduce**: Clear steps to recreate the issue
|
||||
- **Expected Behavior**: What should happen
|
||||
@@ -419,7 +419,7 @@ When reporting bugs, please include:
|
||||
**Module:** my-custom-scanner
|
||||
|
||||
**Steps to Reproduce:**
|
||||
1. Run `uv run fuzzforge modules run my-scanner --assets ./test-target`
|
||||
1. Run `uv run secpipe modules run my-scanner --assets ./test-target`
|
||||
2. Module fails with timeout error
|
||||
|
||||
**Expected:** Module completes analysis
|
||||
@@ -491,7 +491,7 @@ Brief description of what this module does.
|
||||
## Usage
|
||||
|
||||
\`\`\`bash
|
||||
uv run fuzzforge modules run module-name --assets ./path/to/assets
|
||||
uv run secpipe modules run module-name --assets ./path/to/assets
|
||||
\`\`\`
|
||||
|
||||
## Output
|
||||
@@ -552,7 +552,7 @@ For module contributions:
|
||||
Need help contributing?
|
||||
|
||||
- Join our [Discord](https://discord.gg/8XEX33UUwZ)
|
||||
- Read the [Module SDK Documentation](fuzzforge-modules/fuzzforge-modules-sdk/README.md)
|
||||
- Read the [Module SDK Documentation](secpipe-modules/secpipe-modules-sdk/README.md)
|
||||
- Check the module template for examples
|
||||
- Contact: contact@fuzzinglabs.com
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ SHELL := /bin/bash
|
||||
|
||||
# Default target
|
||||
help:
|
||||
@echo "FuzzForge AI Development Commands"
|
||||
@echo "SecPipe AI Development Commands"
|
||||
@echo ""
|
||||
@echo " make install - Install all dependencies"
|
||||
@echo " make sync - Sync shared packages from upstream"
|
||||
@@ -20,17 +20,17 @@ help:
|
||||
install:
|
||||
uv sync
|
||||
|
||||
# Sync shared packages from upstream fuzzforge-core
|
||||
# Sync shared packages from upstream secpipe-core
|
||||
sync:
|
||||
@if [ -z "$(UPSTREAM)" ]; then \
|
||||
echo "Usage: make sync UPSTREAM=/path/to/fuzzforge-core"; \
|
||||
echo "Usage: make sync UPSTREAM=/path/to/secpipe-core"; \
|
||||
exit 1; \
|
||||
fi
|
||||
./scripts/sync-upstream.sh $(UPSTREAM)
|
||||
|
||||
# Format all packages
|
||||
format:
|
||||
@for pkg in packages/fuzzforge-*/; do \
|
||||
@for pkg in packages/secpipe-*/; do \
|
||||
if [ -f "$$pkg/pyproject.toml" ]; then \
|
||||
echo "Formatting $$pkg..."; \
|
||||
cd "$$pkg" && uv run ruff format . && cd -; \
|
||||
@@ -39,7 +39,7 @@ format:
|
||||
|
||||
# Lint all packages
|
||||
lint:
|
||||
@for pkg in packages/fuzzforge-*/; do \
|
||||
@for pkg in packages/secpipe-*/; do \
|
||||
if [ -f "$$pkg/pyproject.toml" ]; then \
|
||||
echo "Linting $$pkg..."; \
|
||||
cd "$$pkg" && uv run ruff check . && cd -; \
|
||||
@@ -48,7 +48,7 @@ lint:
|
||||
|
||||
# Type check all packages
|
||||
typecheck:
|
||||
@for pkg in packages/fuzzforge-*/; do \
|
||||
@for pkg in packages/secpipe-*/; do \
|
||||
if [ -f "$$pkg/pyproject.toml" ] && [ -f "$$pkg/mypy.ini" ]; then \
|
||||
echo "Type checking $$pkg..."; \
|
||||
cd "$$pkg" && uv run mypy . && cd -; \
|
||||
@@ -57,7 +57,7 @@ typecheck:
|
||||
|
||||
# Run all tests
|
||||
test:
|
||||
@for pkg in packages/fuzzforge-*/; do \
|
||||
@for pkg in packages/secpipe-*/; do \
|
||||
if [ -f "$$pkg/pytest.ini" ]; then \
|
||||
echo "Testing $$pkg..."; \
|
||||
cd "$$pkg" && uv run pytest && cd -; \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FuzzForge
|
||||
SecPipe
|
||||
Copyright (c) 2025 FuzzingLabs
|
||||
|
||||
This product includes software developed by FuzzingLabs (https://fuzzforge.ai).
|
||||
@@ -7,6 +7,6 @@ Licensed under the Business Source License 1.1 (BSL).
|
||||
After the Change Date (four years from the date of publication), this version
|
||||
of the Licensed Work will be made available under the Apache License, Version 2.0.
|
||||
|
||||
You may not use the name "FuzzingLabs" or "FuzzForge" nor the names of its
|
||||
You may not use the name "FuzzingLabs" or "SecPipe" nor the names of its
|
||||
contributors to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
@@ -76,8 +76,8 @@ Agent → Crash Analysis: Deduplicate and triage discovered crashes
|
||||
|
||||
If you find SecPipe useful, please **star the repo** to support development! 🚀
|
||||
|
||||
<a href="https://github.com/FuzzingLabs/fuzzforge_ai/stargazers">
|
||||
<img src="https://img.shields.io/github/stars/FuzzingLabs/fuzzforge_ai?style=social" alt="GitHub Stars">
|
||||
<a href="https://github.com/FuzzingLabs/secpipe_ai/stargazers">
|
||||
<img src="https://img.shields.io/github/stars/FuzzingLabs/secpipe_ai?style=social" alt="GitHub Stars">
|
||||
</a>
|
||||
|
||||
---
|
||||
@@ -169,8 +169,8 @@ The hub is open source and can be extended with your own MCP servers. See the [m
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/FuzzingLabs/fuzzforge_ai.git
|
||||
cd fuzzforge_ai
|
||||
git clone https://github.com/FuzzingLabs/secpipe_ai.git
|
||||
cd secpipe_ai
|
||||
|
||||
# Install dependencies
|
||||
uv sync
|
||||
@@ -180,28 +180,28 @@ uv sync
|
||||
|
||||
```bash
|
||||
# Clone the MCP Security Hub
|
||||
git clone https://github.com/FuzzingLabs/mcp-security-hub.git ~/.fuzzforge/hubs/mcp-security-hub
|
||||
git clone https://github.com/FuzzingLabs/mcp-security-hub.git ~/.secpipe/hubs/mcp-security-hub
|
||||
|
||||
# Build the Docker images for the hub tools
|
||||
./scripts/build-hub-images.sh
|
||||
```
|
||||
|
||||
Or use the terminal UI (`uv run fuzzforge ui`) to link hubs interactively.
|
||||
Or use the terminal UI (`uv run secpipe ui`) to link hubs interactively.
|
||||
|
||||
### Configure MCP for Your AI Agent
|
||||
|
||||
```bash
|
||||
# For GitHub Copilot
|
||||
uv run fuzzforge mcp install copilot
|
||||
uv run secpipe mcp install copilot
|
||||
|
||||
# For Claude Code (CLI)
|
||||
uv run fuzzforge mcp install claude-code
|
||||
uv run secpipe mcp install claude-code
|
||||
|
||||
# For Claude Desktop (standalone app)
|
||||
uv run fuzzforge mcp install claude-desktop
|
||||
uv run secpipe mcp install claude-desktop
|
||||
|
||||
# Verify installation
|
||||
uv run fuzzforge mcp status
|
||||
uv run secpipe mcp status
|
||||
```
|
||||
|
||||
**Restart your editor** and your AI agent will have access to SecPipe tools!
|
||||
@@ -228,12 +228,12 @@ See the [Usage Guide](USAGE.md) for detailed setup and advanced workflows.
|
||||
## 📁 Project Structure
|
||||
|
||||
```
|
||||
fuzzforge_ai/
|
||||
├── fuzzforge-mcp/ # MCP server — the core of SecPipe
|
||||
├── fuzzforge-cli/ # Command-line interface & terminal UI
|
||||
├── fuzzforge-common/ # Shared abstractions (containers, storage)
|
||||
├── fuzzforge-runner/ # Container execution engine (Docker/Podman)
|
||||
├── fuzzforge-tests/ # Integration tests
|
||||
secpipe_ai/
|
||||
├── secpipe-mcp/ # MCP server — the core of SecPipe
|
||||
├── secpipe-cli/ # Command-line interface & terminal UI
|
||||
├── secpipe-common/ # Shared abstractions (containers, storage)
|
||||
├── secpipe-runner/ # Container execution engine (Docker/Podman)
|
||||
├── secpipe-tests/ # Integration tests
|
||||
├── mcp-security-hub/ # Default hub: 36 offensive security MCP servers
|
||||
└── scripts/ # Hub image build scripts
|
||||
```
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
# v0.8.0 — MCP Hub Architecture
|
||||
|
||||
SecPipe AI v0.8.0 is a major architectural rewrite. The previous module system has been replaced by the **MCP Hub** architecture — SecPipe now acts as a meta-MCP server that connects AI agents to collections of containerized security tools, discovered and orchestrated at runtime.
|
||||
|
||||
---
|
||||
|
||||
## Highlights
|
||||
|
||||
### MCP Hub System
|
||||
|
||||
SecPipe no longer ships its own security modules. Instead, it connects to **MCP tool hubs** — registries of Dockerized MCP servers that AI agents can discover, chain, and execute autonomously.
|
||||
|
||||
- **Runtime tool discovery** — agents call `list_hub_servers` and `discover_hub_tools` to find available tools
|
||||
- **Agent context convention** — hub tools provide built-in usage tips, workflow guidance, and domain knowledge so agents can use them without human intervention
|
||||
- **Category filtering** — servers are organized by category (`binary-analysis`, `web-security`, `reconnaissance`, etc.) for efficient discovery
|
||||
- **Persistent sessions** — stateful tools like Radare2 run in long-lived containers with `start_hub_server` / `stop_hub_server`
|
||||
- **Volume mounts** — project assets are automatically mounted into tool containers for seamless file access
|
||||
- **Continuous mode** — long-running tools (fuzzers) with real-time status via `start_continuous_hub_tool`
|
||||
|
||||
### MCP Security Hub Integration
|
||||
|
||||
Ships with built-in support for the [MCP Security Hub](https://github.com/FuzzingLabs/mcp-security-hub) — **36 production-ready MCP servers** covering:
|
||||
|
||||
| Category | Servers | Examples |
|
||||
|----------|---------|----------|
|
||||
| Reconnaissance | 8 | Nmap, Masscan, Shodan, WhatWeb |
|
||||
| Web Security | 6 | Nuclei, SQLMap, ffuf, Nikto |
|
||||
| Binary Analysis | 6 | Radare2, Binwalk, YARA, Capa, Ghidra |
|
||||
| Blockchain | 3 | Medusa, Solazy, DAML Viewer |
|
||||
| Cloud Security | 3 | Trivy, Prowler, RoadRecon |
|
||||
| Code Security | 1 | Semgrep |
|
||||
| Secrets Detection | 1 | Gitleaks |
|
||||
| Exploitation | 1 | SearchSploit |
|
||||
| Fuzzing | 2 | Boofuzz, Dharma |
|
||||
| OSINT | 2 | Maigret, DNSTwist |
|
||||
| Threat Intel | 2 | VirusTotal, AlienVault OTX |
|
||||
| Active Directory | 1 | BloodHound |
|
||||
|
||||
> **185+ individual security tools** accessible through a single MCP connection.
|
||||
|
||||
### Terminal UI
|
||||
|
||||
A new interactive terminal interface (`uv run secpipe ui`) for managing hubs and agents:
|
||||
|
||||
- Dashboard with hub status overview
|
||||
- One-click MCP server installation for GitHub Copilot, Claude Code, and Claude Desktop
|
||||
- In-UI Docker image building with live log viewer
|
||||
- Hub linking and registry management
|
||||
|
||||
---
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- The module system has been removed (`list_modules`, `execute_module`, `start_continuous_module`)
|
||||
- Replaced by hub tools: `list_hub_servers`, `discover_hub_tools`, `execute_hub_tool`, `start_hub_server`, `stop_hub_server`, etc.
|
||||
- `make build-modules` replaced by `./scripts/build-hub-images.sh`
|
||||
|
||||
---
|
||||
|
||||
## Other Changes
|
||||
|
||||
- **CI**: GitHub Actions workflows with ruff lint, mypy typecheck, and tests
|
||||
- **Config**: `SECPIPE_USER_DIR` environment variable to override user-global data directory
|
||||
- **Storage**: `~/.secpipe` for user-global data, `.secpipe/` in workspace for project storage
|
||||
- **Docs**: README rewritten for hub-centric architecture
|
||||
+1
-1
@@ -115,7 +115,7 @@ Features under consideration for future releases:
|
||||
|
||||
Have suggestions for the roadmap?
|
||||
|
||||
- Open an issue on [GitHub](https://github.com/FuzzingLabs/fuzzforge_ai/issues)
|
||||
- Open an issue on [GitHub](https://github.com/FuzzingLabs/secpipe_ai/issues)
|
||||
- Join our [Discord](https://discord.gg/8XEX33UUwZ)
|
||||
|
||||
---
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
This guide covers everything you need to know to get started with SecPipe AI — from installation to linking your first MCP hub and running security research workflows with AI.
|
||||
|
||||
> **SecPipe is designed to be used with AI agents** (GitHub Copilot, Claude, etc.) via MCP.
|
||||
> A terminal UI (`fuzzforge ui`) is provided for managing agents and hubs.
|
||||
> A terminal UI (`secpipe ui`) is provided for managing agents and hubs.
|
||||
> The CLI is available for advanced users but the primary experience is through natural language interaction with your AI assistant.
|
||||
|
||||
---
|
||||
@@ -41,12 +41,12 @@ This guide covers everything you need to know to get started with SecPipe AI —
|
||||
|
||||
```bash
|
||||
# 1. Clone and install
|
||||
git clone https://github.com/FuzzingLabs/fuzzforge_ai.git
|
||||
cd fuzzforge_ai
|
||||
git clone https://github.com/FuzzingLabs/secpipe_ai.git
|
||||
cd secpipe_ai
|
||||
uv sync
|
||||
|
||||
# 2. Launch the terminal UI
|
||||
uv run fuzzforge ui
|
||||
uv run secpipe ui
|
||||
|
||||
# 3. Press 'h' → "FuzzingLabs Hub" to clone & link the default security hub
|
||||
# 4. Select an agent row and press Enter to install the MCP server for your agent
|
||||
@@ -63,12 +63,12 @@ Or do it entirely from the command line:
|
||||
|
||||
```bash
|
||||
# Install MCP for your AI agent
|
||||
uv run fuzzforge mcp install copilot # For VS Code + GitHub Copilot
|
||||
uv run secpipe mcp install copilot # For VS Code + GitHub Copilot
|
||||
# OR
|
||||
uv run fuzzforge mcp install claude-code # For Claude Code CLI
|
||||
uv run secpipe mcp install claude-code # For Claude Code CLI
|
||||
|
||||
# Clone and link the default security hub
|
||||
git clone git@github.com:FuzzingLabs/mcp-security-hub.git ~/.fuzzforge/hubs/mcp-security-hub
|
||||
git clone git@github.com:FuzzingLabs/mcp-security-hub.git ~/.secpipe/hubs/mcp-security-hub
|
||||
|
||||
# Build hub tool images (required — tools only run once their image is built)
|
||||
./scripts/build-hub-images.sh
|
||||
@@ -112,7 +112,7 @@ sudo usermod -aG docker $USER
|
||||
```
|
||||
|
||||
> **Note:** Podman is also supported. Use `--engine podman` with CLI commands
|
||||
> or set `FUZZFORGE_ENGINE=podman` environment variable.
|
||||
> or set `SECPIPE_ENGINE=podman` environment variable.
|
||||
|
||||
---
|
||||
|
||||
@@ -121,8 +121,8 @@ sudo usermod -aG docker $USER
|
||||
### 1. Clone the Repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/FuzzingLabs/fuzzforge_ai.git
|
||||
cd fuzzforge_ai
|
||||
git clone https://github.com/FuzzingLabs/secpipe_ai.git
|
||||
cd secpipe_ai
|
||||
```
|
||||
|
||||
### 2. Install Dependencies
|
||||
@@ -136,7 +136,7 @@ This installs all SecPipe components in a virtual environment.
|
||||
### 3. Verify Installation
|
||||
|
||||
```bash
|
||||
uv run fuzzforge --help
|
||||
uv run secpipe --help
|
||||
```
|
||||
|
||||
---
|
||||
@@ -148,7 +148,7 @@ SecPipe ships with a terminal user interface (TUI) built on [Textual](https://te
|
||||
### Launching the UI
|
||||
|
||||
```bash
|
||||
uv run fuzzforge ui
|
||||
uv run secpipe ui
|
||||
```
|
||||
|
||||
### Dashboard
|
||||
@@ -174,7 +174,7 @@ The main screen is split into two panels:
|
||||
Select an agent row in the AI Agents table and press `Enter`:
|
||||
|
||||
- **If the agent is not linked** → a setup dialog opens asking for your container engine (Docker or Podman), then installs the SecPipe MCP configuration
|
||||
- **If the agent is already linked** → a confirmation dialog offers to unlink it (removes the `fuzzforge` entry without touching other MCP servers)
|
||||
- **If the agent is already linked** → a confirmation dialog offers to unlink it (removes the `secpipe` entry without touching other MCP servers)
|
||||
|
||||
The setup auto-detects:
|
||||
- SecPipe installation root
|
||||
@@ -187,7 +187,7 @@ Press `h` to open the hub manager. This is where you manage your MCP hub reposit
|
||||
|
||||
| Button | Action |
|
||||
|--------|--------|
|
||||
| **FuzzingLabs Hub** | One-click clone of the official [mcp-security-hub](https://github.com/FuzzingLabs/mcp-security-hub) repository — clones to `~/.fuzzforge/hubs/mcp-security-hub`, scans for tools, and registers them in `hub-config.json` |
|
||||
| **FuzzingLabs Hub** | One-click clone of the official [mcp-security-hub](https://github.com/FuzzingLabs/mcp-security-hub) repository — clones to `~/.secpipe/hubs/mcp-security-hub`, scans for tools, and registers them in `hub-config.json` |
|
||||
| **Link Path** | Link any local directory as a hub — enter a name and path, SecPipe scans it for `category/tool-name/Dockerfile` patterns |
|
||||
| **Clone URL** | Clone any git repository and link it as a hub |
|
||||
| **Remove** | Unlink the selected hub and remove its servers from the configuration |
|
||||
@@ -242,14 +242,14 @@ The default MCP hub is [mcp-security-hub](https://github.com/FuzzingLabs/mcp-sec
|
||||
|
||||
**Clone it via the UI:**
|
||||
|
||||
1. `uv run fuzzforge ui`
|
||||
1. `uv run secpipe ui`
|
||||
2. Press `h` → click **FuzzingLabs Hub**
|
||||
3. Wait for the clone to finish — servers are auto-registered
|
||||
|
||||
**Or clone manually:**
|
||||
|
||||
```bash
|
||||
git clone git@github.com:FuzzingLabs/mcp-security-hub.git ~/.fuzzforge/hubs/mcp-security-hub
|
||||
git clone git@github.com:FuzzingLabs/mcp-security-hub.git ~/.secpipe/hubs/mcp-security-hub
|
||||
```
|
||||
|
||||
### Linking a Custom Hub
|
||||
@@ -286,7 +286,7 @@ If you prefer the command line over the TUI, you can configure agents directly:
|
||||
### GitHub Copilot
|
||||
|
||||
```bash
|
||||
uv run fuzzforge mcp install copilot
|
||||
uv run secpipe mcp install copilot
|
||||
```
|
||||
|
||||
The command auto-detects:
|
||||
@@ -295,7 +295,7 @@ The command auto-detects:
|
||||
|
||||
**Optional overrides:**
|
||||
```bash
|
||||
uv run fuzzforge mcp install copilot --engine podman
|
||||
uv run secpipe mcp install copilot --engine podman
|
||||
```
|
||||
|
||||
**After installation:** Restart VS Code. SecPipe tools appear in GitHub Copilot Chat.
|
||||
@@ -303,7 +303,7 @@ uv run fuzzforge mcp install copilot --engine podman
|
||||
### Claude Code (CLI)
|
||||
|
||||
```bash
|
||||
uv run fuzzforge mcp install claude-code
|
||||
uv run secpipe mcp install claude-code
|
||||
```
|
||||
|
||||
Installs to `~/.claude.json`. SecPipe tools are available from any directory after restarting Claude.
|
||||
@@ -311,7 +311,7 @@ Installs to `~/.claude.json`. SecPipe tools are available from any directory aft
|
||||
### Claude Desktop
|
||||
|
||||
```bash
|
||||
uv run fuzzforge mcp install claude-desktop
|
||||
uv run secpipe mcp install claude-desktop
|
||||
```
|
||||
|
||||
**After installation:** Restart Claude Desktop.
|
||||
@@ -319,15 +319,15 @@ uv run fuzzforge mcp install claude-desktop
|
||||
### Check Status
|
||||
|
||||
```bash
|
||||
uv run fuzzforge mcp status
|
||||
uv run secpipe mcp status
|
||||
```
|
||||
|
||||
### Remove Configuration
|
||||
|
||||
```bash
|
||||
uv run fuzzforge mcp uninstall copilot
|
||||
uv run fuzzforge mcp uninstall claude-code
|
||||
uv run fuzzforge mcp uninstall claude-desktop
|
||||
uv run secpipe mcp uninstall copilot
|
||||
uv run secpipe mcp uninstall claude-code
|
||||
uv run secpipe mcp uninstall claude-desktop
|
||||
```
|
||||
|
||||
---
|
||||
@@ -377,25 +377,25 @@ AI: Uses semgrep-mcp → "Found 5 findings: 2 high severity SQL injection
|
||||
### UI Command
|
||||
|
||||
```bash
|
||||
uv run fuzzforge ui # Launch the terminal dashboard
|
||||
uv run secpipe ui # Launch the terminal dashboard
|
||||
```
|
||||
|
||||
### MCP Commands
|
||||
|
||||
```bash
|
||||
uv run fuzzforge mcp status # Check agent configuration status
|
||||
uv run fuzzforge mcp install <agent> # Install MCP config (copilot|claude-code|claude-desktop)
|
||||
uv run fuzzforge mcp uninstall <agent> # Remove MCP config
|
||||
uv run fuzzforge mcp generate <agent> # Preview config without installing
|
||||
uv run secpipe mcp status # Check agent configuration status
|
||||
uv run secpipe mcp install <agent> # Install MCP config (copilot|claude-code|claude-desktop)
|
||||
uv run secpipe mcp uninstall <agent> # Remove MCP config
|
||||
uv run secpipe mcp generate <agent> # Preview config without installing
|
||||
```
|
||||
|
||||
### Project Commands
|
||||
|
||||
```bash
|
||||
uv run fuzzforge project init # Initialize a project
|
||||
uv run fuzzforge project info # Show project info
|
||||
uv run fuzzforge project executions # List executions
|
||||
uv run fuzzforge project results <id> # Get execution results
|
||||
uv run secpipe project init # Initialize a project
|
||||
uv run secpipe project info # Show project info
|
||||
uv run secpipe project executions # List executions
|
||||
uv run secpipe project results <id> # Get execution results
|
||||
```
|
||||
|
||||
---
|
||||
@@ -406,21 +406,21 @@ Configure SecPipe using environment variables:
|
||||
|
||||
```bash
|
||||
# Override the SecPipe installation root (auto-detected from cwd by default)
|
||||
export FUZZFORGE_ROOT=/path/to/fuzzforge_ai
|
||||
export SECPIPE_ROOT=/path/to/secpipe_ai
|
||||
|
||||
# Override the user-global data directory (default: ~/.fuzzforge)
|
||||
# Override the user-global data directory (default: ~/.secpipe)
|
||||
# Useful for isolated testing without touching your real installation
|
||||
export FUZZFORGE_USER_DIR=/tmp/my-fuzzforge-test
|
||||
export SECPIPE_USER_DIR=/tmp/my-secpipe-test
|
||||
|
||||
# Storage path for projects and execution results (default: <workspace>/.fuzzforge/storage)
|
||||
export FUZZFORGE_STORAGE__PATH=/path/to/storage
|
||||
# Storage path for projects and execution results (default: <workspace>/.secpipe/storage)
|
||||
export SECPIPE_STORAGE__PATH=/path/to/storage
|
||||
|
||||
# Container engine (Docker is default)
|
||||
export FUZZFORGE_ENGINE__TYPE=docker # or podman
|
||||
export SECPIPE_ENGINE__TYPE=docker # or podman
|
||||
|
||||
# Podman-specific container storage paths
|
||||
export FUZZFORGE_ENGINE__GRAPHROOT=~/.fuzzforge/containers/storage
|
||||
export FUZZFORGE_ENGINE__RUNROOT=~/.fuzzforge/containers/run
|
||||
export SECPIPE_ENGINE__GRAPHROOT=~/.secpipe/containers/storage
|
||||
export SECPIPE_ENGINE__RUNROOT=~/.secpipe/containers/run
|
||||
```
|
||||
|
||||
---
|
||||
@@ -473,7 +473,7 @@ docker build -t <tool-name>:latest mcp-security-hub/<category>/<tool-name>/
|
||||
|
||||
```bash
|
||||
# Check agent configuration
|
||||
uv run fuzzforge mcp status
|
||||
uv run secpipe mcp status
|
||||
|
||||
# Verify the config file path exists and contains valid JSON
|
||||
cat ~/.config/Code/User/mcp.json # Copilot
|
||||
@@ -484,29 +484,29 @@ cat ~/.claude.json # Claude Code
|
||||
|
||||
```bash
|
||||
# Install with Podman engine
|
||||
uv run fuzzforge mcp install copilot --engine podman
|
||||
uv run secpipe mcp install copilot --engine podman
|
||||
|
||||
# Or set environment variable
|
||||
export FUZZFORGE_ENGINE=podman
|
||||
export SECPIPE_ENGINE=podman
|
||||
```
|
||||
|
||||
### Hub Registry
|
||||
|
||||
SecPipe stores linked hub information in `~/.fuzzforge/hubs.json`. If something goes wrong:
|
||||
SecPipe stores linked hub information in `~/.secpipe/hubs.json`. If something goes wrong:
|
||||
|
||||
```bash
|
||||
# View registry
|
||||
cat ~/.fuzzforge/hubs.json
|
||||
cat ~/.secpipe/hubs.json
|
||||
|
||||
# Reset registry
|
||||
rm ~/.fuzzforge/hubs.json
|
||||
rm ~/.secpipe/hubs.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
- 🖥️ Launch `uv run fuzzforge ui` and explore the dashboard
|
||||
- 🖥️ Launch `uv run secpipe ui` and explore the dashboard
|
||||
- 🔒 Clone the [mcp-security-hub](https://github.com/FuzzingLabs/mcp-security-hub) for 40+ security tools
|
||||
- 💬 Join our [Discord](https://discord.gg/8XEX33UUwZ) for support
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
"""FuzzForge terminal user interface."""
|
||||
@@ -1 +0,0 @@
|
||||
"""TUI screens for FuzzForge."""
|
||||
@@ -1,38 +0,0 @@
|
||||
"""FuzzForge Common - Shared abstractions and implementations for FuzzForge.
|
||||
|
||||
This package provides:
|
||||
- Sandbox engine abstractions (Podman, Docker)
|
||||
- Common exceptions
|
||||
|
||||
Example usage:
|
||||
from fuzzforge_common import (
|
||||
AbstractFuzzForgeSandboxEngine,
|
||||
ImageInfo,
|
||||
Podman,
|
||||
PodmanConfiguration,
|
||||
)
|
||||
"""
|
||||
|
||||
from fuzzforge_common.exceptions import FuzzForgeError
|
||||
from fuzzforge_common.sandboxes import (
|
||||
AbstractFuzzForgeEngineConfiguration,
|
||||
AbstractFuzzForgeSandboxEngine,
|
||||
Docker,
|
||||
DockerConfiguration,
|
||||
FuzzForgeSandboxEngines,
|
||||
ImageInfo,
|
||||
Podman,
|
||||
PodmanConfiguration,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"AbstractFuzzForgeEngineConfiguration",
|
||||
"AbstractFuzzForgeSandboxEngine",
|
||||
"Docker",
|
||||
"DockerConfiguration",
|
||||
"FuzzForgeError",
|
||||
"FuzzForgeSandboxEngines",
|
||||
"ImageInfo",
|
||||
"Podman",
|
||||
"PodmanConfiguration",
|
||||
]
|
||||
@@ -1,23 +0,0 @@
|
||||
"""FuzzForge sandbox abstractions and implementations."""
|
||||
|
||||
from fuzzforge_common.sandboxes.engines import (
|
||||
AbstractFuzzForgeEngineConfiguration,
|
||||
AbstractFuzzForgeSandboxEngine,
|
||||
Docker,
|
||||
DockerConfiguration,
|
||||
FuzzForgeSandboxEngines,
|
||||
ImageInfo,
|
||||
Podman,
|
||||
PodmanConfiguration,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"AbstractFuzzForgeEngineConfiguration",
|
||||
"AbstractFuzzForgeSandboxEngine",
|
||||
"Docker",
|
||||
"DockerConfiguration",
|
||||
"FuzzForgeSandboxEngines",
|
||||
"ImageInfo",
|
||||
"Podman",
|
||||
"PodmanConfiguration",
|
||||
]
|
||||
@@ -1,21 +0,0 @@
|
||||
"""Container engine implementations for FuzzForge sandboxes."""
|
||||
|
||||
from fuzzforge_common.sandboxes.engines.base import (
|
||||
AbstractFuzzForgeEngineConfiguration,
|
||||
AbstractFuzzForgeSandboxEngine,
|
||||
ImageInfo,
|
||||
)
|
||||
from fuzzforge_common.sandboxes.engines.docker import Docker, DockerConfiguration
|
||||
from fuzzforge_common.sandboxes.engines.enumeration import FuzzForgeSandboxEngines
|
||||
from fuzzforge_common.sandboxes.engines.podman import Podman, PodmanConfiguration
|
||||
|
||||
__all__ = [
|
||||
"AbstractFuzzForgeEngineConfiguration",
|
||||
"AbstractFuzzForgeSandboxEngine",
|
||||
"Docker",
|
||||
"DockerConfiguration",
|
||||
"FuzzForgeSandboxEngines",
|
||||
"ImageInfo",
|
||||
"Podman",
|
||||
"PodmanConfiguration",
|
||||
]
|
||||
@@ -1,15 +0,0 @@
|
||||
"""Base engine abstractions."""
|
||||
|
||||
from fuzzforge_common.sandboxes.engines.base.configuration import (
|
||||
AbstractFuzzForgeEngineConfiguration,
|
||||
)
|
||||
from fuzzforge_common.sandboxes.engines.base.engine import (
|
||||
AbstractFuzzForgeSandboxEngine,
|
||||
ImageInfo,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"AbstractFuzzForgeEngineConfiguration",
|
||||
"AbstractFuzzForgeSandboxEngine",
|
||||
"ImageInfo",
|
||||
]
|
||||
@@ -1,13 +0,0 @@
|
||||
"""Docker container engine implementation."""
|
||||
|
||||
from fuzzforge_common.sandboxes.engines.docker.cli import DockerCLI
|
||||
from fuzzforge_common.sandboxes.engines.docker.configuration import (
|
||||
DockerConfiguration,
|
||||
)
|
||||
from fuzzforge_common.sandboxes.engines.docker.engine import Docker
|
||||
|
||||
__all__ = [
|
||||
"Docker",
|
||||
"DockerCLI",
|
||||
"DockerConfiguration",
|
||||
]
|
||||
@@ -1,22 +0,0 @@
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
from fuzzforge_common.sandboxes.engines.base.configuration import AbstractFuzzForgeEngineConfiguration
|
||||
from fuzzforge_common.sandboxes.engines.docker.engine import Docker
|
||||
from fuzzforge_common.sandboxes.engines.enumeration import FuzzForgeSandboxEngines
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fuzzforge_common.sandboxes.engines.base.engine import AbstractFuzzForgeSandboxEngine
|
||||
|
||||
|
||||
class DockerConfiguration(AbstractFuzzForgeEngineConfiguration):
|
||||
"""TODO."""
|
||||
|
||||
#: TODO.
|
||||
kind: Literal[FuzzForgeSandboxEngines.DOCKER] = FuzzForgeSandboxEngines.DOCKER
|
||||
|
||||
#: TODO.
|
||||
socket: str
|
||||
|
||||
def into_engine(self) -> AbstractFuzzForgeSandboxEngine:
|
||||
"""TODO."""
|
||||
return Docker(socket=self.socket)
|
||||
@@ -1,13 +0,0 @@
|
||||
"""Podman container engine implementation."""
|
||||
|
||||
from fuzzforge_common.sandboxes.engines.podman.cli import PodmanCLI
|
||||
from fuzzforge_common.sandboxes.engines.podman.configuration import (
|
||||
PodmanConfiguration,
|
||||
)
|
||||
from fuzzforge_common.sandboxes.engines.podman.engine import Podman
|
||||
|
||||
__all__ = [
|
||||
"Podman",
|
||||
"PodmanCLI",
|
||||
"PodmanConfiguration",
|
||||
]
|
||||
@@ -1,22 +0,0 @@
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
from fuzzforge_common.sandboxes.engines.base.configuration import AbstractFuzzForgeEngineConfiguration
|
||||
from fuzzforge_common.sandboxes.engines.enumeration import FuzzForgeSandboxEngines
|
||||
from fuzzforge_common.sandboxes.engines.podman.engine import Podman
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fuzzforge_common.sandboxes.engines.base.engine import AbstractFuzzForgeSandboxEngine
|
||||
|
||||
|
||||
class PodmanConfiguration(AbstractFuzzForgeEngineConfiguration):
|
||||
"""TODO."""
|
||||
|
||||
#: TODO.
|
||||
kind: Literal[FuzzForgeSandboxEngines.PODMAN] = FuzzForgeSandboxEngines.PODMAN
|
||||
|
||||
#: TODO.
|
||||
socket: str
|
||||
|
||||
def into_engine(self) -> AbstractFuzzForgeSandboxEngine:
|
||||
"""TODO."""
|
||||
return Podman(socket=self.socket)
|
||||
@@ -1 +0,0 @@
|
||||
pytest_plugins = ["fuzzforge_tests.fixtures"]
|
||||
@@ -1,5 +0,0 @@
|
||||
"""TODO."""
|
||||
|
||||
|
||||
class FuzzForgeMCPError(Exception):
|
||||
"""TODO."""
|
||||
@@ -1,9 +0,0 @@
|
||||
"""Common test utilities and fixtures for FuzzForge packages.
|
||||
|
||||
This package provides shared test utilities, fixtures, and helpers that can be
|
||||
reused across multiple FuzzForge packages to reduce code duplication and ensure
|
||||
consistency in testing approaches.
|
||||
|
||||
"""
|
||||
|
||||
__all__ = []
|
||||
+41
-41
@@ -8,7 +8,7 @@
|
||||
"category": "active-directory",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -21,7 +21,7 @@
|
||||
"category": "binary-analysis",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -34,7 +34,7 @@
|
||||
"category": "binary-analysis",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -47,7 +47,7 @@
|
||||
"category": "binary-analysis",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -60,7 +60,7 @@
|
||||
"category": "binary-analysis",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -73,7 +73,7 @@
|
||||
"category": "binary-analysis",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -86,7 +86,7 @@
|
||||
"category": "binary-analysis",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -99,7 +99,7 @@
|
||||
"category": "blockchain",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -112,7 +112,7 @@
|
||||
"category": "blockchain",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -125,7 +125,7 @@
|
||||
"category": "blockchain",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -138,7 +138,7 @@
|
||||
"category": "cloud-security",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -151,7 +151,7 @@
|
||||
"category": "cloud-security",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -164,7 +164,7 @@
|
||||
"category": "cloud-security",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -177,7 +177,7 @@
|
||||
"category": "code-security",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -190,7 +190,7 @@
|
||||
"category": "exploitation",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -203,7 +203,7 @@
|
||||
"category": "fuzzing",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -216,7 +216,7 @@
|
||||
"category": "fuzzing",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -229,7 +229,7 @@
|
||||
"category": "osint",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -242,7 +242,7 @@
|
||||
"category": "osint",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -255,7 +255,7 @@
|
||||
"category": "password-cracking",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -270,7 +270,7 @@
|
||||
"NET_RAW"
|
||||
],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -285,7 +285,7 @@
|
||||
"NET_RAW"
|
||||
],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -300,7 +300,7 @@
|
||||
"NET_RAW"
|
||||
],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -315,7 +315,7 @@
|
||||
"NET_RAW"
|
||||
],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -330,7 +330,7 @@
|
||||
"NET_RAW"
|
||||
],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -345,7 +345,7 @@
|
||||
"NET_RAW"
|
||||
],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -360,7 +360,7 @@
|
||||
"NET_RAW"
|
||||
],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -375,7 +375,7 @@
|
||||
"NET_RAW"
|
||||
],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -388,7 +388,7 @@
|
||||
"category": "secrets",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -401,7 +401,7 @@
|
||||
"category": "threat-intel",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -414,7 +414,7 @@
|
||||
"category": "threat-intel",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -429,7 +429,7 @@
|
||||
"NET_RAW"
|
||||
],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -444,7 +444,7 @@
|
||||
"NET_RAW"
|
||||
],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -459,7 +459,7 @@
|
||||
"NET_RAW"
|
||||
],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -474,7 +474,7 @@
|
||||
"NET_RAW"
|
||||
],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -489,7 +489,7 @@
|
||||
"NET_RAW"
|
||||
],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -504,7 +504,7 @@
|
||||
"NET_RAW"
|
||||
],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -517,7 +517,7 @@
|
||||
"category": "code-security",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -530,7 +530,7 @@
|
||||
"category": "code-security",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -543,7 +543,7 @@
|
||||
"category": "code-security",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
@@ -556,7 +556,7 @@
|
||||
"category": "code-security",
|
||||
"capabilities": [],
|
||||
"volumes": [
|
||||
"/home/afredefon/.fuzzforge/hub/workspace:/data"
|
||||
"/home/afredefon/.secpipe/hub/workspace:/data"
|
||||
],
|
||||
"enabled": true,
|
||||
"source_hub": "mcp-security-hub"
|
||||
|
||||
+16
-16
@@ -1,16 +1,16 @@
|
||||
[project]
|
||||
name = "fuzzforge-oss"
|
||||
name = "secpipe-oss"
|
||||
version = "0.8.0"
|
||||
description = "FuzzForge AI - AI-driven security research platform for local execution"
|
||||
description = "SecPipe AI - AI-driven security research platform for local execution"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.14"
|
||||
authors = [
|
||||
{ name = "FuzzingLabs", email = "contact@fuzzinglabs.com" }
|
||||
]
|
||||
dependencies = [
|
||||
"fuzzforge-cli",
|
||||
"fuzzforge-mcp",
|
||||
"fuzzforge-common",
|
||||
"secpipe-cli",
|
||||
"secpipe-mcp",
|
||||
"secpipe-common",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
@@ -18,21 +18,21 @@ dev = [
|
||||
"pytest==9.0.2",
|
||||
"pytest-asyncio==1.3.0",
|
||||
"pytest-httpx==0.36.0",
|
||||
"fuzzforge-tests",
|
||||
"fuzzforge-common",
|
||||
"fuzzforge-mcp",
|
||||
"secpipe-tests",
|
||||
"secpipe-common",
|
||||
"secpipe-mcp",
|
||||
]
|
||||
|
||||
[tool.uv.workspace]
|
||||
members = [
|
||||
"fuzzforge-common",
|
||||
"fuzzforge-mcp",
|
||||
"fuzzforge-cli",
|
||||
"fuzzforge-tests",
|
||||
"secpipe-common",
|
||||
"secpipe-mcp",
|
||||
"secpipe-cli",
|
||||
"secpipe-tests",
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
fuzzforge-common = { workspace = true }
|
||||
fuzzforge-mcp = { workspace = true }
|
||||
fuzzforge-cli = { workspace = true }
|
||||
fuzzforge-tests = { workspace = true }
|
||||
secpipe-common = { workspace = true }
|
||||
secpipe-mcp = { workspace = true }
|
||||
secpipe-cli = { workspace = true }
|
||||
secpipe-tests = { workspace = true }
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[project]
|
||||
name = "fuzzforge-cli"
|
||||
name = "secpipe-cli"
|
||||
version = "0.0.1"
|
||||
description = "FuzzForge CLI - Command-line interface for FuzzForge AI."
|
||||
description = "SecPipe CLI - Command-line interface for SecPipe AI."
|
||||
authors = []
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.14"
|
||||
dependencies = [
|
||||
"fuzzforge-mcp==0.0.1",
|
||||
"secpipe-mcp==0.0.1",
|
||||
"rich>=14.0.0",
|
||||
"textual>=1.0.0",
|
||||
"typer==0.20.1",
|
||||
@@ -23,7 +23,7 @@ tests = [
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
fuzzforge = "fuzzforge_cli.__main__:main"
|
||||
secpipe = "secpipe_cli.__main__:main"
|
||||
|
||||
[tool.uv.sources]
|
||||
fuzzforge-mcp = { workspace = true }
|
||||
secpipe-mcp = { workspace = true }
|
||||
@@ -13,7 +13,7 @@ ignore = [
|
||||
"PLR2004", # allowing comparisons using unamed numerical constants in tests
|
||||
"S101", # allowing 'assert' statements in tests
|
||||
]
|
||||
"src/fuzzforge_cli/tui/**" = [
|
||||
"src/secpipe_cli/tui/**" = [
|
||||
"ARG002", # unused method argument: callback signature
|
||||
"BLE001", # blind exception: broad error handling in UI
|
||||
"C901", # complexity: UI logic
|
||||
@@ -39,7 +39,7 @@ ignore = [
|
||||
"TC002", # TYPE_CHECKING: runtime type needs
|
||||
"TC003", # TYPE_CHECKING: runtime type needs
|
||||
]
|
||||
"src/fuzzforge_cli/commands/mcp.py" = [
|
||||
"src/secpipe_cli/commands/mcp.py" = [
|
||||
"ARG001", # unused argument: callback signature
|
||||
"B904", # raise from: existing pattern
|
||||
"F841", # unused variable: legacy code
|
||||
@@ -48,14 +48,14 @@ ignore = [
|
||||
"PLR0915", # too many statements
|
||||
"SIM108", # ternary: readability preference
|
||||
]
|
||||
"src/fuzzforge_cli/application.py" = [
|
||||
"src/secpipe_cli/application.py" = [
|
||||
"B008", # function call in default: Path.cwd()
|
||||
"PLC0415", # import outside top-level: lazy loading
|
||||
]
|
||||
"src/fuzzforge_cli/commands/projects.py" = [
|
||||
"src/secpipe_cli/commands/projects.py" = [
|
||||
"TC003", # TYPE_CHECKING: runtime type needs
|
||||
]
|
||||
"src/fuzzforge_cli/context.py" = [
|
||||
"src/secpipe_cli/context.py" = [
|
||||
"TC002", # TYPE_CHECKING: runtime type needs
|
||||
"TC003", # TYPE_CHECKING: runtime type needs
|
||||
]
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
"""TODO."""
|
||||
|
||||
from fuzzforge_cli.application import application
|
||||
from secpipe_cli.application import application
|
||||
|
||||
|
||||
def main() -> None:
|
||||
+15
-15
@@ -1,18 +1,18 @@
|
||||
"""FuzzForge CLI application."""
|
||||
"""SecPipe CLI application."""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Annotated
|
||||
|
||||
from fuzzforge_mcp.storage import LocalStorage # type: ignore[import-untyped]
|
||||
from secpipe_mcp.storage import LocalStorage # type: ignore[import-untyped]
|
||||
from typer import Context as TyperContext
|
||||
from typer import Option, Typer
|
||||
|
||||
from fuzzforge_cli.commands import mcp, projects
|
||||
from fuzzforge_cli.context import Context
|
||||
from secpipe_cli.commands import mcp, projects
|
||||
from secpipe_cli.context import Context
|
||||
|
||||
application: Typer = Typer(
|
||||
name="fuzzforge",
|
||||
help="FuzzForge AI - Security research orchestration platform.",
|
||||
name="secpipe",
|
||||
help="SecPipe AI - Security research orchestration platform.",
|
||||
)
|
||||
|
||||
|
||||
@@ -23,21 +23,21 @@ def main(
|
||||
Option(
|
||||
"--project",
|
||||
"-p",
|
||||
envvar="FUZZFORGE_PROJECT__DEFAULT_PATH",
|
||||
help="Path to the FuzzForge project directory.",
|
||||
envvar="SECPIPE_PROJECT__DEFAULT_PATH",
|
||||
help="Path to the SecPipe project directory.",
|
||||
),
|
||||
] = Path.cwd(),
|
||||
storage_path: Annotated[
|
||||
Path,
|
||||
Option(
|
||||
"--storage",
|
||||
envvar="FUZZFORGE_STORAGE__PATH",
|
||||
envvar="SECPIPE_STORAGE__PATH",
|
||||
help="Path to the storage directory.",
|
||||
),
|
||||
] = Path.cwd() / ".fuzzforge" / "storage",
|
||||
] = Path.cwd() / ".secpipe" / "storage",
|
||||
context: TyperContext = None, # type: ignore[assignment]
|
||||
) -> None:
|
||||
"""FuzzForge AI - Security research orchestration platform.
|
||||
"""SecPipe AI - Security research orchestration platform.
|
||||
|
||||
Discover and execute MCP hub tools for security research.
|
||||
|
||||
@@ -56,15 +56,15 @@ application.add_typer(projects.application)
|
||||
|
||||
@application.command(
|
||||
name="ui",
|
||||
help="Launch the FuzzForge terminal interface.",
|
||||
help="Launch the SecPipe terminal interface.",
|
||||
)
|
||||
def launch_ui() -> None:
|
||||
"""Launch the interactive FuzzForge TUI dashboard.
|
||||
"""Launch the interactive SecPipe TUI dashboard.
|
||||
|
||||
Provides a visual dashboard showing AI agent connection status
|
||||
and hub server availability, with wizards for setup and configuration.
|
||||
|
||||
"""
|
||||
from fuzzforge_cli.tui.app import FuzzForgeApp
|
||||
from secpipe_cli.tui.app import SecPipeApp
|
||||
|
||||
FuzzForgeApp().run()
|
||||
SecPipeApp().run()
|
||||
+60
-60
@@ -1,4 +1,4 @@
|
||||
"""MCP server configuration commands for FuzzForge CLI.
|
||||
"""MCP server configuration commands for SecPipe CLI.
|
||||
|
||||
This module provides commands for setting up MCP server connections
|
||||
with various AI agents (VS Code Copilot, Claude Code, etc.).
|
||||
@@ -126,74 +126,74 @@ def _detect_docker_socket() -> str:
|
||||
return "/var/run/docker.sock"
|
||||
|
||||
|
||||
def _find_fuzzforge_root() -> Path:
|
||||
"""Find the FuzzForge installation root.
|
||||
def _find_secpipe_root() -> Path:
|
||||
"""Find the SecPipe installation root.
|
||||
|
||||
:returns: Path to fuzzforge-oss directory.
|
||||
:returns: Path to secpipe-oss directory.
|
||||
|
||||
"""
|
||||
# Check environment variable override first
|
||||
env_root = os.environ.get("FUZZFORGE_ROOT")
|
||||
env_root = os.environ.get("SECPIPE_ROOT")
|
||||
if env_root:
|
||||
return Path(env_root).resolve()
|
||||
|
||||
# Walk up from cwd to find a fuzzforge root (hub-config.json is the marker)
|
||||
# Walk up from cwd to find a secpipe root (hub-config.json is the marker)
|
||||
for parent in [Path.cwd(), *Path.cwd().parents]:
|
||||
if (parent / "hub-config.json").is_file():
|
||||
return parent
|
||||
|
||||
# Fall back to __file__-based search (dev install inside fuzzforge-oss)
|
||||
# Fall back to __file__-based search (dev install inside secpipe-oss)
|
||||
current = Path(__file__).resolve()
|
||||
for parent in current.parents:
|
||||
if (parent / "fuzzforge-mcp").is_dir():
|
||||
if (parent / "secpipe-mcp").is_dir():
|
||||
return parent
|
||||
|
||||
return Path.cwd()
|
||||
|
||||
|
||||
def _generate_mcp_config(
|
||||
fuzzforge_root: Path,
|
||||
secpipe_root: Path,
|
||||
engine_type: str,
|
||||
engine_socket: str,
|
||||
) -> dict[str, Any]:
|
||||
"""Generate MCP server configuration.
|
||||
|
||||
:param fuzzforge_root: Path to fuzzforge-oss installation.
|
||||
:param secpipe_root: Path to secpipe-oss installation.
|
||||
:param engine_type: Container engine type (podman or docker).
|
||||
:param engine_socket: Container engine socket path.
|
||||
:returns: MCP configuration dictionary.
|
||||
|
||||
"""
|
||||
venv_python = fuzzforge_root / ".venv" / "bin" / "python"
|
||||
venv_python = secpipe_root / ".venv" / "bin" / "python"
|
||||
|
||||
# Use uv run if no venv, otherwise use venv python directly
|
||||
if venv_python.exists():
|
||||
command = str(venv_python)
|
||||
args = ["-m", "fuzzforge_mcp"]
|
||||
args = ["-m", "secpipe_mcp"]
|
||||
else:
|
||||
command = "uv"
|
||||
args = ["--directory", str(fuzzforge_root), "run", "fuzzforge-mcp"]
|
||||
args = ["--directory", str(secpipe_root), "run", "secpipe-mcp"]
|
||||
|
||||
# User-global storage paths for FuzzForge containers.
|
||||
# Kept under ~/.fuzzforge so images are built once and shared across
|
||||
# all workspaces — regardless of where `fuzzforge mcp install` is run.
|
||||
# Override with FUZZFORGE_USER_DIR for isolated testing.
|
||||
user_dir_env = os.environ.get("FUZZFORGE_USER_DIR")
|
||||
fuzzforge_home = Path(user_dir_env).resolve() if user_dir_env else Path.home() / ".fuzzforge"
|
||||
graphroot = fuzzforge_home / "containers" / "storage"
|
||||
runroot = fuzzforge_home / "containers" / "run"
|
||||
# User-global storage paths for SecPipe containers.
|
||||
# Kept under ~/.secpipe so images are built once and shared across
|
||||
# all workspaces — regardless of where `secpipe mcp install` is run.
|
||||
# Override with SECPIPE_USER_DIR for isolated testing.
|
||||
user_dir_env = os.environ.get("SECPIPE_USER_DIR")
|
||||
secpipe_home = Path(user_dir_env).resolve() if user_dir_env else Path.home() / ".secpipe"
|
||||
graphroot = secpipe_home / "containers" / "storage"
|
||||
runroot = secpipe_home / "containers" / "run"
|
||||
|
||||
return {
|
||||
"type": "stdio",
|
||||
"command": command,
|
||||
"args": args,
|
||||
"cwd": str(fuzzforge_root),
|
||||
"cwd": str(secpipe_root),
|
||||
"env": {
|
||||
"FUZZFORGE_ENGINE__TYPE": engine_type,
|
||||
"FUZZFORGE_ENGINE__GRAPHROOT": str(graphroot),
|
||||
"FUZZFORGE_ENGINE__RUNROOT": str(runroot),
|
||||
"FUZZFORGE_HUB__ENABLED": "true",
|
||||
"FUZZFORGE_HUB__CONFIG_PATH": str(fuzzforge_root / "hub-config.json"),
|
||||
"SECPIPE_ENGINE__TYPE": engine_type,
|
||||
"SECPIPE_ENGINE__GRAPHROOT": str(graphroot),
|
||||
"SECPIPE_ENGINE__RUNROOT": str(runroot),
|
||||
"SECPIPE_HUB__ENABLED": "true",
|
||||
"SECPIPE_HUB__CONFIG_PATH": str(secpipe_root / "hub-config.json"),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -214,9 +214,9 @@ def status(context: Context) -> None:
|
||||
table.add_column("Agent", style="cyan")
|
||||
table.add_column("Config Path")
|
||||
table.add_column("Status")
|
||||
table.add_column("FuzzForge Configured")
|
||||
table.add_column("SecPipe Configured")
|
||||
|
||||
fuzzforge_root = _find_fuzzforge_root()
|
||||
secpipe_root = _find_secpipe_root()
|
||||
|
||||
agents = [
|
||||
("GitHub Copilot", _get_copilot_mcp_path(), "servers"),
|
||||
@@ -229,12 +229,12 @@ def status(context: Context) -> None:
|
||||
try:
|
||||
config = json.loads(config_path.read_text())
|
||||
servers = config.get(servers_key, {})
|
||||
has_fuzzforge = "fuzzforge" in servers
|
||||
has_secpipe = "secpipe" in servers
|
||||
table.add_row(
|
||||
name,
|
||||
str(config_path),
|
||||
"[green]✓ Exists[/green]",
|
||||
"[green]✓ Yes[/green]" if has_fuzzforge else "[yellow]✗ No[/yellow]",
|
||||
"[green]✓ Yes[/green]" if has_secpipe else "[yellow]✗ No[/yellow]",
|
||||
)
|
||||
except json.JSONDecodeError:
|
||||
table.add_row(
|
||||
@@ -256,7 +256,7 @@ def status(context: Context) -> None:
|
||||
# Show detected environment
|
||||
console.print()
|
||||
console.print("[bold]Detected Environment:[/bold]")
|
||||
console.print(f" FuzzForge Root: {_find_fuzzforge_root()}")
|
||||
console.print(f" SecPipe Root: {_find_secpipe_root()}")
|
||||
console.print(f" Podman Socket: {_detect_podman_socket()}")
|
||||
console.print(f" Docker Socket: {_detect_docker_socket()}")
|
||||
|
||||
@@ -290,7 +290,7 @@ def generate(
|
||||
|
||||
"""
|
||||
console = Console()
|
||||
fuzzforge_root = _find_fuzzforge_root()
|
||||
secpipe_root = _find_secpipe_root()
|
||||
|
||||
# Detect socket
|
||||
if engine == "podman":
|
||||
@@ -300,16 +300,16 @@ def generate(
|
||||
|
||||
# Generate config
|
||||
server_config = _generate_mcp_config(
|
||||
fuzzforge_root=fuzzforge_root,
|
||||
secpipe_root=secpipe_root,
|
||||
engine_type=engine,
|
||||
engine_socket=socket,
|
||||
)
|
||||
|
||||
# Format based on agent
|
||||
if agent == AIAgent.COPILOT:
|
||||
full_config = {"servers": {"fuzzforge": server_config}}
|
||||
full_config = {"servers": {"secpipe": server_config}}
|
||||
else: # Claude Desktop or Claude Code
|
||||
full_config = {"mcpServers": {"fuzzforge": server_config}}
|
||||
full_config = {"mcpServers": {"secpipe": server_config}}
|
||||
|
||||
config_json = json.dumps(full_config, indent=4)
|
||||
|
||||
@@ -322,14 +322,14 @@ def generate(
|
||||
if agent == AIAgent.COPILOT:
|
||||
config_path = _get_copilot_mcp_path()
|
||||
elif agent == AIAgent.CLAUDE_CODE:
|
||||
config_path = _get_claude_code_mcp_path(fuzzforge_root)
|
||||
config_path = _get_claude_code_mcp_path(secpipe_root)
|
||||
else: # Claude Desktop
|
||||
config_path = _get_claude_desktop_mcp_path()
|
||||
|
||||
console.print()
|
||||
console.print(f"[bold]Save to:[/bold] {config_path}")
|
||||
console.print()
|
||||
console.print("[dim]Or run 'fuzzforge mcp install' to install automatically.[/dim]")
|
||||
console.print("[dim]Or run 'secpipe mcp install' to install automatically.[/dim]")
|
||||
|
||||
|
||||
@application.command(
|
||||
@@ -357,14 +357,14 @@ def install(
|
||||
Option(
|
||||
"--force",
|
||||
"-f",
|
||||
help="Overwrite existing fuzzforge configuration.",
|
||||
help="Overwrite existing secpipe configuration.",
|
||||
),
|
||||
] = False,
|
||||
) -> None:
|
||||
"""Install MCP configuration for the specified AI agent.
|
||||
|
||||
This will create or update the MCP configuration file, adding the
|
||||
fuzzforge server configuration.
|
||||
secpipe server configuration.
|
||||
|
||||
:param context: Typer context.
|
||||
:param agent: Target AI agent.
|
||||
@@ -373,7 +373,7 @@ def install(
|
||||
|
||||
"""
|
||||
console = Console()
|
||||
fuzzforge_root = _find_fuzzforge_root()
|
||||
secpipe_root = _find_secpipe_root()
|
||||
|
||||
# Determine config path
|
||||
if agent == AIAgent.COPILOT:
|
||||
@@ -394,7 +394,7 @@ def install(
|
||||
|
||||
# Generate server config
|
||||
server_config = _generate_mcp_config(
|
||||
fuzzforge_root=fuzzforge_root,
|
||||
secpipe_root=secpipe_root,
|
||||
engine_type=engine,
|
||||
engine_socket=socket,
|
||||
)
|
||||
@@ -408,47 +408,47 @@ def install(
|
||||
console.print("[dim]Please fix the file manually or delete it.[/dim]")
|
||||
raise SystemExit(1)
|
||||
|
||||
# Check if fuzzforge already exists
|
||||
# Check if secpipe already exists
|
||||
servers = existing_config.get(servers_key, {})
|
||||
if "fuzzforge" in servers and not force:
|
||||
console.print("[yellow]FuzzForge is already configured.[/yellow]")
|
||||
if "secpipe" in servers and not force:
|
||||
console.print("[yellow]SecPipe is already configured.[/yellow]")
|
||||
console.print("[dim]Use --force to overwrite existing configuration.[/dim]")
|
||||
raise SystemExit(1)
|
||||
|
||||
# Add/update fuzzforge
|
||||
# Add/update secpipe
|
||||
if servers_key not in existing_config:
|
||||
existing_config[servers_key] = {}
|
||||
existing_config[servers_key]["fuzzforge"] = server_config
|
||||
existing_config[servers_key]["secpipe"] = server_config
|
||||
|
||||
full_config = existing_config
|
||||
else:
|
||||
# Create new config
|
||||
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
full_config = {servers_key: {"fuzzforge": server_config}}
|
||||
full_config = {servers_key: {"secpipe": server_config}}
|
||||
|
||||
# Write config
|
||||
config_path.write_text(json.dumps(full_config, indent=4))
|
||||
|
||||
console.print(f"[green]✓ Installed FuzzForge MCP configuration for {agent.value}[/green]")
|
||||
console.print(f"[green]✓ Installed SecPipe MCP configuration for {agent.value}[/green]")
|
||||
console.print()
|
||||
console.print(f"[bold]Configuration file:[/bold] {config_path}")
|
||||
console.print()
|
||||
console.print("[bold]Settings:[/bold]")
|
||||
console.print(f" Engine: {engine}")
|
||||
console.print(f" Socket: {socket}")
|
||||
console.print(f" Hub Config: {fuzzforge_root / 'hub-config.json'}")
|
||||
console.print(f" Hub Config: {secpipe_root / 'hub-config.json'}")
|
||||
console.print()
|
||||
|
||||
console.print("[bold]Next steps:[/bold]")
|
||||
if agent == AIAgent.COPILOT:
|
||||
console.print(" 1. Restart VS Code")
|
||||
console.print(" 2. Open Copilot Chat and look for FuzzForge tools")
|
||||
console.print(" 2. Open Copilot Chat and look for SecPipe tools")
|
||||
elif agent == AIAgent.CLAUDE_CODE:
|
||||
console.print(" 1. Run 'claude' from any directory")
|
||||
console.print(" 2. FuzzForge tools will be available")
|
||||
console.print(" 2. SecPipe tools will be available")
|
||||
else: # Claude Desktop
|
||||
console.print(" 1. Restart Claude Desktop")
|
||||
console.print(" 2. The fuzzforge MCP server will be available")
|
||||
console.print(" 2. The secpipe MCP server will be available")
|
||||
|
||||
|
||||
@application.command(
|
||||
@@ -464,14 +464,14 @@ def uninstall(
|
||||
),
|
||||
],
|
||||
) -> None:
|
||||
"""Remove FuzzForge MCP configuration from the specified AI agent.
|
||||
"""Remove SecPipe MCP configuration from the specified AI agent.
|
||||
|
||||
:param context: Typer context.
|
||||
:param agent: Target AI agent.
|
||||
|
||||
"""
|
||||
console = Console()
|
||||
fuzzforge_root = _find_fuzzforge_root()
|
||||
secpipe_root = _find_secpipe_root()
|
||||
|
||||
# Determine config path
|
||||
if agent == AIAgent.COPILOT:
|
||||
@@ -495,16 +495,16 @@ def uninstall(
|
||||
raise SystemExit(1)
|
||||
|
||||
servers = config.get(servers_key, {})
|
||||
if "fuzzforge" not in servers:
|
||||
console.print("[yellow]FuzzForge is not configured.[/yellow]")
|
||||
if "secpipe" not in servers:
|
||||
console.print("[yellow]SecPipe is not configured.[/yellow]")
|
||||
return
|
||||
|
||||
# Remove fuzzforge
|
||||
del servers["fuzzforge"]
|
||||
# Remove secpipe
|
||||
del servers["secpipe"]
|
||||
|
||||
# Write back
|
||||
config_path.write_text(json.dumps(config, indent=4))
|
||||
|
||||
console.print(f"[green]✓ Removed FuzzForge MCP configuration from {agent.value}[/green]")
|
||||
console.print(f"[green]✓ Removed SecPipe MCP configuration from {agent.value}[/green]")
|
||||
console.print()
|
||||
console.print("[dim]Restart your AI agent for changes to take effect.[/dim]")
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
"""Project management commands for FuzzForge CLI."""
|
||||
"""Project management commands for SecPipe CLI."""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Annotated
|
||||
@@ -7,7 +7,7 @@ from rich.console import Console
|
||||
from rich.table import Table
|
||||
from typer import Argument, Context, Option, Typer
|
||||
|
||||
from fuzzforge_cli.context import get_project_path, get_storage
|
||||
from secpipe_cli.context import get_project_path, get_storage
|
||||
|
||||
application: Typer = Typer(
|
||||
name="project",
|
||||
@@ -16,7 +16,7 @@ application: Typer = Typer(
|
||||
|
||||
|
||||
@application.command(
|
||||
help="Initialize a new FuzzForge project.",
|
||||
help="Initialize a new SecPipe project.",
|
||||
name="init",
|
||||
)
|
||||
def init_project(
|
||||
@@ -28,7 +28,7 @@ def init_project(
|
||||
),
|
||||
] = None,
|
||||
) -> None:
|
||||
"""Initialize a new FuzzForge project.
|
||||
"""Initialize a new SecPipe project.
|
||||
|
||||
Creates the necessary storage directories for the project.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"""FuzzForge CLI context management."""
|
||||
"""SecPipe CLI context management."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
from fuzzforge_mcp.storage import LocalStorage # type: ignore[import-untyped]
|
||||
from secpipe_mcp.storage import LocalStorage # type: ignore[import-untyped]
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typer import Context as TyperContext
|
||||
@@ -20,7 +20,7 @@ class Context:
|
||||
def __init__(self, storage: LocalStorage, project_path: Path) -> None:
|
||||
"""Initialize an instance of the class.
|
||||
|
||||
:param storage: FuzzForge local storage instance.
|
||||
:param storage: SecPipe local storage instance.
|
||||
:param project_path: Path to the current project.
|
||||
|
||||
"""
|
||||
@@ -0,0 +1 @@
|
||||
"""SecPipe terminal user interface."""
|
||||
+18
-18
@@ -1,6 +1,6 @@
|
||||
"""FuzzForge TUI application.
|
||||
"""SecPipe TUI application.
|
||||
|
||||
Main terminal user interface for FuzzForge, providing a dashboard
|
||||
Main terminal user interface for SecPipe, providing a dashboard
|
||||
with AI agent connection status, hub server availability, and
|
||||
hub management capabilities.
|
||||
|
||||
@@ -20,16 +20,16 @@ from textual.containers import Horizontal, Vertical, VerticalScroll
|
||||
from textual.message import Message
|
||||
from textual.widgets import Button, DataTable, Footer, Header
|
||||
|
||||
from fuzzforge_cli.tui.helpers import (
|
||||
from secpipe_cli.tui.helpers import (
|
||||
check_agent_status,
|
||||
check_hub_image,
|
||||
find_fuzzforge_root,
|
||||
find_secpipe_root,
|
||||
get_agent_configs,
|
||||
load_hub_config,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fuzzforge_cli.commands.mcp import AIAgent
|
||||
from secpipe_cli.commands.mcp import AIAgent
|
||||
|
||||
# Agent config entries stored alongside their linked status for row mapping
|
||||
_AgentRow = tuple[str, "AIAgent", Path, str, bool]
|
||||
@@ -70,10 +70,10 @@ class SingleClickDataTable(DataTable[Any]):
|
||||
self.post_message(SingleClickDataTable.RowClicked(self, row_index))
|
||||
|
||||
|
||||
class FuzzForgeApp(App[None]):
|
||||
"""FuzzForge AI terminal user interface."""
|
||||
class SecPipeApp(App[None]):
|
||||
"""SecPipe AI terminal user interface."""
|
||||
|
||||
TITLE = "FuzzForge AI"
|
||||
TITLE = "SecPipe AI"
|
||||
SUB_TITLE = "Security Research Orchestration"
|
||||
|
||||
CSS = """
|
||||
@@ -300,8 +300,8 @@ class FuzzForgeApp(App[None]):
|
||||
table.cursor_type = "row"
|
||||
|
||||
try:
|
||||
fuzzforge_root = find_fuzzforge_root()
|
||||
hub_config = load_hub_config(fuzzforge_root)
|
||||
secpipe_root = find_secpipe_root()
|
||||
hub_config = load_hub_config(secpipe_root)
|
||||
except Exception:
|
||||
table.add_row(
|
||||
Text("Error loading config", style="red"), "", "", ""
|
||||
@@ -400,14 +400,14 @@ class FuzzForgeApp(App[None]):
|
||||
display_name, agent, _config_path, _servers_key, is_linked = self._agent_rows[idx]
|
||||
|
||||
if is_linked:
|
||||
from fuzzforge_cli.tui.screens.agent_setup import AgentUnlinkScreen
|
||||
from secpipe_cli.tui.screens.agent_setup import AgentUnlinkScreen
|
||||
|
||||
self.push_screen(
|
||||
AgentUnlinkScreen(agent, display_name),
|
||||
callback=self._on_agent_changed,
|
||||
)
|
||||
else:
|
||||
from fuzzforge_cli.tui.screens.agent_setup import AgentSetupScreen
|
||||
from secpipe_cli.tui.screens.agent_setup import AgentSetupScreen
|
||||
|
||||
self.push_screen(
|
||||
AgentSetupScreen(agent, display_name),
|
||||
@@ -430,7 +430,7 @@ class FuzzForgeApp(App[None]):
|
||||
|
||||
# If a build is already running, open the live log viewer
|
||||
if image in self._active_builds:
|
||||
from fuzzforge_cli.tui.screens.build_log import BuildLogScreen
|
||||
from secpipe_cli.tui.screens.build_log import BuildLogScreen
|
||||
self._build_dialog_open = True
|
||||
self.push_screen(
|
||||
BuildLogScreen(image),
|
||||
@@ -443,10 +443,10 @@ class FuzzForgeApp(App[None]):
|
||||
return
|
||||
|
||||
if hub_name == "manual":
|
||||
self.notify("Manual servers must be built outside FuzzForge")
|
||||
self.notify("Manual servers must be built outside SecPipe")
|
||||
return
|
||||
|
||||
from fuzzforge_cli.tui.screens.build_image import BuildImageScreen
|
||||
from secpipe_cli.tui.screens.build_image import BuildImageScreen
|
||||
|
||||
self._build_dialog_open = True
|
||||
|
||||
@@ -473,7 +473,7 @@ class FuzzForgeApp(App[None]):
|
||||
@work(thread=True)
|
||||
def _run_build(self, server_name: str, image: str, hub_name: str) -> None:
|
||||
"""Build a Docker/Podman image in a background thread."""
|
||||
from fuzzforge_cli.tui.helpers import build_image, find_dockerfile_for_server
|
||||
from secpipe_cli.tui.helpers import build_image, find_dockerfile_for_server
|
||||
|
||||
logs = self._build_logs.setdefault(image, [])
|
||||
|
||||
@@ -528,7 +528,7 @@ class FuzzForgeApp(App[None]):
|
||||
|
||||
def action_add_fuzzinglabs_hub(self) -> None:
|
||||
"""Open the clone dialog pre-filled with the FuzzingLabs hub URL."""
|
||||
from fuzzforge_cli.tui.screens.hub_manager import CloneHubScreen
|
||||
from secpipe_cli.tui.screens.hub_manager import CloneHubScreen
|
||||
|
||||
self.push_screen(
|
||||
CloneHubScreen(
|
||||
@@ -541,7 +541,7 @@ class FuzzForgeApp(App[None]):
|
||||
|
||||
def action_manage_hubs(self) -> None:
|
||||
"""Open the hub manager."""
|
||||
from fuzzforge_cli.tui.screens.hub_manager import HubManagerScreen
|
||||
from secpipe_cli.tui.screens.hub_manager import HubManagerScreen
|
||||
|
||||
self.push_screen(HubManagerScreen(), callback=self._on_hub_changed)
|
||||
|
||||
+56
-56
@@ -1,4 +1,4 @@
|
||||
"""Shared helpers for FuzzForge TUI and CLI.
|
||||
"""Shared helpers for SecPipe TUI and CLI.
|
||||
|
||||
Provides utility functions for checking AI agent configuration status,
|
||||
hub server image availability, installing/removing MCP configurations,
|
||||
@@ -15,11 +15,11 @@ import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from fuzzforge_cli.commands.mcp import (
|
||||
from secpipe_cli.commands.mcp import (
|
||||
AIAgent,
|
||||
_detect_docker_socket,
|
||||
_detect_podman_socket,
|
||||
_find_fuzzforge_root,
|
||||
_find_secpipe_root,
|
||||
_generate_mcp_config,
|
||||
_get_claude_code_user_mcp_path,
|
||||
_get_claude_desktop_mcp_path,
|
||||
@@ -28,41 +28,41 @@ from fuzzforge_cli.commands.mcp import (
|
||||
|
||||
# --- Hub Management Constants ---
|
||||
|
||||
FUZZFORGE_DEFAULT_HUB_URL = "git@github.com:FuzzingLabs/mcp-security-hub.git"
|
||||
FUZZFORGE_DEFAULT_HUB_NAME = "mcp-security-hub"
|
||||
SECPIPE_DEFAULT_HUB_URL = "git@github.com:FuzzingLabs/mcp-security-hub.git"
|
||||
SECPIPE_DEFAULT_HUB_NAME = "mcp-security-hub"
|
||||
|
||||
|
||||
def get_fuzzforge_user_dir() -> Path:
|
||||
"""Return the user-global ``~/.fuzzforge/`` directory.
|
||||
def get_secpipe_user_dir() -> Path:
|
||||
"""Return the user-global ``~/.secpipe/`` directory.
|
||||
|
||||
Stores data that is shared across all workspaces: cloned hub
|
||||
repositories, the hub registry, container storage (graphroot/runroot),
|
||||
and the hub workspace volume.
|
||||
|
||||
Override with the ``FUZZFORGE_USER_DIR`` environment variable to
|
||||
Override with the ``SECPIPE_USER_DIR`` environment variable to
|
||||
redirect all user-global data to a custom path — useful for testing
|
||||
a fresh install without touching the real ``~/.fuzzforge/``.
|
||||
a fresh install without touching the real ``~/.secpipe/``.
|
||||
|
||||
:return: ``Path.home() / ".fuzzforge"`` or ``$FUZZFORGE_USER_DIR``
|
||||
:return: ``Path.home() / ".secpipe"`` or ``$SECPIPE_USER_DIR``
|
||||
|
||||
"""
|
||||
env_dir = os.environ.get("FUZZFORGE_USER_DIR")
|
||||
env_dir = os.environ.get("SECPIPE_USER_DIR")
|
||||
if env_dir:
|
||||
return Path(env_dir).resolve()
|
||||
return Path.home() / ".fuzzforge"
|
||||
return Path.home() / ".secpipe"
|
||||
|
||||
|
||||
def get_fuzzforge_dir() -> Path:
|
||||
"""Return the project-local ``.fuzzforge/`` directory.
|
||||
def get_secpipe_dir() -> Path:
|
||||
"""Return the project-local ``.secpipe/`` directory.
|
||||
|
||||
Stores data that is specific to the current workspace: fuzzing
|
||||
results and project artifacts. Similar to how ``.git/`` scopes
|
||||
version-control data to a single project.
|
||||
|
||||
:return: ``Path.cwd() / ".fuzzforge"``
|
||||
:return: ``Path.cwd() / ".secpipe"``
|
||||
|
||||
"""
|
||||
return Path.cwd() / ".fuzzforge"
|
||||
return Path.cwd() / ".secpipe"
|
||||
|
||||
# Categories that typically need NET_RAW capability for network access
|
||||
_NET_RAW_CATEGORIES = {"reconnaissance", "web-security"}
|
||||
@@ -101,7 +101,7 @@ def get_agent_configs() -> list[tuple[str, AIAgent, Path, str]]:
|
||||
|
||||
|
||||
def check_agent_status(config_path: Path, servers_key: str) -> tuple[bool, str]:
|
||||
"""Check whether an AI agent has FuzzForge configured.
|
||||
"""Check whether an AI agent has SecPipe configured.
|
||||
|
||||
:param config_path: Path to the agent's MCP config file.
|
||||
:param servers_key: JSON key for the servers dict (e.g. "servers" or "mcpServers").
|
||||
@@ -113,7 +113,7 @@ def check_agent_status(config_path: Path, servers_key: str) -> tuple[bool, str]:
|
||||
try:
|
||||
config = json.loads(config_path.read_text())
|
||||
servers = config.get(servers_key, {})
|
||||
if "fuzzforge" in servers:
|
||||
if "secpipe" in servers:
|
||||
return True, "Linked"
|
||||
return False, "Config exists, not linked"
|
||||
except json.JSONDecodeError:
|
||||
@@ -123,14 +123,14 @@ def check_agent_status(config_path: Path, servers_key: str) -> tuple[bool, str]:
|
||||
def check_hub_image(image: str) -> tuple[bool, str]:
|
||||
"""Check whether a container image exists locally.
|
||||
|
||||
Respects the ``FUZZFORGE_ENGINE__TYPE`` environment variable so that
|
||||
Respects the ``SECPIPE_ENGINE__TYPE`` environment variable so that
|
||||
Podman users see the correct build status instead of always "Not built".
|
||||
|
||||
:param image: Image name (e.g. "semgrep-mcp:latest").
|
||||
:return: Tuple of (is_ready, status_description).
|
||||
|
||||
"""
|
||||
engine = os.environ.get("FUZZFORGE_ENGINE__TYPE", "docker").lower()
|
||||
engine = os.environ.get("SECPIPE_ENGINE__TYPE", "docker").lower()
|
||||
cmd = "podman" if engine == "podman" else "docker"
|
||||
try:
|
||||
result = subprocess.run(
|
||||
@@ -148,14 +148,14 @@ def check_hub_image(image: str) -> tuple[bool, str]:
|
||||
return False, f"{cmd} not found"
|
||||
|
||||
|
||||
def load_hub_config(fuzzforge_root: Path) -> dict[str, Any]:
|
||||
"""Load hub-config.json from the FuzzForge root.
|
||||
def load_hub_config(secpipe_root: Path) -> dict[str, Any]:
|
||||
"""Load hub-config.json from the SecPipe root.
|
||||
|
||||
:param fuzzforge_root: Path to fuzzforge-oss directory.
|
||||
:param secpipe_root: Path to secpipe-oss directory.
|
||||
:return: Parsed hub configuration dict, empty dict on error.
|
||||
|
||||
"""
|
||||
config_path = fuzzforge_root / "hub-config.json"
|
||||
config_path = secpipe_root / "hub-config.json"
|
||||
if not config_path.exists():
|
||||
return {}
|
||||
try:
|
||||
@@ -165,17 +165,17 @@ def load_hub_config(fuzzforge_root: Path) -> dict[str, Any]:
|
||||
return {}
|
||||
|
||||
|
||||
def find_fuzzforge_root() -> Path:
|
||||
"""Find the FuzzForge installation root directory.
|
||||
def find_secpipe_root() -> Path:
|
||||
"""Find the SecPipe installation root directory.
|
||||
|
||||
:return: Path to the fuzzforge-oss directory.
|
||||
:return: Path to the secpipe-oss directory.
|
||||
|
||||
"""
|
||||
return _find_fuzzforge_root()
|
||||
return _find_secpipe_root()
|
||||
|
||||
|
||||
def install_agent_config(agent: AIAgent, engine: str, force: bool = False) -> str:
|
||||
"""Install FuzzForge MCP configuration for an AI agent.
|
||||
"""Install SecPipe MCP configuration for an AI agent.
|
||||
|
||||
:param agent: Target AI agent.
|
||||
:param engine: Container engine type ("docker" or "podman").
|
||||
@@ -183,7 +183,7 @@ def install_agent_config(agent: AIAgent, engine: str, force: bool = False) -> st
|
||||
:return: Result message string.
|
||||
|
||||
"""
|
||||
fuzzforge_root = _find_fuzzforge_root()
|
||||
secpipe_root = _find_secpipe_root()
|
||||
|
||||
if agent == AIAgent.COPILOT:
|
||||
config_path = _get_copilot_mcp_path()
|
||||
@@ -198,7 +198,7 @@ def install_agent_config(agent: AIAgent, engine: str, force: bool = False) -> st
|
||||
socket = _detect_docker_socket() if engine == "docker" else _detect_podman_socket()
|
||||
|
||||
server_config = _generate_mcp_config(
|
||||
fuzzforge_root=fuzzforge_root,
|
||||
secpipe_root=secpipe_root,
|
||||
engine_type=engine,
|
||||
engine_socket=socket,
|
||||
)
|
||||
@@ -210,23 +210,23 @@ def install_agent_config(agent: AIAgent, engine: str, force: bool = False) -> st
|
||||
return f"Error: Invalid JSON in {config_path}"
|
||||
|
||||
servers = existing.get(servers_key, {})
|
||||
if "fuzzforge" in servers and not force:
|
||||
if "secpipe" in servers and not force:
|
||||
return "Already configured (use force to overwrite)"
|
||||
|
||||
if servers_key not in existing:
|
||||
existing[servers_key] = {}
|
||||
existing[servers_key]["fuzzforge"] = server_config
|
||||
existing[servers_key]["secpipe"] = server_config
|
||||
full_config = existing
|
||||
else:
|
||||
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
full_config = {servers_key: {"fuzzforge": server_config}}
|
||||
full_config = {servers_key: {"secpipe": server_config}}
|
||||
|
||||
config_path.write_text(json.dumps(full_config, indent=4))
|
||||
return f"Installed FuzzForge for {agent.value}"
|
||||
return f"Installed SecPipe for {agent.value}"
|
||||
|
||||
|
||||
def uninstall_agent_config(agent: AIAgent) -> str:
|
||||
"""Remove FuzzForge MCP configuration from an AI agent.
|
||||
"""Remove SecPipe MCP configuration from an AI agent.
|
||||
|
||||
:param agent: Target AI agent.
|
||||
:return: Result message string.
|
||||
@@ -251,12 +251,12 @@ def uninstall_agent_config(agent: AIAgent) -> str:
|
||||
return "Error: Invalid JSON in config file"
|
||||
|
||||
servers = config.get(servers_key, {})
|
||||
if "fuzzforge" not in servers:
|
||||
return "FuzzForge is not configured for this agent"
|
||||
if "secpipe" not in servers:
|
||||
return "SecPipe is not configured for this agent"
|
||||
|
||||
del servers["fuzzforge"]
|
||||
del servers["secpipe"]
|
||||
config_path.write_text(json.dumps(config, indent=4))
|
||||
return f"Removed FuzzForge from {agent.value}"
|
||||
return f"Removed SecPipe from {agent.value}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -265,7 +265,7 @@ def uninstall_agent_config(agent: AIAgent) -> str:
|
||||
|
||||
|
||||
def get_hubs_registry_path() -> Path:
|
||||
"""Return path to the hubs registry file (``~/.fuzzforge/hubs.json``).
|
||||
"""Return path to the hubs registry file (``~/.secpipe/hubs.json``).
|
||||
|
||||
Stored in the user-global directory so the registry is shared across
|
||||
all workspaces.
|
||||
@@ -273,11 +273,11 @@ def get_hubs_registry_path() -> Path:
|
||||
:return: Path to the registry JSON file.
|
||||
|
||||
"""
|
||||
return get_fuzzforge_user_dir() / "hubs.json"
|
||||
return get_secpipe_user_dir() / "hubs.json"
|
||||
|
||||
|
||||
def get_default_hubs_dir() -> Path:
|
||||
"""Return default directory for cloned hubs (``~/.fuzzforge/hubs/``).
|
||||
"""Return default directory for cloned hubs (``~/.secpipe/hubs/``).
|
||||
|
||||
Stored in the user-global directory so hubs are cloned once and
|
||||
reused in every workspace.
|
||||
@@ -285,14 +285,14 @@ def get_default_hubs_dir() -> Path:
|
||||
:return: Path to the default hubs directory.
|
||||
|
||||
"""
|
||||
return get_fuzzforge_user_dir() / "hubs"
|
||||
return get_secpipe_user_dir() / "hubs"
|
||||
|
||||
|
||||
def _discover_hub_dirs() -> list[Path]:
|
||||
"""Scan known hub directories for cloned repos.
|
||||
|
||||
Checks both the current global location (``~/.fuzzforge/hubs/``) and the
|
||||
legacy workspace-local location (``<cwd>/.fuzzforge/hubs/``) so that hubs
|
||||
Checks both the current global location (``~/.secpipe/hubs/``) and the
|
||||
legacy workspace-local location (``<cwd>/.secpipe/hubs/``) so that hubs
|
||||
cloned before the global-dir migration are still found.
|
||||
|
||||
:return: List of hub directory paths (each is a direct child with a ``.git``
|
||||
@@ -300,7 +300,7 @@ def _discover_hub_dirs() -> list[Path]:
|
||||
|
||||
"""
|
||||
candidates: list[Path] = []
|
||||
for base in (get_fuzzforge_user_dir() / "hubs", get_fuzzforge_dir() / "hubs"):
|
||||
for base in (get_secpipe_user_dir() / "hubs", get_secpipe_dir() / "hubs"):
|
||||
if base.is_dir():
|
||||
candidates.extend(
|
||||
entry for entry in base.iterdir()
|
||||
@@ -314,8 +314,8 @@ def load_hubs_registry() -> dict[str, Any]:
|
||||
|
||||
If the registry file does not exist, auto-recovers it by scanning known hub
|
||||
directories and rebuilding entries for any discovered hubs. This handles
|
||||
the migration from the old workspace-local ``<cwd>/.fuzzforge/hubs.json``
|
||||
path to the global ``~/.fuzzforge/hubs.json`` path, as well as any case
|
||||
the migration from the old workspace-local ``<cwd>/.secpipe/hubs.json``
|
||||
path to the global ``~/.secpipe/hubs.json`` path, as well as any case
|
||||
where the registry was lost.
|
||||
|
||||
:return: Registry dict with ``hubs`` key containing a list of hub entries.
|
||||
@@ -353,7 +353,7 @@ def load_hubs_registry() -> dict[str, Any]:
|
||||
"name": name,
|
||||
"path": str(hub_dir),
|
||||
"git_url": git_url,
|
||||
"is_default": name == FUZZFORGE_DEFAULT_HUB_NAME,
|
||||
"is_default": name == SECPIPE_DEFAULT_HUB_NAME,
|
||||
})
|
||||
|
||||
registry: dict[str, Any] = {"hubs": hubs}
|
||||
@@ -414,7 +414,7 @@ def scan_hub_for_servers(hub_path: Path) -> list[dict[str, Any]]:
|
||||
"image": f"{tool_name}:latest",
|
||||
"category": category,
|
||||
"capabilities": capabilities,
|
||||
"volumes": [f"{get_fuzzforge_user_dir()}/hub/workspace:/data"],
|
||||
"volumes": [f"{get_secpipe_user_dir()}/hub/workspace:/data"],
|
||||
"enabled": True,
|
||||
}
|
||||
)
|
||||
@@ -571,8 +571,8 @@ def _merge_servers_into_hub_config(
|
||||
:return: Number of newly added servers.
|
||||
|
||||
"""
|
||||
fuzzforge_root = find_fuzzforge_root()
|
||||
config_path = fuzzforge_root / "hub-config.json"
|
||||
secpipe_root = find_secpipe_root()
|
||||
config_path = secpipe_root / "hub-config.json"
|
||||
|
||||
if config_path.exists():
|
||||
try:
|
||||
@@ -608,8 +608,8 @@ def _remove_hub_servers_from_config(hub_name: str) -> int:
|
||||
:return: Number of servers removed.
|
||||
|
||||
"""
|
||||
fuzzforge_root = find_fuzzforge_root()
|
||||
config_path = fuzzforge_root / "hub-config.json"
|
||||
secpipe_root = find_secpipe_root()
|
||||
config_path = secpipe_root / "hub-config.json"
|
||||
|
||||
if not config_path.exists():
|
||||
return 0
|
||||
@@ -675,7 +675,7 @@ def build_image(
|
||||
|
||||
"""
|
||||
if engine is None:
|
||||
engine = os.environ.get("FUZZFORGE_ENGINE__TYPE", "docker").lower()
|
||||
engine = os.environ.get("SECPIPE_ENGINE__TYPE", "docker").lower()
|
||||
engine = "podman" if engine == "podman" else "docker"
|
||||
|
||||
context_dir = str(dockerfile.parent)
|
||||
@@ -0,0 +1 @@
|
||||
"""TUI screens for SecPipe."""
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
"""Agent setup and unlink modal screens for FuzzForge TUI.
|
||||
"""Agent setup and unlink modal screens for SecPipe TUI.
|
||||
|
||||
Provides context-aware modals that receive the target agent directly
|
||||
from the dashboard row selection — no redundant agent picker needed.
|
||||
@@ -12,8 +12,8 @@ from textual.containers import Horizontal, Vertical
|
||||
from textual.screen import ModalScreen
|
||||
from textual.widgets import Button, Label, RadioButton, RadioSet
|
||||
|
||||
from fuzzforge_cli.commands.mcp import AIAgent
|
||||
from fuzzforge_cli.tui.helpers import install_agent_config, uninstall_agent_config
|
||||
from secpipe_cli.commands.mcp import AIAgent
|
||||
from secpipe_cli.tui.helpers import install_agent_config, uninstall_agent_config
|
||||
|
||||
|
||||
class AgentSetupScreen(ModalScreen[str | None]):
|
||||
@@ -76,7 +76,7 @@ class AgentUnlinkScreen(ModalScreen[str | None]):
|
||||
with Vertical(id="unlink-dialog"):
|
||||
yield Label(f"Unlink {self._display_name}?", classes="dialog-title")
|
||||
yield Label(
|
||||
f"This will remove the FuzzForge MCP configuration from {self._display_name}.",
|
||||
f"This will remove the SecPipe MCP configuration from {self._display_name}.",
|
||||
)
|
||||
|
||||
with Horizontal(classes="dialog-buttons"):
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
"""Build-image confirm dialog for FuzzForge TUI.
|
||||
"""Build-image confirm dialog for SecPipe TUI.
|
||||
|
||||
Simple modal that asks the user to confirm before starting a background
|
||||
build. The actual build is managed by the app so the user is never
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
"""Build-log viewer screen for FuzzForge TUI.
|
||||
"""Build-log viewer screen for SecPipe TUI.
|
||||
|
||||
Shows live output of a background build started by the app. Polls the
|
||||
app's ``_build_logs`` buffer every 500 ms so the user can pop this screen
|
||||
+6
-6
@@ -1,4 +1,4 @@
|
||||
"""Hub management screens for FuzzForge TUI.
|
||||
"""Hub management screens for SecPipe TUI.
|
||||
|
||||
Provides modal dialogs for managing linked MCP hub repositories:
|
||||
- HubManagerScreen: list, add, remove linked hubs
|
||||
@@ -18,9 +18,9 @@ from textual.containers import Horizontal, Vertical
|
||||
from textual.screen import ModalScreen
|
||||
from textual.widgets import Button, DataTable, Input, Label, Static
|
||||
|
||||
from fuzzforge_cli.tui.helpers import (
|
||||
FUZZFORGE_DEFAULT_HUB_NAME,
|
||||
FUZZFORGE_DEFAULT_HUB_URL,
|
||||
from secpipe_cli.tui.helpers import (
|
||||
SECPIPE_DEFAULT_HUB_NAME,
|
||||
SECPIPE_DEFAULT_HUB_URL,
|
||||
clone_hub,
|
||||
link_hub,
|
||||
load_hubs_registry,
|
||||
@@ -104,8 +104,8 @@ class HubManagerScreen(ModalScreen[str | None]):
|
||||
elif event.button.id == "btn-clone-default":
|
||||
self.app.push_screen(
|
||||
CloneHubScreen(
|
||||
FUZZFORGE_DEFAULT_HUB_URL,
|
||||
FUZZFORGE_DEFAULT_HUB_NAME,
|
||||
SECPIPE_DEFAULT_HUB_URL,
|
||||
SECPIPE_DEFAULT_HUB_NAME,
|
||||
is_default=True,
|
||||
),
|
||||
callback=self._on_hub_action,
|
||||
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "fuzzforge-common"
|
||||
name = "secpipe-common"
|
||||
version = "0.0.1"
|
||||
description = "FuzzForge's common types and utilities."
|
||||
description = "SecPipe's common types and utilities."
|
||||
authors = []
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.14"
|
||||
@@ -0,0 +1,38 @@
|
||||
"""SecPipe Common - Shared abstractions and implementations for SecPipe.
|
||||
|
||||
This package provides:
|
||||
- Sandbox engine abstractions (Podman, Docker)
|
||||
- Common exceptions
|
||||
|
||||
Example usage:
|
||||
from secpipe_common import (
|
||||
AbstractSecPipeSandboxEngine,
|
||||
ImageInfo,
|
||||
Podman,
|
||||
PodmanConfiguration,
|
||||
)
|
||||
"""
|
||||
|
||||
from secpipe_common.exceptions import SecPipeError
|
||||
from secpipe_common.sandboxes import (
|
||||
AbstractSecPipeEngineConfiguration,
|
||||
AbstractSecPipeSandboxEngine,
|
||||
Docker,
|
||||
DockerConfiguration,
|
||||
SecPipeSandboxEngines,
|
||||
ImageInfo,
|
||||
Podman,
|
||||
PodmanConfiguration,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"AbstractSecPipeEngineConfiguration",
|
||||
"AbstractSecPipeSandboxEngine",
|
||||
"Docker",
|
||||
"DockerConfiguration",
|
||||
"SecPipeError",
|
||||
"SecPipeSandboxEngines",
|
||||
"ImageInfo",
|
||||
"Podman",
|
||||
"PodmanConfiguration",
|
||||
]
|
||||
+3
-3
@@ -4,8 +4,8 @@ if TYPE_CHECKING:
|
||||
from typing import Any
|
||||
|
||||
|
||||
class FuzzForgeError(Exception):
|
||||
"""Base exception for all FuzzForge custom exceptions.
|
||||
class SecPipeError(Exception):
|
||||
"""Base exception for all SecPipe custom exceptions.
|
||||
|
||||
All domain exceptions should inherit from this base to enable
|
||||
consistent exception handling and hierarchy navigation.
|
||||
@@ -13,7 +13,7 @@ class FuzzForgeError(Exception):
|
||||
"""
|
||||
|
||||
def __init__(self, message: str, details: dict[str, Any] | None = None) -> None:
|
||||
"""Initialize FuzzForge error.
|
||||
"""Initialize SecPipe error.
|
||||
|
||||
:param message: Error message.
|
||||
:param details: Optional error details dictionary.
|
||||
+7
-7
@@ -1,8 +1,8 @@
|
||||
"""FuzzForge Hub - Generic MCP server bridge.
|
||||
"""SecPipe Hub - Generic MCP server bridge.
|
||||
|
||||
This module provides a generic bridge to connect FuzzForge with any MCP server.
|
||||
This module provides a generic bridge to connect SecPipe with any MCP server.
|
||||
It allows AI agents to discover and execute tools from external MCP servers
|
||||
(like mcp-security-hub) through the same interface as native FuzzForge modules.
|
||||
(like mcp-security-hub) through the same interface as native SecPipe modules.
|
||||
|
||||
The hub is server-agnostic: it doesn't hardcode any specific tools or servers.
|
||||
Instead, it dynamically discovers tools by connecting to configured MCP servers
|
||||
@@ -15,9 +15,9 @@ Supported transport types:
|
||||
|
||||
"""
|
||||
|
||||
from fuzzforge_common.hub.client import HubClient, HubClientError, PersistentSession
|
||||
from fuzzforge_common.hub.executor import HubExecutionResult, HubExecutor
|
||||
from fuzzforge_common.hub.models import (
|
||||
from secpipe_common.hub.client import HubClient, HubClientError, PersistentSession
|
||||
from secpipe_common.hub.executor import HubExecutionResult, HubExecutor
|
||||
from secpipe_common.hub.models import (
|
||||
HubConfig,
|
||||
HubServer,
|
||||
HubServerConfig,
|
||||
@@ -25,7 +25,7 @@ from fuzzforge_common.hub.models import (
|
||||
HubTool,
|
||||
HubToolParameter,
|
||||
)
|
||||
from fuzzforge_common.hub.registry import HubRegistry
|
||||
from secpipe_common.hub.registry import HubRegistry
|
||||
|
||||
__all__ = [
|
||||
"HubClient",
|
||||
+3
-3
@@ -21,7 +21,7 @@ from dataclasses import dataclass, field
|
||||
from datetime import datetime, timezone
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from fuzzforge_common.hub.models import (
|
||||
from secpipe_common.hub.models import (
|
||||
HubServer,
|
||||
HubServerConfig,
|
||||
HubServerType,
|
||||
@@ -437,7 +437,7 @@ class HubClient:
|
||||
"protocolVersion": "2024-11-05",
|
||||
"capabilities": {},
|
||||
"clientInfo": {
|
||||
"name": "fuzzforge-hub",
|
||||
"name": "secpipe-hub",
|
||||
"version": "0.1.0",
|
||||
},
|
||||
},
|
||||
@@ -570,7 +570,7 @@ class HubClient:
|
||||
msg = f"Docker image not specified for server '{config.name}'"
|
||||
raise HubClientError(msg)
|
||||
|
||||
container_name = f"fuzzforge-{config.name}"
|
||||
container_name = f"secpipe-{config.name}"
|
||||
|
||||
# Remove stale container with same name if it exists
|
||||
try:
|
||||
+3
-3
@@ -12,9 +12,9 @@ from __future__ import annotations
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from fuzzforge_common.hub.client import HubClient, HubClientError, PersistentSession
|
||||
from fuzzforge_common.hub.models import HubServer, HubServerConfig, HubTool
|
||||
from fuzzforge_common.hub.registry import HubRegistry
|
||||
from secpipe_common.hub.client import HubClient, HubClientError, PersistentSession
|
||||
from secpipe_common.hub.models import HubServer, HubServerConfig, HubTool
|
||||
from secpipe_common.hub.registry import HubRegistry
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from structlog.stdlib import BoundLogger
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
"""Data models for FuzzForge Hub.
|
||||
"""Data models for SecPipe Hub.
|
||||
|
||||
This module defines the Pydantic models used to represent MCP servers
|
||||
and their tools in the hub registry.
|
||||
+1
-1
@@ -12,7 +12,7 @@ import json
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
from fuzzforge_common.hub.models import (
|
||||
from secpipe_common.hub.models import (
|
||||
HubConfig,
|
||||
HubServer,
|
||||
HubServerConfig,
|
||||
@@ -0,0 +1,23 @@
|
||||
"""SecPipe sandbox abstractions and implementations."""
|
||||
|
||||
from secpipe_common.sandboxes.engines import (
|
||||
AbstractSecPipeEngineConfiguration,
|
||||
AbstractSecPipeSandboxEngine,
|
||||
Docker,
|
||||
DockerConfiguration,
|
||||
SecPipeSandboxEngines,
|
||||
ImageInfo,
|
||||
Podman,
|
||||
PodmanConfiguration,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"AbstractSecPipeEngineConfiguration",
|
||||
"AbstractSecPipeSandboxEngine",
|
||||
"Docker",
|
||||
"DockerConfiguration",
|
||||
"SecPipeSandboxEngines",
|
||||
"ImageInfo",
|
||||
"Podman",
|
||||
"PodmanConfiguration",
|
||||
]
|
||||
@@ -0,0 +1,21 @@
|
||||
"""Container engine implementations for SecPipe sandboxes."""
|
||||
|
||||
from secpipe_common.sandboxes.engines.base import (
|
||||
AbstractSecPipeEngineConfiguration,
|
||||
AbstractSecPipeSandboxEngine,
|
||||
ImageInfo,
|
||||
)
|
||||
from secpipe_common.sandboxes.engines.docker import Docker, DockerConfiguration
|
||||
from secpipe_common.sandboxes.engines.enumeration import SecPipeSandboxEngines
|
||||
from secpipe_common.sandboxes.engines.podman import Podman, PodmanConfiguration
|
||||
|
||||
__all__ = [
|
||||
"AbstractSecPipeEngineConfiguration",
|
||||
"AbstractSecPipeSandboxEngine",
|
||||
"Docker",
|
||||
"DockerConfiguration",
|
||||
"SecPipeSandboxEngines",
|
||||
"ImageInfo",
|
||||
"Podman",
|
||||
"PodmanConfiguration",
|
||||
]
|
||||
@@ -0,0 +1,15 @@
|
||||
"""Base engine abstractions."""
|
||||
|
||||
from secpipe_common.sandboxes.engines.base.configuration import (
|
||||
AbstractSecPipeEngineConfiguration,
|
||||
)
|
||||
from secpipe_common.sandboxes.engines.base.engine import (
|
||||
AbstractSecPipeSandboxEngine,
|
||||
ImageInfo,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"AbstractSecPipeEngineConfiguration",
|
||||
"AbstractSecPipeSandboxEngine",
|
||||
"ImageInfo",
|
||||
]
|
||||
+6
-6
@@ -3,22 +3,22 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fuzzforge_common.sandboxes.engines.enumeration import (
|
||||
FuzzForgeSandboxEngines,
|
||||
from secpipe_common.sandboxes.engines.enumeration import (
|
||||
SecPipeSandboxEngines,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fuzzforge_common.sandboxes.engines.base.engine import AbstractFuzzForgeSandboxEngine
|
||||
from secpipe_common.sandboxes.engines.base.engine import AbstractSecPipeSandboxEngine
|
||||
|
||||
|
||||
class AbstractFuzzForgeEngineConfiguration(ABC, BaseModel):
|
||||
class AbstractSecPipeEngineConfiguration(ABC, BaseModel):
|
||||
"""TODO."""
|
||||
|
||||
#: TODO.
|
||||
kind: FuzzForgeSandboxEngines
|
||||
kind: SecPipeSandboxEngines
|
||||
|
||||
@abstractmethod
|
||||
def into_engine(self) -> AbstractFuzzForgeSandboxEngine:
|
||||
def into_engine(self) -> AbstractSecPipeSandboxEngine:
|
||||
"""TODO."""
|
||||
message: str = f"method 'into_engine' is not implemented for class '{self.__class__.__name__}'"
|
||||
raise NotImplementedError(message)
|
||||
+6
-6
@@ -10,10 +10,10 @@ if TYPE_CHECKING:
|
||||
class ImageInfo:
|
||||
"""Information about a container image."""
|
||||
|
||||
#: Full image reference (e.g., "localhost/fuzzforge-module-echidna:latest").
|
||||
#: Full image reference (e.g., "localhost/secpipe-module-echidna:latest").
|
||||
reference: str
|
||||
|
||||
#: Repository name (e.g., "localhost/fuzzforge-module-echidna").
|
||||
#: Repository name (e.g., "localhost/secpipe-module-echidna").
|
||||
repository: str
|
||||
|
||||
#: Image tag (e.g., "latest").
|
||||
@@ -29,8 +29,8 @@ class ImageInfo:
|
||||
labels: dict[str, str] | None = None
|
||||
|
||||
|
||||
class AbstractFuzzForgeSandboxEngine(ABC):
|
||||
"""Abstract class used as a base for all FuzzForge sandbox engine classes."""
|
||||
class AbstractSecPipeSandboxEngine(ABC):
|
||||
"""Abstract class used as a base for all SecPipe sandbox engine classes."""
|
||||
|
||||
@abstractmethod
|
||||
def list_images(self, filter_prefix: str | None = None) -> list[ImageInfo]:
|
||||
@@ -140,7 +140,7 @@ class AbstractFuzzForgeSandboxEngine(ABC):
|
||||
|
||||
:param image: Full image reference to pull.
|
||||
:param timeout: Timeout in seconds for the pull operation.
|
||||
:raises FuzzForgeError: If pull fails.
|
||||
:raises SecPipeError: If pull fails.
|
||||
|
||||
"""
|
||||
message: str = f"method 'pull_image' is not implemented for class '{self.__class__.__name__}'"
|
||||
@@ -306,7 +306,7 @@ class AbstractFuzzForgeSandboxEngine(ABC):
|
||||
|
||||
Creates a temporary container, copies the file, and removes the container.
|
||||
|
||||
:param image: Image reference (e.g., "fuzzforge-rust-analyzer:latest").
|
||||
:param image: Image reference (e.g., "secpipe-rust-analyzer:latest").
|
||||
:param path: Path to file inside image.
|
||||
:returns: File contents as string.
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
"""Docker container engine implementation."""
|
||||
|
||||
from secpipe_common.sandboxes.engines.docker.cli import DockerCLI
|
||||
from secpipe_common.sandboxes.engines.docker.configuration import (
|
||||
DockerConfiguration,
|
||||
)
|
||||
from secpipe_common.sandboxes.engines.docker.engine import Docker
|
||||
|
||||
__all__ = [
|
||||
"Docker",
|
||||
"DockerCLI",
|
||||
"DockerConfiguration",
|
||||
]
|
||||
+6
-6
@@ -13,8 +13,8 @@ from pathlib import Path, PurePath
|
||||
from tempfile import NamedTemporaryFile
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
from fuzzforge_common.exceptions import FuzzForgeError
|
||||
from fuzzforge_common.sandboxes.engines.base.engine import AbstractFuzzForgeSandboxEngine, ImageInfo
|
||||
from secpipe_common.exceptions import SecPipeError
|
||||
from secpipe_common.sandboxes.engines.base.engine import AbstractSecPipeSandboxEngine, ImageInfo
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from structlog.stdlib import BoundLogger
|
||||
@@ -27,7 +27,7 @@ def get_logger() -> BoundLogger:
|
||||
return cast("BoundLogger", get_logger())
|
||||
|
||||
|
||||
class DockerCLI(AbstractFuzzForgeSandboxEngine):
|
||||
class DockerCLI(AbstractSecPipeSandboxEngine):
|
||||
"""Docker engine using CLI commands.
|
||||
|
||||
This implementation uses subprocess calls to the Docker CLI,
|
||||
@@ -37,7 +37,7 @@ class DockerCLI(AbstractFuzzForgeSandboxEngine):
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the DockerCLI engine."""
|
||||
AbstractFuzzForgeSandboxEngine.__init__(self)
|
||||
AbstractSecPipeSandboxEngine.__init__(self)
|
||||
|
||||
def _base_cmd(self) -> list[str]:
|
||||
"""Get base Docker command.
|
||||
@@ -147,7 +147,7 @@ class DockerCLI(AbstractFuzzForgeSandboxEngine):
|
||||
get_logger().info("image pulled successfully", image=image)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
message = f"Failed to pull image '{image}': {exc.stderr}"
|
||||
raise FuzzForgeError(message) from exc
|
||||
raise SecPipeError(message) from exc
|
||||
|
||||
def tag_image(self, source: str, target: str) -> None:
|
||||
"""Tag an image with a new name.
|
||||
@@ -440,7 +440,7 @@ class DockerCLI(AbstractFuzzForgeSandboxEngine):
|
||||
|
||||
Uses docker run with --entrypoint override to read the file via cat.
|
||||
|
||||
:param image: Image reference (e.g., "fuzzforge-rust-analyzer:latest").
|
||||
:param image: Image reference (e.g., "secpipe-rust-analyzer:latest").
|
||||
:param path: Path to file inside image.
|
||||
:returns: File contents as string.
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
from secpipe_common.sandboxes.engines.base.configuration import AbstractSecPipeEngineConfiguration
|
||||
from secpipe_common.sandboxes.engines.docker.engine import Docker
|
||||
from secpipe_common.sandboxes.engines.enumeration import SecPipeSandboxEngines
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from secpipe_common.sandboxes.engines.base.engine import AbstractSecPipeSandboxEngine
|
||||
|
||||
|
||||
class DockerConfiguration(AbstractSecPipeEngineConfiguration):
|
||||
"""TODO."""
|
||||
|
||||
#: TODO.
|
||||
kind: Literal[SecPipeSandboxEngines.DOCKER] = SecPipeSandboxEngines.DOCKER
|
||||
|
||||
#: TODO.
|
||||
socket: str
|
||||
|
||||
def into_engine(self) -> AbstractSecPipeSandboxEngine:
|
||||
"""TODO."""
|
||||
return Docker(socket=self.socket)
|
||||
+2
-2
@@ -2,13 +2,13 @@ from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from fuzzforge_common.sandboxes.engines.base.engine import AbstractFuzzForgeSandboxEngine, ImageInfo
|
||||
from secpipe_common.sandboxes.engines.base.engine import AbstractSecPipeSandboxEngine, ImageInfo
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path, PurePath
|
||||
|
||||
|
||||
class Docker(AbstractFuzzForgeSandboxEngine):
|
||||
class Docker(AbstractSecPipeSandboxEngine):
|
||||
"""TODO."""
|
||||
|
||||
#: TODO.
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
from enum import StrEnum
|
||||
|
||||
|
||||
class FuzzForgeSandboxEngines(StrEnum):
|
||||
class SecPipeSandboxEngines(StrEnum):
|
||||
"""TODO."""
|
||||
|
||||
#: TODO.
|
||||
@@ -0,0 +1,13 @@
|
||||
"""Podman container engine implementation."""
|
||||
|
||||
from secpipe_common.sandboxes.engines.podman.cli import PodmanCLI
|
||||
from secpipe_common.sandboxes.engines.podman.configuration import (
|
||||
PodmanConfiguration,
|
||||
)
|
||||
from secpipe_common.sandboxes.engines.podman.engine import Podman
|
||||
|
||||
__all__ = [
|
||||
"Podman",
|
||||
"PodmanCLI",
|
||||
"PodmanConfiguration",
|
||||
]
|
||||
+8
-8
@@ -15,8 +15,8 @@ from pathlib import Path, PurePath
|
||||
from tempfile import NamedTemporaryFile
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
from fuzzforge_common.exceptions import FuzzForgeError
|
||||
from fuzzforge_common.sandboxes.engines.base.engine import AbstractFuzzForgeSandboxEngine, ImageInfo
|
||||
from secpipe_common.exceptions import SecPipeError
|
||||
from secpipe_common.sandboxes.engines.base.engine import AbstractSecPipeSandboxEngine, ImageInfo
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from structlog.stdlib import BoundLogger
|
||||
@@ -43,7 +43,7 @@ def _is_running_under_snap() -> bool:
|
||||
return os.getenv("SNAP") is not None
|
||||
|
||||
|
||||
class PodmanCLI(AbstractFuzzForgeSandboxEngine):
|
||||
class PodmanCLI(AbstractSecPipeSandboxEngine):
|
||||
"""Podman engine using CLI with custom storage paths.
|
||||
|
||||
This implementation uses subprocess calls to the Podman CLI with --root
|
||||
@@ -71,7 +71,7 @@ class PodmanCLI(AbstractFuzzForgeSandboxEngine):
|
||||
|
||||
Custom storage is used when running under Snap AND paths are provided.
|
||||
|
||||
:raises FuzzForgeError: If running on macOS (Podman not supported).
|
||||
:raises SecPipeError: If running on macOS (Podman not supported).
|
||||
"""
|
||||
import sys # noqa: PLC0415
|
||||
|
||||
@@ -81,9 +81,9 @@ class PodmanCLI(AbstractFuzzForgeSandboxEngine):
|
||||
" brew install --cask docker\n"
|
||||
" # Or download from https://docker.com/products/docker-desktop"
|
||||
)
|
||||
raise FuzzForgeError(msg)
|
||||
raise SecPipeError(msg)
|
||||
|
||||
AbstractFuzzForgeSandboxEngine.__init__(self)
|
||||
AbstractSecPipeSandboxEngine.__init__(self)
|
||||
|
||||
# Use custom storage only under Snap (to fix XDG_DATA_HOME issues)
|
||||
self.__use_custom_storage = _is_running_under_snap() and graphroot is not None and runroot is not None
|
||||
@@ -206,7 +206,7 @@ class PodmanCLI(AbstractFuzzForgeSandboxEngine):
|
||||
get_logger().info("image pulled successfully", image=image)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
message = f"Failed to pull image '{image}': {exc.stderr}"
|
||||
raise FuzzForgeError(message) from exc
|
||||
raise SecPipeError(message) from exc
|
||||
|
||||
def tag_image(self, source: str, target: str) -> None:
|
||||
"""Tag an image with a new name.
|
||||
@@ -501,7 +501,7 @@ class PodmanCLI(AbstractFuzzForgeSandboxEngine):
|
||||
|
||||
Uses podman run with --entrypoint override to read the file via cat.
|
||||
|
||||
:param image: Image reference (e.g., "fuzzforge-rust-analyzer:latest").
|
||||
:param image: Image reference (e.g., "secpipe-rust-analyzer:latest").
|
||||
:param path: Path to file inside image.
|
||||
:returns: File contents as string.
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
from secpipe_common.sandboxes.engines.base.configuration import AbstractSecPipeEngineConfiguration
|
||||
from secpipe_common.sandboxes.engines.enumeration import SecPipeSandboxEngines
|
||||
from secpipe_common.sandboxes.engines.podman.engine import Podman
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from secpipe_common.sandboxes.engines.base.engine import AbstractSecPipeSandboxEngine
|
||||
|
||||
|
||||
class PodmanConfiguration(AbstractSecPipeEngineConfiguration):
|
||||
"""TODO."""
|
||||
|
||||
#: TODO.
|
||||
kind: Literal[SecPipeSandboxEngines.PODMAN] = SecPipeSandboxEngines.PODMAN
|
||||
|
||||
#: TODO.
|
||||
socket: str
|
||||
|
||||
def into_engine(self) -> AbstractSecPipeSandboxEngine:
|
||||
"""TODO."""
|
||||
return Podman(socket=self.socket)
|
||||
+8
-8
@@ -8,8 +8,8 @@ from typing import TYPE_CHECKING, cast
|
||||
|
||||
from podman.errors import ImageNotFound
|
||||
|
||||
from fuzzforge_common.exceptions import FuzzForgeError
|
||||
from fuzzforge_common.sandboxes.engines.base.engine import AbstractFuzzForgeSandboxEngine, ImageInfo
|
||||
from secpipe_common.exceptions import SecPipeError
|
||||
from secpipe_common.sandboxes.engines.base.engine import AbstractSecPipeSandboxEngine, ImageInfo
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from podman import PodmanClient
|
||||
@@ -24,7 +24,7 @@ def get_logger() -> BoundLogger:
|
||||
return cast("BoundLogger", get_logger())
|
||||
|
||||
|
||||
class Podman(AbstractFuzzForgeSandboxEngine):
|
||||
class Podman(AbstractSecPipeSandboxEngine):
|
||||
"""TODO."""
|
||||
|
||||
#: TODO.
|
||||
@@ -36,7 +36,7 @@ class Podman(AbstractFuzzForgeSandboxEngine):
|
||||
:param socket: TODO.
|
||||
|
||||
"""
|
||||
AbstractFuzzForgeSandboxEngine.__init__(self)
|
||||
AbstractSecPipeSandboxEngine.__init__(self)
|
||||
self.__socket = socket
|
||||
|
||||
def get_client(self) -> PodmanClient:
|
||||
@@ -99,7 +99,7 @@ class Podman(AbstractFuzzForgeSandboxEngine):
|
||||
images = list(client.images.load(file_path=archive))
|
||||
if len(images) != 1:
|
||||
message: str = "expected only one image"
|
||||
raise FuzzForgeError(message)
|
||||
raise SecPipeError(message)
|
||||
image = images[0]
|
||||
image.tag(repository=repository, tag="latest")
|
||||
|
||||
@@ -254,7 +254,7 @@ class Podman(AbstractFuzzForgeSandboxEngine):
|
||||
|
||||
:param image: Full image reference to pull.
|
||||
:param timeout: Timeout in seconds for the pull operation.
|
||||
:raises FuzzForgeError: If pull fails.
|
||||
:raises SecPipeError: If pull fails.
|
||||
|
||||
"""
|
||||
client: PodmanClient = self.get_client()
|
||||
@@ -265,7 +265,7 @@ class Podman(AbstractFuzzForgeSandboxEngine):
|
||||
get_logger().info("image pulled successfully", image=image)
|
||||
except Exception as exc:
|
||||
message = f"Failed to pull image '{image}': {exc}"
|
||||
raise FuzzForgeError(message) from exc
|
||||
raise SecPipeError(message) from exc
|
||||
|
||||
def tag_image(self, source: str, target: str) -> None:
|
||||
"""Tag an image with a new name.
|
||||
@@ -524,7 +524,7 @@ class Podman(AbstractFuzzForgeSandboxEngine):
|
||||
|
||||
Creates a temporary container, reads the file, and removes the container.
|
||||
|
||||
:param image: Image reference (e.g., "fuzzforge-rust-analyzer:latest").
|
||||
:param image: Image reference (e.g., "secpipe-rust-analyzer:latest").
|
||||
:param path: Path to file inside image.
|
||||
:returns: File contents as string.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pytest_plugins = ["secpipe_tests.fixtures"]
|
||||
+1
-1
@@ -4,7 +4,7 @@ from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
from fuzzforge_common.sandboxes.engines.docker.cli import DockerCLI
|
||||
from secpipe_common.sandboxes.engines.docker.cli import DockerCLI
|
||||
|
||||
|
||||
def test_docker_cli_base_cmd() -> None:
|
||||
+3
-3
@@ -9,8 +9,8 @@ from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
from fuzzforge_common.exceptions import FuzzForgeError
|
||||
from fuzzforge_common.sandboxes.engines.podman.cli import PodmanCLI, _is_running_under_snap
|
||||
from secpipe_common.exceptions import SecPipeError
|
||||
from secpipe_common.sandboxes.engines.podman.cli import PodmanCLI, _is_running_under_snap
|
||||
|
||||
|
||||
# Helper to mock Linux platform for testing (since Podman is Linux-only)
|
||||
@@ -60,7 +60,7 @@ def test_snap_detection_when_snap_not_set() -> None:
|
||||
def test_podman_cli_blocks_macos() -> None:
|
||||
"""Test that PodmanCLI raises error on macOS."""
|
||||
with mock.patch.object(sys, "platform", "darwin"):
|
||||
with pytest.raises(FuzzForgeError) as exc_info:
|
||||
with pytest.raises(SecPipeError) as exc_info:
|
||||
PodmanCLI()
|
||||
assert "Podman is not supported on macOS" in str(exc_info.value)
|
||||
assert "Docker" in str(exc_info.value)
|
||||
@@ -8,4 +8,4 @@ WORKDIR /app
|
||||
|
||||
RUN /bin/uv venv && /bin/uv pip install --find-links /wheels $PACKAGE
|
||||
|
||||
CMD [ "/bin/uv", "run", "uvicorn", "fuzzforge_mcp.application:app"]
|
||||
CMD [ "/bin/uv", "run", "uvicorn", "secpipe_mcp.application:app"]
|
||||
@@ -21,16 +21,16 @@ Use the SecPipe CLI to automatically configure MCP for your AI agent:
|
||||
|
||||
```bash
|
||||
# For GitHub Copilot
|
||||
uv run fuzzforge mcp install copilot
|
||||
uv run secpipe mcp install copilot
|
||||
|
||||
# For Claude Code (VS Code extension)
|
||||
uv run fuzzforge mcp install claude-code
|
||||
uv run secpipe mcp install claude-code
|
||||
|
||||
# For Claude Desktop (standalone app)
|
||||
uv run fuzzforge mcp install claude-desktop
|
||||
uv run secpipe mcp install claude-desktop
|
||||
|
||||
# Verify installation
|
||||
uv run fuzzforge mcp status
|
||||
uv run secpipe mcp status
|
||||
```
|
||||
|
||||
After installation, restart your AI agent to activate the connection.
|
||||
@@ -44,13 +44,13 @@ For custom setups, you can manually configure the MCP server.
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"fuzzforge": {
|
||||
"command": "/path/to/fuzzforge_ai/.venv/bin/python",
|
||||
"args": ["-m", "fuzzforge_mcp"],
|
||||
"cwd": "/path/to/fuzzforge_ai",
|
||||
"secpipe": {
|
||||
"command": "/path/to/secpipe_ai/.venv/bin/python",
|
||||
"args": ["-m", "secpipe_mcp"],
|
||||
"cwd": "/path/to/secpipe_ai",
|
||||
"env": {
|
||||
"FUZZFORGE_MODULES_PATH": "/path/to/fuzzforge_ai/fuzzforge-modules",
|
||||
"FUZZFORGE_ENGINE__TYPE": "docker"
|
||||
"SECPIPE_MODULES_PATH": "/path/to/secpipe_ai/secpipe-modules",
|
||||
"SECPIPE_ENGINE__TYPE": "docker"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,14 +62,14 @@ For custom setups, you can manually configure the MCP server.
|
||||
```json
|
||||
{
|
||||
"servers": {
|
||||
"fuzzforge": {
|
||||
"secpipe": {
|
||||
"type": "stdio",
|
||||
"command": "/path/to/fuzzforge_ai/.venv/bin/python",
|
||||
"args": ["-m", "fuzzforge_mcp"],
|
||||
"cwd": "/path/to/fuzzforge_ai",
|
||||
"command": "/path/to/secpipe_ai/.venv/bin/python",
|
||||
"args": ["-m", "secpipe_mcp"],
|
||||
"cwd": "/path/to/secpipe_ai",
|
||||
"env": {
|
||||
"FUZZFORGE_MODULES_PATH": "/path/to/fuzzforge_ai/fuzzforge-modules",
|
||||
"FUZZFORGE_ENGINE__TYPE": "docker"
|
||||
"SECPIPE_MODULES_PATH": "/path/to/secpipe_ai/secpipe-modules",
|
||||
"SECPIPE_ENGINE__TYPE": "docker"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,14 +81,14 @@ For custom setups, you can manually configure the MCP server.
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"fuzzforge": {
|
||||
"secpipe": {
|
||||
"type": "stdio",
|
||||
"command": "/path/to/fuzzforge_ai/.venv/bin/python",
|
||||
"args": ["-m", "fuzzforge_mcp"],
|
||||
"cwd": "/path/to/fuzzforge_ai",
|
||||
"command": "/path/to/secpipe_ai/.venv/bin/python",
|
||||
"args": ["-m", "secpipe_mcp"],
|
||||
"cwd": "/path/to/secpipe_ai",
|
||||
"env": {
|
||||
"FUZZFORGE_MODULES_PATH": "/path/to/fuzzforge_ai/fuzzforge-modules",
|
||||
"FUZZFORGE_ENGINE__TYPE": "docker"
|
||||
"SECPIPE_MODULES_PATH": "/path/to/secpipe_ai/secpipe-modules",
|
||||
"SECPIPE_ENGINE__TYPE": "docker"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,10 +99,10 @@ For custom setups, you can manually configure the MCP server.
|
||||
|
||||
| Variable | Required | Default | Description |
|
||||
| -------- | -------- | ------- | ----------- |
|
||||
| `FUZZFORGE_MODULES_PATH` | Yes | - | Path to the modules directory |
|
||||
| `FUZZFORGE_ENGINE__TYPE` | No | `docker` | Container engine (`docker` or `podman`) |
|
||||
| `FUZZFORGE_ENGINE__GRAPHROOT` | No | - | Container storage path (Podman under Snap only) |
|
||||
| `FUZZFORGE_ENGINE__RUNROOT` | No | - | Container runtime state path (Podman under Snap only) |
|
||||
| `SECPIPE_MODULES_PATH` | Yes | - | Path to the modules directory |
|
||||
| `SECPIPE_ENGINE__TYPE` | No | `docker` | Container engine (`docker` or `podman`) |
|
||||
| `SECPIPE_ENGINE__GRAPHROOT` | No | - | Container storage path (Podman under Snap only) |
|
||||
| `SECPIPE_ENGINE__RUNROOT` | No | - | Container runtime state path (Podman under Snap only) |
|
||||
|
||||
## Available Tools
|
||||
|
||||
@@ -161,10 +161,10 @@ For testing during development, you can run the MCP server directly:
|
||||
|
||||
```bash
|
||||
# Run MCP server in stdio mode (for AI agents)
|
||||
uv run python -m fuzzforge_mcp
|
||||
uv run python -m secpipe_mcp
|
||||
|
||||
# Run HTTP server for testing (not for production)
|
||||
uv run uvicorn fuzzforge_mcp.application:app --reload
|
||||
uv run uvicorn secpipe_mcp.application:app --reload
|
||||
```
|
||||
|
||||
## Architecture
|
||||
@@ -213,5 +213,5 @@ uv run pytest
|
||||
## See Also
|
||||
|
||||
- [SecPipe Main README](../README.md) - Overall project documentation
|
||||
- [Module SDK](../fuzzforge-modules/fuzzforge-modules-sdk/README.md) - Creating custom modules
|
||||
- [Module SDK](../secpipe-modules/secpipe-modules-sdk/README.md) - Creating custom modules
|
||||
- [Model Context Protocol](https://modelcontextprotocol.io/) - MCP specification
|
||||
@@ -1,13 +1,13 @@
|
||||
[project]
|
||||
name = "fuzzforge-mcp"
|
||||
name = "secpipe-mcp"
|
||||
version = "0.0.1"
|
||||
description = "FuzzForge MCP Server - AI agent gateway for FuzzForge AI."
|
||||
description = "SecPipe MCP Server - AI agent gateway for SecPipe AI."
|
||||
authors = []
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.14"
|
||||
dependencies = [
|
||||
"fastmcp==2.14.1",
|
||||
"fuzzforge-common==0.0.1",
|
||||
"secpipe-common==0.0.1",
|
||||
"pydantic==2.12.4",
|
||||
"pydantic-settings==2.12.0",
|
||||
"pyyaml>=6.0",
|
||||
@@ -15,7 +15,7 @@ dependencies = [
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
fuzzforge-mcp = "fuzzforge_mcp.__main__:main"
|
||||
secpipe-mcp = "secpipe_mcp.__main__:main"
|
||||
|
||||
[project.optional-dependencies]
|
||||
lints = [
|
||||
@@ -24,12 +24,12 @@ lints = [
|
||||
"ruff==0.14.4",
|
||||
]
|
||||
tests = [
|
||||
"fuzzforge-tests==0.0.1",
|
||||
"secpipe-tests==0.0.1",
|
||||
"pytest==9.0.2",
|
||||
"pytest-asyncio==1.3.0",
|
||||
"pytest-httpx==0.36.0",
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
fuzzforge-common = { workspace = true }
|
||||
fuzzforge-tests = { workspace = true }
|
||||
secpipe-common = { workspace = true }
|
||||
secpipe-tests = { workspace = true }
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
"""FuzzForge MCP Server entry point."""
|
||||
"""SecPipe MCP Server entry point."""
|
||||
|
||||
from fuzzforge_mcp.application import mcp
|
||||
from secpipe_mcp.application import mcp
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Run the FuzzForge MCP server in stdio mode.
|
||||
"""Run the SecPipe MCP server in stdio mode.
|
||||
|
||||
This is the primary entry point for AI agent integration.
|
||||
The server communicates via stdin/stdout using the MCP protocol.
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
"""FuzzForge MCP Server Application.
|
||||
"""SecPipe MCP Server Application.
|
||||
|
||||
This is the main entry point for the FuzzForge MCP server, providing
|
||||
This is the main entry point for the SecPipe MCP server, providing
|
||||
AI agents with tools to discover and execute MCP hub tools for
|
||||
security research.
|
||||
|
||||
@@ -12,8 +12,8 @@ from typing import TYPE_CHECKING
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.middleware.error_handling import ErrorHandlingMiddleware
|
||||
|
||||
from fuzzforge_mcp import resources, tools
|
||||
from fuzzforge_mcp.settings import Settings
|
||||
from secpipe_mcp import resources, tools
|
||||
from secpipe_mcp.settings import Settings
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import AsyncGenerator
|
||||
@@ -35,9 +35,9 @@ async def lifespan(_: FastMCP) -> AsyncGenerator[Settings]:
|
||||
|
||||
|
||||
mcp: FastMCP = FastMCP(
|
||||
name="FuzzForge MCP Server",
|
||||
name="SecPipe MCP Server",
|
||||
instructions="""
|
||||
FuzzForge is a security research orchestration platform. Use these tools to:
|
||||
SecPipe is a security research orchestration platform. Use these tools to:
|
||||
|
||||
1. **List hub servers**: Discover registered MCP tool servers
|
||||
2. **Discover tools**: Find available tools from hub servers
|
||||
+6
-6
@@ -1,4 +1,4 @@
|
||||
"""Dependency injection helpers for FuzzForge MCP."""
|
||||
"""Dependency injection helpers for SecPipe MCP."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -7,9 +7,9 @@ from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from fastmcp.server.dependencies import get_context
|
||||
|
||||
from fuzzforge_mcp.exceptions import FuzzForgeMCPError
|
||||
from fuzzforge_mcp.settings import Settings
|
||||
from fuzzforge_mcp.storage import LocalStorage
|
||||
from secpipe_mcp.exceptions import SecPipeMCPError
|
||||
from secpipe_mcp.settings import Settings
|
||||
from secpipe_mcp.storage import LocalStorage
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fastmcp import Context
|
||||
@@ -41,13 +41,13 @@ def get_settings() -> Settings:
|
||||
"""Get MCP server settings from context.
|
||||
|
||||
:return: Settings instance.
|
||||
:raises FuzzForgeMCPError: If settings not available.
|
||||
:raises SecPipeMCPError: If settings not available.
|
||||
|
||||
"""
|
||||
context: Context = get_context()
|
||||
if context.request_context is None:
|
||||
message: str = "Request context not available"
|
||||
raise FuzzForgeMCPError(message)
|
||||
raise SecPipeMCPError(message)
|
||||
return cast("Settings", context.request_context.lifespan_context)
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
"""TODO."""
|
||||
|
||||
|
||||
class SecPipeMCPError(Exception):
|
||||
"""TODO."""
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
"""FuzzForge MCP Resources."""
|
||||
"""SecPipe MCP Resources."""
|
||||
|
||||
from fastmcp import FastMCP
|
||||
|
||||
from fuzzforge_mcp.resources import executions, project
|
||||
from secpipe_mcp.resources import executions, project
|
||||
|
||||
mcp: FastMCP = FastMCP()
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
"""Execution resources for FuzzForge MCP."""
|
||||
"""Execution resources for SecPipe MCP."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -8,12 +8,12 @@ from typing import Any
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.exceptions import ResourceError
|
||||
|
||||
from fuzzforge_mcp.dependencies import get_project_path, get_storage
|
||||
from secpipe_mcp.dependencies import get_project_path, get_storage
|
||||
|
||||
mcp: FastMCP = FastMCP()
|
||||
|
||||
|
||||
@mcp.resource("fuzzforge://executions/")
|
||||
@mcp.resource("secpipe://executions/")
|
||||
async def list_executions() -> list[dict[str, Any]]:
|
||||
"""List all executions for the current project.
|
||||
|
||||
@@ -41,7 +41,7 @@ async def list_executions() -> list[dict[str, Any]]:
|
||||
raise ResourceError(message) from exception
|
||||
|
||||
|
||||
@mcp.resource("fuzzforge://executions/{execution_id}")
|
||||
@mcp.resource("secpipe://executions/{execution_id}")
|
||||
async def get_execution(execution_id: str) -> dict[str, Any]:
|
||||
"""Get information about a specific execution.
|
||||
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
"""Project resources for FuzzForge MCP."""
|
||||
"""Project resources for SecPipe MCP."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -8,12 +8,12 @@ from typing import Any
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.exceptions import ResourceError
|
||||
|
||||
from fuzzforge_mcp.dependencies import get_project_path, get_settings, get_storage
|
||||
from secpipe_mcp.dependencies import get_project_path, get_settings, get_storage
|
||||
|
||||
mcp: FastMCP = FastMCP()
|
||||
|
||||
|
||||
@mcp.resource("fuzzforge://project")
|
||||
@mcp.resource("secpipe://project")
|
||||
async def get_project() -> dict[str, Any]:
|
||||
"""Get information about the current project.
|
||||
|
||||
@@ -44,9 +44,9 @@ async def get_project() -> dict[str, Any]:
|
||||
raise ResourceError(message) from exception
|
||||
|
||||
|
||||
@mcp.resource("fuzzforge://project/settings")
|
||||
@mcp.resource("secpipe://project/settings")
|
||||
async def get_project_settings() -> dict[str, Any]:
|
||||
"""Get current FuzzForge settings.
|
||||
"""Get current SecPipe settings.
|
||||
|
||||
Returns the active configuration for the MCP server including
|
||||
engine, storage, and hub settings.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user