Files
trufflehog/pkg/tui/pages/source_select/source_select.go
Hon Kwokandmcastorina 4598244167 Use placeholder as default if field left empty and is required (#1642)
* Use placeholder as default if field left empty and is required

Co-authored-by: mcastorina <[email protected]>

* Drop unused func

* uncomment azure source

* update wording

---------

Co-authored-by: mcastorina <[email protected]>
2023-10-02 17:21:40 -07:00

224 lines
6.6 KiB
Go

package source_select
import (
"time"
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/trufflesecurity/trufflehog/v3/pkg/tui/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/tui/components/selector"
"github.com/trufflesecurity/trufflehog/v3/pkg/tui/styles"
)
// TODO: Review light theme styling
var (
titleStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFDF5")).
Background(lipgloss.Color(styles.Colors["bronze"])).
Padding(0, 1)
// FIXME: Hon pls help
errorStatusMessageStyle = lipgloss.NewStyle().
Foreground(lipgloss.AdaptiveColor{Dark: "#ff0000"}).
Render
selectedSourceItemStyle = lipgloss.NewStyle().
Border(lipgloss.NormalBorder(), false, false, false, true).
BorderForeground(lipgloss.AdaptiveColor{Dark: styles.Colors["sprout"], Light: styles.Colors["bronze"]}).
Foreground(lipgloss.AdaptiveColor{Dark: styles.Colors["sprout"], Light: styles.Colors["fern"]}).
Padding(0, 0, 0, 1)
selectedDescription = selectedSourceItemStyle.Copy().
Foreground(lipgloss.AdaptiveColor{Dark: styles.Colors["sprout"], Light: styles.Colors["sprout"]})
)
type listKeyMap struct {
toggleHelpMenu key.Binding
}
type (
SourceSelect struct {
common.Common
sourcesList list.Model
keys *listKeyMap
delegateKeys *delegateKeyMap
selector *selector.Selector
}
)
func New(c common.Common) *SourceSelect {
var (
delegateKeys = newDelegateKeyMap()
listKeys = &listKeyMap{
toggleHelpMenu: key.NewBinding(
key.WithKeys("H"),
key.WithHelp("H", "toggle help"),
),
}
)
// Make list of SourceItems.
SourceItems := []list.Item{
// Open source sources.
OssItem("Git", "Scan git repositories."),
OssItem("GitHub", "Scan GitHub repositories and/or organizations."),
OssItem("GitLab", "Scan GitLab repositories."),
OssItem("Filesystem", "Scan your filesystem by selecting what directories to scan."),
OssItem("AWS S3", "Scan Amazon S3 buckets."),
OssItem("CircleCI", "Scan CircleCI, a CI/CD platform."),
OssItem("Syslog", "Scan syslog, event data logs."),
OssItem("Docker", "Scan a Docker instance, a containerized application."),
OssItem("GCS (Google Cloud Storage)", "Scan a Google Cloud Storage instance."),
// Enterprise sources.
EnterpriseItem("Artifactory", "Scan JFrog Artifactory packages."),
EnterpriseItem("Azure Repos", "Scan Microsoft Azure repositories."),
EnterpriseItem("BitBucket", "Scan Atlassian's Git-based source code repository hosting service."),
EnterpriseItem("Buildkite", "Scan Buildkite, a CI/CD platform."),
EnterpriseItem("Confluence", "Scan Atlassian's web-based wiki and knowledge base."),
EnterpriseItem("Gerrit", "Scan Gerrit, a code collaboration tool"),
EnterpriseItem("Jenkins ", "Scan Jenkins, a CI/CD platform."),
EnterpriseItem("Jira", "Scan Atlassian's issue & project tracking software."),
EnterpriseItem("Slack", "Scan Slack, a messaging and communication platform."),
EnterpriseItem("Microsoft Teams", "Scan Microsoft Teams, a messaging and communication platform."),
EnterpriseItem("Microsoft Sharepoint", "Scan Microsoft Sharepoint, a collaboration and document management platform."),
EnterpriseItem("Google Drive", "Scan Google Drive, a cloud-based storage and file sync service."),
}
// Setup list
delegate := newSourceItemDelegate(delegateKeys)
delegate.Styles.SelectedTitle = selectedSourceItemStyle
delegate.Styles.SelectedDesc = selectedDescription
sourcesList := list.New(SourceItems, delegate, 0, 0)
sourcesList.Title = "Sources"
sourcesList.Styles.Title = titleStyle
sourcesList.StatusMessageLifetime = 10 * time.Second
sourcesList.AdditionalFullHelpKeys = func() []key.Binding {
return []key.Binding{
listKeys.toggleHelpMenu,
}
}
sourcesList.SetShowStatusBar(false)
sel := selector.New(c, []selector.IdentifiableItem{}, delegate)
return &SourceSelect{
Common: c,
sourcesList: sourcesList,
keys: listKeys,
delegateKeys: delegateKeys,
selector: sel,
}
}
func (m *SourceSelect) Init() tea.Cmd {
return nil
}
func (m *SourceSelect) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd
switch msg := msg.(type) {
case tea.WindowSizeMsg:
h, v := styles.AppStyle.GetFrameSize()
m.sourcesList.SetSize(msg.Width-h, msg.Height-v)
case tea.KeyMsg:
// Don't match any of the keys below if we're actively filtering.
if m.sourcesList.FilterState() == list.Filtering {
break
}
switch {
case key.Matches(msg, m.keys.toggleHelpMenu):
m.sourcesList.SetShowHelp(!m.sourcesList.ShowHelp())
return m, nil
}
}
// This will also call our delegate's update function.
newListModel, cmd := m.sourcesList.Update(msg)
m.sourcesList = newListModel
cmds = append(cmds, cmd)
if m.selector != nil {
sel, cmd := m.selector.Update(msg)
m.selector = sel.(*selector.Selector)
cmds = append(cmds, cmd)
}
return m, tea.Batch(cmds...)
}
func (m *SourceSelect) View() string {
return styles.AppStyle.Render(m.sourcesList.View())
}
func (m *SourceSelect) ShortHelp() []key.Binding {
// TODO: actually return something
return nil
}
func (m *SourceSelect) FullHelp() [][]key.Binding {
// TODO: actually return something
return nil
}
func newSourceItemDelegate(keys *delegateKeyMap) list.DefaultDelegate {
d := list.NewDefaultDelegate()
d.UpdateFunc = func(msg tea.Msg, m *list.Model) tea.Cmd {
selectedSourceItem, ok := m.SelectedItem().(SourceItem)
if !ok {
return nil
}
if msg, ok := msg.(tea.KeyMsg); ok && key.Matches(msg, keys.choose) {
if selectedSourceItem.enterprise {
return m.NewStatusMessage(errorStatusMessageStyle(
"That's an enterprise only source. Learn more at trufflesecurity.com",
))
}
return func() tea.Msg {
return selector.SelectMsg{IdentifiableItem: selectedSourceItem}
}
}
return nil
}
help := []key.Binding{keys.choose}
d.ShortHelpFunc = func() []key.Binding { return help }
d.FullHelpFunc = func() [][]key.Binding { return [][]key.Binding{help} }
return d
}
type delegateKeyMap struct {
choose key.Binding
}
// Additional short help entries. This satisfies the help.KeyMap interface and
// is entirely optional.
func (d delegateKeyMap) ShortHelp() []key.Binding {
return []key.Binding{d.choose}
}
// Additional full help entries. This satisfies the help.KeyMap interface and
// is entirely optional.
func (d delegateKeyMap) FullHelp() [][]key.Binding {
return [][]key.Binding{{d.choose}}
}
func newDelegateKeyMap() *delegateKeyMap {
return &delegateKeyMap{
choose: key.NewBinding(
key.WithKeys("enter"),
key.WithHelp("enter", "choose"),
),
}
}