Compare commits

...

11 Commits

Author SHA1 Message Date
AFredefon
be009a4094 rename: FuzzForge → SecPipe
Rename the entire project from FuzzForge to SecPipe:
- Python packages: fuzzforge_cli → secpipe_cli, fuzzforge_common → secpipe_common,
  fuzzforge_mcp → secpipe_mcp, fuzzforge_tests → secpipe_tests
- Directories: fuzzforge-cli → secpipe-cli, fuzzforge-common → secpipe-common,
  fuzzforge-mcp → secpipe-mcp, fuzzforge-tests → secpipe-tests
- Environment variables: FUZZFORGE_* → SECPIPE_*
- MCP server name: SecPipe MCP Server
- CI workflows, Makefile, Dockerfile, hub-config, NOTICE updated
- Fix mcp-server.yml to use uvicorn secpipe_mcp.application:app
2026-04-09 04:10:46 +02:00
AFredefon
bbf864e88b Merge pull request #55 from FuzzingLabs/feat/report-generation
feat: implement report generation
2026-04-08 03:14:45 +02:00
AFredefon
d04797b21d Merge pull request #54 from FuzzingLabs/feat/skill-packs
feat: implement skill packs system
2026-04-08 03:10:47 +02:00
AFredefon
0ea8c4bd1d Merge pull request #53 from FuzzingLabs/feat/artifact-management
feat: implement artifact management tools
2026-04-08 03:09:31 +02:00
AFredefon
af7532c811 Merge pull request #52 from FuzzingLabs/feat/workflow-hints
feat: implement workflow suggestions pipeline
2026-04-08 03:08:52 +02:00
AFredefon
0d410bd5b4 feat: implement report generation 2026-04-07 16:25:36 +02:00
AFredefon
d3a20b3846 feat: implement skill packs system 2026-04-07 16:12:14 +02:00
tduhamel42
01e6bc3fb1 docs: rename FuzzForge to SecPipe in all markdown files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 14:04:49 +02:00
tduhamel42
b634214e01 Update README.md 2026-04-03 13:52:06 +02:00
AFredefon
e7022c2c82 Merge pull request #48 from FuzzingLabs/dev 2026-03-17 08:15:42 +01:00
AFredefon
07c32de294 Merge pull request #46 from FuzzingLabs/dev
Refactor hub integration and enhance TUI with new features
2026-03-11 08:07:05 +01:00
123 changed files with 1720 additions and 881 deletions

View File

@@ -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

View File

@@ -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

1
.gitignore vendored
View File

@@ -13,3 +13,4 @@ __pycache__
# User-specific hub config (generated at runtime)
hub-config.json
*.egg-info/

View File

@@ -1,8 +1,8 @@
# Contributing to FuzzForge AI
# Contributing to SecPipe AI
Thank you for your interest in contributing to FuzzForge AI! We welcome contributions from the community and are excited to collaborate with you.
Thank you for your interest in contributing to SecPipe AI! We welcome contributions from the community and are excited to collaborate with you.
**Our Vision**: FuzzForge aims to be a **universal platform for security research** across all cybersecurity domains. Through our modular architecture, any security tool—from fuzzing engines to cloud scanners, from mobile app analyzers to IoT security tools—can be integrated as a containerized module and controlled via AI agents.
**Our Vision**: SecPipe aims to be a **universal platform for security research** across all cybersecurity domains. Through our modular architecture, any security tool—from fuzzing engines to cloud scanners, from mobile app analyzers to IoT security tools—can be integrated as a containerized module and controlled via AI agents.
## Ways to Contribute
@@ -13,7 +13,7 @@ Thank you for your interest in contributing to FuzzForge AI! We welcome contribu
- **Documentation** - Improve guides, tutorials, and module documentation
- **Testing** - Help test new features and report issues
- **AI Integration** - Improve MCP tools and AI agent interactions
- **Tool Integrations** - Wrap existing security tools as FuzzForge modules
- **Tool Integrations** - Wrap existing security tools as SecPipe modules
## Contribution Guidelines
@@ -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
FuzzForge 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 @@ FuzzForge uses a modular architecture where security tools run as isolated conta
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 @@ FuzzForge uses a modular architecture where security tools run as isolated conta
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 @@ FuzzForge uses a modular architecture where security tools run as isolated conta
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 @@ FuzzForge uses a modular architecture where security tools run as isolated conta
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,12 +206,12 @@ FuzzForge uses a modular architecture where security tools run as isolated conta
- **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
FuzzForge is designed to support modules across **all cybersecurity domains**. The modular architecture allows any security tool to be containerized and integrated. Here are the main categories:
SecPipe is designed to support modules across **all cybersecurity domains**. The modular architecture allows any security tool to be containerized and integrated. Here are the main categories:
**Application Security**
- Fuzzing engines (coverage-guided, grammar-based, mutation-based)
@@ -273,8 +273,8 @@ FuzzForge is designed to support modules across **all cybersecurity domains**. T
```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]):
@@ -341,7 +341,7 @@ uv run pytest
## Contributing to Core Features
Beyond modules, you can contribute to FuzzForge's core components.
Beyond modules, you can contribute to SecPipe's core components.
**Useful Resources:**
- [Project Structure](README.md) - Overview of the codebase
@@ -350,18 +350,18 @@ Beyond modules, you can contribute to FuzzForge'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 FuzzForge'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 FuzzForge'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 FuzzForge's core components.
When reporting bugs, please include:
- **Environment**: OS, Python version, Docker version, uv version
- **FuzzForge 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
@@ -538,7 +538,7 @@ Before submitting a new module:
## License
By contributing to FuzzForge AI, you agree that your contributions will be licensed under the same license as the project (see [LICENSE](LICENSE)).
By contributing to SecPipe AI, you agree that your contributions will be licensed under the same license as the project (see [LICENSE](LICENSE)).
For module contributions:
- Modules you create remain under the project license
@@ -552,12 +552,12 @@ 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
---
**Thank you for making FuzzForge better!**
**Thank you for making SecPipe better!**
Every contribution, no matter how small, helps build a stronger security research platform. Whether you're creating a module for web security, cloud scanning, mobile analysis, or any other cybersecurity domain, your work makes FuzzForge more powerful and versatile for the entire security community!
Every contribution, no matter how small, helps build a stronger security research platform. Whether you're creating a module for web security, cloud scanning, mobile analysis, or any other cybersecurity domain, your work makes SecPipe more powerful and versatile for the entire security community!

View File

@@ -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 -; \

4
NOTICE
View File

@@ -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.

View File

@@ -1,4 +1,4 @@
<h1 align="center"> FuzzForge AI</h1>
<h1 align="center">SecPipe</h1>
<h3 align="center">AI-Powered Security Research Orchestration via MCP</h3>
<p align="center">
@@ -6,7 +6,6 @@
<a href="LICENSE"><img src="https://img.shields.io/badge/license-BSL%201.1-blue" alt="License: BSL 1.1"></a>
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.12%2B-blue" alt="Python 3.12+"/></a>
<a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-compatible-green" alt="MCP Compatible"/></a>
<a href="https://fuzzforge.ai"><img src="https://img.shields.io/badge/Website-fuzzforge.ai-purple" alt="Website"/></a>
</p>
<p align="center">
@@ -26,19 +25,19 @@
---
> 🚧 **FuzzForge AI is under active development.** Expect breaking changes and new features!
> 🚧 **SecPipe AI is under active development.** Expect breaking changes and new features!
---
## 🚀 Overview
**FuzzForge AI** is an open-source MCP server that enables AI agents (GitHub Copilot, Claude, etc.) to orchestrate security research workflows through the **Model Context Protocol (MCP)**.
**SecPipe AI** is an open-source MCP server that enables AI agents (GitHub Copilot, Claude, etc.) to orchestrate security research workflows through the **Model Context Protocol (MCP)**.
FuzzForge connects your AI assistant to **MCP tool hubs** — collections of containerized security tools that the agent can discover, chain, and execute autonomously. Instead of manually running security tools, describe what you want and let your AI assistant handle it.
SecPipe connects your AI assistant to **MCP tool hubs** — collections of containerized security tools that the agent can discover, chain, and execute autonomously. Instead of manually running security tools, describe what you want and let your AI assistant handle it.
### The Core: Hub Architecture
FuzzForge acts as a **meta-MCP server** — a single MCP endpoint that gives your AI agent access to tools from multiple MCP hub servers. Each hub server is a containerized security tool (Binwalk, YARA, Radare2, Nmap, etc.) that the agent can discover at runtime.
SecPipe acts as a **meta-MCP server** — a single MCP endpoint that gives your AI agent access to tools from multiple MCP hub servers. Each hub server is a containerized security tool (Binwalk, YARA, Radare2, Nmap, etc.) that the agent can discover at runtime.
- **🔍 Discovery**: The agent lists available hub servers and discovers their tools
- **🤖 AI-Native**: Hub tools provide agent context — usage tips, workflow guidance, and domain knowledge
@@ -75,10 +74,10 @@ Agent → Crash Analysis: Deduplicate and triage discovered crashes
## ⭐ Support the Project
If you find FuzzForge useful, please **star the repo** to support development! 🚀
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>
---
@@ -106,7 +105,7 @@ If you find FuzzForge useful, please **star the repo** to support development!
│ MCP Protocol (stdio)
┌─────────────────────────────────────────────────────────────────┐
FuzzForge MCP Server │
SecPipe MCP Server
│ │
│ Projects Hub Discovery Hub Execution │
│ ┌──────────────┐ ┌──────────────────┐ ┌───────────────────┐ │
@@ -135,7 +134,7 @@ If you find FuzzForge useful, please **star the repo** to support development!
## 🔧 MCP Security Hub
FuzzForge ships with built-in support for the **[MCP Security Hub](https://github.com/FuzzingLabs/mcp-security-hub)** — a collection of 36 production-ready, Dockerized MCP servers covering offensive security:
SecPipe ships with built-in support for the **[MCP Security Hub](https://github.com/FuzzingLabs/mcp-security-hub)** — a collection of 36 production-ready, Dockerized MCP servers covering offensive security:
| Category | Servers | Examples |
|----------|---------|----------|
@@ -170,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
@@ -181,31 +180,31 @@ 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 FuzzForge tools!
**Restart your editor** and your AI agent will have access to SecPipe tools!
---
@@ -220,7 +219,7 @@ Once installed, just talk to your AI agent:
"Run nuclei against https://example.com"
```
The agent will use FuzzForge to discover the right hub tools, chain them into a pipeline, and return results — all without you touching a terminal.
The agent will use SecPipe to discover the right hub tools, chain them into a pipeline, and return results — all without you touching a terminal.
See the [Usage Guide](USAGE.md) for detailed setup and advanced workflows.
@@ -229,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 FuzzForge
├── 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
```
@@ -263,4 +262,4 @@ BSL 1.1 - See [LICENSE](LICENSE) for details.
<p align="center">
<strong>Maintained by <a href="https://fuzzinglabs.com">FuzzingLabs</a></strong>
<br>
</p>
</p>

65
RELEASE_NOTES.md Normal file
View File

@@ -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

View File

@@ -1,6 +1,6 @@
# FuzzForge AI Roadmap
# SecPipe AI Roadmap
This document outlines the planned features and development direction for FuzzForge AI.
This document outlines the planned features and development direction for SecPipe AI.
---
@@ -10,27 +10,27 @@ This document outlines the planned features and development direction for FuzzFo
**Status:** 🔄 Planned
Integrate [mcp-security-hub](https://github.com/FuzzingLabs/mcp-security-hub) tools into FuzzForge, giving AI agents access to 28 MCP servers and 163+ security tools through a unified interface.
Integrate [mcp-security-hub](https://github.com/FuzzingLabs/mcp-security-hub) tools into SecPipe, giving AI agents access to 28 MCP servers and 163+ security tools through a unified interface.
#### How It Works
Unlike native FuzzForge modules (built with the SDK), mcp-security-hub tools are **standalone MCP servers**. The integration will bridge these tools so they can be:
Unlike native SecPipe modules (built with the SDK), mcp-security-hub tools are **standalone MCP servers**. The integration will bridge these tools so they can be:
- Discovered via `list_modules` alongside native modules
- Executed through FuzzForge's orchestration layer
- Executed through SecPipe's orchestration layer
- Chained with native modules in workflows
| Aspect | Native Modules | MCP Hub Tools |
|--------|----------------|---------------|
| **Runtime** | FuzzForge SDK container | Standalone MCP server container |
| **Runtime** | SecPipe SDK container | Standalone MCP server container |
| **Protocol** | Direct execution | MCP-to-MCP bridge |
| **Configuration** | Module config | Tool-specific args |
| **Output** | FuzzForge results format | Tool-native format (normalized) |
| **Output** | SecPipe results format | Tool-native format (normalized) |
#### Goals
- Unified discovery of all available tools (native + hub)
- Orchestrate hub tools through FuzzForge's workflow engine
- Orchestrate hub tools through SecPipe's workflow engine
- Normalize outputs for consistent result handling
- No modification required to mcp-security-hub tools
@@ -65,7 +65,7 @@ AI Agent:
**Status:** 🔄 Planned
A graphical interface to manage FuzzForge without the command line.
A graphical interface to manage SecPipe without the command line.
#### Goals
@@ -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)
---

136
USAGE.md
View File

@@ -1,9 +1,9 @@
# FuzzForge AI Usage Guide
# SecPipe AI Usage Guide
This guide covers everything you need to know to get started with FuzzForge AI — from installation to linking your first MCP hub and running security research workflows with AI.
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.
> **FuzzForge 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.
> **SecPipe is designed to be used with AI agents** (GitHub Copilot, Claude, etc.) via MCP.
> 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.
---
@@ -27,7 +27,7 @@ This guide covers everything you need to know to get started with FuzzForge AI
- [GitHub Copilot](#github-copilot)
- [Claude Code (CLI)](#claude-code-cli)
- [Claude Desktop](#claude-desktop)
- [Using FuzzForge with AI](#using-fuzzforge-with-ai)
- [Using SecPipe with AI](#using-secpipe-with-ai)
- [CLI Reference](#cli-reference)
- [Environment Variables](#environment-variables)
- [Troubleshooting](#troubleshooting)
@@ -41,12 +41,12 @@ This guide covers everything you need to know to get started with FuzzForge 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
@@ -76,13 +76,13 @@ git clone git@github.com:FuzzingLabs/mcp-security-hub.git ~/.fuzzforge/hubs/mcp-
# Restart your AI agent — done!
```
> **Note:** FuzzForge uses Docker by default. Podman is also supported via `--engine podman`.
> **Note:** SecPipe uses Docker by default. Podman is also supported via `--engine podman`.
---
## Prerequisites
Before installing FuzzForge AI, ensure you have:
Before installing SecPipe AI, ensure you have:
- **Python 3.12+** — [Download Python](https://www.python.org/downloads/)
- **uv** package manager — [Install uv](https://docs.astral.sh/uv/)
@@ -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
@@ -131,24 +131,24 @@ cd fuzzforge_ai
uv sync
```
This installs all FuzzForge components in a virtual environment.
This installs all SecPipe components in a virtual environment.
### 3. Verify Installation
```bash
uv run fuzzforge --help
uv run secpipe --help
```
---
## Terminal UI
FuzzForge ships with a terminal user interface (TUI) built on [Textual](https://textual.textualize.io/) for managing AI agents and MCP hub servers from a single dashboard.
SecPipe ships with a terminal user interface (TUI) built on [Textual](https://textual.textualize.io/) for managing AI agents and MCP hub servers from a single dashboard.
### Launching the UI
```bash
uv run fuzzforge ui
uv run secpipe ui
```
### Dashboard
@@ -173,11 +173,11 @@ 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 FuzzForge 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 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 `secpipe` entry without touching other MCP servers)
The setup auto-detects:
- FuzzForge installation root
- SecPipe installation root
- Docker/Podman socket path
- Hub configuration from `hub-config.json`
@@ -187,8 +187,8 @@ 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` |
| **Link Path** | Link any local directory as a hub — enter a name and path, FuzzForge scans it for `category/tool-name/Dockerfile` patterns |
| **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 |
@@ -219,7 +219,7 @@ my-hub/
└── ...
```
FuzzForge scans for the pattern `category/tool-name/Dockerfile` and auto-generates server configuration entries for each discovered tool.
SecPipe scans for the pattern `category/tool-name/Dockerfile` and auto-generates server configuration entries for each discovered tool.
### FuzzingLabs Security Hub
@@ -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,32 +286,32 @@ 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:
- **FuzzForge root** — Where FuzzForge is installed
- **SecPipe root** — Where SecPipe is installed
- **Docker socket** — Auto-detects `/var/run/docker.sock`
**Optional overrides:**
```bash
uv run fuzzforge mcp install copilot --engine podman
uv run secpipe mcp install copilot --engine podman
```
**After installation:** Restart VS Code. FuzzForge tools appear in GitHub Copilot Chat.
**After installation:** Restart VS Code. SecPipe tools appear in GitHub Copilot Chat.
### Claude Code (CLI)
```bash
uv run fuzzforge mcp install claude-code
uv run secpipe mcp install claude-code
```
Installs to `~/.claude.json`. FuzzForge tools are available from any directory after restarting Claude.
Installs to `~/.claude.json`. SecPipe tools are available from any directory after restarting Claude.
### Claude Desktop
```bash
uv run fuzzforge mcp install claude-desktop
uv run secpipe mcp install claude-desktop
```
**After installation:** Restart Claude Desktop.
@@ -319,28 +319,28 @@ 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
```
---
## Using FuzzForge with AI
## Using SecPipe with AI
Once MCP is configured and hub images are built, interact with FuzzForge through natural language with your AI assistant.
Once MCP is configured and hub images are built, interact with SecPipe through natural language with your AI assistant.
### Example Conversations
**Discover available tools:**
```
You: "What security tools are available in FuzzForge?"
You: "What security tools are available in SecPipe?"
AI: Queries hub tools → "I found 15 tools across categories: nmap for
port scanning, binwalk for firmware analysis, semgrep for code
scanning, cargo-fuzzer for Rust fuzzing..."
@@ -377,50 +377,50 @@ 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
```
---
## Environment Variables
Configure FuzzForge using environment variables:
Configure SecPipe using environment variables:
```bash
# Override the FuzzForge installation root (auto-detected from cwd by default)
export FUZZFORGE_ROOT=/path/to/fuzzforge_ai
# Override the SecPipe installation root (auto-detected from cwd by default)
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
FuzzForge 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

View File

@@ -1,3 +0,0 @@
# FuzzForge CLI
...

View File

@@ -1 +0,0 @@
"""FuzzForge terminal user interface."""

View File

@@ -1 +0,0 @@
"""TUI screens for FuzzForge."""

View File

@@ -1,3 +0,0 @@
# FuzzForge Common
...

View File

@@ -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",
]

View File

@@ -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",
]

View File

@@ -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",
]

View File

@@ -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",
]

View File

@@ -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",
]

View File

@@ -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)

View File

@@ -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",
]

View File

@@ -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)

View File

@@ -1 +0,0 @@
pytest_plugins = ["fuzzforge_tests.fixtures"]

View File

@@ -1,5 +0,0 @@
"""TODO."""
class FuzzForgeMCPError(Exception):
"""TODO."""

View File

@@ -1,3 +0,0 @@
# fuzzforge-tests
Common test utilities and fixtures for FuzzForge packages.

View File

@@ -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__ = []

View File

@@ -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"

View File

@@ -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 }

3
secpipe-cli/README.md Normal file
View File

@@ -0,0 +1,3 @@
# SecPipe CLI
...

View File

@@ -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 }

View File

@@ -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
]

View File

@@ -1,6 +1,6 @@
"""TODO."""
from fuzzforge_cli.application import application
from secpipe_cli.application import application
def main() -> None:

View File

@@ -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()

View File

@@ -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]")

View File

@@ -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.

View File

@@ -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.
"""

View File

@@ -0,0 +1 @@
"""SecPipe terminal user interface."""

View File

@@ -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)

View File

@@ -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)

View File

@@ -0,0 +1 @@
"""TUI screens for SecPipe."""

View File

@@ -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"):

View File

@@ -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

View File

@@ -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

View File

@@ -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,

3
secpipe-common/README.md Normal file
View File

@@ -0,0 +1,3 @@
# SecPipe Common
...

View File

@@ -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"

View File

@@ -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",
]

View File

@@ -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.

View File

@@ -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",

View File

@@ -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:

View File

@@ -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

View File

@@ -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.

View File

@@ -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,

View File

@@ -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",
]

View File

@@ -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",
]

View File

@@ -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",
]

View File

@@ -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)

View File

@@ -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.

View File

@@ -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",
]

View File

@@ -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.

View File

@@ -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)

View File

@@ -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.

View File

@@ -1,7 +1,7 @@
from enum import StrEnum
class FuzzForgeSandboxEngines(StrEnum):
class SecPipeSandboxEngines(StrEnum):
"""TODO."""
#: TODO.

View File

@@ -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",
]

View File

@@ -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.

View File

@@ -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)

View File

@@ -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.

View File

@@ -0,0 +1 @@
pytest_plugins = ["secpipe_tests.fixtures"]

View File

@@ -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:

View File

@@ -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)

View File

@@ -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"]

View File

@@ -1,10 +1,10 @@
# FuzzForge MCP
# SecPipe MCP
Model Context Protocol (MCP) server that enables AI agents to orchestrate FuzzForge security research modules.
Model Context Protocol (MCP) server that enables AI agents to orchestrate SecPipe security research modules.
## Overview
FuzzForge MCP provides a standardized interface for AI agents (Claude Code, GitHub Copilot, Claude Desktop) to:
SecPipe MCP provides a standardized interface for AI agents (Claude Code, GitHub Copilot, Claude Desktop) to:
- List and discover available security modules
- Execute modules in isolated containers
@@ -17,20 +17,20 @@ The server communicates with AI agents using the [Model Context Protocol](https:
### Automatic Installation (Recommended)
Use the FuzzForge CLI to automatically configure MCP for your AI agent:
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
@@ -110,7 +110,7 @@ The MCP server exposes the following tools to AI agents:
### Project Management
- **`init_project`** - Initialize a new FuzzForge project
- **`init_project`** - Initialize a new SecPipe project
- **`set_project_assets`** - Set initial assets (source code, contracts, etc.) for the project
### Module Management
@@ -135,7 +135,7 @@ The server also provides resources for accessing:
### From AI Agent (e.g., Claude Code)
Once configured, AI agents can interact with FuzzForge naturally:
Once configured, AI agents can interact with SecPipe naturally:
```text
User: List the available security modules
@@ -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
@@ -178,14 +178,14 @@ uv run uvicorn fuzzforge_mcp.application:app --reload
│ stdio/JSON-RPC
┌─────────────────────────────────────────┐
FuzzForge MCP Server │
SecPipe MCP Server
│ Tools: init_project, list_modules, │
│ execute_module, execute_workflow│
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
FuzzForge Runner │
SecPipe Runner
│ Podman/Docker Orchestration │
└─────────────────────────────────────────┘
@@ -212,6 +212,6 @@ uv run pytest
## See Also
- [FuzzForge Main README](../README.md) - Overall project documentation
- [Module SDK](../fuzzforge-modules/fuzzforge-modules-sdk/README.md) - Creating custom modules
- [SecPipe Main README](../README.md) - Overall project documentation
- [Module SDK](../secpipe-modules/secpipe-modules-sdk/README.md) - Creating custom modules
- [Model Context Protocol](https://modelcontextprotocol.io/) - MCP specification

View File

@@ -1,20 +1,21 @@
[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",
"structlog==25.5.0",
]
[project.scripts]
fuzzforge-mcp = "fuzzforge_mcp.__main__:main"
secpipe-mcp = "secpipe_mcp.__main__:main"
[project.optional-dependencies]
lints = [
@@ -23,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 }

View File

@@ -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.

View File

@@ -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
@@ -53,40 +53,30 @@ Typical workflow:
4. Discover tools from servers with `discover_hub_tools`
5. Execute hub tools with `execute_hub_tool`
Skill packs:
Use `list_skills` to see available analysis pipelines (e.g. firmware-analysis).
Load one with `load_skill("firmware-analysis")` to get domain-specific guidance
and a scoped list of relevant hub servers. Skill packs describe the methodology
follow the pipeline steps while adapting to what you find at each stage.
Agent context convention:
When you call `discover_hub_tools`, some servers return an `agent_context` field
with usage tips, known issues, rule templates, and workflow guidance. Always read
this context before using the server's tools.
Artifact tracking:
After each `execute_hub_tool` call, new output files are automatically tracked.
Use `list_artifacts` to find files produced by previous tools instead of parsing
paths from tool output text. Filter by source server or file type.
File access in containers:
- Assets set via `set_project_assets` are mounted read-only at `/app/uploads/` and `/app/samples/`
- A writable output directory is mounted at `/app/output/` use it for extraction results, reports, etc.
- Always use container paths (e.g. `/app/uploads/file`) when passing file arguments to hub tools
Stateful tools:
- Some tools (e.g. radare2-mcp) require multi-step sessions. Use `start_hub_server` to launch
- Some tools require multi-step sessions. Use `start_hub_server` to launch
a persistent container, then `execute_hub_tool` calls reuse that container. Stop with `stop_hub_server`.
Firmware analysis pipeline (when analyzing firmware images):
1. **binwalk-mcp** (`binwalk_scan` + `binwalk_extract`) identify and extract filesystem from firmware
2. **yara-mcp** (`yara_scan_with_rules`) scan extracted files with vulnerability rules to prioritize targets
3. **radare2-mcp** (persistent session) confirm dangerous code paths
4. **searchsploit-mcp** (`search_exploitdb`) query version strings from radare2 against ExploitDB
Run steps 3 and 4 outputs feed into a final triage summary.
radare2-mcp agent context (upstream tool no embedded context):
- Start a persistent session with `start_hub_server("radare2-mcp")` before any calls.
- IMPORTANT: the `open_file` tool requires the parameter name `file_path` (with underscore),
not `filepath`. Example: `execute_hub_tool("hub:radare2-mcp:open_file", {"file_path": "/app/output/..."})`
- Workflow: `open_file` `analyze` `list_imports` `xrefs_to` `run_command` with `pdf @ <addr>`.
- Static binary fallback: firmware binaries are often statically linked. When `list_imports`
returns an empty result, fall back to `list_symbols` and search for dangerous function names
(system, strcpy, gets, popen, sprintf) in the output. Then use `xrefs_to` on their addresses.
- For string extraction, use `run_command` with `iz` (data section strings).
The `list_all_strings` tool may return garbled output for large binaries.
- For decompilation, use `run_command` with `pdc @ <addr>` (pseudo-C) or `pdf @ <addr>`
(annotated disassembly). The `decompile` tool may fail with "not available in current mode".
- Stop the session with `stop_hub_server("radare2-mcp")` when done.
""",
lifespan=lifespan,
)

View File

@@ -1,15 +1,15 @@
"""Dependency injection helpers for FuzzForge MCP."""
"""Dependency injection helpers for SecPipe MCP."""
from __future__ import annotations
from pathlib import Path
from typing import TYPE_CHECKING, cast
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
@@ -21,6 +21,9 @@ _current_project_path: Path | None = None
# Singleton storage instance
_storage: LocalStorage | None = None
# Currently loaded skill pack (set by load_skill)
_active_skill: dict[str, Any] | None = None
def set_current_project_path(project_path: Path) -> None:
"""Set the current project path.
@@ -38,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)
@@ -75,3 +78,22 @@ def get_storage() -> LocalStorage:
settings = get_settings()
_storage = LocalStorage(settings.storage.path)
return _storage
def set_active_skill(skill: dict[str, Any] | None) -> None:
"""Set (or clear) the currently loaded skill pack.
:param skill: Parsed skill dict, or None to unload.
"""
global _active_skill
_active_skill = skill
def get_active_skill() -> dict[str, Any] | None:
"""Get the currently loaded skill pack.
:return: Active skill dict, or None if no skill is loaded.
"""
return _active_skill

View File

@@ -0,0 +1,5 @@
"""TODO."""
class SecPipeMCPError(Exception):
"""TODO."""

Some files were not shown because too many files have changed in this diff Show More