Skip to content

feat: comprehensive CLI overhaul with new commands, MCP expansion, and chat#126

Merged
tim-thacker-nullify merged 6 commits intomainfrom
feat/comprehensive-cli-overhaul
Mar 1, 2026
Merged

feat: comprehensive CLI overhaul with new commands, MCP expansion, and chat#126
tim-thacker-nullify merged 6 commits intomainfrom
feat/comprehensive-cli-overhaul

Conversation

@tim-thacker-nullify
Copy link
Copy Markdown
Member

Summary

  • Rename DAST → Pentest + BugHunt: Replaces the generic dast command with pentest (local Docker + cloud scanning) and bughunt (cloud-only), reflecting the actual product terminology
  • Interactive Chat (nullify chat): WebSocket-based chat with Nullify AI agents, supporting both interactive REPL and single-shot modes
  • Setup Wizard (nullify init): First-run experience that configures domain, authentication, repo detection, and MCP integration for AI tools
  • Unified Commands: nullify findings (cross-scanner query), nullify status (security posture overview), nullify ci gate (quality gate), nullify ci report (PR markdown summary)
  • MCP Expansion (~43% → ~80%+): Added infrastructure, code review, comment tools, MCP resources (nullify://posture, nullify://repos), MCP prompts (security-review, triage-finding, explain-vulnerability), and composite workflows (remediate_finding, get_critical_path, get_security_trends)
  • Code Quality Foundation: HTTPClient interface, typed APIError, shared git detection (internal/lib/git.go), test utilities (internal/testutil/), ParseCustomerDomain() helper

New Commands

Command Description
nullify init Interactive setup wizard
nullify pentest Pentest scans (replaces dast)
nullify bughunt Cloud-based bug hunting
nullify chat Interactive AI chat
nullify findings Unified cross-scanner findings
nullify status Security posture overview
nullify ci gate CI quality gate
nullify ci report PR markdown report

New Packages

Package Purpose
internal/chat/ WebSocket chat client, REPL, renderer
internal/pentest/ Pentest scan logic (renamed from internal/dast/)
internal/wizard/ Step-based setup wizard
internal/testutil/ Mock client, test context helpers
internal/lib/git.go Shared git repo detection

Deleted

  • cmd/cli/cmd/dast.go, internal/dast/, internal/commands/dast_bridge.go, internal/mcp/tools_dast.go, internal/config/config.go

Test plan

  • go build ./... compiles cleanly
  • go vet ./... passes
  • go test -skip TestIntegration ./... — all tests pass (54 files changed, 3102 insertions)
  • nullify --help shows all new commands
  • nullify init — manual test of setup wizard flow
  • nullify chat "hello" — manual test of single-shot chat
  • nullify mcp serve — verify all new tools/resources/prompts registered
  • nullify pentest --help — verify flags and generated subcommands
  • nullify ci gate --severity-threshold critical — verify gate logic

🤖 Generated with Claude Code

@tim-thacker-nullify tim-thacker-nullify added the minor Minor version updates (features) label Feb 26, 2026
jonathanlam
jonathanlam previously approved these changes Feb 26, 2026
tim-thacker-nullify and others added 4 commits February 27, 2026 08:25
…d chat

Rename DAST to Pentest + BugHunt to reflect the product accurately,
add interactive chat with Nullify AI agents via WebSocket, setup wizard,
unified findings/status commands, CI/CD gate and report commands, and
expand MCP tool coverage from ~43% to ~80%+ of API endpoints.

New commands: init, pentest, bughunt, chat, findings, status, ci gate, ci report
New MCP: infrastructure, code reviews, comments, resources, prompts, composite workflows
New packages: internal/chat, internal/pentest, internal/wizard, internal/testutil, internal/lib/git

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add features section with key value props upfront
- Document all auth subcommands, MCP scoping, chat flags, and config
- Consolidate MCP setup into single block with tool/path table
- Add go install, install script --host flag, and requirements section
- Split commands into logical groups (core, auth, scanning, CI/CD, tooling)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix goroutine leak in chat mock test (select{} → error return)
- Fix URL encoding vulnerability using url.Values in query string builders
- Extract shared lib.BuildQueryString and lib.DoGet to eliminate DRY violations
- Wire HTTPClient interface into NullifyClient for testability
- Wire APIError into HandleError for structured error responses
- Replace interface{} with any throughout new code
- Add TTY detection before emitting ANSI codes in chat renderer
- Fix init-time capture bug in wizard SummaryStep
- Fix URL bug in status.go (malformed query string with &limit=1)
- Align CI type filter names with findings.go conventions
- Add tests for countFindings, severitiesAboveThreshold, summarizeFindingsResponse
- Add tests for BuildQueryString, DoGet, endpoint filtering

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nullify-reviewer
Copy link
Copy Markdown

Code review

Found 6 issues:

  1. lib.DoGet uses http.NewRequest without context, dropping cancellation/deadline signals from all callers. The function does not even accept a context.Context parameter. Every caller (ci gate, ci report, findings, status) has a ctx available but cannot pass it through. PR fix: address security issues, bugs, dead code, and context propagation #125 established the pattern of using http.NewRequestWithContext in internal/client/client.go — this new helper regresses that fix.

cli/internal/lib/http.go

Lines 37 to 40 in ca46f9d

func DoGet(httpClient Doer, baseURL, path string) (string, error) {
req, err := http.NewRequest("GET", baseURL+path, nil)
if err != nil {
return "", err

  1. ci gate silently continues on all lib.DoGet errors (if err != nil { continue }). If every scanner endpoint fails due to an expired token, unreachable host, or any other error, totalFindings remains 0 and the gate exits 0 with "Gate passed" — providing false assurance in CI with no findings actually checked.

cli/cmd/cli/cmd/ci.go

Lines 79 to 82 in ca46f9d

body, err := lib.DoGet(nullifyClient.HttpClient, nullifyClient.BaseURL, ep.path+qs)
if err != nil {
continue
}

  1. writeMCPConfig creates directories with 0755 and writes files with 0644. The existing auth package (internal/auth/config.go:73) uses 0600 for config files and 0700 for directories. MCP config files are written adjacent to sensitive credential paths. PR fix: address security issues, bugs, dead code, and context propagation #125 explicitly fixed this permission pattern — these new files repeat the old mistake.

if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
return os.WriteFile(path, append(data, '\n'), 0644)

  1. get_security_posture_summary builds request URLs as ep.path + qs + "&limit=1". When queryParams is empty, buildQueryString returns "", producing URLs like /sast/findings&limit=1 — missing the ? separator. Every call with no filter parameters hits an invalid URL. The correct approach (used by other tools in the same file, e.g. line 93) is to pass "limit", "1" as extra args to buildQueryString.

result, err := doGet(c, ep.path+qs+"&limit=1")

  1. The endpoint list in get_security_posture_summary still uses "dast_pentest" as the internal type name after the DAST→Pentest rename. All other references in this PR (e.g. allScannerEndpoints() in findings.go) use "pentest". MCP tool output from this tool will have "type": "dast_pentest" — inconsistent with every other scanner reference in the codebase.

{"dast_pentest", "/dast/pentest/findings"},

  1. get_security_posture_summary only queries 5 of the 7 scanner types — bughunt and cspm are absent from its endpoint list. The tool description claims coverage "across all finding types" but silently omits two categories. allScannerEndpoints() in findings.go covers all 7 types; this tool should match that set or explicitly document the omission.

mcp.WithDescription("Get a high-level security posture summary across all finding types (SAST, SCA dependencies, SCA containers, secrets, pentest). Returns counts by severity for each finding type. This is typically the first tool an AI agent should call to understand the overall security state."),
),
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
qs := buildQueryString(queryParams)
type findingCount struct {
Type string `json:"type"`
Error string `json:"error,omitempty"`
Data json.RawMessage `json:"data,omitempty"`
}
endpoints := []struct {
name string
path string
}{
{"sast", "/sast/findings"},
{"sca_dependencies", "/sca/dependencies/findings"},
{"sca_containers", "/sca/containers/findings"},
{"secrets", "/secrets/findings"},
{"dast_pentest", "/dast/pentest/findings"},
}

Generated by the Platform-Tools/code-review lambda using Claude Code Sonnet 4.6.

tim-thacker-nullify and others added 2 commits March 1, 2026 09:53
Resolve conflicts in go.mod and go.sum by taking newer dependency
versions from main (go-github/v84, mcp-go v0.44.1, logger v1.31.0)
while preserving gorilla/websocket from the feature branch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…p-invariant calls

Thread ctx from MCP tool handlers through doGet/doPost/doPut/doRequest so
HTTP requests respect cancellation. Hoist buildQueryString out of loops in
get_security_posture_summary and get_findings_for_repo. Also includes
context propagation for lib.DoGet, CI gate error handling improvements,
and tighter file permissions for MCP config.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@tim-thacker-nullify tim-thacker-nullify added this pull request to the merge queue Mar 1, 2026
Merged via the queue into main with commit 4c0db2c Mar 1, 2026
2 checks passed
@tim-thacker-nullify tim-thacker-nullify deleted the feat/comprehensive-cli-overhaul branch March 1, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor Minor version updates (features)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants