# SecureGit Plugin Development Guide
## Overview
SecureGit's plugin architecture allows you to integrate any security scanning tool into the SecureGit ecosystem. Plugins can be written in any language and can wrap existing security tools, enabling the security community to leverage decades of battle-tested scanners.
## Plugin Types
### 1. Built-in Rust Plugins
- Compiled directly into the SecureGit binary
- Highest performance, zero startup overhead
- Implement the `SecurityPlugin` trait
- Examples: secrets, patterns, entropy, binary detection
### 2. External Executable Plugins
- Standalone scripts or binaries
- Language-agnostic (Bash, Python, Go, Node.js, etc.)
- Communicate via JSON on stdin/stdout
- Located in `~/.config/securegit/plugins/`
### 3. Dynamic Library Plugins (Native)
- Compiled shared libraries (.so/.dll/.dylib)
- Any language with C FFI support
- Loaded at runtime via `libloading`
- High performance with dynamic loading
### 4. WebAssembly Plugins (Future)
- Sandboxed, portable execution
- Compile from Rust, Go, C++, AssemblyScript
- Cross-platform compatibility
- Security isolation
## Plugin Interface
### Input Format
Plugins receive scan context as JSON via stdin or as command arguments:
```json
{
"file_path": "/path/to/file.py",
"scan_phase": "post_extract",
"content_base64": "IyEvdXNyL2Jpbi9lbnYgcHl0aG9u...",
"metadata": {
"size": 1024,
"mime_type": "text/x-python"
}
}
```
### Output Format
Plugins must return findings as JSON on stdout:
```json
{
"plugin_name": "bandit",
"version": "1.0.0",
"findings": [
{
"id": "B201",
"title": "flask_debug_true",
"description": "A Flask app is run with debug=True, which exposes the debugger",
"severity": "high",
"confidence": "high",
"file_path": "/path/to/file.py",
"line_start": 42,
"line_end": 42,
"evidence": "app.run(debug=True)",
"remediation": "Set debug=False in production",
"references": [
"https://flask.palletsprojects.com/en/2.0.x/security/"
],
"cwe_ids": [489]
}
],
"scanned_files": 1,
"duration_ms": 123
}
```
### Severity Levels
- `critical` - Immediate security threat (exposed credentials, RCE vulnerabilities)
- `high` - Serious security issue (injection flaws, weak crypto)
- `medium` - Moderate risk (missing security headers, outdated dependencies)
- `low` - Minor concern (code quality issues with security implications)
- `info` - Informational finding
## Creating a Plugin
### Example 1: Bash Script Plugin
Wrap any command-line security tool:
```bash
#!/bin/bash
# ~/.config/securegit/plugins/gitleaks
# Plugin metadata (optional, for discovery)
# NAME: gitleaks
# VERSION: 1.0.0
# DESCRIPTION: Detect hardcoded secrets using gitleaks
# REQUIRES: gitleaks
set -euo pipefail
FILE_PATH="$1"
# Check if gitleaks is installed
if ! command -v gitleaks &> /dev/null; then
echo '{"error": "gitleaks not found in PATH"}' >&2
exit 1
fi
# Run gitleaks and convert output to SecureGit format
gitleaks detect --no-git -f "$FILE_PATH" --report-format json 2>/dev/null | \
jq -c '{
plugin_name: "gitleaks",
version: "1.0.0",
findings: [.[] | {
id: .RuleID,
title: ("Secret detected: " + .Description),
description: .Description,
severity: "critical",
confidence: "high",
file_path: .File,
line_start: .StartLine,
line_end: .EndLine,
evidence: .Secret[0:50]
}],
scanned_files: 1
}'
```
Make it executable:
```bash
chmod +x ~/.config/securegit/plugins/gitleaks
```
### Example 2: Python Plugin
Integrate Python security tools:
```python
#!/usr/bin/env python3
# ~/.config/securegit/plugins/bandit
import json
import sys
import subprocess
from pathlib import Path
def scan_file(file_path):
"""Run Bandit security scanner on a Python file."""
# Run bandit
result = subprocess.run(
['bandit', '-f', 'json', file_path],
capture_output=True,
text=True
)
if result.returncode not in [0, 1]:
return {"error": f"Bandit failed: {result.stderr}"}
bandit_output = json.loads(result.stdout)
# Convert to SecureGit format
findings = []
for issue in bandit_output.get('results', []):
findings.append({
"id": issue['test_id'],
"title": issue['test_name'],
"description": issue['issue_text'],
"severity": issue['issue_severity'].lower(),
"confidence": issue['issue_confidence'].lower(),
"file_path": issue['filename'],
"line_start": issue['line_number'],
"evidence": issue['code'],
"cwe_ids": [issue.get('cwe', {}).get('id')] if 'cwe' in issue else []
})
return {
"plugin_name": "bandit",
"version": "1.0.0",
"findings": findings,
"scanned_files": 1
}
if __name__ == "__main__":
if len(sys.argv) < 2:
print(json.dumps({"error": "No file path provided"}), file=sys.stderr)
sys.exit(1)
file_path = sys.argv[1]
# Only scan Python files
if not file_path.endswith('.py'):
print(json.dumps({
"plugin_name": "bandit",
"findings": [],
"scanned_files": 0
}))
sys.exit(0)
result = scan_file(file_path)
print(json.dumps(result))
```
### Example 3: Go Plugin
High-performance compiled plugin:
```go
// ~/.config/securegit/plugins/gosec.go
package main
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"strings"
)
type Finding struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Severity string `json:"severity"`
FilePath string `json:"file_path"`
LineStart int `json:"line_start"`
Evidence string `json:"evidence"`
}
type PluginResult struct {
PluginName string `json:"plugin_name"`
Version string `json:"version"`
Findings []Finding `json:"findings"`
ScannedFiles int `json:"scanned_files"`
}
func scanFile(filePath string) (*PluginResult, error) {
// Run gosec
cmd := exec.Command("gosec", "-fmt", "json", filePath)
output, err := cmd.Output()
if err != nil && len(output) == 0 {
return nil, fmt.Errorf("gosec failed: %v", err)
}
// Parse gosec output
var gosecResult map[string]interface{}
if err := json.Unmarshal(output, &gosecResult); err != nil {
return nil, err
}
// Convert to SecureGit format
findings := []Finding{}
if issues, ok := gosecResult["Issues"].([]interface{}); ok {
for _, issue := range issues {
i := issue.(map[string]interface{})
findings = append(findings, Finding{
ID: i["rule_id"].(string),
Title: i["details"].(string),
Severity: strings.ToLower(i["severity"].(string)),
FilePath: i["file"].(string),
LineStart: int(i["line"].(float64)),
Evidence: i["code"].(string),
})
}
}
return &PluginResult{
PluginName: "gosec",
Version: "1.0.0",
Findings: findings,
ScannedFiles: 1,
}, nil
}
func main() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, `{"error": "No file path provided"}`)
os.Exit(1)
}
result, err := scanFile(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, `{"error": "%s"}`, err)
os.Exit(1)
}
output, _ := json.Marshal(result)
fmt.Println(string(output))
}
```
Compile:
```bash
go build -o ~/.config/securegit/plugins/gosec gosec.go
```
## Wrapping Existing Tools
### ClamAV Malware Scanner
```bash
#!/bin/bash
# ~/.config/securegit/plugins/clamav
FILE="$1"
BEGIN { findings = "" }
/FOUND/ {
if (findings != "") findings = findings ","
findings = findings sprintf("{\"id\":\"CLAMAV_MALWARE\",\"title\":\"Malware detected\",\"severity\":\"critical\",\"file_path\":\"%s\",\"evidence\":\"%s\"}", file, $0)
}
END {
printf "{\"plugin_name\":\"clamav\",\"findings\":[%s]}\n", findings
}'
```
### YARA Rules
```bash
#!/bin/bash
# ~/.config/securegit/plugins/yara-rules
FILE="$1"
RULES_DIR="$HOME/.config/securegit/yara-rules"
if [ ! -d "$RULES_DIR" ]; then
echo '{"error": "YARA rules not found. Run: git clone https://github.com/Yara-Rules/rules ~/.config/securegit/yara-rules"}' >&2
exit 1
fi
yara -r "$RULES_DIR" "$FILE" 2>/dev/null | \
awk -v file="$FILE" '{
printf "{\"plugin_name\":\"yara\",\"findings\":[{\"id\":\"%s\",\"title\":\"YARA rule matched: %s\",\"severity\":\"high\",\"file_path\":\"%s\"}]}\n", $1, $1, file
}'
```
### Trivy Container Scanner
```bash
#!/bin/bash
# ~/.config/securegit/plugins/trivy
FILE="$1"
# Only scan Dockerfiles and container-related files
if [[ ! "$FILE" =~ (Dockerfile|docker-compose\.ya?ml) ]]; then
echo '{"plugin_name":"trivy","findings":[]}'
exit 0
fi
plugin_name: "trivy",
findings: [.Results[]?.Misconfigurations[]? | {
id: .ID,
title: .Title,
description: .Description,
severity: (.Severity | ascii_downcase),
file_path: "'$FILE'",
remediation: .Resolution
}]
}'
```
## Plugin Installation
### Manual Installation
```bash
# Create plugin directory
mkdir -p ~/.config/securegit/plugins
# Copy plugin
cp my-scanner ~/.config/securegit/plugins/
# Make executable
chmod +x ~/.config/securegit/plugins/my-scanner
# Test plugin
### Unit Test
```bash
# Create test file with known issue
echo 'aws_key = "AKIAIOSFODNN7EXAMPLE"' > test.py
# Run plugin directly
# {
# "plugin_name": "gitleaks",
# "findings": [
# {
# "id": "aws-access-key",
# "severity": "critical",
# ...
# }
# ]
# }
```
### Integration Test
```bash
# Run with SecureGit
securegit scan test.py --plugin gitleaks
# Should detect the hardcoded AWS key
```
## Best Practices
### Performance
- Exit quickly for irrelevant files (check file extension)
- Use streaming/incremental processing for large files
- Cache results when appropriate
- Set reasonable timeouts
### Error Handling
- Return errors via stderr in JSON format
- Use non-zero exit codes only for fatal errors
- Gracefully handle missing dependencies
- Provide clear error messages
### Output
- Always return valid JSON
- Redact or truncate sensitive evidence
- Include actionable remediation advice
- Provide relevant security references
### Security
- Validate all input paths (prevent path traversal)
- Don't execute user-provided code
- Limit resource consumption (CPU, memory, disk)
- Run with minimal required permissions
## Plugin Registry
### Submitting to Registry
1. Create plugin repository with structure:
```
my-plugin/
├── plugin.json # Metadata
├── install.sh # Installation script
├── scanner # Plugin executable
└── README.md # Documentation
```
2. `plugin.json` format:
```json
{
"name": "my-scanner",
"version": "1.0.0",
"description": "Security scanner for X",
"author": "Your Name",
"license": "MIT",
"homepage": "https://github.com/user/my-scanner",
"requires": ["external-tool >= 2.0"],
"platforms": ["linux", "darwin"],
"tags": ["secrets", "static-analysis", "python"]
}
```
3. Submit PR to plugin registry repository
### Plugin Discovery
```bash
# Search registry
securegit plugin search yara
# View plugin info
securegit plugin info yara-rules
# Install from registry
securegit plugin install yara-rules
# Update plugins
securegit plugin update --all
```
## Community Plugins
Visit the plugin registry for community-maintained scanners:
https://github.com/armyknifelabs-tools/securegit-plugins
### Popular Categories
- Secret detection (gitleaks, trufflehog, detect-secrets)
- Language-specific SAST (bandit, gosec, brakeman)
- Container security (trivy, grype, hadolint)
- License compliance (licensee, scancode, fossology)
- Malware detection (clamav, yara)
- Infrastructure as Code (tfsec, checkov, terrascan)
## Support
- Documentation: https://github.com/armyknifelabs-tools/securegit
- Issues: https://github.com/armyknifelabs-tools/securegit/issues
- Plugin Registry: https://github.com/armyknifelabs-tools/securegit-plugins