commands/cli: fix parsing of incorrect permutations
parseOpts now does some preliminary path screening to prevent command sequences like `ipfs <hash> cat` from succeeding. The tests affected by this have been slightly altered, but should be restored once parseOpts is decoupled from path analysis. Command suggestion printing has also been factored into a single function. Fixes: #2501 License: MIT Signed-off-by: Thomas Gardner <tmg@fastmail.com>
Thomas Gardner committed
Apr 1, 2016 at 21:42 UTC
3e824412d981085e256e97bbd80c2cd801a5cf1e
4 files changed
+39
-28
commands/cli/cmd_suggestion.go
+14
@@ -1,6 +1,7 @@
1
package cli
2
3
import (
4
+ "fmt"
5
"sort"
6
"strings"
7
@@ -69,3 +70,16 @@ func suggestUnknownCmd(args []string, root *cmds.Command) []string {
70
}
71
return sFinal
72
}
73
+
74
+func printSuggestions(inputs []string, root *cmds.Command) (err error) {
75
+
76
+ suggestions := suggestUnknownCmd(inputs, root)
77
+ if len(suggestions) > 1 {
78
+ err = fmt.Errorf("Unknown Command \"%s\"\n\nDid you mean any of these?\n\n\t%s", inputs[0], strings.Join(suggestions, "\n\t"))
79
+ } else if len(suggestions) > 0 {
80
+ err = fmt.Errorf("Unknown Command \"%s\"\n\nDid you mean this?\n\n\t%s", inputs[0], suggestions[0])
81
+ } else {
82
+ err = fmt.Errorf("Unknown Command \"%s\"\n", inputs[0])
83
+ }
84
+ return
85
+}
commands/cli/parse.go
+7
-9
@@ -227,6 +227,11 @@ func parseOpts(args []string, root *cmds.Command) (
227
}
228
} else {
229
stringVals = append(stringVals, arg)
230
+ if len(path) == 0 {
231
+ // found a typo or early argument
232
+ err = printSuggestions(stringVals, root)
233
+ return
234
+ }
235
}
236
}
237
}
@@ -268,15 +273,8 @@ func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursi
273
// and the last arg definition is not variadic (or there are no definitions), return an error
274
notVariadic := len(argDefs) == 0 || !argDefs[len(argDefs)-1].Variadic
275
if notVariadic && len(inputs) > len(argDefs) {
271
- suggestions := suggestUnknownCmd(inputs, root)
272
-
273
- if len(suggestions) > 1 {
274
- return nil, nil, fmt.Errorf("Unknown Command \"%s\"\n\nDid you mean any of these?\n\n\t%s", inputs[0], strings.Join(suggestions, "\n\t"))
275
- } else if len(suggestions) > 0 {
276
- return nil, nil, fmt.Errorf("Unknown Command \"%s\"\n\nDid you mean this?\n\n\t%s", inputs[0], suggestions[0])
277
- } else {
278
- return nil, nil, fmt.Errorf("Unknown Command \"%s\"\n", inputs[0])
279
- }
276
+ err := printSuggestions(inputs, root)
277
+ return nil, nil, err
278
}
279
280
stringArgs := make([]string, 0, numInputs)
commands/cli/parse_test.go
+18
-14
@@ -78,6 +78,9 @@ func TestOptionParsing(t *testing.T) {
78
}
79
80
testHelper := func(args string, expectedOpts kvs, expectedWords words, expectErr bool) {
81
+ var opts map[string]interface{}
82
+ var input []string
83
+
84
_, opts, input, _, err := parseOpts(strings.Split(args, " "), cmd)
85
if expectErr {
86
if err == nil {
@@ -99,9 +102,8 @@ func TestOptionParsing(t *testing.T) {
102
testHelper(args, expectedOpts, expectedWords, false)
103
}
104
102
- test("-", kvs{}, words{"-"})
105
+ test("test -", kvs{}, words{"-"})
106
testFail("-b -b")
104
- test("beep boop", kvs{}, words{"beep", "boop"})
107
test("test beep boop", kvs{}, words{"beep", "boop"})
108
testFail("-s")
109
test("-s foo", kvs{"s": "foo"}, words{})
@@ -110,25 +112,27 @@ func TestOptionParsing(t *testing.T) {
112
test("-b", kvs{"b": true}, words{})
113
test("-bs foo", kvs{"b": true, "s": "foo"}, words{})
114
test("-sb", kvs{"s": "b"}, words{})
113
- test("-b foo", kvs{"b": true}, words{"foo"})
114
- test("--bool foo", kvs{"bool": true}, words{"foo"})
115
+ test("-b test foo", kvs{"b": true}, words{"foo"})
116
+ test("--bool test foo", kvs{"bool": true}, words{"foo"})
117
testFail("--bool=foo")
118
testFail("--string")
119
test("--string foo", kvs{"string": "foo"}, words{})
120
test("--string=foo", kvs{"string": "foo"}, words{})
121
test("-- -b", kvs{}, words{"-b"})
120
- test("foo -b", kvs{"b": true}, words{"foo"})
122
+ test("test foo -b", kvs{"b": true}, words{"foo"})
123
test("-b=false", kvs{"b": false}, words{})
124
test("-b=true", kvs{"b": true}, words{})
123
- test("-b=false foo", kvs{"b": false}, words{"foo"})
124
- test("-b=true foo", kvs{"b": true}, words{"foo"})
125
- test("--bool=true foo", kvs{"bool": true}, words{"foo"})
126
- test("--bool=false foo", kvs{"bool": false}, words{"foo"})
127
- test("-b=FaLsE foo", kvs{"b": false}, words{"foo"})
128
- test("-b=TrUe foo", kvs{"b": true}, words{"foo"})
129
- test("-b true", kvs{"b": true}, words{"true"})
130
- test("-b false", kvs{"b": true}, words{"false"})
131
- test("-b --string foo bar", kvs{"b": true, "string": "foo"}, words{"bar"})
125
+ test("-b=false test foo", kvs{"b": false}, words{"foo"})
126
+ test("-b=true test foo", kvs{"b": true}, words{"foo"})
127
+ test("--bool=true test foo", kvs{"bool": true}, words{"foo"})
128
+ test("--bool=false test foo", kvs{"bool": false}, words{"foo"})
129
+ test("-b test true", kvs{"b": true}, words{"true"})
130
+ test("-b test false", kvs{"b": true}, words{"false"})
131
+ test("-b=FaLsE test foo", kvs{"b": false}, words{"foo"})
132
+ test("-b=TrUe test foo", kvs{"b": true}, words{"foo"})
133
+ test("-b test true", kvs{"b": true}, words{"true"})
134
+ test("-b test false", kvs{"b": true}, words{"false"})
135
+ test("-b --string foo test bar", kvs{"b": true, "string": "foo"}, words{"bar"})
136
test("-b=false --string bar", kvs{"b": false, "string": "bar"}, words{})
137
}
138
core/commands/root.go
-5
@@ -13,11 +13,6 @@ import (
13
14
var log = logging.Logger("core/commands")
15
16
-type TestOutput struct {
17
- Foo string
18
- Bar int
19
-}
20
-
16
const (
17
ApiOption = "api"
18
)