test: add regression tests for config secrets protection (#11061)
Marcin Rataj committed
Nov 17, 2025 at 19:10 UTC
597f2b827d03f88ee02717a7d14116e1e563475d
1 file changed
+164
test/cli/config_secrets_test.go
new
+164
@@ -0,0 +1,164 @@
1
+package cli
2
+
3
+import (
4
+ "strings"
5
+ "testing"
6
+
7
+ "github.com/ipfs/kubo/test/cli/harness"
8
+ "github.com/stretchr/testify/assert"
9
+ "github.com/tidwall/sjson"
10
+)
11
+
12
+func TestConfigSecrets(t *testing.T) {
13
+ t.Parallel()
14
+
15
+ t.Run("Identity.PrivKey protection", func(t *testing.T) {
16
+ t.Parallel()
17
+
18
+ t.Run("Identity.PrivKey is concealed in config show", func(t *testing.T) {
19
+ t.Parallel()
20
+ node := harness.NewT(t).NewNode().Init()
21
+
22
+ // Read the actual config file to get the real PrivKey
23
+ configFile := node.ReadFile(node.ConfigFile())
24
+ assert.Contains(t, configFile, "PrivKey")
25
+
26
+ // config show should NOT contain the PrivKey
27
+ configShow := node.RunIPFS("config", "show").Stdout.String()
28
+ assert.NotContains(t, configShow, "PrivKey")
29
+ })
30
+
31
+ t.Run("Identity.PrivKey cannot be read via ipfs config", func(t *testing.T) {
32
+ t.Parallel()
33
+ node := harness.NewT(t).NewNode().Init()
34
+
35
+ // Attempting to read Identity.PrivKey should fail
36
+ res := node.RunIPFS("config", "Identity.PrivKey")
37
+ assert.Equal(t, 1, res.ExitCode())
38
+ assert.Contains(t, res.Stderr.String(), "cannot show or change private key")
39
+ })
40
+
41
+ t.Run("Identity.PrivKey cannot be read via ipfs config Identity", func(t *testing.T) {
42
+ t.Parallel()
43
+ node := harness.NewT(t).NewNode().Init()
44
+
45
+ // Attempting to read Identity section should fail (it contains PrivKey)
46
+ res := node.RunIPFS("config", "Identity")
47
+ assert.Equal(t, 1, res.ExitCode())
48
+ assert.Contains(t, res.Stderr.String(), "cannot show or change private key")
49
+ })
50
+
51
+ t.Run("Identity.PrivKey cannot be set via config replace", func(t *testing.T) {
52
+ t.Parallel()
53
+ // Key rotation must be done in offline mode via the dedicated `ipfs key rotate` command.
54
+ // This test ensures PrivKey cannot be changed via config replace.
55
+ node := harness.NewT(t).NewNode().Init()
56
+
57
+ configShow := node.RunIPFS("config", "show").Stdout.String()
58
+
59
+ // Try to inject a PrivKey via config replace
60
+ configJSON := MustVal(sjson.Set(configShow, "Identity.PrivKey", "CAASqAkwggSkAgEAAo"))
61
+ node.WriteBytes("new-config", []byte(configJSON))
62
+ res := node.RunIPFS("config", "replace", "new-config")
63
+ assert.Equal(t, 1, res.ExitCode())
64
+ assert.Contains(t, res.Stderr.String(), "setting private key")
65
+ })
66
+
67
+ t.Run("Identity.PrivKey is preserved when re-injecting config", func(t *testing.T) {
68
+ t.Parallel()
69
+ node := harness.NewT(t).NewNode().Init()
70
+
71
+ // Read the original config file
72
+ originalConfig := node.ReadFile(node.ConfigFile())
73
+ assert.Contains(t, originalConfig, "PrivKey")
74
+
75
+ // Extract the PrivKey value for comparison
76
+ var origPrivKey string
77
+ assert.Contains(t, originalConfig, "PrivKey")
78
+ // Simple extraction - find the PrivKey line
79
+ for _, line := range strings.Split(originalConfig, "\n") {
80
+ if strings.Contains(line, "\"PrivKey\":") {
81
+ origPrivKey = line
82
+ break
83
+ }
84
+ }
85
+ assert.NotEmpty(t, origPrivKey)
86
+
87
+ // Get config show output (which should NOT contain PrivKey)
88
+ configShow := node.RunIPFS("config", "show").Stdout.String()
89
+ assert.NotContains(t, configShow, "PrivKey")
90
+
91
+ // Re-inject the config via config replace
92
+ node.WriteBytes("config-show", []byte(configShow))
93
+ node.IPFS("config", "replace", "config-show")
94
+
95
+ // The PrivKey should still be in the config file
96
+ newConfig := node.ReadFile(node.ConfigFile())
97
+ assert.Contains(t, newConfig, "PrivKey")
98
+
99
+ // Verify the PrivKey line is the same
100
+ var newPrivKey string
101
+ for _, line := range strings.Split(newConfig, "\n") {
102
+ if strings.Contains(line, "\"PrivKey\":") {
103
+ newPrivKey = line
104
+ break
105
+ }
106
+ }
107
+ assert.Equal(t, origPrivKey, newPrivKey, "PrivKey should be preserved")
108
+ })
109
+ })
110
+
111
+ t.Run("TLS security validation", func(t *testing.T) {
112
+ t.Parallel()
113
+
114
+ t.Run("AutoConf.TLSInsecureSkipVerify defaults to false", func(t *testing.T) {
115
+ t.Parallel()
116
+ node := harness.NewT(t).NewNode().Init()
117
+
118
+ // Check the default value in a fresh init
119
+ res := node.RunIPFS("config", "AutoConf.TLSInsecureSkipVerify")
120
+ // Field may not exist (exit code 1) or be false/empty (exit code 0)
121
+ // Both are acceptable as they mean "not true"
122
+ output := res.Stdout.String()
123
+ assert.NotContains(t, output, "true", "default should not be true")
124
+ })
125
+
126
+ t.Run("AutoConf.TLSInsecureSkipVerify can be set to true", func(t *testing.T) {
127
+ t.Parallel()
128
+ node := harness.NewT(t).NewNode().Init()
129
+
130
+ // Set to true
131
+ node.IPFS("config", "AutoConf.TLSInsecureSkipVerify", "true", "--json")
132
+
133
+ // Verify it was set
134
+ res := node.RunIPFS("config", "AutoConf.TLSInsecureSkipVerify")
135
+ assert.Equal(t, 0, res.ExitCode())
136
+ assert.Contains(t, res.Stdout.String(), "true")
137
+ })
138
+
139
+ t.Run("HTTPRetrieval.TLSInsecureSkipVerify defaults to false", func(t *testing.T) {
140
+ t.Parallel()
141
+ node := harness.NewT(t).NewNode().Init()
142
+
143
+ // Check the default value in a fresh init
144
+ res := node.RunIPFS("config", "HTTPRetrieval.TLSInsecureSkipVerify")
145
+ // Field may not exist (exit code 1) or be false/empty (exit code 0)
146
+ // Both are acceptable as they mean "not true"
147
+ output := res.Stdout.String()
148
+ assert.NotContains(t, output, "true", "default should not be true")
149
+ })
150
+
151
+ t.Run("HTTPRetrieval.TLSInsecureSkipVerify can be set to true", func(t *testing.T) {
152
+ t.Parallel()
153
+ node := harness.NewT(t).NewNode().Init()
154
+
155
+ // Set to true
156
+ node.IPFS("config", "HTTPRetrieval.TLSInsecureSkipVerify", "true", "--json")
157
+
158
+ // Verify it was set
159
+ res := node.RunIPFS("config", "HTTPRetrieval.TLSInsecureSkipVerify")
160
+ assert.Equal(t, 0, res.ExitCode())
161
+ assert.Contains(t, res.Stdout.String(), "true")
162
+ })
163
+ })
164
+}