Skip to content
Merged
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
56 changes: 4 additions & 52 deletions cmd/pipeleek/main.go
Original file line number Diff line number Diff line change
@@ -1,64 +1,16 @@
package main

import (
"encoding/json"
"io"
"os"

"github.com/CompassSecurity/pipeleek/internal/cmd"
"golang.org/x/term"
"github.com/CompassSecurity/pipeleek/pkg/logging"
)

var originalTermState *term.State

type TerminalRestoringWriter struct {
underlying io.Writer
}

func (w *TerminalRestoringWriter) Write(p []byte) (n int, err error) {
var logEntry map[string]interface{}
if err := json.Unmarshal(p, &logEntry); err == nil {
if level, ok := logEntry["level"].(string); ok && level == "fatal" {
_, _ = w.underlying.Write(p)
restoreTerminalState()
os.Exit(1)
}
}
return w.underlying.Write(p)
}

func main() {
saveTerminalState()
defer restoreTerminalState()

cmd.TerminalRestorer = restoreTerminalState
logging.InstallExitHandler()
defer logging.StopShortcutListeners()

err := cmd.Execute()
if err != nil {
os.Exit(1)
logging.Exit(1)
}
}

func saveTerminalState() {
// #nosec G115 -- os.Stdin file descriptor is provided by the runtime and fits into int on supported platforms
stdinFD := int(os.Stdin.Fd())
if term.IsTerminal(stdinFD) {
state, err := term.GetState(stdinFD)
if err == nil {
originalTermState = state
}
}
}

func restoreTerminalState() {
if originalTermState != nil {
// #nosec G115 -- os.Stdin file descriptor is provided by the runtime and fits into int on supported platforms
stdinFD := int(os.Stdin.Fd())
_ = term.Restore(stdinFD, originalTermState)
}
}

// GetTerminalRestoringWriter wraps the given writer to restore terminal state on fatal logs
func GetTerminalRestoringWriter(w io.Writer) io.Writer {
return &TerminalRestoringWriter{underlying: w}
}
5 changes: 5 additions & 0 deletions docs/introduction/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ pipeleek gl scan -u https://gitlab.com -t glpat-xxxxx --log-level=trace

You can change interactively between log levels by pressing `t`: Trace, `d`: Debug, `i`: info, `w`: Warn, `e`: Error.

Keyboard shortcuts are enabled only when standard input is a terminal. Pressing
Escape or Ctrl+C stops the shortcut listener and restores normal terminal input.
Terminal input is also restored when a command finishes, returns an error, logs
a fatal error, or completes graceful shutdown.

