Skip to content

Commit

Permalink
Merge pull request #19 from projectdiscovery/bugfix-stringslice
Browse files Browse the repository at this point in the history
Removing normalization of case-sensitive parameters
  • Loading branch information
ehsandeep authored Jul 29, 2021
2 parents 971dc93 + b93905d commit 5986cc1
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 119 deletions.
63 changes: 45 additions & 18 deletions goflags.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,48 @@ func (flagSet *FlagSet) IntVar(field *int, long string, defaultValue int, usage
return flagData
}

// NormalizedStringSliceVarP adds a path slice flag with a shortname and longname.
// It supports comma separated values, that are normalized (lower-cased, stripped of any leading and trailing whitespaces and quotes)
func (flagSet *FlagSet) NormalizedStringSliceVarP(field *NormalizedStringSlice, long, short string, defaultValue []string, usage string) *FlagData {
for _, item := range defaultValue {
_ = field.Set(item)
}

flag.Var(field, short, usage)
flag.Var(field, long, usage)

flagData := &FlagData{
usage: usage,
short: short,
long: long,
defaultValue: field.createStringArrayDefaultValue(),
}
flagSet.flagKeys.Set(short, flagData)
flagSet.flagKeys.Set(long, flagData)
return flagData
}

// NormalizedStringSliceVar adds a path slice flag with a long name
// It supports comma separated values, that are normalized (lower-cased, stripped of any leading and trailing whitespaces and quotes)
func (flagSet *FlagSet) NormalizedStringSliceVar(field *NormalizedStringSlice, long string, defaultValue []string, usage string) *FlagData {
for _, item := range defaultValue {
_ = field.Set(item)
}

flag.Var(field, long, usage)

flagData := &FlagData{
usage: usage,
long: long,
defaultValue: field.createStringArrayDefaultValue(),
}
flagSet.flagKeys.Set(long, flagData)
return flagData
}

