Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] Protect raw/time queries by defaulting to last 24 hours if not set explicitly #165

Merged
merged 3 commits into from
Aug 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions cmd/goQuery/cmd/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ You can specify "ANY" to query all interfaces.
`,
"First": `Upper/lower bound on flow timestamp

DEFAULTS

--first will default to the last 30 days if not provided. In case
a "time" attribute is involved (e.g. for "time" or "raw" queries),
the default is lowered to the last 24 hours. This is to protect
against accidentally querying the entire database.

--last will default to the current time if not provided

ALLOWED FORMATS

1357800683 EPOCH
Expand Down
36 changes: 27 additions & 9 deletions cmd/goQuery/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,15 +239,6 @@ func entrypoint(cmd *cobra.Command, args []string) error {
return nil
}

// handle the defaults for time
if queryArgs.First == "" {
// by default, go back one month in time
queryArgs.First = time.Now().AddDate(0, -1, 0).Format(time.ANSIC)
}
if queryArgs.Last == "" {
queryArgs.Last = time.Now().Format(time.ANSIC)
}

// check if arguments should be loaded from disk. The cmdLineParams are taken as
// the base for this to allow modification of single parameters
if viper.GetString(conf.StoredQuery) != "" {
Expand All @@ -273,6 +264,9 @@ func entrypoint(cmd *cobra.Command, args []string) error {
queryArgs.Query = args[0]
}

// make sure there's protection against unbounded time intervals
queryArgs = setDefaultTimeRange(&queryArgs)

queryCtx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
defer stop()

Expand Down Expand Up @@ -390,3 +384,27 @@ func entrypoint(cmd *cobra.Command, args []string) error {
}
return nil
}

// setDefaultTimeRange handles the defaults for time arguments if they aren't set
func setDefaultTimeRange(args *query.Args) query.Args {
logger := logging.Logger()
if args.First == "" {
logger.Debug("setting default value for 'first'")

// protect against queries that are possibly too large and only go back a day if a time attribute
// is included. This is only done if first wasn't explicitly set. If it is, it must be assumed that
// the caller knows the possible extend of a "time" query
if strings.Contains(args.Query, types.TimeName) || strings.Contains(args.Query, types.RawCompoundQuery) {
logger.With("query", args.Query).Debug("time attribute detected, limiting time range to one day")
args.First = time.Now().AddDate(0, 0, -1).Format(time.ANSIC)
} else {
// by default, go back one month in time
args.First = time.Now().AddDate(0, -1, 0).Format(time.ANSIC)
}
}
if args.Last == "" {
logger.Debug("setting default value for 'last'")
args.Last = time.Now().Format(time.ANSIC)
}
return *args
}
23 changes: 14 additions & 9 deletions pkg/formatting/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ func (s Sizeable) String() string {
return Size(uint64(s))
}

// Durationable is a time.Duration that can be printed in a human readable format and
// will prepend 'd' for 'days' in case the duration is above 24 hours
type Durationable time.Duration

func (d Durationable) String() string {
return Duration(time.Duration(d))
}

// Count takes a number and prints it in a human readable format,
// e.g. 1000 -> 1k, 1000000 -> 1M, 1000000000 -> 1G
func Count(val uint64) string {
Expand Down Expand Up @@ -62,14 +70,11 @@ func Size(size uint64) string {

// Duration prints out d in a human-readable duration format
func Duration(d time.Duration) string {
if d/time.Hour != 0 {
return fmt.Sprintf("%dh%2dm", d/time.Hour, d%time.Hour/time.Minute)
}
if d/time.Minute != 0 {
return fmt.Sprintf("%dm%2ds", d/time.Minute, d%time.Minute/time.Second)
}
if d/time.Second != 0 {
return fmt.Sprintf("%.1fs", d.Seconds())
// enhance the classic duration Stringer to print out days
days := d / (24 * time.Hour)
if days != 0 {
d = d - (days * 24 * time.Hour)
return fmt.Sprintf("%dd%s", days, d.Round(time.Millisecond))
}
return fmt.Sprintf("%dms", d/time.Millisecond)
return fmt.Sprintf("%s", d.Round(time.Millisecond))
}
11 changes: 6 additions & 5 deletions pkg/formatting/format_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,13 @@ func TestDuration(t *testing.T) {
input time.Duration
expected string
}{
{0, "0ms"},
{0, "0s"},
{1 * time.Millisecond, "1ms"},
{1 * time.Second, "1.0s"},
{1*time.Second + 232*time.Millisecond, "1.2s"},
{1*time.Minute + 3*time.Second, "1m 3s"},
{1*time.Hour + 3*time.Minute + 3*time.Second, "1h 3m"},
{1 * time.Second, "1s"},
{1*time.Second + 232*time.Millisecond, "1.232s"},
{1*time.Minute + 3*time.Second, "1m3s"},
{1*time.Hour + 3*time.Minute + 3*time.Second, "1h3m3s"},
{25*time.Hour + 3*time.Minute + 3*time.Second, "1d1h3m3s"},
}

for _, test := range tests {
Expand Down
23 changes: 13 additions & 10 deletions pkg/results/TablePrinter.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@ func columns(selector types.LabelSelector, attributes []types.Attribute, d types

for _, attrib := range attributes {
switch attrib.Name() {
case "sip":
case types.SipName:
cols = append(cols, OutcolSip)
case "dip":
case types.DipName:
cols = append(cols, OutcolDip)
case "proto":
case types.ProtoName:
cols = append(cols, OutcolProto)
case "dport":
case types.DportName:
cols = append(cols, OutcolDport)
}
}
Expand Down Expand Up @@ -616,37 +616,40 @@ func (t *TextTablePrinter) Footer(result *Result) {
fmt.Fprintln(t.writer)
}

textFormatter := TextFormatter{}

// Summary
fmt.Fprintf(t.footwriter, "Timespan / Interface\t: [%s, %s] / %s\n",
fmt.Fprintf(t.footwriter, "Timespan / Interface\t: [%s, %s] (%s) / %s\n",
result.Summary.First.Format(types.DefaultTimeOutputFormat),
result.Summary.Last.Format(types.DefaultTimeOutputFormat),
formatting.Durationable(result.Summary.Last.Sub(result.Summary.First).Round(time.Minute)),
strings.Join(result.Summary.Interfaces, ","))
fmt.Fprintf(t.footwriter, "Sorted by\t: %s\n",
describe(t.sort, t.direction))
if result.Summary.Timings.ResolutionDuration > 0 {
fmt.Fprintf(t.footwriter, "Reverse DNS stats\t: RDNS took %s, timeout was %s\n",
TextFormatter{}.Duration(result.Summary.Timings.ResolutionDuration),
TextFormatter{}.Duration(t.resolveTimeout))
formatting.Durationable(result.Summary.Timings.ResolutionDuration),
formatting.Durationable(t.resolveTimeout))
}

var hitsDisplayed string
if result.Summary.Hits.Displayed < 1000 {
hitsDisplayed = fmt.Sprintf("%d", result.Summary.Hits.Displayed)
} else {
hitsDisplayed = strings.TrimSpace(TextFormatter{}.Count(uint64(result.Summary.Hits.Displayed)))
hitsDisplayed = strings.TrimSpace(textFormatter.Count(uint64(result.Summary.Hits.Displayed)))
}

var hitsTotal string
if result.Summary.Hits.Total < 1000 {
hitsTotal = fmt.Sprintf("%d", result.Summary.Hits.Total)
} else {
hitsTotal = strings.TrimSpace(TextFormatter{}.Count(uint64(result.Summary.Hits.Total)))
hitsTotal = strings.TrimSpace(textFormatter.Count(uint64(result.Summary.Hits.Total)))
}

fmt.Fprintf(t.footwriter, "Query stats\t: displayed top %s hits out of %s in %s\n",
hitsDisplayed,
hitsTotal,
TextFormatter{}.Duration(result.Summary.Timings.QueryDuration))
textFormatter.Duration(result.Summary.Timings.QueryDuration))
if result.Query.Condition != "" {
fmt.Fprintf(t.footwriter, "Conditions:\t: %s\n",
result.Query.Condition)
Expand Down
23 changes: 16 additions & 7 deletions pkg/types/columns.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func (i ipAttribute) String() string {

// Name returns the attributes name
func (SipAttribute) Name() string {
return "sip"
return SipName
}

func (SipAttribute) attributeMarker() {}
Expand Down Expand Up @@ -227,6 +227,15 @@ func AllColumns() []string {

const attrSep = ","

const (
TalkConvCompoundQuery = "talk_conv"
TalkSrcCompoundQuery = "talk_src"
TalkDstCompoundQuery = "talk_dst"
AppsPortCompoundQuery = "apps_port"
AggTalkPortCompoundQuery = "agg_talk_port"
RawCompoundQuery = "raw"
)

// ToAttribueNames translates query abbreviations into the attributes they hold
func ToAttributeNames(queryType string) (attrs []string) {
// covers the case where aliases and attribute/label names are mixed (e.g. talk_conv,dport)
Expand All @@ -239,17 +248,17 @@ func ToAttributeNames(queryType string) (attrs []string) {
}

switch queryType {
case "talk_conv":
case TalkConvCompoundQuery:
return []string{SipName, DipName}
case "talk_src":
case TalkSrcCompoundQuery:
return []string{SipName}
case "talk_dst":
case TalkDstCompoundQuery:
return []string{DipName}
case "apps_port":
case AppsPortCompoundQuery:
return []string{DportName, ProtoName}
case "agg_talk_port":
case AggTalkPortCompoundQuery:
return []string{SipName, DipName, DportName, ProtoName}
case "raw":
case RawCompoundQuery:
return AllColumns()
}
// We didn't match any of the preset query types, so we are dealing with
Expand Down