```bash
pipeleek gl scan -u https://gitlab.com -t glpat-[redacted] --secrets-verification=false --verbose
# Human Pressed d on keyboard
Expand Down
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module github.com/CompassSecurity/pipeleek
go 1.26.0

require (
atomicgo.dev/keyboard v0.2.10
code.gitea.io/sdk/gitea v0.25.1
github.com/CircleCI-Public/circleci-cli v0.1.38646
github.com/PuerkitoBio/goquery v1.13.0
Expand Down Expand Up @@ -76,7 +75,7 @@ require (
github.com/charlievieth/fastwalk v1.0.14 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/containerd/console v1.0.5 // indirect
github.com/containerd/console v1.0.5
github.com/couchbase/gocb/v2 v2.11.0 // indirect
github.com/couchbase/gocbcore/v10 v10.8.0 // indirect
github.com/couchbase/gocbcoreps v0.1.3 // indirect
Expand Down Expand Up @@ -237,7 +236,7 @@ require (
github.com/wandb/parallel v0.2.3
github.com/yosuke-furukawa/json5 v0.1.1
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sys v0.49.0 // indirect
golang.org/x/sys v0.49.0
golang.org/x/time v0.14.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1
Expand Down
21 changes: 0 additions & 21 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg=
atomicgo.dev/assert v0.0.2/go.mod h1:ut4NcI3QDdJtlmAxQULOmA13Gz6e2DWbSAS8RUOmNYQ=
atomicgo.dev/cursor v0.2.0 h1:H6XN5alUJ52FZZUkI7AlJbUc1aW38GWZalpYRPpoPOw=
atomicgo.dev/cursor v0.2.0/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU=
atomicgo.dev/keyboard v0.2.10 h1:v7mvUKUZLHIggxULEIuWbT+WkkyQSgdbA201EziAhHU=
atomicgo.dev/keyboard v0.2.10/go.mod h1:ap/z5ilnhLqYq852m6kPeTq5Z6aESGWu5mzRpJlC6aI=
atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs=
atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c=
cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI=
Expand Down Expand Up @@ -48,8 +40,6 @@ github.com/BobuSumisu/aho-corasick v1.0.3/go.mod h1:hm4jLcvZKI2vRF2WDU1N4p/jpWtp
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/CircleCI-Public/circleci-cli v0.1.38646 h1:kymjys07c2Vk3y/BnHpMz24sU0zhpGlwJm5n76KdJrs=
github.com/CircleCI-Public/circleci-cli v0.1.38646/go.mod h1:XeQQRIYyT7CYNMCI3BAkURiUxgzZZ1EKWqPkO9pTHNs=
github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4=
github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
Expand Down Expand Up @@ -296,8 +286,6 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dq
github.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8=
github.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc=
github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY=
github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA=
github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/graph-gophers/graphql-go v1.9.0 h1:yu0ucKHLc5qGpRwLYKIWtr9bOoxovkWasuBrPQwlHls=
Expand Down Expand Up @@ -356,10 +344,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
Expand Down Expand Up @@ -390,8 +375,6 @@ github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLO
github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
github.com/lucasjones/reggen v0.0.0-20200904144131-37ba4fa293bb h1:w1g9wNDIE/pHSTmAaUhv4TZQuPBS6GV3mMz5hkgziIU=
github.com/lucasjones/reggen v0.0.0-20200904144131-37ba4fa293bb/go.mod h1:5ELEyG+X8f+meRWHuqUOewBOhvHkl7M76pdGEansxW4=
github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e h1:Q6MvJtQK/iRcRtzAscm/zF23XxJlbECiGPyRicsX+Ak=
Expand Down Expand Up @@ -494,8 +477,6 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/pterm/pterm v0.12.83 h1:ie+YmGmA727VuhxBlyGr74Ks+7McV6kT99IB8EU80aA=
github.com/pterm/pterm v0.12.83/go.mod h1:xlgc6bFWyJIMtmLJvGim+L7jhSReilOlOnodeIYe4Tk=
github.com/rabbitmq/amqp091-go v1.14.0 h1:RSaT7aOKt/OrkVUyswPDW29lnRz9psuGmfZFBmLqLek=
github.com/rabbitmq/amqp091-go v1.14.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
github.com/rhysd/actionlint v1.7.12 h1:vQ4GeJN86C0QH+gTUQcs8McmK62OLT3kmakPMtEWYnY=
Expand Down Expand Up @@ -618,8 +599,6 @@ github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yosuke-furukawa/json5 v0.1.1 h1:0F9mNwTvOuDNH243hoPqvf+dxa5QsKnZzU20uNsh3ZI=
Expand Down
83 changes: 12 additions & 71 deletions internal/cmd/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package common

import (
"bytes"
"encoding/json"
"io"
"os"
"runtime"
Expand All @@ -15,7 +14,6 @@ import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"golang.org/x/term"
)

// Version information - set via ldflags during build
Expand All @@ -27,20 +25,16 @@ var (

// Log configuration
var (
originalTermState *term.State
JsonLogoutput bool
LogFile string
LogColor bool
LogDebug bool
LogLevel string
IgnoreProxy bool
JsonLogoutput bool
LogFile string
LogColor bool
LogDebug bool
LogLevel string
IgnoreProxy bool
// logFileCloser holds the currently open log file so it can be closed on cleanup.
logFileCloser io.Closer
)

// TerminalRestorer is a function that can be called to restore terminal state
var TerminalRestorer func()

// CustomWriter wraps an os.File with proper cross-platform newline handling
type CustomWriter struct {
Writer *os.File
Expand Down Expand Up @@ -73,55 +67,6 @@ func (cw *CustomWriter) Write(p []byte) (n int, err error) {
return originalLen, nil
}

// TerminalRestoringWriter wraps an io.Writer to restore terminal state on fatal logs
type TerminalRestoringWriter struct {
underlying io.Writer
}

func (w *TerminalRestoringWriter) Write(p []byte) (n int, err error) {
var logEntry map[string]interface{}
if err := json.Unmarshal(p, &logEntry); err == nil {
if level, ok := logEntry["level"].(string); ok && level == "fatal" {
_, _ = w.underlying.Write(p)
RestoreTerminalState()
os.Exit(1)
}
}
return w.underlying.Write(p)
}

// FatalHook is a zerolog hook that restores terminal state before fatal exits
type FatalHook struct{}

func (h FatalHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
if level == zerolog.FatalLevel {
if TerminalRestorer != nil {
TerminalRestorer()
}
}
}

// SaveTerminalState saves the current terminal state for later restoration
func SaveTerminalState() {
// #nosec G115 -- os.Stdin file descriptor is provided by the runtime and fits into int on supported platforms
stdinFD := int(os.Stdin.Fd())
if term.IsTerminal(stdinFD) {
state, err := term.GetState(stdinFD)
if err == nil {
originalTermState = state
}
}
}

// RestoreTerminalState restores the terminal to its saved state
func RestoreTerminalState() {
if originalTermState != nil {
// #nosec G115 -- os.Stdin file descriptor is provided by the runtime and fits into int on supported platforms
stdinFD := int(os.Stdin.Fd())
_ = term.Restore(stdinFD, originalTermState)
}
}

// InitLogger initializes the zerolog logger with the configured options
func InitLogger(cmd *cobra.Command) {
defaultOut := &CustomWriter{Writer: os.Stdout}
Expand All @@ -146,14 +91,12 @@ func InitLogger(cmd *cobra.Command) {
}
}

fatalHook := FatalHook{}

if JsonLogoutput {
// For JSON output, wrap with HitLevelWriter to transform level field
hitWriter := &logging.HitLevelWriter{}
hitWriter.SetOutput(defaultOut)
logging.SetGlobalHitWriter(hitWriter)
log.Logger = zerolog.New(hitWriter).With().Timestamp().Logger().Hook(fatalHook)
log.Logger = zerolog.New(hitWriter).With().Timestamp().Logger()
} else {
// For console output, use custom FormatLevel to color the hit level
output := zerolog.ConsoleWriter{
Expand All @@ -166,7 +109,7 @@ func InitLogger(cmd *cobra.Command) {
hitWriter := &logging.HitLevelWriter{}
hitWriter.SetOutput(&output)
logging.SetGlobalHitWriter(hitWriter)
log.Logger = zerolog.New(hitWriter).With().Timestamp().Logger().Hook(fatalHook)
log.Logger = zerolog.New(hitWriter).With().Timestamp().Logger()
}
}

Expand Down Expand Up @@ -264,19 +207,17 @@ func SetupPersistentPreRun(cmd *cobra.Command) {
InitLogger(c)
SetGlobalLogLevel(c)
httpclient.SetIgnoreProxy(IgnoreProxy)
go logging.ShortcutListeners(nil)
logging.ShortcutListeners(nil)
}
}

// Run executes the common startup sequence and runs the provided root command
func Run(rootCmd *cobra.Command) {
SaveTerminalState()
defer RestoreTerminalState()

TerminalRestorer = RestoreTerminalState
logging.InstallExitHandler()
defer logging.StopShortcutListeners()

err := rootCmd.Execute()
if err != nil {
os.Exit(1)
logging.Exit(1)
}
}
Loading
Loading