@cryptotaxi247 / kubo / commits / 597f2b827

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 +}