mirror of
https://github.com/davidallendj/magellan.git
synced 2025-12-20 03:27:03 -07:00
feat: updated cache implementation and fixed bugs
This commit is contained in:
parent
dcfd926056
commit
f53829c6ee
7 changed files with 550 additions and 97 deletions
177
cmd/cache.go
177
cmd/cache.go
|
|
@ -2,10 +2,12 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/araddon/dateparse"
|
||||
"github.com/charmbracelet/bubbles/table"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
|
|
@ -18,6 +20,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
timestamp time.Time
|
||||
cacheOutputFormat string
|
||||
interactive bool
|
||||
withHosts []string
|
||||
|
|
@ -26,74 +29,103 @@ var (
|
|||
|
||||
var cacheCmd = &cobra.Command{
|
||||
Use: "cache",
|
||||
Short: "Manage found assets in cache.",
|
||||
Short: "Manage assets (new, add, remove, edit, etc.) in cache.",
|
||||
}
|
||||
|
||||
var cacheNewCmd = &cobra.Command{
|
||||
Use: "new",
|
||||
Example: ` // create a new cache database
|
||||
magellan cache new ./path/to/new_cache.db
|
||||
`,
|
||||
Short: "Create a new cache at specified path.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
var cacheAddCmd = &cobra.Command{
|
||||
Use: "add [host] [port] [protocol] <timestamp>",
|
||||
Example: ` // create a new entry in cache
|
||||
magellan cache add https://172.16.0.105 443,5000 tcp "06/25/2025"
|
||||
|
||||
// create new entry with no timestamp specified with specific cache
|
||||
magellan cache add https://172.16.0.111 443 tcp --cache ./assets.db
|
||||
`,
|
||||
Short: "Add an entry to the specified cache.",
|
||||
Args: cobra.RangeArgs(3, 4),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// set positional args to vars
|
||||
host = args[0]
|
||||
protocol = args[2]
|
||||
|
||||
// convert ports
|
||||
for _, portStr := range strings.Split(args[1], ",") {
|
||||
port, err := strconv.ParseInt(portStr, 10, 16)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to convert port value")
|
||||
continue
|
||||
}
|
||||
ports = append(ports, int(port))
|
||||
}
|
||||
|
||||
// parse timestamp
|
||||
if len(args) == 4 {
|
||||
var err error
|
||||
timestamp, err = dateparse.ParseAny(args[3])
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to parse time arg")
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
log.Warn().Msg("time not specified...using current time")
|
||||
timestamp = time.Now()
|
||||
}
|
||||
|
||||
for _, port := range ports {
|
||||
// create a new entry in cache using flag arguments
|
||||
err := sqlite.InsertRemoteAssets(cachePath, magellan.RemoteAsset{
|
||||
Host: host,
|
||||
Port: port,
|
||||
Protocol: protocol,
|
||||
Timestamp: timestamp,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error().Err(err).
|
||||
Str("path", cachePath).
|
||||
Str("host", host).
|
||||
Msg("failed to insert asset into cache")
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
var cacheRemoveCmd = &cobra.Command{
|
||||
Use: "remove",
|
||||
Short: "Remove a host from a scanned cache list.",
|
||||
Use: "remove [hosts...]",
|
||||
Example: ` // remove an entry in cache
|
||||
magellan cache remove https://172.16.0.113
|
||||
`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Short: "Remove hosts from cache.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
assets := []magellan.RemoteAsset{}
|
||||
|
||||
// add all assets directly from positional args
|
||||
for _, arg := range args {
|
||||
var (
|
||||
port int
|
||||
uri *url.URL
|
||||
err error
|
||||
)
|
||||
uri, err = url.ParseRequestURI(arg)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to parse arg")
|
||||
}
|
||||
|
||||
// convert port to its "proper" type
|
||||
if uri.Port() == "" {
|
||||
uri.Host += ":443"
|
||||
}
|
||||
port, err = strconv.Atoi(uri.Port())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to convert port to integer type")
|
||||
}
|
||||
asset := magellan.RemoteAsset{
|
||||
Host: fmt.Sprintf("%s://%s", uri.Scheme, uri.Hostname()),
|
||||
Port: port,
|
||||
}
|
||||
assets = append(assets, asset)
|
||||
err := sqlite.DeleteRemoteAssetsByHost(cachePath, args...)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("path", cachePath).Msg("failed to delete assets in cache")
|
||||
}
|
||||
|
||||
// Add all assets with specified hosts (same host different different ports)
|
||||
// This should produce the following SQL:
|
||||
// DELETE FROM magellan_scanned_assets WHERE host=:host
|
||||
for _, host := range withHosts {
|
||||
assets = append(assets, magellan.RemoteAsset{
|
||||
Host: host,
|
||||
Port: -1,
|
||||
})
|
||||
}
|
||||
|
||||
// Add all assets with specified ports (same port different hosts)
|
||||
// This should produce the following SQL:
|
||||
// DELETE FROM magellan_scanned_assets WHERE port=:port
|
||||
for _, port := range withPorts {
|
||||
assets = append(assets, magellan.RemoteAsset{
|
||||
Host: "",
|
||||
Port: port,
|
||||
})
|
||||
}
|
||||
if len(assets) <= 0 {
|
||||
log.Error().Msg("nothing to do")
|
||||
os.Exit(1)
|
||||
}
|
||||
sqlite.DeleteScannedAssets(cachePath, assets...)
|
||||
},
|
||||
}
|
||||
|
||||
var cacheEditCmd = &cobra.Command{
|
||||
Use: "edit",
|
||||
Example: ` magellan cache edit
|
||||
magellan cache edit --host https://172.16.0.101 --port 443 --protocol udp
|
||||
magellan cache edit --host https://172.16.0.101
|
||||
Example: ` // star the cache editor
|
||||
magellan cache edit -i
|
||||
|
||||
// edit a single entry only changing values specified (e.g. port and protocol)
|
||||
magellan cache edit https://172.16.0.101 --port 443 --protocol udp
|
||||
|
||||
// edit two entries' time stamps
|
||||
magellan cache edit https://172.16.0.101 https://172.16.0.102 --timestamp 06/25/2025
|
||||
`,
|
||||
Args: cobra.ExactArgs(0),
|
||||
Short: "Edit existing cache data.",
|
||||
|
|
@ -106,17 +138,17 @@ var cacheEditCmd = &cobra.Command{
|
|||
|
||||
if interactive {
|
||||
// load the assets found from scan
|
||||
scannedResults, err := sqlite.GetScannedAssets(cachePath)
|
||||
scannedResults, err := sqlite.GetRemoteAssets(cachePath)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("path", cachePath).Msg("failed to get scanned assets from cache")
|
||||
}
|
||||
|
||||
// set columns to cache headers
|
||||
columns = []table.Column{
|
||||
{Title: "hosts", Width: 20},
|
||||
{Title: "ports", Width: 5},
|
||||
{Title: "protocol", Width: 8},
|
||||
{Title: "timestamp", Width: 12},
|
||||
{Title: "host", Width: 30},
|
||||
{Title: "ports", Width: 8},
|
||||
{Title: "protocol", Width: 10},
|
||||
{Title: "timestamp", Width: 20},
|
||||
}
|
||||
|
||||
// set rows to cache data
|
||||
|
|
@ -129,7 +161,7 @@ var cacheEditCmd = &cobra.Command{
|
|||
})
|
||||
}
|
||||
|
||||
// new table
|
||||
// create a new table
|
||||
assetsTable := table.New(
|
||||
table.WithColumns(columns),
|
||||
table.WithRows(rows),
|
||||
|
|
@ -150,11 +182,16 @@ var cacheEditCmd = &cobra.Command{
|
|||
Bold(false)
|
||||
assetsTable.SetStyles(styles)
|
||||
|
||||
m := cache.Model{Table: assetsTable}
|
||||
m := cache.NewModel(cachePath, &assetsTable)
|
||||
if _, err := tea.NewProgram(m, tea.WithAltScreen()).Run(); err != nil {
|
||||
fmt.Println("Error running program:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
// non-interactive editting
|
||||
// for _, host := range args {
|
||||
|
||||
// }
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -169,7 +206,7 @@ var cacheInfoCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
func init() {
|
||||
// remove
|
||||
// remove row from cache
|
||||
cacheRemoveCmd.Flags().StringSliceVar(&withHosts, "with-hosts", []string{}, "Remove all assets with specified hosts")
|
||||
cacheRemoveCmd.Flags().IntSliceVar(&withPorts, "with-ports", []int{}, "Remove all assets with specified ports")
|
||||
|
||||
|
|
@ -181,16 +218,18 @@ func init() {
|
|||
cacheInfoCmd.Flags().StringVarP(&cacheOutputFormat, "format", "F", FORMAT_LIST, "Set the output format (list|json|yaml)")
|
||||
|
||||
cacheCmd.AddCommand(
|
||||
cacheNewCmd,
|
||||
cacheAddCmd,
|
||||
cacheRemoveCmd,
|
||||
cacheInfoCmd,
|
||||
cacheEditCmd,
|
||||
ListCmd,
|
||||
cacheInfoCmd,
|
||||
ListCmd, // magellan cache list (alias for 'magellan list')
|
||||
)
|
||||
rootCmd.AddCommand(cacheCmd)
|
||||
}
|
||||
|
||||
func printCacheInfo(format string) {
|
||||
assets, err := sqlite.GetScannedAssets(cachePath)
|
||||
assets, err := sqlite.GetRemoteAssets(cachePath)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("path", cachePath).Msg("failed to get assets to print cache info")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue