Allow !~= for website/uri
This commit is contained in:
@@ -12,16 +12,18 @@ import (
|
||||
|
||||
// sharedFlags holds the flags common to every subcommand.
|
||||
type sharedFlags struct {
|
||||
targets []string
|
||||
jsonOut bool
|
||||
website string
|
||||
prefix string
|
||||
uri string
|
||||
status string // expression: "200", "!=200", ">=400", etc.
|
||||
websiteRe string // RE2 regex against website
|
||||
uriRe string // RE2 regex against request URI
|
||||
isTor string // "", "1" / "!=0" (TOR only), "0" / "!=1" (non-TOR only)
|
||||
asn string // expression: "12345", "!=65000", ">=1000", etc.
|
||||
targets []string
|
||||
jsonOut bool
|
||||
website string
|
||||
prefix string
|
||||
uri string
|
||||
status string // expression: "200", "!=200", ">=400", etc.
|
||||
websiteRe string // RE2 regex against website
|
||||
uriRe string // RE2 regex against request URI
|
||||
websiteReNeg string // RE2 regex exclusion against website
|
||||
uriReNeg string // RE2 regex exclusion against request URI
|
||||
isTor string // "", "1" / "!=0" (TOR only), "0" / "!=1" (non-TOR only)
|
||||
asn string // expression: "12345", "!=65000", ">=1000", etc.
|
||||
}
|
||||
|
||||
// bindShared registers the shared flags on fs and returns a pointer to the
|
||||
@@ -36,6 +38,8 @@ func bindShared(fs *flag.FlagSet) (*sharedFlags, *string) {
|
||||
fs.StringVar(&sf.status, "status", "", "filter: HTTP status expression (200, !=200, >=400, <500, …)")
|
||||
fs.StringVar(&sf.websiteRe, "website-re", "", "filter: RE2 regex against website")
|
||||
fs.StringVar(&sf.uriRe, "uri-re", "", "filter: RE2 regex against request URI")
|
||||
fs.StringVar(&sf.websiteReNeg, "website-re-neg", "", "filter: RE2 regex exclusion against website")
|
||||
fs.StringVar(&sf.uriReNeg, "uri-re-neg", "", "filter: RE2 regex exclusion against request URI")
|
||||
fs.StringVar(&sf.isTor, "is-tor", "", "filter: TOR traffic (1 or !=0 = TOR only; 0 or !=1 = non-TOR only)")
|
||||
fs.StringVar(&sf.asn, "asn", "", "filter: ASN expression (12345, !=65000, >=1000, <64512, …)")
|
||||
return sf, target
|
||||
@@ -60,7 +64,7 @@ func parseTargets(s string) []string {
|
||||
}
|
||||
|
||||
func buildFilter(sf *sharedFlags) *pb.Filter {
|
||||
if sf.website == "" && sf.prefix == "" && sf.uri == "" && sf.status == "" && sf.websiteRe == "" && sf.uriRe == "" && sf.isTor == "" && sf.asn == "" {
|
||||
if sf.website == "" && sf.prefix == "" && sf.uri == "" && sf.status == "" && sf.websiteRe == "" && sf.uriRe == "" && sf.websiteReNeg == "" && sf.uriReNeg == "" && sf.isTor == "" && sf.asn == "" {
|
||||
return nil
|
||||
}
|
||||
f := &pb.Filter{}
|
||||
@@ -88,6 +92,12 @@ func buildFilter(sf *sharedFlags) *pb.Filter {
|
||||
if sf.uriRe != "" {
|
||||
f.UriRegex = &sf.uriRe
|
||||
}
|
||||
if sf.websiteReNeg != "" {
|
||||
f.WebsiteRegexExclude = &sf.websiteReNeg
|
||||
}
|
||||
if sf.uriReNeg != "" {
|
||||
f.UriRegexExclude = &sf.uriReNeg
|
||||
}
|
||||
switch sf.isTor {
|
||||
case "1", "!=0":
|
||||
f.Tor = pb.TorFilter_TOR_YES
|
||||
|
||||
@@ -19,9 +19,11 @@ var andRe = regexp.MustCompile(`(?i)\s+and\s+`)
|
||||
//
|
||||
// status=200 status!=200 status>=400 status>400 status<=500 status<500
|
||||
// website=example.com — exact match
|
||||
// website~=gouda.* — RE2 regex
|
||||
// website~=gouda.* — RE2 regex match
|
||||
// website!~=gouda.* — RE2 regex exclusion
|
||||
// uri=/api/v1/ — exact match
|
||||
// uri~=^/api/.* — RE2 regex
|
||||
// uri~=^/api/.* — RE2 regex match
|
||||
// uri!~=^/ct/.* — RE2 regex exclusion
|
||||
// prefix=1.2.3.0/24 — exact match
|
||||
//
|
||||
// Values may be enclosed in double or single quotes.
|
||||
@@ -57,6 +59,8 @@ func applyTerm(term string, fs *filterState) error {
|
||||
|
||||
var op, value string
|
||||
switch {
|
||||
case strings.HasPrefix(rest, "!~="):
|
||||
op, value = "!~=", rest[3:]
|
||||
case strings.HasPrefix(rest, "~="):
|
||||
op, value = "~=", rest[2:]
|
||||
case strings.HasPrefix(rest, "!="):
|
||||
@@ -96,8 +100,10 @@ func applyTerm(term string, fs *filterState) error {
|
||||
fs.Website = value
|
||||
case "~=":
|
||||
fs.WebsiteRe = value
|
||||
case "!~=":
|
||||
fs.WebsiteReNeg = value
|
||||
default:
|
||||
return fmt.Errorf("website only supports = and ~=, not %q", op)
|
||||
return fmt.Errorf("website only supports =, ~=, and !~=, not %q", op)
|
||||
}
|
||||
case "uri":
|
||||
switch op {
|
||||
@@ -105,8 +111,10 @@ func applyTerm(term string, fs *filterState) error {
|
||||
fs.URI = value
|
||||
case "~=":
|
||||
fs.URIRe = value
|
||||
case "!~=":
|
||||
fs.URIReNeg = value
|
||||
default:
|
||||
return fmt.Errorf("uri only supports = and ~=, not %q", op)
|
||||
return fmt.Errorf("uri only supports =, ~=, and !~=, not %q", op)
|
||||
}
|
||||
case "prefix":
|
||||
if op != "=" {
|
||||
@@ -164,6 +172,9 @@ func FilterExprString(f filterState) string {
|
||||
if f.WebsiteRe != "" {
|
||||
parts = append(parts, "website~="+quoteMaybe(f.WebsiteRe))
|
||||
}
|
||||
if f.WebsiteReNeg != "" {
|
||||
parts = append(parts, "website!~="+quoteMaybe(f.WebsiteReNeg))
|
||||
}
|
||||
if f.Prefix != "" {
|
||||
parts = append(parts, "prefix="+quoteMaybe(f.Prefix))
|
||||
}
|
||||
@@ -173,6 +184,9 @@ func FilterExprString(f filterState) string {
|
||||
if f.URIRe != "" {
|
||||
parts = append(parts, "uri~="+quoteMaybe(f.URIRe))
|
||||
}
|
||||
if f.URIReNeg != "" {
|
||||
parts = append(parts, "uri!~="+quoteMaybe(f.URIReNeg))
|
||||
}
|
||||
if f.Status != "" {
|
||||
parts = append(parts, statusTermStr(f.Status))
|
||||
}
|
||||
|
||||
@@ -48,14 +48,16 @@ type TableRow struct {
|
||||
|
||||
// filterState holds the filter fields parsed from URL params.
|
||||
type filterState struct {
|
||||
Website string
|
||||
Prefix string
|
||||
URI string
|
||||
Status string // expression: "200", "!=200", ">=400", etc.
|
||||
WebsiteRe string // RE2 regex against website
|
||||
URIRe string // RE2 regex against request URI
|
||||
IsTor string // "", "1" (TOR only), "0" (non-TOR only)
|
||||
ASN string // expression: "12345", "!=65000", ">=1000", etc.
|
||||
Website string
|
||||
Prefix string
|
||||
URI string
|
||||
Status string // expression: "200", "!=200", ">=400", etc.
|
||||
WebsiteRe string // RE2 regex against website
|
||||
URIRe string // RE2 regex against request URI
|
||||
WebsiteReNeg string // RE2 regex exclusion against website
|
||||
URIReNeg string // RE2 regex exclusion against request URI
|
||||
IsTor string // "", "1" (TOR only), "0" (non-TOR only)
|
||||
ASN string // expression: "12345", "!=65000", ">=1000", etc.
|
||||
}
|
||||
|
||||
// QueryParams holds all parsed URL parameters for one page request.
|
||||
@@ -156,20 +158,22 @@ func (h *Handler) parseParams(r *http.Request) QueryParams {
|
||||
GroupByS: grpS,
|
||||
N: n,
|
||||
Filter: filterState{
|
||||
Website: q.Get("f_website"),
|
||||
Prefix: q.Get("f_prefix"),
|
||||
URI: q.Get("f_uri"),
|
||||
Status: q.Get("f_status"),
|
||||
WebsiteRe: q.Get("f_website_re"),
|
||||
URIRe: q.Get("f_uri_re"),
|
||||
IsTor: q.Get("f_is_tor"),
|
||||
ASN: q.Get("f_asn"),
|
||||
Website: q.Get("f_website"),
|
||||
Prefix: q.Get("f_prefix"),
|
||||
URI: q.Get("f_uri"),
|
||||
Status: q.Get("f_status"),
|
||||
WebsiteRe: q.Get("f_website_re"),
|
||||
URIRe: q.Get("f_uri_re"),
|
||||
WebsiteReNeg: q.Get("f_website_re_neg"),
|
||||
URIReNeg: q.Get("f_uri_re_neg"),
|
||||
IsTor: q.Get("f_is_tor"),
|
||||
ASN: q.Get("f_asn"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildFilter(f filterState) *pb.Filter {
|
||||
if f.Website == "" && f.Prefix == "" && f.URI == "" && f.Status == "" && f.WebsiteRe == "" && f.URIRe == "" && f.IsTor == "" && f.ASN == "" {
|
||||
if f.Website == "" && f.Prefix == "" && f.URI == "" && f.Status == "" && f.WebsiteRe == "" && f.URIRe == "" && f.WebsiteReNeg == "" && f.URIReNeg == "" && f.IsTor == "" && f.ASN == "" {
|
||||
return nil
|
||||
}
|
||||
out := &pb.Filter{}
|
||||
@@ -194,6 +198,12 @@ func buildFilter(f filterState) *pb.Filter {
|
||||
if f.URIRe != "" {
|
||||
out.UriRegex = &f.URIRe
|
||||
}
|
||||
if f.WebsiteReNeg != "" {
|
||||
out.WebsiteRegexExclude = &f.WebsiteReNeg
|
||||
}
|
||||
if f.URIReNeg != "" {
|
||||
out.UriRegexExclude = &f.URIReNeg
|
||||
}
|
||||
switch f.IsTor {
|
||||
case "1":
|
||||
out.Tor = pb.TorFilter_TOR_YES
|
||||
@@ -234,6 +244,12 @@ func (p QueryParams) toValues() url.Values {
|
||||
if p.Filter.URIRe != "" {
|
||||
v.Set("f_uri_re", p.Filter.URIRe)
|
||||
}
|
||||
if p.Filter.WebsiteReNeg != "" {
|
||||
v.Set("f_website_re_neg", p.Filter.WebsiteReNeg)
|
||||
}
|
||||
if p.Filter.URIReNeg != "" {
|
||||
v.Set("f_uri_re_neg", p.Filter.URIReNeg)
|
||||
}
|
||||
if p.Filter.IsTor != "" {
|
||||
v.Set("f_is_tor", p.Filter.IsTor)
|
||||
}
|
||||
@@ -261,7 +277,8 @@ func (p QueryParams) buildURL(overrides map[string]string) string {
|
||||
func (p QueryParams) clearFilterURL() string {
|
||||
return p.buildURL(map[string]string{
|
||||
"f_website": "", "f_prefix": "", "f_uri": "", "f_status": "",
|
||||
"f_website_re": "", "f_uri_re": "", "f_is_tor": "", "f_asn": "",
|
||||
"f_website_re": "", "f_uri_re": "", "f_website_re_neg": "", "f_uri_re_neg": "",
|
||||
"f_is_tor": "", "f_asn": "",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -344,6 +361,18 @@ func buildCrumbs(p QueryParams) []Crumb {
|
||||
RemoveURL: p.buildURL(map[string]string{"f_uri_re": ""}),
|
||||
})
|
||||
}
|
||||
if p.Filter.WebsiteReNeg != "" {
|
||||
crumbs = append(crumbs, Crumb{
|
||||
Text: "website!~=" + p.Filter.WebsiteReNeg,
|
||||
RemoveURL: p.buildURL(map[string]string{"f_website_re_neg": ""}),
|
||||
})
|
||||
}
|
||||
if p.Filter.URIReNeg != "" {
|
||||
crumbs = append(crumbs, Crumb{
|
||||
Text: "uri!~=" + p.Filter.URIReNeg,
|
||||
RemoveURL: p.buildURL(map[string]string{"f_uri_re_neg": ""}),
|
||||
})
|
||||
}
|
||||
switch p.Filter.IsTor {
|
||||
case "1":
|
||||
crumbs = append(crumbs, Crumb{
|
||||
|
||||
Reference in New Issue
Block a user