// StringSliceVarP adds a string slice flag with a shortname and longname
// Supports ONE value at a time. Adding multiple values require repeating the argument (-flag value1 -flag value2)
// No value normalization is happening.
func (flagSet *FlagSet) StringSliceVarP(field *StringSlice, long, short string, defaultValue []string, usage string) *FlagData {
for _, item := range defaultValue {
_ = field.Set(item)
Expand All @@ -342,6 +383,8 @@ func (flagSet *FlagSet) StringSliceVarP(field *StringSlice, long, short string,
}

// StringSliceVar adds a string slice flag with a longname
// Supports ONE value at a time. Adding multiple values require repeating the argument (-flag value1 -flag value2)
// No value normalization is happening.
func (flagSet *FlagSet) StringSliceVar(field *StringSlice, long string, defaultValue []string, usage string) *FlagData {
for _, item := range defaultValue {
_ = field.Set(item)
Expand All @@ -358,21 +401,6 @@ func (flagSet *FlagSet) StringSliceVar(field *StringSlice, long string, defaultV
return flagData
}

func (stringSlice *StringSlice) createStringArrayDefaultValue() string {
defaultBuilder := &strings.Builder{}
defaultBuilder.WriteString("[")
for i, k := range *stringSlice {
defaultBuilder.WriteString("\"")
defaultBuilder.WriteString(k)
defaultBuilder.WriteString("\"")
if i != len(*stringSlice)-1 {
defaultBuilder.WriteString(", ")
}
}
defaultBuilder.WriteString("]")
return defaultBuilder.String()
}

func (flagSet *FlagSet) usageFunc() {
cliOutput := flag.CommandLine.Output()
fmt.Fprintf(cliOutput, "%s\n\n", flagSet.description)
Expand Down Expand Up @@ -415,9 +443,8 @@ func (flagSet *FlagSet) usageFuncForGroups(cliOutput io.Writer, writer *tabwrite
flagSet.flagKeys.forEach(func(key string, data *FlagData) {
currentFlag := flag.CommandLine.Lookup(key)

isUnique := uniqueDeduper.isUnique(data)
if data.group == "" {
if !isUnique {
if !uniqueDeduper.isUnique(data) {
return
}
otherOptions = append(otherOptions, createUsageString(data, currentFlag))
Expand All @@ -427,7 +454,7 @@ func (flagSet *FlagSet) usageFuncForGroups(cliOutput io.Writer, writer *tabwrite
if !strings.EqualFold(data.group, group.name) {
return
}
if !isUnique {
if !uniqueDeduper.isUnique(data) {
return
}
result := createUsageString(data, currentFlag)
Expand Down
102 changes: 83 additions & 19 deletions goflags_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package goflags

import (
"bytes"
"flag"
"io/ioutil"
"os"
Expand Down Expand Up @@ -82,25 +83,62 @@ func TestUsageOrder(t *testing.T) {
var intData int
var boolData bool

flagSet.StringVar(&stringData, "string-value", "", "String example value example")
flagSet.StringVarP(&stringData, "", "ts2", "test-string", "String with default value example #2")
flagSet.StringVar(&stringData, "string-with-default-value", "test-string", "String with default value example")
flagSet.StringVarP(&stringData, "string-with-default-value2", "ts", "test-string", "String with default value example #2")
flagSet.StringSliceVar(&stringSliceData, "slice-value", []string{}, "String slice flag example value")
flagSet.StringSliceVarP(&stringSliceData, "slice-value2", "sv", []string{}, "String slice flag example value #2")
flagSet.StringSliceVar(&stringSliceData, "slice-with-default-value", []string{"a", "b", "c"}, "String slice flag with default example values")
flagSet.StringSliceVarP(&stringSliceData2, "slice-with-default-value2", "swdf", []string{"a", "b", "c"}, "String slice flag with default example values #2")
flagSet.IntVar(&intData, "int-value", 0, "Int value example")
flagSet.IntVarP(&intData, "int-value2", "iv", 0, "Int value example #2")
flagSet.IntVar(&intData, "int-with-default-value", 12, "Int with default value example")
flagSet.IntVarP(&intData, "int-with-default-value2", "iwdv", 12, "Int with default value example #2")
flagSet.BoolVar(&boolData, "bool-value", false, "Bool value example")
flagSet.BoolVarP(&boolData, "bool-value2", "bv", false, "Bool value example #2")
flagSet.BoolVar(&boolData, "bool-with-default-value", true, "Bool with default value example")
flagSet.BoolVarP(&boolData, "bool-with-default-value2", "bwdv", true, "Bool with default value example #2")
flagSet.SetGroup("String", "String")
flagSet.StringVar(&stringData, "string-value", "", "String example value example").Group("String")
flagSet.StringVarP(&stringData, "", "ts2", "test-string", "String with default value example #2").Group("String")
flagSet.StringVar(&stringData, "string-with-default-value", "test-string", "String with default value example").Group("String")
flagSet.StringVarP(&stringData, "string-with-default-value2", "ts", "test-string", "String with default value example #2").Group("String")

flagSet.SetGroup("StringSlice", "StringSlice")
flagSet.StringSliceVar(&stringSliceData, "slice-value", []string{}, "String slice flag example value").Group("StringSlice")
flagSet.StringSliceVarP(&stringSliceData, "slice-value2", "sv", []string{}, "String slice flag example value #2").Group("StringSlice")
flagSet.StringSliceVar(&stringSliceData, "slice-with-default-value", []string{"a", "b", "c"}, "String slice flag with default example values").Group("StringSlice")
flagSet.StringSliceVarP(&stringSliceData2, "slice-with-default-value2", "swdf", []string{"a", "b", "c"}, "String slice flag with default example values #2").Group("StringSlice")

flagSet.SetGroup("Integer", "Integer")
flagSet.IntVar(&intData, "int-value", 0, "Int value example").Group("Integer")
flagSet.IntVarP(&intData, "int-value2", "iv", 0, "Int value example #2").Group("Integer")
flagSet.IntVar(&intData, "int-with-default-value", 12, "Int with default value example").Group("Integer")
flagSet.IntVarP(&intData, "int-with-default-value2", "iwdv", 12, "Int with default value example #2").Group("Integer")

flagSet.SetGroup("Bool", "Boolean")
flagSet.BoolVar(&boolData, "bool-value", false, "Bool value example").Group("Bool")
flagSet.BoolVarP(&boolData, "bool-value2", "bv", false, "Bool value example #2").Group("Bool")
flagSet.BoolVar(&boolData, "bool-with-default-value", true, "Bool with default value example").Group("Bool")
flagSet.BoolVarP(&boolData, "bool-with-default-value2", "bwdv", true, "Bool with default value example #2").Group("Bool")

output := &bytes.Buffer{}
flag.CommandLine.SetOutput(output)

flagSet.usageFunc()
// TODO try to retrieve the data written to the stdout/err and do some assertions on it

resultOutput := output.String()
actual := resultOutput[strings.Index(resultOutput, "Flags:\n"):]

expected :=
`Flags:
STRING:
-string-value string String example value example
-ts2 string String with default value example #2 (default "test-string")
-string-with-default-value string String with default value example (default "test-string")
-ts, -string-with-default-value2 string String with default value example #2 (default "test-string")
STRINGSLICE:
-slice-value string[] String slice flag example value
-sv, -slice-value2 string[] String slice flag example value #2
-slice-with-default-value string[] String slice flag with default example values (default ["a", "b", "c"])
-swdf, -slice-with-default-value2 string[] String slice flag with default example values #2 (default ["a", "b", "c"])
INTEGER:
-int-value int Int value example
-iv, -int-value2 int Int value example #2
-int-with-default-value int Int with default value example (default 12)
-iwdv, -int-with-default-value2 int Int with default value example #2 (default 12)
BOOLEAN:
-bool-value Bool value example
-bv, -bool-value2 Bool value example #2
-bool-with-default-value Bool with default value example (default true)
-bwdv, -bool-with-default-value2 Bool with default value example #2 (default true)
`
assert.Equal(t, actual, expected)

tearDown(t.Name())
}
Expand All @@ -112,6 +150,8 @@ func TestIncorrectStringFlagsCausePanic(t *testing.T) {
flagSet.StringVar(&stringData, "", "test-string", "String with default value example")
assert.Panics(t, flagSet.usageFunc)

// env GOOS=linux GOARCH=amd64 go build main.go -o nuclei

tearDown(t.Name())
}

Expand Down Expand Up @@ -154,7 +194,7 @@ type testSliceValue []interface{}
func (value testSliceValue) String() string { return "" }
func (value testSliceValue) Set(string) error { return nil }

func TestMe(t *testing.T) {
func TestCustomSliceUsageType(t *testing.T) {
testCases := map[string]flag.Flag{
"string[]": {Value: &StringSlice{}},
"value[]": {Value: &testSliceValue{}},
Expand All @@ -166,6 +206,30 @@ func TestMe(t *testing.T) {
}
}

func TestParseStringSlice(t *testing.T) {
flagSet := NewFlagSet()

var stringSlice StringSlice
flagSet.StringSliceVarP(&stringSlice, "header", "H", []string{}, "Header values. Expected usage: -H \"header1\":\"value1\" -H \"header2\":\"value2\"")

header1 := "\"header1:value1\""
header2 := "\" HEADER 2: VALUE2 \""
header3 := "\"header3\":\"value3, value4\""

os.Args = []string{
"./appName",
"-H", header1,
"-header", header2,
"-H", header3,
}

err := flagSet.Parse()
assert.Nil(t, err)

assert.Equal(t, stringSlice, StringSlice{header1, header2, header3})
}

func tearDown(uniqueValue string) { // sadly there is no official support for setup/teardown/test
flag.CommandLine = flag.NewFlagSet(uniqueValue, flag.PanicOnError)
flag.CommandLine = flag.NewFlagSet(uniqueValue, flag.ContinueOnError)
flag.CommandLine.Usage = flag.Usage
}
46 changes: 0 additions & 46 deletions misc.go

This file was deleted.

36 changes: 0 additions & 36 deletions misc_test.go

This file was deleted.

Loading

0 comments on commit 5986cc1

Please sign in to comment.