Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions cmd/stepsecurity-dev-machine-guard/main.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
package main

import (
"context"
"encoding/json"
"fmt"
"io"
"os"
"runtime"
"time"

"github.com/step-security/dev-machine-guard/internal/buildinfo"
"github.com/step-security/dev-machine-guard/internal/cli"
"github.com/step-security/dev-machine-guard/internal/config"
"github.com/step-security/dev-machine-guard/internal/detector"
"github.com/step-security/dev-machine-guard/internal/device"
"github.com/step-security/dev-machine-guard/internal/executor"
"github.com/step-security/dev-machine-guard/internal/launchd"
"github.com/step-security/dev-machine-guard/internal/output"
"github.com/step-security/dev-machine-guard/internal/progress"
"github.com/step-security/dev-machine-guard/internal/scan"
"github.com/step-security/dev-machine-guard/internal/schtasks"
Expand Down Expand Up @@ -160,6 +167,17 @@ func main() {
}

default:
// --npmrc: focused, verbose pretty audit of npm config only.
// Bypasses all other detectors so the run is fast (~1s) and the
// output is exclusively about npm config — useful when the user is
// debugging a specific .npmrc / registry / token issue.
if cfg.NPMRCOnly {
if err := runNPMRCOnly(exec, cfg); err != nil {
log.Error("%v", err)
os.Exit(1)
}
return
}
// Community mode or auto-detect enterprise
switch {
case cfg.OutputFormatSet || cfg.HTMLOutputFile != "":
Expand All @@ -184,3 +202,49 @@ func main() {
}
}
}

// runNPMRCOnly executes only the npmrc detector and renders the verbose
// pretty view (or JSON when --json is also passed). Skips the inventory of
// IDEs / AI tools / brew / node / etc. for speed.
func runNPMRCOnly(exec executor.Executor, cfg *cli.Config) error {
ctx := context.Background()

// Resolve search dirs the same way the main scan does, so project-level
// .npmrc discovery walks the same tree.
searchDirs := make([]string, 0, len(cfg.SearchDirs))
for _, d := range cfg.SearchDirs {
if d == "$HOME" {
if u, err := exec.LoggedInUser(); err == nil {
d = u.HomeDir
}
}
searchDirs = append(searchDirs, d)
}

dev := device.Gather(ctx, exec)
loggedInUser, _ := exec.LoggedInUser()

d := detector.NewNPMRCDetector(exec)
audit := d.Detect(ctx, searchDirs, loggedInUser)
// Phase B: snapshot diff. Errors are non-fatal (would just produce a
// FirstRun=true diff next time around).
_ = detector.AttachDiff(ctx, exec, &audit, time.Now().Unix(), dev.Hostname)

// JSON path: emit just the audit object so callers can pipe it into jq
// without wading through the rest of ScanResult.
if cfg.OutputFormat == "json" {
enc := jsonEncoder(os.Stdout)
return enc.Encode(audit)
}

output.PrettyNPMRC(os.Stdout, &audit, dev, cfg.ColorMode)
return nil
}

// jsonEncoder returns a 2-space-indented JSON encoder that doesn't HTML-escape.
func jsonEncoder(w io.Writer) *json.Encoder {
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
enc.SetEscapeHTML(false)
return enc
}
5 changes: 5 additions & 0 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type Config struct {
EnableBrewScan *bool // nil=auto, true/false=explicit
EnablePythonScan *bool // nil=auto, true/false=explicit
IncludeBundledPlugins bool // --include-bundled-plugins: include bundled/platform plugins in output
NPMRCOnly bool // --npmrc: run only the npmrc audit, render verbose pretty output, skip everything else
SearchDirs []string // defaults to ["$HOME"]
}

Expand Down Expand Up @@ -87,6 +88,8 @@ func Parse(args []string) (*Config, error) {
cfg.EnablePythonScan = &v
case arg == "--include-bundled-plugins":
cfg.IncludeBundledPlugins = true
case arg == "--npmrc":
cfg.NPMRCOnly = true
case strings.HasPrefix(arg, "--color="):
mode := strings.TrimPrefix(arg, "--color=")
if mode != "auto" && mode != "always" && mode != "never" {
Expand Down Expand Up @@ -163,6 +166,8 @@ Options:
--enable-python-scan Enable Python package scanning
--disable-python-scan Disable Python package scanning
--include-bundled-plugins Include bundled/platform plugins in output (Windows)
--npmrc Run ONLY the npm config audit and print a verbose pretty view
(skips IDE / AI / Brew / Python / Node scans for speed)
--log-level=LEVEL Log level: error | warn | info | debug (default: info)
--verbose Shortcut for --log-level=debug
--color=WHEN Color mode: auto | always | never (default: auto)
Expand Down
Loading
Loading