detached 33a243c

masker_parity_test.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3package executor
4
5import (
6	"encoding/base64"
7	"strings"
8	"testing"
9)
10
11// Cases ported from actions/runner src/Test/L0/HostContextL0.cs
12// (DefaultSecretMaskers, SecretSectionMasking), MIT, (c) 2019 GitHub; see
13// THIRD_PARTY.md.
14func TestDefaultSecretMaskers(t *testing.T) {
15	m := newMasker()
16	for _, v := range []string{"Password123!", "Pass\"word\"123!", "Pass word 123!", "Pass<word>123!", "Pass'word'123!", "\"Password123!!\"", "\"short\""} {
17		m.add(v)
18	}
19	b64 := func(s string) string { return base64.StdEncoding.EncodeToString([]byte(s)) }
20	for _, tc := range []struct{ in, want string }{
21		{"123Password123!123", "123***123"},
22		{"password123", "password123"},
23		{`123Pass\"word\"123!123`, "123***123"},
24		{"123Pass%20word%20123%21123", "123***123"},
25		{"123Pass&lt;word&gt;123!123", "123***123"},
26		{"123Pass''word''123!123", "123***123"},
27		{b64(":Password123!"), "OlBh***"},
28		{b64("a:Password123!"), "YTpQ***"},
29		{b64("ab:Password123!"), "YWI6***"},
30		{b64("abc:Password123!"), "YWJjOlBh***"},
31		{b64("abcd:Password123!"), "YWJjZDpQ***"},
32		{b64("abcde:Password123!"), "YWJjZGU6***"},
33		{"123Password123!!123", "123***123"},
34		{"123short123", "123short123"},
35		{"123\"short\"123", "123***123"},
36	} {
37		if got := m.mask(tc.in); got != tc.want {
38			t.Errorf("mask(%q) = %q, want %q", tc.in, got, tc.want)
39		}
40	}
41}
42
43func TestSecretSectionMasking(t *testing.T) {
44	for _, tc := range []struct{ secret, raw, want string }{
45		{"secret&secret&secret", "secret&secret&\x1b[96msecret\x1b[0m", "***\x1b[96m***\x1b[0m"},
46		{"secret&secret+secret", "secret&\x1b[96msecret+secret\x1b[0m", "***\x1b[96m***\x1b[0m"},
47		{"secret+secret&secret", "secret+secret&\x1b[96msecret\x1b[0m", "***\x1b[96m***\x1b[0m"},
48		{"secret&secret&+secretsecret", "secret&secret&+\x1b[96ms\x1b[0mecretsecret", "***\x1b[96ms\x1b[0m***"},
49		{"secret&+secret&secret", "secret&+\x1b[96ms\x1b[0mecret&secret", "***\x1b[96ms\x1b[0m***"},
50		{"secret&+secret&+secret", "secret&+\x1b[96ms\x1b[0mecret&+secret", "***\x1b[96ms\x1b[0m***"},
51		{"secret&+secret&secret&+secret", "secret&+\x1b[96ms\x1b[0mecret&secret&+secret", "***\x1b[96ms\x1b[0m***"},
52		{"secret&secret&+", "secret&secret&+\x1b[96m\x1b[0m", "***\x1b[96m\x1b[0m"},
53	} {
54		m := newMasker(tc.secret)
55		if got := m.mask(tc.raw); got != tc.want {
56			t.Errorf("secret %q: mask(%q) = %q, want %q", tc.secret, tc.raw, got, tc.want)
57		}
58	}
59}
60
61// Encoders of ValueEncoders.cs, one by one.
62func TestValueEncoders(t *testing.T) {
63	for _, tc := range []struct{ name, got, want string }{
64		{"cmdline", strings.ReplaceAll(`a"b`, `"`, `\"`), `a\"b`},
65		{"json", jsonStringEscape("a\"b\\c\n\t\x01/é\u2028"), `a\"b\\c\n\t\u0001/é\u2028`},
66		{"uri", uriDataEscape("a b/c~d-é"), "a%20b%2Fc~d-%C3%A9"},
67		{"xml", xmlDataEscape(`<a href="x">'&'</a>`), "&lt;a href=&quot;x&quot;&gt;&apos;&amp;&apos;&lt;/a&gt;"},
68		{"trim 9 chars", trimDoubleQuotes(`"12345678"`), "12345678"},
69		{"trim 8 chars", trimDoubleQuotes(`"123456"`), ""},
70		{"trim no quote", trimDoubleQuotes(`12345678901`), ""},
71		{"pre", powerShellPreAmpersandEscape("secret&secret&secret"), "secret&secret&"},
72		{"pre plus", powerShellPreAmpersandEscape("secret&+secret&secret"), "secret&+"},
73		{"pre short", powerShellPreAmpersandEscape("ab&c"), ""},
74		{"pre no ampersand", powerShellPreAmpersandEscape("secretsecret"), ""},
75		{"post", powerShellPostAmpersandEscape("secret&secret&secret"), "secret"},
76		{"post plus", powerShellPostAmpersandEscape("secret&+Xsecret&secret"), "secret&secret"},
77		{"post plus, nothing after", powerShellPostAmpersandEscape("secret&+X"), ""},
78		{"post short", powerShellPostAmpersandEscape("secret&abc"), ""},
79	} {
80		if tc.got != tc.want {
81			t.Errorf("%s: %q, want %q", tc.name, tc.got, tc.want)
82		}
83	}
84}
85
86// Adding a value after the first masks makes later text masked, empty values
87// are ignored, overlapping matches merge into one ***.
88func TestMaskerAddAndMerge(t *testing.T) {
89	m := newMasker()
90	m.add("")
91	if m.mask("abc") != "abc" {
92		t.Fatal("empty value masks")
93	}
94	m.add("abcd")
95	m.add("cdef")
96	if got := m.mask("xabcdefy abcd"); got != "x***y ***" {
97		t.Errorf("%q", got)
98	}
99	if got := m.mask(""); got != "" {
100		t.Error(got)
101	}
102	// the same value again changes nothing
103	m.add("abcd")
104	if got := m.mask("abcd"); got != "***" {
105		t.Error(got)
106	}
107}
108
109// SecretMasker.AddValue skips a value only when it was added before as an
110// original, not when it is already one of the forms of another value: the
111// encodings of "one%20two" are masked after "one two" was added, although
112// "one%20two" itself is the URI form of "one two" (GO-001).
113func TestMaskerAddsEncodingsOfAValueThatIsAnEncodingOfAnother(t *testing.T) {
114	m := newMasker()
115	m.add("one two")
116	m.add("one%20two")
117	for _, text := range []string{"one two", "one%20two", "one%2520two"} {
118		if got := m.mask(text); got != "***" {
119			t.Errorf("mask(%q) = %q", text, got)
120		}
121	}
122	// the same original twice is still a no-op
123	m.add("one%20two")
124	if n := len(m.list); n == 0 {
125		t.Fatal("empty")
126	}
127	before := len(m.list)
128	m.add("one%20two")
129	if len(m.list) != before {
130		t.Error("an original added twice grew the list")
131	}
132}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 4

Tags 11

↑↓ moveenter openesc close