From 97ed48a9d36d5247b7c56b0022864af71b01b253 Mon Sep 17 00:00:00 2001 From: brittonhayes Date: Sun, 13 Mar 2022 17:28:44 -0700 Subject: [PATCH 1/4] feat: terminal user interface --- go.mod | 13 +++++- go.sum | 35 ++++++++++++++- internal/commands/hunt.go | 11 +++++ pkg/tui/component/banner.go | 9 ++++ pkg/tui/component/component.go | 13 ++++++ pkg/tui/component/config.go | 81 ++++++++++++++++++++++++++++++++++ pkg/tui/component/input.go | 45 +++++++++++++++++++ pkg/tui/component/output.go | 31 +++++++++++++ pkg/tui/component/table.go | 58 ++++++++++++++++++++++++ pkg/tui/tui.go | 77 ++++++++++++++++++++++++++++++++ 10 files changed, 370 insertions(+), 3 deletions(-) create mode 100644 pkg/tui/component/banner.go create mode 100644 pkg/tui/component/component.go create mode 100644 pkg/tui/component/config.go create mode 100644 pkg/tui/component/input.go create mode 100644 pkg/tui/component/output.go create mode 100644 pkg/tui/component/table.go create mode 100644 pkg/tui/tui.go diff --git a/go.mod b/go.mod index 3d1b1b8..edee8d9 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,12 @@ go 1.17 require ( github.com/BurntSushi/toml v1.0.0 github.com/Masterminds/sprig v2.22.0+incompatible + github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 + github.com/go-playground/validator/v10 v10.10.1 github.com/gookit/color v1.5.0 github.com/mitchellh/go-homedir v1.1.0 github.com/pkg/errors v0.9.1 + github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8 github.com/rs/zerolog v1.26.1 github.com/spf13/afero v1.8.1 github.com/spf13/cobra v1.3.0 @@ -20,18 +23,24 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect + github.com/gdamore/encoding v1.0.0 // indirect github.com/gitleaks/go-gitdiff v0.7.4 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect github.com/google/uuid v1.1.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/kr/text v0.2.0 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.5 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/pelletier/go-toml v1.9.4 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/cast v1.4.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -40,7 +49,7 @@ require ( golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect + golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect golang.org/x/text v0.3.7 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.66.2 // indirect ) diff --git a/go.sum b/go.sum index 8d11f75..e64fcec 100644 --- a/go.sum +++ b/go.sum @@ -123,6 +123,10 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 h1:QqwPZCwh/k1uYqq6uXSb9TRDhTkfQbO80v8zhnIe5zM= +github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gitleaks/go-gitdiff v0.7.4 h1:8vICc4moyRR2poklblThdQ0ckMet22mEvFJSxPsiDlk= github.com/gitleaks/go-gitdiff v0.7.4/go.mod h1:pKz0X4YzCKZs30BL+weqBIG7mx0jl4tF1uXV9ZyNvrA= @@ -133,6 +137,14 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.10.1 h1:uA0+amWMiglNZKZ9FJRKUAe9U3RX91eVn1JYXMWt7ig= +github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -278,12 +290,17 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -298,6 +315,8 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= @@ -330,6 +349,7 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -352,8 +372,15 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8 h1:xe+mmCnDN82KhC010l3NfYlA8ZbOuzbXAzSYBa6wbMc= +github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8/go.mod h1:WIfMkQNY+oq/mWwtsjOYHIZBuwthioY2srOmljJkTnk= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.25.0/go.mod h1:7KHcEGe0QZPOm2IE4Kpb5rTh6n1h2hIgS5OOnu1rUaI= github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc= @@ -439,6 +466,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e h1:1SzTfNOXwIS2oWiMF+6qu0OUDKb0dauo6MoDUQyu+yU= golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -521,6 +549,7 @@ golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -599,6 +628,7 @@ golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -624,6 +654,9 @@ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211210111614-af8b64212486 h1:5hpz5aRr+W1erYCL5JRhSUBJRph7l9XkNveoExlrKYk= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/internal/commands/hunt.go b/internal/commands/hunt.go index a6e423b..1d2eeaa 100644 --- a/internal/commands/hunt.go +++ b/internal/commands/hunt.go @@ -9,6 +9,7 @@ import ( "github.com/brittonhayes/pillager/pkg/format" "github.com/brittonhayes/pillager/pkg/hunter" + "github.com/brittonhayes/pillager/pkg/tui" "github.com/spf13/cobra" ) @@ -20,6 +21,7 @@ var ( reporter string templ string workers int + interactive bool ) // huntCmd represents the hunt command. @@ -67,6 +69,10 @@ var huntCmd = &cobra.Command{ return err } + if interactive { + return runInteractive(h) + } + results, err := h.Hunt() if err != nil { return err @@ -81,8 +87,13 @@ var huntCmd = &cobra.Command{ }, } +func runInteractive(h *hunter.Hunter) error { + return tui.Run(h) +} + func init() { rootCmd.AddCommand(huntCmd) + huntCmd.Flags().BoolVarP(&interactive, "interactive", "i", false, "run in interactive mode") huntCmd.Flags().IntVarP(&workers, "workers", "w", runtime.NumCPU(), "number of concurrent workers") huntCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "enable scanner verbose output") huntCmd.Flags().StringVarP(&level, "log-level", "l", "error", "set logging level") diff --git a/pkg/tui/component/banner.go b/pkg/tui/component/banner.go new file mode 100644 index 0000000..7d9bcbf --- /dev/null +++ b/pkg/tui/component/banner.go @@ -0,0 +1,9 @@ +package component + +const BannerText = ` +░█▀█░▀█▀░█░░░█░░░█▀█░█▀▀░█▀▀░█▀▄ +░█▀▀░░█░░█░░░█░░░█▀█░█░█░█▀▀░█▀▄ +░▀░░░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀▀▀░▀▀▀░▀░▀ + +Pillage filesystems for loot. +` diff --git a/pkg/tui/component/component.go b/pkg/tui/component/component.go new file mode 100644 index 0000000..4c5197f --- /dev/null +++ b/pkg/tui/component/component.go @@ -0,0 +1,13 @@ +package component + +import ( + "github.com/go-playground/validator/v10" + "github.com/rivo/tview" +) + +var validate *validator.Validate + +type Component interface { + View() *tview.Flex + Validate() error +} diff --git a/pkg/tui/component/config.go b/pkg/tui/component/config.go new file mode 100644 index 0000000..70e8672 --- /dev/null +++ b/pkg/tui/component/config.go @@ -0,0 +1,81 @@ +package component + +import ( + "fmt" + "log" + "path/filepath" + + "github.com/brittonhayes/pillager/pkg/hunter" + "github.com/gdamore/tcell/v2" + "github.com/go-playground/validator/v10" + "github.com/rivo/tview" +) + +type Config struct { + title string `validate:"required"` + hunter *hunter.Hunter +} + +func NewConfig(h *hunter.Hunter) *Config { + return &Config{ + title: " Current Config ", + hunter: h, + } +} + +func (c *Config) View() *tview.Flex { + if err := c.Validate(); err != nil { + log.Fatal(err) + } + + aboutFlex := tview.NewFlex().SetDirection(tview.FlexRow).AddItem(tview.NewTextView().SetText(BannerText).SetTextAlign(tview.AlignCenter), 0, 1, false) + + currentFlex := c.currentConfigView() + aboutFlex.SetBorder(false).SetBorderPadding(1, 1, 1, 1) + currentFlex.SetBorder(false).SetTitle(c.title).SetBorderPadding(0, 0, 1, 1) + + flex := tview.NewFlex().SetDirection(tview.FlexRow). + AddItem(aboutFlex, 0, 1, false). + AddItem(currentFlex, 0, 2, false) + return flex +} + +func (c *Config) Validate() error { + validate = validator.New() + return validate.Struct(c) +} + +func (c *Config) currentConfigView() *tview.Flex { + absScanPath, err := filepath.Abs(c.hunter.ScanPath) + if err != nil { + absScanPath = c.hunter.ScanPath + } + + rulesText := c.hunter.Gitleaks.Description + if c.hunter.Gitleaks.Description == "" { + rulesText = "No description found in gitleaks configuration" + } + + flex := tview.NewFlex().SetDirection(tview.FlexRow). + AddItem(c.newItem("ScanPath", absScanPath), 0, 1, false). + AddItem(c.newItem("Rules", rulesText), 0, 1, false). + AddItem(c.newItem("Format", fmt.Sprintf("%T", c.hunter.Reporter)[7:]), 3, 0, false). + AddItem(c.newItem("NumWorkers", fmt.Sprintf("%d", c.hunter.Workers)), 3, 0, false). + AddItem(c.newItem("Verbose", fmt.Sprintf("%v", c.hunter.Verbose)), 3, 0, false). + AddItem(c.newItem("Redact", fmt.Sprintf("%v", c.hunter.Redact)), 3, 0, false) + + return flex +} + +func (c *Config) newItem(title, text string) *tview.Form { + t := tview.NewForm() + + t.AddInputField("", text, 0, nil, nil) + + t.SetBorder(true). + SetBorderPadding(0, 0, 1, 1). + SetTitle(fmt.Sprintf(" %s ", title)). + SetTitleAlign(0). + SetTitleColor(tcell.ColorGray) + return t +} diff --git a/pkg/tui/component/input.go b/pkg/tui/component/input.go new file mode 100644 index 0000000..490a2a1 --- /dev/null +++ b/pkg/tui/component/input.go @@ -0,0 +1,45 @@ +package component + +import ( + "log" + + "github.com/gdamore/tcell/v2" + "github.com/go-playground/validator/v10" + "github.com/rivo/tview" +) + +type Input struct { + handler func(key tcell.Key) `validate:"required"` + label string `validate:"required"` + title string `validate:"required"` +} + +func NewInput(label string, handler func(key tcell.Key)) *Input { + return &Input{ + title: " scan path ", + label: label, + handler: handler, + } +} + +func (i *Input) View() *tview.Flex { + if err := i.Validate(); err != nil { + log.Fatal(err) + } + + inputField := tview.NewInputField(). + SetLabel(i.label). + SetDoneFunc(i.handler). + SetFieldWidth(0) + + flex := tview.NewFlex().SetDirection(tview.FlexColumn). + AddItem(inputField, 0, 1, true) + flex.SetBorder(true).SetTitle(i.title).SetBorderPadding(0, 1, 1, 1) + + return flex +} + +func (i *Input) Validate() error { + validate = validator.New() + return validate.Struct(i) +} diff --git a/pkg/tui/component/output.go b/pkg/tui/component/output.go new file mode 100644 index 0000000..125628e --- /dev/null +++ b/pkg/tui/component/output.go @@ -0,0 +1,31 @@ +package component + +import ( + "log" + + "github.com/go-playground/validator/v10" + "github.com/rivo/tview" +) + +type Output struct{} + +func NewOutput() *Output { + return &Output{} +} + +func (o *Output) View() *tview.Flex { + if err := o.Validate(); err != nil { + log.Fatal(err) + } + + flex := tview.NewFlex(). + SetDirection(tview.FlexRow). + AddItem(tview.NewTextView().SetTextAlign(tview.AlignCenter), 0, 1, false) + + return flex +} + +func (o *Output) Validate() error { + validate = validator.New() + return validate.Struct(o) +} diff --git a/pkg/tui/component/table.go b/pkg/tui/component/table.go new file mode 100644 index 0000000..a05977f --- /dev/null +++ b/pkg/tui/component/table.go @@ -0,0 +1,58 @@ +package component + +import ( + "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" + "github.com/zricethezav/gitleaks/v8/report" +) + +type Table struct { + findings []report.Finding +} + +func NewTable(rows []report.Finding) *Table { + return &Table{ + findings: rows, + } +} + +func (t *Table) View() tview.Primitive { + if len(t.findings) == 0 { + return tview.NewTextView().SetText("No results") + } + + table := tview.NewTable(). + SetBorders(true) + + columns := []string{ + "FILE", + "TYPE", + "SECRET", + } + + for index, column := range columns { + table.SetCell(0, index, + tview.NewTableCell(column). + SetTextColor(tcell.ColorLightCyan). + SetAlign(tview.AlignCenter)) + } + + for i := 0; i < len(t.findings); i++ { + table.SetCell(i+1, 0, + tview.NewTableCell(t.findings[i].File). + SetTextColor(tcell.ColorLightCyan). + SetAlign(tview.AlignCenter)) + + table.SetCell(i+1, 1, + tview.NewTableCell(t.findings[i].Description). + SetTextColor(tcell.ColorLightCyan). + SetAlign(tview.AlignCenter)) + + table.SetCell(i+1, 2, + tview.NewTableCell(t.findings[i].Secret). + SetTextColor(tcell.ColorLightCyan). + SetAlign(tview.AlignCenter)) + } + + return table +} diff --git a/pkg/tui/tui.go b/pkg/tui/tui.go new file mode 100644 index 0000000..4a1ede5 --- /dev/null +++ b/pkg/tui/tui.go @@ -0,0 +1,77 @@ +package tui + +import ( + "log" + "os" + + "github.com/brittonhayes/pillager/pkg/hunter" + "github.com/brittonhayes/pillager/pkg/tui/component" + "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" +) + +// theme holds the color theme for the tview TUI. +var theme = tview.Theme{ + PrimitiveBackgroundColor: tcell.Color(272727), + ContrastBackgroundColor: tcell.Color(448488), + MoreContrastBackgroundColor: tcell.ColorGreen, + BorderColor: tcell.ColorWhite, + TitleColor: tcell.ColorWhite, + GraphicsColor: tcell.ColorWhite, + PrimaryTextColor: tcell.ColorWhite, + SecondaryTextColor: tcell.ColorYellow, + TertiaryTextColor: tcell.ColorGreen, + InverseTextColor: tcell.Color(448488), + ContrastSecondaryTextColor: tcell.ColorDarkCyan, +} + +func Run(h *hunter.Hunter) error { + app := tview.NewApplication() + tview.Styles = theme + + configView := component.NewConfig(h).View() + outputView := component.NewOutput().View() + + content := tview.NewFlex(). + AddItem(configView, 0, 1, true). + AddItem(outputView, 0, 2, false) + + content.SetBorder(true). + SetTitle(" pillager "). + SetBorderPadding(1, 1, 1, 1) + + label := "Enter Scan Path: " + input := component.NewInput(label, func(key tcell.Key) { + if key != tcell.KeyEnter { + return + } + + findings, err := h.Hunt() + if err != nil { + log.Printf("\n\n%v", err) + } + + table := component.NewTable(findings).View() + outputView.Clear().AddItem(table, 0, 1, false) + }).View() + + flex := tview.NewFlex().SetDirection(tview.FlexRow). + AddItem(content, 0, 3, false). + AddItem(input, 0, 1, false) + + app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + switch event.Rune() { + case 'i': + app.SetFocus(input) + case 'c': + app.SetFocus(content) + case 'q': + app.Stop() + os.Exit(0) + } + + return event + }) + + return app.SetRoot(flex, true).SetFocus(input).EnableMouse(true).Run() +} From 315264ecf0f39b992a88a4456d27f750834da7d3 Mon Sep 17 00:00:00 2001 From: brittonhayes Date: Sun, 13 Mar 2022 18:51:00 -0700 Subject: [PATCH 2/4] feat: bare bones interactive tui --- .golangci.yaml | 1 - internal/commands/hunt.go | 2 ++ pkg/format/format.go | 2 ++ pkg/format/report.go | 12 ++++++++ pkg/tui/component/config.go | 6 ++-- pkg/tui/component/output.go | 15 +++++++--- pkg/tui/component/table.go | 4 ++- pkg/tui/tui.go | 57 ++++++++++++++++++------------------- 8 files changed, 60 insertions(+), 39 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 7307ed4..7cdf1ea 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -6,7 +6,6 @@ linters: - godot - godox - dupl - - exhaustive - funlen - gocritic - goprintffuncname diff --git a/internal/commands/hunt.go b/internal/commands/hunt.go index 7ed50b5..20f0fcf 100644 --- a/internal/commands/hunt.go +++ b/internal/commands/hunt.go @@ -95,6 +95,8 @@ var huntCmd = &cobra.Command{ } func runInteractive(h *hunter.Hunter) error { + h.Verbose = false + h.Debug = false return tui.Run(h) } diff --git a/pkg/format/format.go b/pkg/format/format.go index 461a59b..e765868 100644 --- a/pkg/format/format.go +++ b/pkg/format/format.go @@ -11,6 +11,8 @@ func StringToReporter(s string) Reporter { switch strings.ToLower(s) { case "json": return JSON{} + case "raw": + return Raw{} case "yaml": return YAML{} case "toml": diff --git a/pkg/format/report.go b/pkg/format/report.go index ab4ada3..2dd372f 100644 --- a/pkg/format/report.go +++ b/pkg/format/report.go @@ -19,6 +19,18 @@ type Reporter interface { type JSON struct{} func (j JSON) Report(w io.Writer, findings []report.Finding) error { + encoder := json.NewEncoder(w) + encoder.SetIndent("", "\t") + if err := encoder.Encode(&findings); err != nil { + return err + } + + return nil +} + +type Raw struct{} + +func (r Raw) Report(w io.Writer, findings []report.Finding) error { encoder := json.NewEncoder(w) if err := encoder.Encode(&findings); err != nil { return err diff --git a/pkg/tui/component/config.go b/pkg/tui/component/config.go index 70e8672..05eccf5 100644 --- a/pkg/tui/component/config.go +++ b/pkg/tui/component/config.go @@ -67,10 +67,10 @@ func (c *Config) currentConfigView() *tview.Flex { return flex } -func (c *Config) newItem(title, text string) *tview.Form { - t := tview.NewForm() +func (c *Config) newItem(title, text string) *tview.TextView { + t := tview.NewTextView() - t.AddInputField("", text, 0, nil, nil) + t.SetText(text) t.SetBorder(true). SetBorderPadding(0, 0, 1, 1). diff --git a/pkg/tui/component/output.go b/pkg/tui/component/output.go index 125628e..cc5020b 100644 --- a/pkg/tui/component/output.go +++ b/pkg/tui/component/output.go @@ -7,10 +7,14 @@ import ( "github.com/rivo/tview" ) -type Output struct{} +type Output struct { + text string +} -func NewOutput() *Output { - return &Output{} +func NewOutput(text string) *Output { + return &Output{ + text: text, + } } func (o *Output) View() *tview.Flex { @@ -18,9 +22,12 @@ func (o *Output) View() *tview.Flex { log.Fatal(err) } + body := tview.NewTextView().SetScrollable(true).SetTextAlign(tview.AlignLeft).SetText(o.text) + body.SetBorder(true).SetBorderPadding(0, 1, 1, 1) + flex := tview.NewFlex(). SetDirection(tview.FlexRow). - AddItem(tview.NewTextView().SetTextAlign(tview.AlignCenter), 0, 1, false) + AddItem(body, 0, 1, false) return flex } diff --git a/pkg/tui/component/table.go b/pkg/tui/component/table.go index a05977f..d3f20f5 100644 --- a/pkg/tui/component/table.go +++ b/pkg/tui/component/table.go @@ -18,7 +18,9 @@ func NewTable(rows []report.Finding) *Table { func (t *Table) View() tview.Primitive { if len(t.findings) == 0 { - return tview.NewTextView().SetText("No results") + fallback := tview.NewTextView().SetText("No results").SetTextColor(tcell.ColorOrange) + fallback.SetBorder(true).SetBorderPadding(0, 1, 1, 1) + return fallback } table := tview.NewTable(). diff --git a/pkg/tui/tui.go b/pkg/tui/tui.go index 4a1ede5..83781d4 100644 --- a/pkg/tui/tui.go +++ b/pkg/tui/tui.go @@ -1,6 +1,8 @@ package tui import ( + "bytes" + "fmt" "log" "os" @@ -30,48 +32,43 @@ func Run(h *hunter.Hunter) error { tview.Styles = theme configView := component.NewConfig(h).View() - outputView := component.NewOutput().View() + outputView := component.NewOutput("press enter to search").View() - content := tview.NewFlex(). - AddItem(configView, 0, 1, true). - AddItem(outputView, 0, 2, false) - - content.SetBorder(true). - SetTitle(" pillager "). + container := tview.NewFlex() + container.SetBorder(true). + SetTitle(fmt.Sprintf(" %s ", "pillager")). SetBorderPadding(1, 1, 1, 1) - label := "Enter Scan Path: " - input := component.NewInput(label, func(key tcell.Key) { - if key != tcell.KeyEnter { - return - } + container.AddItem(configView, 0, 1, false).AddItem(outputView, 0, 2, false) - findings, err := h.Hunt() - if err != nil { - log.Printf("\n\n%v", err) - } + flex := tview.NewFlex().SetDirection(tview.FlexRow).AddItem(container, 0, 3, true) + + app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + switch event.Key() { + case tcell.KeyEnter: - table := component.NewTable(findings).View() - outputView.Clear().AddItem(table, 0, 1, false) - }).View() + findings, err := h.Hunt() + if err != nil { + log.Printf("\n\n%v", err) + } - flex := tview.NewFlex().SetDirection(tview.FlexRow). - AddItem(content, 0, 3, false). - AddItem(input, 0, 1, false) + buf := bytes.NewBuffer(nil) + if err := h.Report(buf, findings); err != nil { + log.Printf("\n\n%v", err) + } - app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { - switch event.Rune() { - case 'i': - app.SetFocus(input) - case 'c': - app.SetFocus(content) - case 'q': + outputView.Clear().AddItem(component.NewOutput(buf.String()).View(), 0, 2, false) + + case tcell.KeyClear: app.Stop() os.Exit(0) + + default: + return event } return event }) - return app.SetRoot(flex, true).SetFocus(input).EnableMouse(true).Run() + return app.SetRoot(flex, true).EnableMouse(true).Run() } From 207d335dd840b64b0d7db046064947cb7e8c77b6 Mon Sep 17 00:00:00 2001 From: brittonhayes Date: Sun, 20 Mar 2022 19:47:51 -0700 Subject: [PATCH 3/4] feat: terminal user interface with bubbletea --- .pre-commit-config.yaml | 2 +- go.mod | 25 +++++--- go.sum | 81 ++++++++++++++++++------- internal/commands/hunt.go | 8 +-- pkg/tui/component/banner.go | 9 --- pkg/tui/component/component.go | 13 ---- pkg/tui/component/config.go | 81 ------------------------- pkg/tui/component/input.go | 45 -------------- pkg/tui/component/output.go | 38 ------------ pkg/tui/component/table.go | 60 ------------------- pkg/tui/model/keymap.go | 49 +++++++++++++++ pkg/tui/model/model.go | 106 +++++++++++++++++++++++++++++++++ pkg/tui/model/spinner.go | 17 ++++++ pkg/tui/model/table.go | 37 ++++++++++++ pkg/tui/model/view.go | 91 ++++++++++++++++++++++++++++ pkg/tui/style/style.go | 22 +++++++ pkg/tui/tui.go | 74 ----------------------- 17 files changed, 401 insertions(+), 357 deletions(-) delete mode 100644 pkg/tui/component/banner.go delete mode 100644 pkg/tui/component/component.go delete mode 100644 pkg/tui/component/config.go delete mode 100644 pkg/tui/component/input.go delete mode 100644 pkg/tui/component/output.go delete mode 100644 pkg/tui/component/table.go create mode 100644 pkg/tui/model/keymap.go create mode 100644 pkg/tui/model/model.go create mode 100644 pkg/tui/model/spinner.go create mode 100644 pkg/tui/model/table.go create mode 100644 pkg/tui/model/view.go create mode 100644 pkg/tui/style/style.go delete mode 100644 pkg/tui/tui.go diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 925f136..db4819a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ repos: - repo: https://github.com/golangci/golangci-lint - rev: v1.44.2 + rev: v1.45.0 hooks: - id: golangci-lint diff --git a/go.mod b/go.mod index edee8d9..a3857f5 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,14 @@ go 1.17 require ( github.com/BurntSushi/toml v1.0.0 github.com/Masterminds/sprig v2.22.0+incompatible - github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 - github.com/go-playground/validator/v10 v10.10.1 + github.com/brittonhayes/glitter v0.2.0 + github.com/charmbracelet/bubbles v0.10.3 + github.com/charmbracelet/bubbletea v0.20.0 + github.com/charmbracelet/lipgloss v0.5.0 + github.com/evertras/bubble-table v0.8.4 github.com/gookit/color v1.5.0 github.com/mitchellh/go-homedir v1.1.0 github.com/pkg/errors v0.9.1 - github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8 github.com/rs/zerolog v1.26.1 github.com/spf13/afero v1.8.1 github.com/spf13/cobra v1.3.0 @@ -22,25 +24,29 @@ require ( require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect + github.com/atotto/clipboard v0.1.4 // indirect + github.com/containerd/console v1.0.3 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/gdamore/encoding v1.0.0 // indirect github.com/gitleaks/go-gitdiff v0.7.4 // indirect - github.com/go-playground/locales v0.14.0 // indirect - github.com/go-playground/universal-translator v0.18.0 // indirect - github.com/google/uuid v1.1.2 // indirect + github.com/google/uuid v1.2.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/leodido/go-urn v1.2.1 // indirect + github.com/kr/pretty v0.3.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.5 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 // indirect github.com/pelletier/go-toml v1.9.4 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.8.0 // indirect github.com/spf13/cast v1.4.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -49,7 +55,8 @@ require ( golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect - golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect + golang.org/x/term v0.0.0-20210422114643-f5beecf764ed // indirect golang.org/x/text v0.3.7 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.66.2 // indirect ) diff --git a/go.sum b/go.sum index e64fcec..7788e64 100644 --- a/go.sum +++ b/go.sum @@ -72,16 +72,36 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/brittonhayes/glitter v0.2.0 h1:p3p+WAgFvkPwFtN5rzjT9fx1c1UYiWYmRgl5op1/gtQ= +github.com/brittonhayes/glitter v0.2.0/go.mod h1:wFAjLUmCA/0EqSReefEwQ46eJGEtmJmLiuimGN0K33c= +github.com/brittonhayes/pkg v0.5.1/go.mod h1:Nl7GzQY5iqM9wwztYdOpqpGpbjJhrYOXEExdFS0MFKE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charmbracelet/bubbles v0.8.0/go.mod h1:5WX1sSSjNCgCrzvRMN/z23HxvWaa+AI16Ch0KPZPeDs= +github.com/charmbracelet/bubbles v0.10.3 h1:fKarbRaObLn/DCsZO4Y3vKCwRUzynQD9L+gGev1E/ho= +github.com/charmbracelet/bubbles v0.10.3/go.mod h1:jOA+DUF1rjZm7gZHcNyIVW+YrBPALKfpGVdJu8UiJsA= +github.com/charmbracelet/bubbletea v0.13.1/go.mod h1:tp9tr9Dadh0PLhgiwchE5zZJXm5543JYjHG9oY+5qSg= +github.com/charmbracelet/bubbletea v0.14.0/go.mod h1:b5lOf5mLjMg1tRn1HVla54guZB+jvsyV0yYAQja95zE= +github.com/charmbracelet/bubbletea v0.19.3/go.mod h1:VuXF2pToRxDUHcBUcPmCRUHRvFATM4Ckb/ql1rBl3KA= +github.com/charmbracelet/bubbletea v0.20.0 h1:/b8LEPgCbNr7WWZ2LuE/BV1/r4t5PyYJtDb+J3vpwxc= +github.com/charmbracelet/bubbletea v0.20.0/go.mod h1:zpkze1Rioo4rJELjRyGlm9T2YNou1Fm4LIJQSa5QMEM= +github.com/charmbracelet/harmonica v0.1.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= +github.com/charmbracelet/lipgloss v0.1.2/go.mod h1:5D8zradw52m7QmxRF6QgwbwJi9je84g8MkWiGN07uKg= +github.com/charmbracelet/lipgloss v0.2.1/go.mod h1:uiZUfrHLQN14I0lJ5591WtcHyY1X76pOIPSaRKPY6dE= +github.com/charmbracelet/lipgloss v0.4.0/go.mod h1:vmdkHvce7UzX6xkyf4cca8WlwdQ5RQr8fzta+xl7BOM= +github.com/charmbracelet/lipgloss v0.5.0 h1:lulQHuVeodSgDez+3rGiuxlPVXSnhth442DATR2/8t8= +github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -98,6 +118,10 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -117,16 +141,14 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= +github.com/evertras/bubble-table v0.8.4 h1:8VP8IPHASriMQuQYu69PWayHfPNAB3KM8nSwaS6spJ4= +github.com/evertras/bubble-table v0.8.4/go.mod h1:OZq5XdGas3p4OZi/if0NLoAeIlgpCDvCgcGG6lD4oGY= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= -github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 h1:QqwPZCwh/k1uYqq6uXSb9TRDhTkfQbO80v8zhnIe5zM= -github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gitleaks/go-gitdiff v0.7.4 h1:8vICc4moyRR2poklblThdQ0ckMet22mEvFJSxPsiDlk= github.com/gitleaks/go-gitdiff v0.7.4/go.mod h1:pKz0X4YzCKZs30BL+weqBIG7mx0jl4tF1uXV9ZyNvrA= @@ -137,14 +159,6 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.10.1 h1:uA0+amWMiglNZKZ9FJRKUAe9U3RX91eVn1JYXMWt7ig= -github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -196,6 +210,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -216,8 +231,9 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -297,8 +313,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= @@ -314,7 +330,12 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -343,6 +364,17 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.7.2/go.mod h1:ct2L5N2lmix82RaY3bMWwVu/jUFc9Ule0KGDCiKYPh8= +github.com/muesli/termenv v0.8.1/go.mod h1:kzt/D/4a88RoheZmwfqorY3A+tnsSMA9HJC/fQSFKo0= +github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw= +github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= +github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 h1:QANkGiGr39l1EESqrE0gZw0/AJNYzIvoGLhIoVYtluI= +github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -372,8 +404,7 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8 h1:xe+mmCnDN82KhC010l3NfYlA8ZbOuzbXAzSYBa6wbMc= -github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8/go.mod h1:WIfMkQNY+oq/mWwtsjOYHIZBuwthioY2srOmljJkTnk= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -390,10 +421,12 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM= +github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -463,10 +496,12 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e h1:1SzTfNOXwIS2oWiMF+6qu0OUDKb0dauo6MoDUQyu+yU= golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -549,7 +584,6 @@ golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -619,16 +653,18 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -654,9 +690,8 @@ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211210111614-af8b64212486 h1:5hpz5aRr+W1erYCL5JRhSUBJRph7l9XkNveoExlrKYk= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210422114643-f5beecf764ed h1:Ei4bQjjpYUsS4efOUz+5Nz++IVkHk87n2zBA0NxBWc0= +golang.org/x/term v0.0.0-20210422114643-f5beecf764ed/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/internal/commands/hunt.go b/internal/commands/hunt.go index 20f0fcf..4e3e678 100644 --- a/internal/commands/hunt.go +++ b/internal/commands/hunt.go @@ -10,7 +10,8 @@ import ( "github.com/brittonhayes/pillager/pkg/format" "github.com/brittonhayes/pillager/pkg/hunter" "github.com/brittonhayes/pillager/pkg/rules" - "github.com/brittonhayes/pillager/pkg/tui" + "github.com/brittonhayes/pillager/pkg/tui/model" + tea "github.com/charmbracelet/bubbletea" "github.com/spf13/cobra" ) @@ -95,9 +96,8 @@ var huntCmd = &cobra.Command{ } func runInteractive(h *hunter.Hunter) error { - h.Verbose = false - h.Debug = false - return tui.Run(h) + p := tea.NewProgram(model.NewModel(h), tea.WithAltScreen()) + return p.Start() } func init() { diff --git a/pkg/tui/component/banner.go b/pkg/tui/component/banner.go deleted file mode 100644 index 7d9bcbf..0000000 --- a/pkg/tui/component/banner.go +++ /dev/null @@ -1,9 +0,0 @@ -package component - -const BannerText = ` -░█▀█░▀█▀░█░░░█░░░█▀█░█▀▀░█▀▀░█▀▄ -░█▀▀░░█░░█░░░█░░░█▀█░█░█░█▀▀░█▀▄ -░▀░░░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀▀▀░▀▀▀░▀░▀ - -Pillage filesystems for loot. -` diff --git a/pkg/tui/component/component.go b/pkg/tui/component/component.go deleted file mode 100644 index 4c5197f..0000000 --- a/pkg/tui/component/component.go +++ /dev/null @@ -1,13 +0,0 @@ -package component - -import ( - "github.com/go-playground/validator/v10" - "github.com/rivo/tview" -) - -var validate *validator.Validate - -type Component interface { - View() *tview.Flex - Validate() error -} diff --git a/pkg/tui/component/config.go b/pkg/tui/component/config.go deleted file mode 100644 index 05eccf5..0000000 --- a/pkg/tui/component/config.go +++ /dev/null @@ -1,81 +0,0 @@ -package component - -import ( - "fmt" - "log" - "path/filepath" - - "github.com/brittonhayes/pillager/pkg/hunter" - "github.com/gdamore/tcell/v2" - "github.com/go-playground/validator/v10" - "github.com/rivo/tview" -) - -type Config struct { - title string `validate:"required"` - hunter *hunter.Hunter -} - -func NewConfig(h *hunter.Hunter) *Config { - return &Config{ - title: " Current Config ", - hunter: h, - } -} - -func (c *Config) View() *tview.Flex { - if err := c.Validate(); err != nil { - log.Fatal(err) - } - - aboutFlex := tview.NewFlex().SetDirection(tview.FlexRow).AddItem(tview.NewTextView().SetText(BannerText).SetTextAlign(tview.AlignCenter), 0, 1, false) - - currentFlex := c.currentConfigView() - aboutFlex.SetBorder(false).SetBorderPadding(1, 1, 1, 1) - currentFlex.SetBorder(false).SetTitle(c.title).SetBorderPadding(0, 0, 1, 1) - - flex := tview.NewFlex().SetDirection(tview.FlexRow). - AddItem(aboutFlex, 0, 1, false). - AddItem(currentFlex, 0, 2, false) - return flex -} - -func (c *Config) Validate() error { - validate = validator.New() - return validate.Struct(c) -} - -func (c *Config) currentConfigView() *tview.Flex { - absScanPath, err := filepath.Abs(c.hunter.ScanPath) - if err != nil { - absScanPath = c.hunter.ScanPath - } - - rulesText := c.hunter.Gitleaks.Description - if c.hunter.Gitleaks.Description == "" { - rulesText = "No description found in gitleaks configuration" - } - - flex := tview.NewFlex().SetDirection(tview.FlexRow). - AddItem(c.newItem("ScanPath", absScanPath), 0, 1, false). - AddItem(c.newItem("Rules", rulesText), 0, 1, false). - AddItem(c.newItem("Format", fmt.Sprintf("%T", c.hunter.Reporter)[7:]), 3, 0, false). - AddItem(c.newItem("NumWorkers", fmt.Sprintf("%d", c.hunter.Workers)), 3, 0, false). - AddItem(c.newItem("Verbose", fmt.Sprintf("%v", c.hunter.Verbose)), 3, 0, false). - AddItem(c.newItem("Redact", fmt.Sprintf("%v", c.hunter.Redact)), 3, 0, false) - - return flex -} - -func (c *Config) newItem(title, text string) *tview.TextView { - t := tview.NewTextView() - - t.SetText(text) - - t.SetBorder(true). - SetBorderPadding(0, 0, 1, 1). - SetTitle(fmt.Sprintf(" %s ", title)). - SetTitleAlign(0). - SetTitleColor(tcell.ColorGray) - return t -} diff --git a/pkg/tui/component/input.go b/pkg/tui/component/input.go deleted file mode 100644 index 490a2a1..0000000 --- a/pkg/tui/component/input.go +++ /dev/null @@ -1,45 +0,0 @@ -package component - -import ( - "log" - - "github.com/gdamore/tcell/v2" - "github.com/go-playground/validator/v10" - "github.com/rivo/tview" -) - -type Input struct { - handler func(key tcell.Key) `validate:"required"` - label string `validate:"required"` - title string `validate:"required"` -} - -func NewInput(label string, handler func(key tcell.Key)) *Input { - return &Input{ - title: " scan path ", - label: label, - handler: handler, - } -} - -func (i *Input) View() *tview.Flex { - if err := i.Validate(); err != nil { - log.Fatal(err) - } - - inputField := tview.NewInputField(). - SetLabel(i.label). - SetDoneFunc(i.handler). - SetFieldWidth(0) - - flex := tview.NewFlex().SetDirection(tview.FlexColumn). - AddItem(inputField, 0, 1, true) - flex.SetBorder(true).SetTitle(i.title).SetBorderPadding(0, 1, 1, 1) - - return flex -} - -func (i *Input) Validate() error { - validate = validator.New() - return validate.Struct(i) -} diff --git a/pkg/tui/component/output.go b/pkg/tui/component/output.go deleted file mode 100644 index cc5020b..0000000 --- a/pkg/tui/component/output.go +++ /dev/null @@ -1,38 +0,0 @@ -package component - -import ( - "log" - - "github.com/go-playground/validator/v10" - "github.com/rivo/tview" -) - -type Output struct { - text string -} - -func NewOutput(text string) *Output { - return &Output{ - text: text, - } -} - -func (o *Output) View() *tview.Flex { - if err := o.Validate(); err != nil { - log.Fatal(err) - } - - body := tview.NewTextView().SetScrollable(true).SetTextAlign(tview.AlignLeft).SetText(o.text) - body.SetBorder(true).SetBorderPadding(0, 1, 1, 1) - - flex := tview.NewFlex(). - SetDirection(tview.FlexRow). - AddItem(body, 0, 1, false) - - return flex -} - -func (o *Output) Validate() error { - validate = validator.New() - return validate.Struct(o) -} diff --git a/pkg/tui/component/table.go b/pkg/tui/component/table.go deleted file mode 100644 index d3f20f5..0000000 --- a/pkg/tui/component/table.go +++ /dev/null @@ -1,60 +0,0 @@ -package component - -import ( - "github.com/gdamore/tcell/v2" - "github.com/rivo/tview" - "github.com/zricethezav/gitleaks/v8/report" -) - -type Table struct { - findings []report.Finding -} - -func NewTable(rows []report.Finding) *Table { - return &Table{ - findings: rows, - } -} - -func (t *Table) View() tview.Primitive { - if len(t.findings) == 0 { - fallback := tview.NewTextView().SetText("No results").SetTextColor(tcell.ColorOrange) - fallback.SetBorder(true).SetBorderPadding(0, 1, 1, 1) - return fallback - } - - table := tview.NewTable(). - SetBorders(true) - - columns := []string{ - "FILE", - "TYPE", - "SECRET", - } - - for index, column := range columns { - table.SetCell(0, index, - tview.NewTableCell(column). - SetTextColor(tcell.ColorLightCyan). - SetAlign(tview.AlignCenter)) - } - - for i := 0; i < len(t.findings); i++ { - table.SetCell(i+1, 0, - tview.NewTableCell(t.findings[i].File). - SetTextColor(tcell.ColorLightCyan). - SetAlign(tview.AlignCenter)) - - table.SetCell(i+1, 1, - tview.NewTableCell(t.findings[i].Description). - SetTextColor(tcell.ColorLightCyan). - SetAlign(tview.AlignCenter)) - - table.SetCell(i+1, 2, - tview.NewTableCell(t.findings[i].Secret). - SetTextColor(tcell.ColorLightCyan). - SetAlign(tview.AlignCenter)) - } - - return table -} diff --git a/pkg/tui/model/keymap.go b/pkg/tui/model/keymap.go new file mode 100644 index 0000000..df804d2 --- /dev/null +++ b/pkg/tui/model/keymap.go @@ -0,0 +1,49 @@ +package model + +import ( + "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" + tea "github.com/charmbracelet/bubbletea" +) + +type keymap struct { + filter key.Binding + start key.Binding + quit key.Binding +} + +func newKeyMap() keymap { + k := keymap{ + filter: key.NewBinding( + key.WithKeys(tea.KeyCtrlI.String()), + key.WithHelp("ctrl+i", "filter"), + ), + start: key.NewBinding( + key.WithKeys(tea.KeyEnter.String()), + key.WithHelp("enter", "start"), + ), + quit: key.NewBinding( + key.WithKeys("q", "ctrl+c"), + key.WithHelp("q", "quit"), + ), + } + + // Assert complies with help interface + var _ help.KeyMap = k + + return k +} + +// FullHelp returns keybindings for the expanded help view. It's part of the +// key.Map interface. +func (k keymap) FullHelp() [][]key.Binding { + return [][]key.Binding{ + {k.start, k.quit}, + {k.filter}, + } +} + +// ShortHelp returns keybindings for the short help view. +func (k keymap) ShortHelp() []key.Binding { + return []key.Binding{k.start, k.quit} +} diff --git a/pkg/tui/model/model.go b/pkg/tui/model/model.go new file mode 100644 index 0000000..3c7adb8 --- /dev/null +++ b/pkg/tui/model/model.go @@ -0,0 +1,106 @@ +package model + +import ( + "time" + + "github.com/brittonhayes/pillager/pkg/hunter" + "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/spinner" + tea "github.com/charmbracelet/bubbletea" + "github.com/evertras/bubble-table/table" + "github.com/zricethezav/gitleaks/v8/report" +) + +const ( + columnKeyID = "id" + columnKeyFile = "file" + columnKeySecret = "secret" + columnKeyRule = "rule" +) + +type model struct { + keymap keymap + loading loading + header header + body body + footer footer + table table.Model + + hunter *hunter.Hunter + results []report.Finding + err error +} + +type loading struct { + active bool + spinner spinner.Model +} + +type header struct { + title string + subtitle string +} + +type body struct { + toast string + message string +} + +type footer struct { + help help.Model +} + +type resultsMsg struct{ results []report.Finding } + +type errMsg struct{ err error } + +// For messages that contain errors it's often handy to also implement the +// error interface on the message. +func (e errMsg) Error() string { return e.err.Error() } + +func NewModel(hunt *hunter.Hunter) model { + // Create table model + t := newTable() + // Create keymap + k := newKeyMap() + // Create loading spinner. + s := newSpinner() + // Create help model + h := help.New() + + return model{ + hunter: hunt, + header: header{ + title: "Pillager", + subtitle: "Hunt inside the file system for valuable information", + }, + table: t, + body: body{ + message: "", + }, + footer: footer{ + help: h, + }, + loading: loading{ + active: false, + spinner: s, + }, + keymap: k, + } +} + +func startScan(h *hunter.Hunter) tea.Cmd { + return func() tea.Msg { + h.Debug = false + h.Verbose = false + time.Sleep(2 * time.Second) + results, err := h.Hunt() + if err != nil { + // There was an error making our request. Wrap the error we received + // in a message and return it. + return errMsg{err} + } + + return resultsMsg{results} + } +} diff --git a/pkg/tui/model/spinner.go b/pkg/tui/model/spinner.go new file mode 100644 index 0000000..d4324f2 --- /dev/null +++ b/pkg/tui/model/spinner.go @@ -0,0 +1,17 @@ +package model + +import ( + "time" + + "github.com/brittonhayes/pillager/pkg/tui/style" + "github.com/charmbracelet/bubbles/spinner" +) + +func newSpinner() spinner.Model { + // Create loading spinner. + s := spinner.NewModel() + s.Spinner = spinner.Dot + s.Style = style.Spinner + s.HideFor = 250 * time.Millisecond + return s +} diff --git a/pkg/tui/model/table.go b/pkg/tui/model/table.go new file mode 100644 index 0000000..d2a30fe --- /dev/null +++ b/pkg/tui/model/table.go @@ -0,0 +1,37 @@ +package model + +import ( + "strings" + + "github.com/brittonhayes/pillager/pkg/tui/style" + "github.com/evertras/bubble-table/table" + "github.com/zricethezav/gitleaks/v8/report" +) + +func newTable() table.Model { + // Create table model + t := table.New([]table.Column{ + table.NewColumn(columnKeyID, strings.ToTitle(columnKeyID), 4), + table.NewColumn(columnKeyFile, strings.Title(columnKeyFile), 40), + table.NewColumn(columnKeySecret, strings.Title(columnKeySecret), 30), + table.NewColumn(columnKeyRule, strings.Title(columnKeyRule), 35), + }).WithPageSize(10).Focused(true).HighlightStyle(style.Highlight) + return t +} + +func addRowData(data []report.Finding) []table.Row { + rows := []table.Row{} + + for i, entry := range data { + row := table.NewRow(table.RowData{ + columnKeyID: i + 1, + columnKeyFile: entry.File, + columnKeySecret: entry.Secret, + columnKeyRule: entry.Description, + }) + + rows = append(rows, row) + } + + return rows +} diff --git a/pkg/tui/model/view.go b/pkg/tui/model/view.go new file mode 100644 index 0000000..647a2e8 --- /dev/null +++ b/pkg/tui/model/view.go @@ -0,0 +1,91 @@ +package model + +import ( + "fmt" + "time" + + "github.com/brittonhayes/pillager/pkg/tui/style" + "github.com/charmbracelet/bubbles/key" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +func (m model) Init() tea.Cmd { + return nil +} + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var ( + cmd tea.Cmd + cmds []tea.Cmd + ) + + m.table, cmd = m.table.Update(msg) + cmds = append(cmds, cmd) + + m.loading.spinner, cmd = m.loading.spinner.Update(msg) + cmds = append(cmds, cmd) + + switch msg := msg.(type) { + + case resultsMsg: + m.loading.active = false + m.results = msg.results + rows := addRowData(msg.results) + m.table = m.table.WithRows(rows) + return m, nil + + case errMsg: + // There was an error. Note it in the model. And tell the runtime + // we're done and want to quit. + m.err = msg.err + time.Sleep(3 * time.Second) + return m, tea.Quit + + case tea.KeyMsg: + switch { + case key.Matches(msg, m.keymap.quit): + return m, tea.Quit + + case key.Matches(msg, m.keymap.filter): + m.table = m.table.StartFilterTyping() + return m, nil + + case key.Matches(msg, m.keymap.start): + m.loading.active = true + return m, tea.Batch(startScan(m.hunter), m.loading.spinner.Tick) + default: + return m, m.loading.spinner.Tick + } + } + + return m, tea.Batch(cmds...) +} + +func (m model) View() string { + // If there's an error, print it out and don't do anything else. + if m.err != nil { + return style.Error.Render(fmt.Sprintf("\n Uh oh! Something went\n%s\n\n", m.err)) + } + + m.body.toast = "" + if m.loading.active || m.loading.spinner.Visible() { + m.body.message = fmt.Sprintf("Scanning for secrets in %q with %d workers %s", m.hunter.ScanPath, m.hunter.Workers, m.loading.spinner.View()) + } else if m.results != nil { + + m.body.toast = style.Highlight.Render(fmt.Sprintf("Found %d secrets in path %q", len(m.results), m.hunter.ScanPath)) + m.body.message = m.table.View() + } + + title := style.Title.Render(m.header.title) + subtitle := style.Subtitle.Render(m.header.subtitle) + header := style.Header.Render(title + "\n" + subtitle) + + message := style.Text.Render(m.body.message) + body := lipgloss.JoinVertical(lipgloss.Top, m.body.toast, message) + + help := style.Faint.MarginTop(2).Render(m.footer.help.View(m.keymap)) + footer := lipgloss.JoinVertical(lipgloss.Top, help) + + return lipgloss.JoinVertical(lipgloss.Top, header, body, footer) +} diff --git a/pkg/tui/style/style.go b/pkg/tui/style/style.go new file mode 100644 index 0000000..9332604 --- /dev/null +++ b/pkg/tui/style/style.go @@ -0,0 +1,22 @@ +package style + +import ( + "github.com/brittonhayes/glitter/theme" + "github.com/charmbracelet/lipgloss" +) + +var ( + styleBase = lipgloss.NewStyle().Align(lipgloss.Left).Foreground(theme.Nord.Primary.Foreground) + + Header = lipgloss.NewStyle().Padding(2, 0).Align(lipgloss.Left) + + Title = styleBase.Copy().Bold(true).Foreground(theme.Nord.Normal.Cyan) + Subtitle = Title.Copy().Bold(false).Italic(true).Foreground(theme.Nord.Primary.Foreground) + + Text = styleBase.Copy().Padding(0).Bold(false) + Error = styleBase.Copy().Foreground(theme.Nord.Normal.Yellow) + Faint = styleBase.Copy().Foreground(theme.Nord.Primary.DimForeground).Faint(true) + Spinner = styleBase.Copy().Foreground(theme.Nord.Bright.Cyan) + Highlight = lipgloss.NewStyle().Foreground(theme.Nord.Normal.Cyan) + Accent = lipgloss.NewStyle().Foreground(theme.Nord.Bright.Magenta) +) diff --git a/pkg/tui/tui.go b/pkg/tui/tui.go deleted file mode 100644 index 83781d4..0000000 --- a/pkg/tui/tui.go +++ /dev/null @@ -1,74 +0,0 @@ -package tui - -import ( - "bytes" - "fmt" - "log" - "os" - - "github.com/brittonhayes/pillager/pkg/hunter" - "github.com/brittonhayes/pillager/pkg/tui/component" - "github.com/gdamore/tcell/v2" - "github.com/rivo/tview" -) - -// theme holds the color theme for the tview TUI. -var theme = tview.Theme{ - PrimitiveBackgroundColor: tcell.Color(272727), - ContrastBackgroundColor: tcell.Color(448488), - MoreContrastBackgroundColor: tcell.ColorGreen, - BorderColor: tcell.ColorWhite, - TitleColor: tcell.ColorWhite, - GraphicsColor: tcell.ColorWhite, - PrimaryTextColor: tcell.ColorWhite, - SecondaryTextColor: tcell.ColorYellow, - TertiaryTextColor: tcell.ColorGreen, - InverseTextColor: tcell.Color(448488), - ContrastSecondaryTextColor: tcell.ColorDarkCyan, -} - -func Run(h *hunter.Hunter) error { - app := tview.NewApplication() - tview.Styles = theme - - configView := component.NewConfig(h).View() - outputView := component.NewOutput("press enter to search").View() - - container := tview.NewFlex() - container.SetBorder(true). - SetTitle(fmt.Sprintf(" %s ", "pillager")). - SetBorderPadding(1, 1, 1, 1) - - container.AddItem(configView, 0, 1, false).AddItem(outputView, 0, 2, false) - - flex := tview.NewFlex().SetDirection(tview.FlexRow).AddItem(container, 0, 3, true) - - app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { - switch event.Key() { - case tcell.KeyEnter: - - findings, err := h.Hunt() - if err != nil { - log.Printf("\n\n%v", err) - } - - buf := bytes.NewBuffer(nil) - if err := h.Report(buf, findings); err != nil { - log.Printf("\n\n%v", err) - } - - outputView.Clear().AddItem(component.NewOutput(buf.String()).View(), 0, 2, false) - - case tcell.KeyClear: - app.Stop() - os.Exit(0) - - default: - return event - } - - return event - }) - - return app.SetRoot(flex, true).EnableMouse(true).Run() -} From e3acef2bef4d89d716e6c300434b9fdb63c3eccf Mon Sep 17 00:00:00 2001 From: brittonhayes Date: Mon, 21 Mar 2022 02:00:07 -0700 Subject: [PATCH 4/4] feat: inspect mode for table results --- go.mod | 4 +++ internal/commands/hunt.go | 3 +- pkg/tui/model/keymap.go | 38 +++++++++++++++--------- pkg/tui/model/model.go | 58 ++++++++++++++++++++++--------------- pkg/tui/model/table.go | 52 +++++++++++++++++++++++++++------ pkg/tui/model/table_test.go | 25 ++++++++++++++++ pkg/tui/model/view.go | 52 ++++++++++++++++++++++----------- pkg/tui/style/style.go | 4 +-- 8 files changed, 170 insertions(+), 66 deletions(-) create mode 100644 pkg/tui/model/table_test.go diff --git a/go.mod b/go.mod index a3857f5..494069d 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/spf13/afero v1.8.1 github.com/spf13/cobra v1.3.0 github.com/spf13/viper v1.10.1 + github.com/stretchr/testify v1.7.0 github.com/zricethezav/gitleaks/v8 v8.3.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -26,6 +27,7 @@ require ( github.com/Masterminds/semver v1.5.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/containerd/console v1.0.3 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/gitleaks/go-gitdiff v0.7.4 // indirect github.com/google/uuid v1.2.0 // indirect @@ -45,6 +47,7 @@ require ( github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 // indirect github.com/pelletier/go-toml v1.9.4 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.8.0 // indirect github.com/spf13/cast v1.4.1 // indirect @@ -59,4 +62,5 @@ require ( golang.org/x/text v0.3.7 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.66.2 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/internal/commands/hunt.go b/internal/commands/hunt.go index 4e3e678..7f29ac9 100644 --- a/internal/commands/hunt.go +++ b/internal/commands/hunt.go @@ -96,7 +96,8 @@ var huntCmd = &cobra.Command{ } func runInteractive(h *hunter.Hunter) error { - p := tea.NewProgram(model.NewModel(h), tea.WithAltScreen()) + m := model.NewModel(h) + p := tea.NewProgram(m, tea.WithAltScreen()) return p.Start() } diff --git a/pkg/tui/model/keymap.go b/pkg/tui/model/keymap.go index df804d2..7dff25b 100644 --- a/pkg/tui/model/keymap.go +++ b/pkg/tui/model/keymap.go @@ -7,24 +7,34 @@ import ( ) type keymap struct { - filter key.Binding - start key.Binding - quit key.Binding + Filter key.Binding + Inspect key.Binding + Start key.Binding + Quit key.Binding + Help key.Binding } func newKeyMap() keymap { k := keymap{ - filter: key.NewBinding( - key.WithKeys(tea.KeyCtrlI.String()), - key.WithHelp("ctrl+i", "filter"), + Inspect: key.NewBinding( + key.WithKeys("i"), + key.WithHelp("i", "inspect"), ), - start: key.NewBinding( + Filter: key.NewBinding( + key.WithKeys("f"), + key.WithHelp("f", "filter"), + ), + Start: key.NewBinding( key.WithKeys(tea.KeyEnter.String()), - key.WithHelp("enter", "start"), + key.WithHelp("enter", "scan"), + ), + Quit: key.NewBinding( + key.WithKeys("ctrl+c", "q", tea.KeyEsc.String()), + key.WithHelp("ctrl+c/esc/q", "quit"), ), - quit: key.NewBinding( - key.WithKeys("q", "ctrl+c"), - key.WithHelp("q", "quit"), + Help: key.NewBinding( + key.WithKeys("?"), + key.WithHelp("?", "help"), ), } @@ -38,12 +48,12 @@ func newKeyMap() keymap { // key.Map interface. func (k keymap) FullHelp() [][]key.Binding { return [][]key.Binding{ - {k.start, k.quit}, - {k.filter}, + {k.Help, k.Start, k.Inspect}, + {k.Quit}, } } // ShortHelp returns keybindings for the short help view. func (k keymap) ShortHelp() []key.Binding { - return []key.Binding{k.start, k.quit} + return []key.Binding{k.Help, k.Start, k.Inspect, k.Quit} } diff --git a/pkg/tui/model/model.go b/pkg/tui/model/model.go index 3c7adb8..a60867e 100644 --- a/pkg/tui/model/model.go +++ b/pkg/tui/model/model.go @@ -1,21 +1,17 @@ package model import ( - "time" + "fmt" + "os" "github.com/brittonhayes/pillager/pkg/hunter" + "github.com/brittonhayes/pillager/pkg/tui/style" "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" "github.com/evertras/bubble-table/table" "github.com/zricethezav/gitleaks/v8/report" -) - -const ( - columnKeyID = "id" - columnKeyFile = "file" - columnKeySecret = "secret" - columnKeyRule = "rule" + "golang.org/x/term" ) type model struct { @@ -23,12 +19,15 @@ type model struct { loading loading header header body body - footer footer + help help.Model table table.Model hunter *hunter.Hunter results []report.Finding err error + + width int + height int } type loading struct { @@ -41,26 +40,34 @@ type header struct { subtitle string } -type body struct { - toast string - message string +type selected struct { + visible bool + text string } -type footer struct { - help help.Model +type body struct { + toast string + selected selected + message string } type resultsMsg struct{ results []report.Finding } type errMsg struct{ err error } -// For messages that contain errors it's often handy to also implement the -// error interface on the message. -func (e errMsg) Error() string { return e.err.Error() } +func (e errMsg) Error() string { + return fmt.Sprintf("🔥 Uh oh! Well that's not good. Looks like something went wrong and the application has exited: \n\n%s\n\n%s", style.Error.Render(e.Error()), "Press [q] to quit.") +} func NewModel(hunt *hunter.Hunter) model { + width, height, err := term.GetSize(int(os.Stdout.Fd())) + if err != nil { + width = 80 + height = 24 + } + // Create table model - t := newTable() + t := newTable(width) // Create keymap k := newKeyMap() // Create loading spinner. @@ -68,7 +75,7 @@ func NewModel(hunt *hunter.Hunter) model { // Create help model h := help.New() - return model{ + m := model{ hunter: hunt, header: header{ title: "Pillager", @@ -78,22 +85,27 @@ func NewModel(hunt *hunter.Hunter) model { body: body{ message: "", }, - footer: footer{ - help: h, - }, + help: h, loading: loading{ active: false, spinner: s, }, keymap: k, + width: width, + height: height, } + + return m +} + +func (m model) Dimensions() (int, int) { + return m.width, m.height } func startScan(h *hunter.Hunter) tea.Cmd { return func() tea.Msg { h.Debug = false h.Verbose = false - time.Sleep(2 * time.Second) results, err := h.Hunt() if err != nil { // There was an error making our request. Wrap the error we received diff --git a/pkg/tui/model/table.go b/pkg/tui/model/table.go index d2a30fe..fda51df 100644 --- a/pkg/tui/model/table.go +++ b/pkg/tui/model/table.go @@ -1,6 +1,7 @@ package model import ( + "path/filepath" "strings" "github.com/brittonhayes/pillager/pkg/tui/style" @@ -8,14 +9,29 @@ import ( "github.com/zricethezav/gitleaks/v8/report" ) -func newTable() table.Model { +const ( + columnKeyID = "id" + columnWidthID = 4 + + columnKeyFile = "file" + columnWidthFile = 20 + + columnKeySecret = "secret" + columnWidthSecret = 35 + + columnKeyRule = "rule" + columnWidthRule = 35 +) + +func newTable(width int) table.Model { // Create table model - t := table.New([]table.Column{ - table.NewColumn(columnKeyID, strings.ToTitle(columnKeyID), 4), - table.NewColumn(columnKeyFile, strings.Title(columnKeyFile), 40), - table.NewColumn(columnKeySecret, strings.Title(columnKeySecret), 30), - table.NewColumn(columnKeyRule, strings.Title(columnKeyRule), 35), - }).WithPageSize(10).Focused(true).HighlightStyle(style.Highlight) + cols := []table.Column{ + table.NewColumn(columnKeyID, strings.ToTitle(columnKeyID), limit(columnWidthID, width)), + table.NewColumn(columnKeyFile, strings.Title(columnKeyFile), limit(columnWidthFile, width)).WithFiltered(true), + table.NewColumn(columnKeySecret, strings.Title(columnKeySecret), limit(columnWidthSecret, width)), + table.NewColumn(columnKeyRule, strings.Title(columnKeyRule), limit(columnWidthRule, width)).WithFiltered(true), + } + t := table.New(cols).Focused(true).HighlightStyle(style.Highlight).WithPageSize(10) return t } @@ -25,13 +41,31 @@ func addRowData(data []report.Finding) []table.Row { for i, entry := range data { row := table.NewRow(table.RowData{ columnKeyID: i + 1, - columnKeyFile: entry.File, + columnKeyFile: filepath.Base(entry.File), columnKeySecret: entry.Secret, columnKeyRule: entry.Description, }) - rows = append(rows, row) } return rows } + +func limit(w, max int) int { + if w > max { + return max + } + + return w +} + +func truncate(s string, width int) string { + t := s + if len(s) > width { + if width > 3 { + width -= 3 + } + t = "..." + s[width:] + } + return t +} diff --git a/pkg/tui/model/table_test.go b/pkg/tui/model/table_test.go new file mode 100644 index 0000000..a8a41a3 --- /dev/null +++ b/pkg/tui/model/table_test.go @@ -0,0 +1,25 @@ +package model + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTruncate(t *testing.T) { + t.Run("truncates string", func(t *testing.T) { + arg := "foobarbaz" + want := "...barbaz" + got := truncate(arg, 3) + + assert.Equal(t, want, got) + }) + + t.Run("truncates filepath", func(t *testing.T) { + arg := "C:/Users/foo/bar/baz/qux/quux.go" + want := "...Users/foo/bar/baz/qux/quux.go" + got := truncate(arg, 6) + + assert.Equal(t, want, got) + }) +} diff --git a/pkg/tui/model/view.go b/pkg/tui/model/view.go index 647a2e8..417d0fa 100644 --- a/pkg/tui/model/view.go +++ b/pkg/tui/model/view.go @@ -2,12 +2,13 @@ package model import ( "fmt" - "time" + "strings" "github.com/brittonhayes/pillager/pkg/tui/style" "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" + "github.com/zricethezav/gitleaks/v8/report" ) func (m model) Init() tea.Cmd { @@ -27,7 +28,6 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmds = append(cmds, cmd) switch msg := msg.(type) { - case resultsMsg: m.loading.active = false m.results = msg.results @@ -36,22 +36,23 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil case errMsg: - // There was an error. Note it in the model. And tell the runtime - // we're done and want to quit. m.err = msg.err - time.Sleep(3 * time.Second) - return m, tea.Quit + return m, nil case tea.KeyMsg: switch { - case key.Matches(msg, m.keymap.quit): + case key.Matches(msg, m.keymap.Quit): return m, tea.Quit - case key.Matches(msg, m.keymap.filter): - m.table = m.table.StartFilterTyping() + case key.Matches(msg, m.keymap.Inspect): + m.body.selected.visible = !m.body.selected.visible + return m, nil + + case key.Matches(msg, m.keymap.Help): + m.help.ShowAll = !m.help.ShowAll return m, nil - case key.Matches(msg, m.keymap.start): + case key.Matches(msg, m.keymap.Start): m.loading.active = true return m, tea.Batch(startScan(m.hunter), m.loading.spinner.Tick) default: @@ -62,19 +63,36 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, tea.Batch(cmds...) } +func (m model) selectedView() string { + if m.results != nil && m.body.selected.visible { + w := &strings.Builder{} + err := m.hunter.Report(w, []report.Finding{m.selectedRow()}) + if err != nil { + m.err = err + } + + selected := style.Faint.Italic(true).Render(fmt.Sprintf("\nCurrent selection:\n%s", w.String())) + return selected + } + return "" +} + +func (m model) selectedRow() report.Finding { + return m.results[m.table.HighlightedRow().Data[columnKeyID].(int)-1] +} + func (m model) View() string { - // If there's an error, print it out and don't do anything else. if m.err != nil { - return style.Error.Render(fmt.Sprintf("\n Uh oh! Something went\n%s\n\n", m.err)) + return m.err.Error() } m.body.toast = "" if m.loading.active || m.loading.spinner.Visible() { - m.body.message = fmt.Sprintf("Scanning for secrets in %q with %d workers %s", m.hunter.ScanPath, m.hunter.Workers, m.loading.spinner.View()) + m.body.message = fmt.Sprintf("%s Scanning for secrets in %q with %d workers", m.loading.spinner.View(), m.hunter.ScanPath, m.hunter.Workers) } else if m.results != nil { - m.body.toast = style.Highlight.Render(fmt.Sprintf("Found %d secrets in path %q", len(m.results), m.hunter.ScanPath)) m.body.message = m.table.View() + m.body.message += m.selectedView() } title := style.Title.Render(m.header.title) @@ -82,10 +100,10 @@ func (m model) View() string { header := style.Header.Render(title + "\n" + subtitle) message := style.Text.Render(m.body.message) - body := lipgloss.JoinVertical(lipgloss.Top, m.body.toast, message) + body := lipgloss.JoinVertical(lipgloss.Top, m.body.toast, message, m.body.selected.text) - help := style.Faint.MarginTop(2).Render(m.footer.help.View(m.keymap)) - footer := lipgloss.JoinVertical(lipgloss.Top, help) + help := m.help.View(m.keymap) + footer := help return lipgloss.JoinVertical(lipgloss.Top, header, body, footer) } diff --git a/pkg/tui/style/style.go b/pkg/tui/style/style.go index 9332604..bebf80b 100644 --- a/pkg/tui/style/style.go +++ b/pkg/tui/style/style.go @@ -8,13 +8,13 @@ import ( var ( styleBase = lipgloss.NewStyle().Align(lipgloss.Left).Foreground(theme.Nord.Primary.Foreground) - Header = lipgloss.NewStyle().Padding(2, 0).Align(lipgloss.Left) + Header = lipgloss.NewStyle().Padding(1, 0).Align(lipgloss.Left) Title = styleBase.Copy().Bold(true).Foreground(theme.Nord.Normal.Cyan) Subtitle = Title.Copy().Bold(false).Italic(true).Foreground(theme.Nord.Primary.Foreground) Text = styleBase.Copy().Padding(0).Bold(false) - Error = styleBase.Copy().Foreground(theme.Nord.Normal.Yellow) + Error = styleBase.Copy().MarginLeft(2).Foreground(theme.Nord.Normal.Yellow) Faint = styleBase.Copy().Foreground(theme.Nord.Primary.DimForeground).Faint(true) Spinner = styleBase.Copy().Foreground(theme.Nord.Bright.Cyan) Highlight = lipgloss.NewStyle().Foreground(theme.Nord.Normal.Cyan)