diff --git a/pkg/utils/security/coverage.html b/pkg/utils/security/coverage.html
new file mode 100644
index 00000000000..48a3bde5676
--- /dev/null
+++ b/pkg/utils/security/coverage.html
@@ -0,0 +1,221 @@
+
+
+
+
+
+
+
/*
+Copyright 2023 The Fluid Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package security
+
+import (
+ "fmt"
+ "strings"
+)
+
+// According to https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting
+// a -> a
+// a b -> a b
+// $a -> $'$a'
+// $'a' -> $'$\'$a'\'
+func EscapeBashStr(s string) string {
+ if !containsOne(s, []rune{'$', '`', '&', ';', '>', '|', '(', ')'}) {
+ return s
+ }
+ s = strings.ReplaceAll(s, `\`, `\\`)
+ s = strings.ReplaceAll(s, `'`, `\'`)
+ if strings.Contains(s, `\\`) {
+ s = strings.ReplaceAll(s, `\\\\`, `\\`)
+ s = strings.ReplaceAll(s, `\\\'`, `\'`)
+ s = strings.ReplaceAll(s, `\\"`, `\"`)
+ s = strings.ReplaceAll(s, `\\a`, `\a`)
+ s = strings.ReplaceAll(s, `\\b`, `\b`)
+ s = strings.ReplaceAll(s, `\\e`, `\e`)
+ s = strings.ReplaceAll(s, `\\E`, `\E`)
+ s = strings.ReplaceAll(s, `\\n`, `\n`)
+ s = strings.ReplaceAll(s, `\\r`, `\r`)
+ s = strings.ReplaceAll(s, `\\t`, `\t`)
+ s = strings.ReplaceAll(s, `\\v`, `\v`)
+ s = strings.ReplaceAll(s, `\\?`, `\?`)
+ }
+ return fmt.Sprintf(`$'%s'`, s)
+}
+
+func containsOne(target string, chars []rune) bool {
+ charMap := make(map[rune]bool, len(chars))
+ for _, c := range chars {
+ charMap[c] = true
+ }
+ for _, s := range target {
+ if charMap[s] {
+ return true
+ }
+ }
+ return false
+}
+
+
+
/*
+Copyright 2023 The Fluid Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package security
+
+import "strings"
+
+var sensitiveKeys map[string]bool = map[string]bool{
+ "aws.secretKey": true,
+ "aws.accessKeyId": true,
+ "fs.oss.accessKeyId": true,
+ "fs.oss.accessKeySecret": true,
+}
+
+func FilterCommand(command []string) (filteredCommand []string) {
+ for _, str := range command {
+ filteredCommand = append(filteredCommand, FilterString(str))
+ }
+
+ return
+}
+
+func FilterString(line string) string {
+ for s := range sensitiveKeys {
+ // if the log line contains a secret value redact it
+ if strings.Contains(line, s) {
+ line = s + "=[ redacted ]"
+ }
+ }
+
+ return line
+}
+
+func UpdateSensitiveKey(key string) {
+ if _, found := sensitiveKeys[key]; !found {
+ sensitiveKeys[key] = true
+ }
+}
+
+
+
+
+
+
diff --git a/pkg/utils/security/filter.go b/pkg/utils/security/filter.go
index 3d8d77bd666..ff93bc643e7 100644
--- a/pkg/utils/security/filter.go
+++ b/pkg/utils/security/filter.go
@@ -34,14 +34,37 @@ func FilterCommand(command []string) (filteredCommand []string) {
}
func FilterString(line string) string {
+ result := line
for s := range sensitiveKeys {
- // if the log line contains a secret value redact it
- if strings.Contains(line, s) {
- line = s + "=[ redacted ]"
+ // if the log line contains a secret key, redact its value
+ if strings.Contains(result, s) {
+ // Look for pattern "key=" and replace everything after = until space or end
+ searchPattern := s + "="
+ idx := strings.Index(result, searchPattern)
+
+ for idx != -1 {
+ // Find the end of the value (next space or end of string)
+ startValue := idx + len(searchPattern)
+ endValue := startValue
+
+ // Find where the value ends (space, newline, or end of string)
+ for endValue < len(result) && result[endValue] != ' ' && result[endValue] != '\n' && result[endValue] != '\t' {
+ endValue++
+ }
+
+ // Replace the value with [ redacted ]
+ result = result[:startValue] + "[ redacted ]" + result[endValue:]
+
+ // Look for next occurrence
+ idx = strings.Index(result[startValue+len("[ redacted ]"):], searchPattern)
+ if idx != -1 {
+ idx = idx + startValue + len("[ redacted ]")
+ }
+ }
}
}
- return line
+ return result
}
func UpdateSensitiveKey(key string) {
diff --git a/pkg/utils/security/filter_test.go b/pkg/utils/security/filter_test.go
index 707bff37d01..b1be9cfa26b 100644
--- a/pkg/utils/security/filter_test.go
+++ b/pkg/utils/security/filter_test.go
@@ -17,72 +17,86 @@ limitations under the License.
package security
import (
- "reflect"
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
"testing"
)
-func TestFilterCommand(t *testing.T) {
-
- type testCase struct {
- name string
- input []string
- expect []string
- }
-
- testCases := []testCase{
- {
- name: "withSensitiveKey",
- input: []string{"mount", "fs", "aws.secretKey=xxxxxxxxx"},
- expect: []string{"mount", "fs", "aws.secretKey=[ redacted ]"},
- }, {
- name: "withOutSensitiveKey",
- input: []string{"mount", "fs", "alluxio.underfs.s3.inherit.acl=false"},
- expect: []string{"mount", "fs", "alluxio.underfs.s3.inherit.acl=false"},
- }, {
- name: "key",
- input: []string{"mount", "fs", "alluxio.underfs.s3.inherit.acl=false", "aws.secretKey=xxxxxxxxx"},
- expect: []string{"mount", "fs", "alluxio.underfs.s3.inherit.acl=false", "aws.secretKey=[ redacted ]"},
- },
- }
+func TestSecurity(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "Security Suite")
+}
- for _, test := range testCases {
- got := FilterCommand(test.input)
- if !reflect.DeepEqual(got, test.expect) {
- t.Errorf("testcase %s is failed due to expect %v, but got %v", test.name, test.expect, got)
+var _ = Describe("FilterCommand", func() {
+ BeforeEach(func() {
+ // Reset sensitiveKeys to its initial state before each test
+ sensitiveKeys = map[string]bool{
+ "aws.secretKey": true,
+ "aws.accessKeyId": true,
+ "fs.oss.accessKeyId": true,
+ "fs.oss.accessKeySecret": true,
}
- }
+ })
-}
+ Context("when filtering commands", func() {
+ It("should redact sensitive keys", func() {
+ input := []string{"mount", "fs", "aws.secretKey=xxxxxxxxx"}
+ expect := []string{"mount", "fs", "aws.secretKey=[ redacted ]"}
+ got := FilterCommand(input)
+ Expect(got).To(Equal(expect))
+ })
-func TestFilterCommandWithSensitive(t *testing.T) {
+ It("should not modify commands without sensitive keys", func() {
+ input := []string{"mount", "fs", "alluxio.underfs.s3.inherit.acl=false"}
+ expect := []string{"mount", "fs", "alluxio.underfs.s3.inherit.acl=false"}
+ got := FilterCommand(input)
+ Expect(got).To(Equal(expect))
+ })
- type testCase struct {
- name string
- filterKey string
- input []string
- expect []string
- }
+ It("should redact sensitive keys while preserving other parameters", func() {
+ input := []string{"mount", "fs", "alluxio.underfs.s3.inherit.acl=false", "aws.secretKey=xxxxxxxxx"}
+ expect := []string{"mount", "fs", "alluxio.underfs.s3.inherit.acl=false", "aws.secretKey=[ redacted ]"}
+ got := FilterCommand(input)
+ Expect(got).To(Equal(expect))
+ })
- testCases := []testCase{
- {
- name: "NotAddSensitiveKey",
- filterKey: "test",
- input: []string{"mount", "fs", "fs.azure.account.key=xxxxxxxxx"},
- expect: []string{"mount", "fs", "fs.azure.account.key=xxxxxxxxx"},
- }, {
- name: "AddSensitiveKey",
- filterKey: "fs.azure.account.key",
- input: []string{"mount", "fs", "fs.azure.account.key=false"},
- expect: []string{"mount", "fs", "fs.azure.account.key=[ redacted ]"},
- },
- }
+ It("should redact multiple sensitive keys in a single string", func() {
+ input := []string{"aws.secretKey=secret and aws.accessKeyId=key"}
+ expect := []string{"aws.secretKey=[ redacted ] and aws.accessKeyId=[ redacted ]"}
+ got := FilterCommand(input)
+ Expect(got).To(Equal(expect))
+ })
+ })
+})
- for _, test := range testCases {
- UpdateSensitiveKey(test.filterKey)
- got := FilterCommand(test.input)
- if !reflect.DeepEqual(got, test.expect) {
- t.Errorf("testcase %s is failed due to expect %v, but got %v", test.name, test.expect, got)
+var _ = Describe("FilterCommandWithSensitive", func() {
+ BeforeEach(func() {
+ // Reset sensitiveKeys to its initial state before each test
+ sensitiveKeys = map[string]bool{
+ "aws.secretKey": true,
+ "aws.accessKeyId": true,
+ "fs.oss.accessKeyId": true,
+ "fs.oss.accessKeySecret": true,
}
- }
+ })
-}
+ Context("when updating sensitive keys", func() {
+ It("should not redact keys that are not added as sensitive", func() {
+ filterKey := "test"
+ input := []string{"mount", "fs", "fs.azure.account.key=xxxxxxxxx"}
+ expect := []string{"mount", "fs", "fs.azure.account.key=xxxxxxxxx"}
+ UpdateSensitiveKey(filterKey)
+ got := FilterCommand(input)
+ Expect(got).To(Equal(expect))
+ })
+
+ It("should redact keys that are added as sensitive", func() {
+ filterKey := "fs.azure.account.key"
+ input := []string{"mount", "fs", "fs.azure.account.key=false"}
+ expect := []string{"mount", "fs", "fs.azure.account.key=[ redacted ]"}
+ UpdateSensitiveKey(filterKey)
+ got := FilterCommand(input)
+ Expect(got).To(Equal(expect))
+ })
+ })
+})