Skip to content

Commit b077d73

Browse files
committed
Shcheck
1 parent 34a0f5c commit b077d73

6 files changed

Lines changed: 445 additions & 1 deletion

File tree

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@
1515
## Important Files
1616
- docs/PROJECT_NOTES.md - Contains project overview, goals, and current implementation status
1717
- Makefile - Contains build, test, and lint commands
18+
- deployments/Dockerfile - contains containerized version

cmd/wass-mcp/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/tb0hdan/wass-mcp/pkg/tools/history"
2525
"github.com/tb0hdan/wass-mcp/pkg/tools/nikto"
2626
"github.com/tb0hdan/wass-mcp/pkg/tools/nuclei"
27+
"github.com/tb0hdan/wass-mcp/pkg/tools/shcheck"
2728
"github.com/tb0hdan/wass-mcp/pkg/tools/wapiti"
2829
)
2930

@@ -89,6 +90,7 @@ func main() {
8990
nikto.New(logger),
9091
wapiti.New(logger),
9192
nuclei.New(logger),
93+
shcheck.New(logger),
9294
}
9395

9496
// Create tool instances.

deployments/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ RUN sed -i 's/^Components: main$/Components: main contrib non-free/' /etc/apt/so
2727
&& apt-get update && apt-get install -y --no-install-recommends \
2828
ca-certificates \
2929
nikto \
30+
python3-pip \
3031
wapiti \
32+
&& pip3 install --no-cache-dir --break-system-packages shcheck \
3133
&& rm -rf /var/lib/apt/lists/* \
3234
&& apt-get clean
3335

docs/PROJECT_NOTES.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ wass-mcp/
4242
│ │ │ └── wapiti.go # Wapiti scanner tool
4343
│ │ ├── nuclei/
4444
│ │ │ └── nuclei.go # Nuclei scanner tool
45+
│ │ ├── shcheck/
46+
│ │ │ └── shcheck.go # Security headers checker tool
4547
│ │ ├── fullscan/
4648
│ │ │ └── fullscan.go # Parallel full scan tool
4749
│ │ └── history/
@@ -143,6 +145,29 @@ Template-based vulnerability scanner using Nuclei. Performs fast scanning using
143145
- Detailed finding information
144146
- Affected URLs and parameters
145147

148+
### shcheck
149+
150+
Security headers checker using shcheck.py. Analyzes HTTP response headers for security best practices including Content-Security-Policy, Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options, and more.
151+
152+
**Input:**
153+
| Parameter | Type | Description |
154+
|-----------|------|-------------|
155+
| `host` | string | Target hostname or IP |
156+
| `port` | int | Target port (default: 80) |
157+
| `vhost` | string | Virtual host header (optional) |
158+
| `max_lines` | int | Max output lines (pagination) |
159+
| `offset` | int | Line offset (pagination) |
160+
161+
**Example:**
162+
```json
163+
{"host": "example.com", "port": 443}
164+
```
165+
166+
**Output:** Returns JSON output including:
167+
- Present security headers with their values
168+
- Missing security headers that should be configured
169+
- Deprecated headers detected
170+
146171
### full_scan
147172

148173
Comprehensive security scan using all available scanners in parallel. Merges results into a unified report.
@@ -164,7 +189,7 @@ Comprehensive security scan using all available scanners in parallel. Merges res
164189
**Output:** Unified report containing:
165190
- Scan summary with timing for each scanner
166191
- Success/failure status per scanner
167-
- Merged results from all scanners (nikto, wapiti, nuclei)
192+
- Merged results from all scanners (nikto, wapiti, nuclei, shcheck)
168193

169194
**Features:**
170195
- Runs all available scanners in parallel
@@ -316,6 +341,7 @@ The project includes comprehensive test coverage for all packages:
316341
| `pkg/models` | Data models | JSON serialization, field validation |
317342
| `pkg/tools` | Tool wrapper | Execution logging, timing, error handling |
318343
| `pkg/tools/history` | History tool | All history actions (list, get, delete, clear) |
344+
| `pkg/tools/shcheck` | shcheck tool | Security headers checker tests |
319345
| `pkg/types` | Constants | Value validation |
320346

321347
### Running Tests
@@ -383,3 +409,7 @@ BSD 3-Clause License - Copyright (c) 2026, Bohdan Turkynevych.
383409
- Added shared `ApplyPagination()` and `FormatScannerOutput()` functions
384410
- Added shared constants `DefaultHost` and `DefaultPort` in `pkg/types`
385411
- Reduced code duplication across nikto, wapiti, nuclei, and fullscan tools
412+
- **v1.4:** Added shcheck security headers checker tool:
413+
- Analyzes HTTP security headers using shcheck.py with JSON output
414+
- Supports vhost via custom Host header
415+
- Included in full_scan parallel scanning

pkg/tools/shcheck/shcheck.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package shcheck
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os/exec"
7+
8+
"github.com/modelcontextprotocol/go-sdk/mcp"
9+
"github.com/rs/zerolog"
10+
"github.com/tb0hdan/wass-mcp/pkg/server"
11+
"github.com/tb0hdan/wass-mcp/pkg/tools"
12+
)
13+
14+
const (
15+
binaryName = "shcheck.py"
16+
description = "shcheck is a security headers checker that analyzes HTTP response headers for security best practices."
17+
headerVerb = "output"
18+
)
19+
20+
// Tool implements the shcheck security headers scanner.
21+
type Tool struct {
22+
tools.BaseScanner
23+
}
24+
25+
// Scan performs the shcheck scan and returns the output.
26+
func (t *Tool) Scan(ctx context.Context, params tools.ScanParams) tools.ScanResult {
27+
host, port := t.ResolveHostPort(params.Host, params.Port)
28+
29+
targetURL := tools.BuildTargetURL(host, port)
30+
t.Logger.Info().Msgf("Running shcheck scan on %s", targetURL)
31+
32+
args := []string{"-j", "-d", targetURL}
33+
if params.Vhost != "" {
34+
args = append(args, "-a", fmt.Sprintf("Host: %s", params.Vhost))
35+
}
36+
37+
cmd := exec.CommandContext(ctx, binaryName, args...) //nolint:gosec
38+
output, err := cmd.CombinedOutput()
39+
40+
if err != nil {
41+
return tools.ScanResult{
42+
Output: string(output),
43+
Error: fmt.Errorf("failed to execute shcheck: %w", err),
44+
}
45+
}
46+
47+
return tools.ScanResult{
48+
Output: string(output),
49+
Error: nil,
50+
}
51+
}
52+
53+
// Register registers the shcheck tool with the MCP server.
54+
func (t *Tool) Register(srv *server.Server) error {
55+
return t.RegisterTool(srv, t.Handler)
56+
}
57+
58+
// Handler handles MCP tool requests.
59+
func (t *Tool) Handler(ctx context.Context, _ *mcp.CallToolRequest, input tools.ScannerInput) (*mcp.CallToolResult, any, error) {
60+
if err := t.ValidateInput(input); err != nil {
61+
return nil, nil, err
62+
}
63+
64+
host, port := t.ResolveHostPort(input.Host, input.Port)
65+
66+
params := tools.ScanParams{
67+
Host: host,
68+
Port: port,
69+
Vhost: input.Vhost,
70+
}
71+
72+
scanResult := t.Scan(ctx, params)
73+
if scanResult.Error != nil {
74+
return nil, nil, fmt.Errorf("%w\nOutput: %s", scanResult.Error, scanResult.Output)
75+
}
76+
77+
targetURL := tools.BuildTargetURL(host, port)
78+
resultText := tools.FormatScannerOutput(binaryName, headerVerb, targetURL, scanResult.Output, input.MaxLines, input.Offset)
79+
80+
return &mcp.CallToolResult{
81+
Content: []mcp.Content{
82+
&mcp.TextContent{Text: resultText},
83+
},
84+
}, nil, nil
85+
}
86+
87+
// New creates a new shcheck scanner tool.
88+
func New(logger zerolog.Logger) tools.Scanner {
89+
return &Tool{
90+
BaseScanner: tools.NewBaseScanner(binaryName, description, logger),
91+
}
92+
}

0 commit comments

Comments
 (0)