Skip to content

Commit 79f9f2f

Browse files
author
于果
committed
Merge "master" into "feature/yg/meego-7022395131-ve-login-support"
Conflicts: README.EN.MD
2 parents 8f954e0 + 42a5926 commit 79f9f2f

7 files changed

Lines changed: 221 additions & 3 deletions

File tree

README.EN.MD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,3 +417,7 @@ The following code examples are provided:
417417
```shell
418418
ve rds_mysql ModifyDBInstanceIPList --body '{"InstanceId":"xxxxxx", "GroupName": "xxxxxx", "IPList": ["10.20.30.40", "50.60.70.80"]}'
419419
```
420+
421+
## Security and privacy
422+
This project takes security seriously.
423+
For vulnerability reporting and supported versions, see [SECURITY.md](SECURITY.md)

README.MD

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,10 @@ ve <service name> <action> [--parameter1 value1 --parameter2 value2 ...]
436436
ve rds_mysql ModifyDBInstanceIPList --body '{"InstanceId":"xxxxxx", "GroupName": "xxxxxx", "IPList": ["10.20.30.40", "50.60.70.80"]}'
437437
```
438438

439-
439+
440+
## Security and privacy
441+
This project takes security seriously.
442+
For vulnerability reporting and supported versions, see [SECURITY.md](SECURITY.md)
440443

441444

442445

SECURITY.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Security and privacy
2+
If you discover potential security issues in the project, or believe you may have found a security issue, please notify the ByteDance security team through our [security center](https://security.bytedance.com/src/) or [vulnerability reporting email](mailto:src@bytedance.com). Please do not create public GitHub Issues.
3+
We will assess the vulnerability based on the Common Vulnerability Scoring System (CVSS 3.1). The security team will keep you updated on key progress and may request further information or guidance from you. You are welcome to contact us via the email or website mentioned above to ask questions or discuss disclosure matters.
4+
To protect the security of our customers, ByteDance requests that you do not publish or share information regarding the vulnerability in any public forum, nor publish or share data involving users, until the vulnerability has been remediated and our users have been notified. Please understand that the time required for remediation depends on the severity of the vulnerability and the scope of the impact.
5+
Individuals, companies, and security teams may wish to publish security advisories on their own websites or other forums. Please contact us via the email or website mentioned above prior to publication to discuss the information that can be disclosed and to coordinate the disclosure timeline.
6+
## Bug Bounty Reward
7+
[For the policy of bug bounty reward](https://bytedance.larkoffice.com/docx/ZstQd7bbooDctqxBCAmcFasOngd), if you have any questions about the rules, please contact [https://src.bytedance.com/home](https://src.bytedance.com/home) for consultation.

cmd/cmd_action.go

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"fmt"
66
"sort"
7+
"strconv"
78
"strings"
89

910
"github.com/spf13/cobra"
@@ -87,6 +88,7 @@ func doAction(ctx *Context, serviceName, action string) (err error) {
8788
method := "GET"
8889
contentType := ""
8990
apiInfo := rootSupport.GetApiInfo(serviceName, action)
91+
apiMeta := rootSupport.GetApiMeta(serviceName, action)
9092

9193
if apiInfo != nil && apiInfo.Method != "" {
9294
method = apiInfo.Method
@@ -100,7 +102,14 @@ func doAction(ctx *Context, serviceName, action string) (err error) {
100102
for _, f := range ctx.dynamicFlags.flags {
101103
// rebuild input
102104
if f.Name != "body" {
103-
if a, success := util.ParseToJsonArrayOrObject(strings.TrimSpace(f.value)); success {
105+
// Skip JSON parsing for parameters whose declared type is "string".
106+
// Without this, a value like '{"Statement":[...]}' is deserialized
107+
// into a Go map and then flattened into query params, which breaks
108+
// APIs that expect a raw JSON string (e.g. IAM CreatePolicy's
109+
// PolicyDocument parameter).
110+
if isStringParam(apiMeta, f.Name) {
111+
input[f.Name] = f.value
112+
} else if a, success := util.ParseToJsonArrayOrObject(strings.TrimSpace(f.value)); success {
104113
input[f.Name] = a
105114
} else {
106115
input[f.Name] = f.value
@@ -173,6 +182,69 @@ func doAction(ctx *Context, serviceName, action string) (err error) {
173182
return
174183
}
175184

185+
// isStringParam reports whether the named parameter should be treated as a
186+
// literal string when rebuilding request input.
187+
//
188+
// This includes both parameters declared as type "string" and indexed
189+
// elements of repeated string arrays whose metadata key ends with ".N" and is
190+
// declared as "array[string]" (for example ResourceNames.N -> --ResourceNames.0).
191+
// In those cases the caller must NOT attempt to parse the value as JSON.
192+
func isStringParam(apiMeta *ApiMeta, name string) bool {
193+
mt, matchedKey, ok := getRequestMetaType(apiMeta, name)
194+
if !ok {
195+
return false
196+
}
197+
198+
switch mt.TypeName {
199+
case "string":
200+
return true
201+
case "array[string]":
202+
return isIndexedStringArrayElement(matchedKey)
203+
default:
204+
return false
205+
}
206+
}
207+
208+
func isIndexedStringArrayElement(matchedKey string) bool {
209+
return strings.HasSuffix(matchedKey, ".N")
210+
}
211+
212+
func getRequestMetaType(apiMeta *ApiMeta, name string) (*MetaType, string, bool) {
213+
if apiMeta == nil || apiMeta.Request == nil || apiMeta.Request.MetaTypes == nil {
214+
return nil, "", false
215+
}
216+
217+
if mt, ok := apiMeta.Request.MetaTypes[name]; ok {
218+
return mt, name, true
219+
}
220+
221+
normalizedName := normalizeMetaTypeKey(name)
222+
if normalizedName == name {
223+
return nil, "", false
224+
}
225+
226+
mt, ok := apiMeta.Request.MetaTypes[normalizedName]
227+
return mt, normalizedName, ok
228+
}
229+
230+
func normalizeMetaTypeKey(name string) string {
231+
parts := strings.Split(name, ".")
232+
changed := false
233+
234+
for i, part := range parts {
235+
if _, err := strconv.Atoi(part); err == nil {
236+
parts[i] = "N"
237+
changed = true
238+
}
239+
}
240+
241+
if !changed {
242+
return name
243+
}
244+
245+
return strings.Join(parts, ".")
246+
}
247+
176248
func actionUsageTemplate(params []string) string {
177249
sort.Strings(params)
178250

cmd/cmd_action_test.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package cmd
2+
3+
import "testing"
4+
5+
func TestIsStringParam(t *testing.T) {
6+
tests := []struct {
7+
name string
8+
apiMeta *ApiMeta
9+
param string
10+
expected bool
11+
}{
12+
{
13+
name: "nil apiMeta",
14+
apiMeta: nil,
15+
param: "PolicyDocument",
16+
expected: false,
17+
},
18+
{
19+
name: "string type param",
20+
apiMeta: &ApiMeta{
21+
Request: &Meta{
22+
MetaTypes: map[string]*MetaType{
23+
"PolicyDocument": {TypeName: "string"},
24+
"PolicyName": {TypeName: "string"},
25+
},
26+
},
27+
},
28+
param: "PolicyDocument",
29+
expected: true,
30+
},
31+
{
32+
name: "non-string type param",
33+
apiMeta: &ApiMeta{
34+
Request: &Meta{
35+
MetaTypes: map[string]*MetaType{
36+
"Filters": {TypeName: "object"},
37+
},
38+
},
39+
},
40+
param: "Filters",
41+
expected: false,
42+
},
43+
{
44+
name: "unknown param",
45+
apiMeta: &ApiMeta{
46+
Request: &Meta{
47+
MetaTypes: map[string]*MetaType{
48+
"PolicyName": {TypeName: "string"},
49+
},
50+
},
51+
},
52+
param: "Unknown",
53+
expected: false,
54+
},
55+
{
56+
name: "indexed repeated string param",
57+
apiMeta: &ApiMeta{
58+
Request: &Meta{
59+
MetaTypes: map[string]*MetaType{
60+
"TagFilters.N.Key": {TypeName: "string"},
61+
},
62+
},
63+
},
64+
param: "TagFilters.1.Key",
65+
expected: true,
66+
},
67+
{
68+
name: "indexed repeated string array element",
69+
apiMeta: &ApiMeta{
70+
Request: &Meta{
71+
MetaTypes: map[string]*MetaType{
72+
"TagFilters.N.Values.N": {TypeName: "array[string]"},
73+
},
74+
},
75+
},
76+
param: "TagFilters.1.Values.1",
77+
expected: true,
78+
},
79+
{
80+
name: "indexed root string array element",
81+
apiMeta: &ApiMeta{
82+
Request: &Meta{
83+
MetaTypes: map[string]*MetaType{
84+
"ResourceNames.N": {TypeName: "array[string]"},
85+
},
86+
},
87+
},
88+
param: "ResourceNames.0",
89+
expected: true,
90+
},
91+
{
92+
name: "root string array is not treated as string literal",
93+
apiMeta: &ApiMeta{
94+
Request: &Meta{
95+
MetaTypes: map[string]*MetaType{
96+
"ResourceNames": {TypeName: "array[string]"},
97+
},
98+
},
99+
},
100+
param: "ResourceNames",
101+
expected: false,
102+
},
103+
{
104+
name: "indexed repeated non-string param",
105+
apiMeta: &ApiMeta{
106+
Request: &Meta{
107+
MetaTypes: map[string]*MetaType{
108+
"TagFilters.N": {TypeName: "object"},
109+
},
110+
},
111+
},
112+
param: "TagFilters.1",
113+
expected: false,
114+
},
115+
}
116+
117+
for _, tt := range tests {
118+
t.Run(tt.name, func(t *testing.T) {
119+
got := isStringParam(tt.apiMeta, tt.param)
120+
if got != tt.expected {
121+
t.Errorf("isStringParam(%v, %q) = %v, want %v", tt.apiMeta, tt.param, got, tt.expected)
122+
}
123+
})
124+
}
125+
}

cmd/root_support.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@ func (r *RootSupport) GetVersion(svc string) string {
117117
return r.Versions[svc]
118118
}
119119

120+
func (r *RootSupport) GetApiMeta(svc string, action string) *ApiMeta {
121+
if metas, ok := r.SupportTypes[svc]; ok {
122+
return metas[action]
123+
}
124+
return nil
125+
}
126+
120127
func (r *RootSupport) GetApiInfo(svc string, action string) *ApiInfo {
121128
for k, v := range r.SupportAction {
122129
if k == svc {

cmd/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ var clientVersionAndUserAgentHandler = request.NamedHandler{
1212
}
1313

1414
const clientName = "volcengine-cli"
15-
const clientVersion = "1.0.39"
15+
const clientVersion = "1.0.40"

0 commit comments

Comments
 (0)