Skip to content

Commit b5b940f

Browse files
chmouelsavitaashture
authored andcommitted
Make sure to sort secrets by longest when hiding
When we hide secrets in the logs we need to make sure that we start from the longest one, otherwise if we have two secrets with the same prefix the shortest one may hide first and leak the end of the longest one. Since we are in a loop and text is replace the substitutions would not occurs. Signed-off-by: Chmouel Boudjnah <[email protected]>
1 parent 91af4d6 commit b5b940f

File tree

3 files changed

+58
-4
lines changed

3 files changed

+58
-4
lines changed

docs/content/docs/guide/statuses.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,11 @@ failed task.
4545
To prevent exposing secrets, Pipelines-as-Code analyze the PipelineRun and
4646
replace secret values with hidden characters. This is achieved by retrieving
4747
all secrets from the environment variables associated with tasks and steps, and
48-
searching for matches of these values in the output snippet. These matches are
49-
then replaced with a `"*****"` placeholder hiding these secrets.
48+
searching for matches of these values in the output snippet.
49+
50+
These matches are first sorted by the longest and then replaced with a
51+
`"*****"` placeholder in the output snippet. This ensures that the output
52+
will not contain any leaked secrets.
5053

5154
The hiding of the secret does not support concealing secrets from `workspaces`
5255
and

pkg/secrets/secrets.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import (
1212

1313
const leakedReplacement = "*****"
1414

15-
// GetSecretsAttachedToPipelineRun get all secrets attached to a PipelineRun and grab their values
15+
// GetSecretsAttachedToPipelineRun get all secrets attached to a PipelineRun and
16+
// grab their values attached to it
1617
func GetSecretsAttachedToPipelineRun(ctx context.Context, k kubeinteraction.Interface, pr *tektonv1.PipelineRun) []ktypes.SecretValue {
1718
ret := []ktypes.SecretValue{}
1819
// check if pipelineRef is defined or exist
@@ -61,9 +62,25 @@ func GetSecretsAttachedToPipelineRun(ctx context.Context, k kubeinteraction.Inte
6162
return ret
6263
}
6364

65+
// sortSecretsByLongests sort all secrets by length, the longest first
66+
// if we don't sort by longest then if there two passwords with the same prefix
67+
// the shortest one will replace and would leak the end of the passwords of the longest after
68+
func sortSecretsByLongests(values []ktypes.SecretValue) []ktypes.SecretValue {
69+
ret := []ktypes.SecretValue{}
70+
ret = append(ret, values...)
71+
for i := 0; i < len(ret); i++ {
72+
for j := i + 1; j < len(ret); j++ {
73+
if len(ret[i].Value) < len(ret[j].Value) {
74+
ret[i], ret[j] = ret[j], ret[i]
75+
}
76+
}
77+
}
78+
return ret
79+
}
80+
6481
// ReplaceSecretsInText this will take a text snippet and hide the leaked secret
6582
func ReplaceSecretsInText(text string, values []ktypes.SecretValue) string {
66-
for _, sv := range values {
83+
for _, sv := range sortSecretsByLongests(values) {
6784
text = strings.ReplaceAll(text, sv.Value, leakedReplacement)
6885
}
6986
return text

pkg/secrets/secrets_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,21 @@ func TestReplaceSecretsInText(t *testing.T) {
202202
},
203203
},
204204
},
205+
{
206+
name: "replace secrets in text with same prefix",
207+
text: "I am the most beautifuller person in the world with the most beautiful friends",
208+
result: "I am the most ***** person in the world with the most ***** friends",
209+
values: []types.SecretValue{
210+
{
211+
Name: "beautiful-secret",
212+
Value: "beautiful",
213+
},
214+
{
215+
Name: "most-beautiful-secret",
216+
Value: "beautifuller",
217+
},
218+
},
219+
},
205220
}
206221
for _, tt := range tests {
207222
t.Run(tt.name, func(t *testing.T) {
@@ -210,3 +225,22 @@ func TestReplaceSecretsInText(t *testing.T) {
210225
})
211226
}
212227
}
228+
229+
func TestSortByLongest(t *testing.T) {
230+
values := []types.SecretValue{
231+
{
232+
Name: "beautiful-secret",
233+
Value: "beautiful",
234+
},
235+
{
236+
Name: "Even more beautiful",
237+
Value: "beautifuller",
238+
},
239+
{
240+
Name: "Not that beautiful",
241+
Value: "notpretty",
242+
},
243+
}
244+
ret := sortSecretsByLongests(values)
245+
assert.Equal(t, ret[0].Name, "Even more beautiful")
246+
}

0 commit comments

Comments
 (0)