@cryptotaxi247 / kubo / commits / 20f815162

fix: enforce identity CID size limits (#10949)

* fix: enforce identity CID size limits - validate --inline-limit against verifcid.MaxDigestSize - add error when --hash=identity exceeds size limit - add tests for identity CID overflow scenarios - update help text to show maximum inline limit This prevents creation of unbounded identity CIDs by enforcing the 128-byte limit defined in https://github.com/ipfs/boxo/pull/1018 Fixes #6011 IPIP: https://github.com/ipfs/specs/pull/512

Marcin Rataj committed Sep 9, 2025 at 20:22 UTC 20f8151628278cb1e1734a208df6aad0104fea03
11 files changed +345 -15
core/commands/add.go
+8 -1
@@ -16,6 +16,7 @@ import (
16 "github.com/ipfs/boxo/files"
17 mfs "github.com/ipfs/boxo/mfs"
18 "github.com/ipfs/boxo/path"
19 + "github.com/ipfs/boxo/verifcid"
20 cmds "github.com/ipfs/go-ipfs-cmds"
21 ipld "github.com/ipfs/go-ipld-format"
22 coreiface "github.com/ipfs/kubo/core/coreiface"
@@ -203,7 +204,7 @@ https://github.com/ipfs/kubo/blob/master/docs/config.md#import
204 cmds.IntOption(maxHAMTFanoutOptionName, "Limit the maximum number of links of a UnixFS HAMT directory node to this (power of 2, multiple of 8). WARNING: experimental, Import.UnixFSHAMTDirectorySizeThreshold is safer. Default: Import.UnixFSHAMTDirectoryMaxFanout"),
205 // Experimental Features
206 cmds.BoolOption(inlineOptionName, "Inline small blocks into CIDs. WARNING: experimental"),
206 - cmds.IntOption(inlineLimitOptionName, "Maximum block size to inline. WARNING: experimental").WithDefault(32),
207 + cmds.IntOption(inlineLimitOptionName, fmt.Sprintf("Maximum block size to inline. Maximum: %d bytes. WARNING: experimental", verifcid.DefaultMaxIdentityDigestSize)).WithDefault(32),
208 cmds.BoolOption(noCopyOptionName, "Add the file using filestore. Implies raw-leaves. WARNING: experimental"),
209 cmds.BoolOption(fstoreCacheOptionName, "Check the filestore for pre-existing blocks. WARNING: experimental"),
210 cmds.BoolOption(preserveModeOptionName, "Apply existing POSIX permissions to created UnixFS entries. WARNING: experimental, forces dag-pb for root block, disables raw-leaves"),
@@ -262,6 +263,12 @@ https://github.com/ipfs/kubo/blob/master/docs/config.md#import
263 hashFunStr, _ := req.Options[hashOptionName].(string)
264 inline, _ := req.Options[inlineOptionName].(bool)
265 inlineLimit, _ := req.Options[inlineLimitOptionName].(int)
266 +
267 + // Validate inline-limit doesn't exceed the maximum identity digest size
268 + if inline && inlineLimit > verifcid.DefaultMaxIdentityDigestSize {
269 + return fmt.Errorf("inline-limit %d exceeds maximum allowed size of %d bytes", inlineLimit, verifcid.DefaultMaxIdentityDigestSize)
270 + }
271 +
272 toFilesStr, toFilesSet := req.Options[toFilesOptionName].(string)
273 preserveMode, _ := req.Options[preserveModeOptionName].(bool)
274 preserveMtime, _ := req.Options[preserveMtimeOptionName].(bool)
docs/changelogs/v0.38.md
+13
@@ -28,6 +28,19 @@ Gateway error pages now provide more actionable information during content retri
28 > - **Enhanced error details**: Timeout errors now display the retrieval phase where failure occurred (e.g., "connecting to providers", "fetching data") and up to 3 peer IDs that were attempted but couldn't deliver the content, making it easier to diagnose network or provider issues.
29 > - **Retry button on all error pages**: Every gateway error page now includes a retry button for quick page refresh without manual URL re-entry.
30
31 +#### 🛠️ Identity CID size enforcement and `ipfs files write` fixes
32 +
33 +**Identity CID size limits are now enforced**
34 +
35 +Identity CIDs use [multihash `0x00`](https://github.com/multiformats/multicodec/blob/master/table.csv#L2) to embed data directly in the CID without hashing. This experimental optimization was designed for tiny data where a CID reference would be larger than the data itself, but without size limits it was easy to misuse and could turn into an anti-pattern that wastes resources and enables abuse. This release enforces a maximum of 128 bytes for identity CIDs - attempting to exceed this limit will return a clear error message.
36 +
37 +- `ipfs add --inline-limit` and `--hash=identity` now enforce the 128-byte maximum (error when exceeded)
38 +- `ipfs files write` prevents creation of oversized identity CIDs
39 +
40 +**Multiple `ipfs files write` bugs have been fixed**
41 +
42 +This release resolves several long-standing MFS issues: raw nodes now preserve their codec instead of being forced to dag-pb, append operations on raw nodes work correctly by converting to UnixFS when needed, and identity CIDs properly inherit the full CID prefix from parent directories.
43 +
44 ### 📦️ Important dependency updates
45
46 ### 📝 Changelog
docs/examples/kubo-as-a-library/go.mod
+1 -1
@@ -7,7 +7,7 @@ go 1.25
7 replace github.com/ipfs/kubo => ./../../..
8
9 require (
10 - github.com/ipfs/boxo v0.34.1-0.20250908170437-7d2493027364
10 + github.com/ipfs/boxo v0.34.1-0.20250909170220-e69f67e94c11
11 github.com/ipfs/kubo v0.0.0-00010101000000-000000000000
12 github.com/libp2p/go-libp2p v0.43.0
13 github.com/multiformats/go-multiaddr v0.16.1
docs/examples/kubo-as-a-library/go.sum
+2 -2
@@ -287,8 +287,8 @@ github.com/ipfs-shipyard/nopfs/ipfs v0.25.0 h1:OqNqsGZPX8zh3eFMO8Lf8EHRRnSGBMqcd
287 github.com/ipfs-shipyard/nopfs/ipfs v0.25.0/go.mod h1:BxhUdtBgOXg1B+gAPEplkg/GpyTZY+kCMSfsJvvydqU=
288 github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
289 github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
290 -github.com/ipfs/boxo v0.34.1-0.20250908170437-7d2493027364 h1:VdRdPlosNYdlENC0UsCxapHala/Q1Me6yBY5ChKUw7s=
291 -github.com/ipfs/boxo v0.34.1-0.20250908170437-7d2493027364/go.mod h1:rXql6ncaLZZfLqDG3Cuw9ZYQKd3rMU5bk1TGXF0+ZL0=
290 +github.com/ipfs/boxo v0.34.1-0.20250909170220-e69f67e94c11 h1:EsCbOKE+giLtrFTysnbTzIRQENOiLdcpOY3kV3y6wlU=
291 +github.com/ipfs/boxo v0.34.1-0.20250909170220-e69f67e94c11/go.mod h1:rXql6ncaLZZfLqDG3Cuw9ZYQKd3rMU5bk1TGXF0+ZL0=
292 github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
293 github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=
294 github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk=
gc/gc.go
+1 -1
@@ -165,7 +165,7 @@ func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots
165 }
166
167 verboseCidError := func(err error) error {
168 - if strings.Contains(err.Error(), verifcid.ErrBelowMinimumHashLength.Error()) ||
168 + if strings.Contains(err.Error(), verifcid.ErrDigestTooSmall.Error()) ||
169 strings.Contains(err.Error(), verifcid.ErrPossiblyInsecureHashFunction.Error()) {
170 err = fmt.Errorf("\"%s\"\nPlease run 'ipfs pin verify'"+ // nolint
171 " to list insecure hashes. If you want to read them,"+
go.mod
+1 -1
@@ -22,7 +22,7 @@ require (
22 github.com/hashicorp/go-version v1.7.0
23 github.com/ipfs-shipyard/nopfs v0.0.14
24 github.com/ipfs-shipyard/nopfs/ipfs v0.25.0
25 - github.com/ipfs/boxo v0.34.1-0.20250908170437-7d2493027364
25 + github.com/ipfs/boxo v0.34.1-0.20250909170220-e69f67e94c11
26 github.com/ipfs/go-block-format v0.2.2
27 github.com/ipfs/go-cid v0.5.0
28 github.com/ipfs/go-cidutil v0.1.0
go.sum
+2 -2
@@ -354,8 +354,8 @@ github.com/ipfs-shipyard/nopfs/ipfs v0.25.0 h1:OqNqsGZPX8zh3eFMO8Lf8EHRRnSGBMqcd
354 github.com/ipfs-shipyard/nopfs/ipfs v0.25.0/go.mod h1:BxhUdtBgOXg1B+gAPEplkg/GpyTZY+kCMSfsJvvydqU=
355 github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
356 github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
357 -github.com/ipfs/boxo v0.34.1-0.20250908170437-7d2493027364 h1:VdRdPlosNYdlENC0UsCxapHala/Q1Me6yBY5ChKUw7s=
358 -github.com/ipfs/boxo v0.34.1-0.20250908170437-7d2493027364/go.mod h1:rXql6ncaLZZfLqDG3Cuw9ZYQKd3rMU5bk1TGXF0+ZL0=
357 +github.com/ipfs/boxo v0.34.1-0.20250909170220-e69f67e94c11 h1:EsCbOKE+giLtrFTysnbTzIRQENOiLdcpOY3kV3y6wlU=
358 +github.com/ipfs/boxo v0.34.1-0.20250909170220-e69f67e94c11/go.mod h1:rXql6ncaLZZfLqDG3Cuw9ZYQKd3rMU5bk1TGXF0+ZL0=
359 github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
360 github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=
361 github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk=
test/cli/identity_cid_test.go new
+310
@@ -0,0 +1,310 @@
1 +package cli
2 +
3 +import (
4 + "fmt"
5 + "os"
6 + "path/filepath"
7 + "strings"
8 + "testing"
9 +
10 + "github.com/ipfs/boxo/verifcid"
11 + "github.com/ipfs/kubo/config"
12 + "github.com/ipfs/kubo/test/cli/harness"
13 + "github.com/stretchr/testify/assert"
14 + "github.com/stretchr/testify/require"
15 +)
16 +
17 +func TestIdentityCIDOverflowProtection(t *testing.T) {
18 + t.Parallel()
19 +
20 + t.Run("ipfs add --hash=identity with small data succeeds", func(t *testing.T) {
21 + t.Parallel()
22 + node := harness.NewT(t).NewNode().Init().StartDaemon()
23 + defer node.StopDaemon()
24 +
25 + // small data that fits in identity CID
26 + smallData := "small data"
27 + tempFile := filepath.Join(node.Dir, "small.txt")
28 + err := os.WriteFile(tempFile, []byte(smallData), 0644)
29 + require.NoError(t, err)
30 +
31 + res := node.IPFS("add", "--hash=identity", tempFile)
32 + assert.NoError(t, res.Err)
33 + cid := strings.Fields(res.Stdout.String())[1]
34 +
35 + // verify it's actually using identity hash
36 + res = node.IPFS("cid", "format", "-f", "%h", cid)
37 + assert.NoError(t, res.Err)
38 + assert.Equal(t, "identity", res.Stdout.Trimmed())
39 + })
40 +
41 + t.Run("ipfs add --hash=identity with large data fails", func(t *testing.T) {
42 + t.Parallel()
43 + node := harness.NewT(t).NewNode().Init().StartDaemon()
44 + defer node.StopDaemon()
45 +
46 + // data larger than verifcid.DefaultMaxIdentityDigestSize
47 + largeData := strings.Repeat("x", verifcid.DefaultMaxIdentityDigestSize+50)
48 + tempFile := filepath.Join(node.Dir, "large.txt")
49 + err := os.WriteFile(tempFile, []byte(largeData), 0644)
50 + require.NoError(t, err)
51 +
52 + res := node.RunIPFS("add", "--hash=identity", tempFile)
53 + assert.NotEqual(t, 0, res.ExitErr.ExitCode())
54 + // should error with digest too large message
55 + assert.Contains(t, res.Stderr.String(), "digest too large")
56 + })
57 +
58 + t.Run("ipfs add --inline with valid --inline-limit succeeds", func(t *testing.T) {
59 + t.Parallel()
60 + node := harness.NewT(t).NewNode().Init().StartDaemon()
61 + defer node.StopDaemon()
62 +
63 + smallData := "small inline data"
64 + tempFile := filepath.Join(node.Dir, "inline.txt")
65 + err := os.WriteFile(tempFile, []byte(smallData), 0644)
66 + require.NoError(t, err)
67 +
68 + // use limit just under the maximum
69 + limit := verifcid.DefaultMaxIdentityDigestSize - 10
70 + res := node.IPFS("add", "--inline", fmt.Sprintf("--inline-limit=%d", limit), tempFile)
71 + assert.NoError(t, res.Err)
72 + cid := strings.Fields(res.Stdout.String())[1]
73 +
74 + // verify the CID is using identity hash (inline)
75 + res = node.IPFS("cid", "format", "-f", "%h", cid)
76 + assert.NoError(t, res.Err)
77 + assert.Equal(t, "identity", res.Stdout.Trimmed())
78 +
79 + // verify the codec (may be dag-pb or raw depending on kubo version)
80 + res = node.IPFS("cid", "format", "-f", "%c", cid)
81 + assert.NoError(t, res.Err)
82 + // Accept either raw or dag-pb as both are valid for inline data
83 + codec := res.Stdout.Trimmed()
84 + assert.True(t, codec == "raw" || codec == "dag-pb", "expected raw or dag-pb codec, got %s", codec)
85 + })
86 +
87 + t.Run("ipfs add --inline with excessive --inline-limit fails", func(t *testing.T) {
88 + t.Parallel()
89 + node := harness.NewT(t).NewNode().Init().StartDaemon()
90 + defer node.StopDaemon()
91 +
92 + smallData := "data"
93 + tempFile := filepath.Join(node.Dir, "inline2.txt")
94 + err := os.WriteFile(tempFile, []byte(smallData), 0644)
95 + require.NoError(t, err)
96 +
97 + excessiveLimit := verifcid.DefaultMaxIdentityDigestSize + 50
98 + res := node.RunIPFS("add", "--inline", fmt.Sprintf("--inline-limit=%d", excessiveLimit), tempFile)
99 + assert.NotEqual(t, 0, res.ExitErr.ExitCode())
100 + assert.Contains(t, res.Stderr.String(), fmt.Sprintf("inline-limit %d exceeds maximum allowed size of %d bytes", excessiveLimit, verifcid.DefaultMaxIdentityDigestSize))
101 + })
102 +
103 + t.Run("ipfs files write --hash=identity appending to identity CID switches to configured hash", func(t *testing.T) {
104 + t.Parallel()
105 + node := harness.NewT(t).NewNode().Init().StartDaemon()
106 + defer node.StopDaemon()
107 +
108 + // create initial small file with identity CID
109 + initialData := "initial"
110 + tempFile := filepath.Join(node.Dir, "initial.txt")
111 + err := os.WriteFile(tempFile, []byte(initialData), 0644)
112 + require.NoError(t, err)
113 +
114 + res := node.IPFS("add", "--hash=identity", tempFile)
115 + assert.NoError(t, res.Err)
116 + cid1 := strings.Fields(res.Stdout.String())[1]
117 +
118 + // verify initial CID uses identity
119 + res = node.IPFS("cid", "format", "-f", "%h", cid1)
120 + assert.NoError(t, res.Err)
121 + assert.Equal(t, "identity", res.Stdout.Trimmed())
122 +
123 + // copy to MFS
124 + res = node.IPFS("files", "cp", fmt.Sprintf("/ipfs/%s", cid1), "/identity-file")
125 + assert.NoError(t, res.Err)
126 +
127 + // append data that would exceed identity CID limit
128 + appendData := strings.Repeat("a", verifcid.DefaultMaxIdentityDigestSize)
129 + appendFile := filepath.Join(node.Dir, "append.txt")
130 + err = os.WriteFile(appendFile, []byte(appendData), 0644)
131 + require.NoError(t, err)
132 +
133 + // append to the end of the file
134 + // get the current data size
135 + res = node.IPFS("files", "stat", "--format", "<size>", "/identity-file")
136 + assert.NoError(t, res.Err)
137 + size := res.Stdout.Trimmed()
138 + // this should succeed because DagModifier in boxo handles the overflow
139 + res = node.IPFS("files", "write", "--hash=identity", "--offset="+size, "/identity-file", appendFile)
140 + assert.NoError(t, res.Err)
141 +
142 + // check that the file now uses non-identity hash
143 + res = node.IPFS("files", "stat", "--hash", "/identity-file")
144 + assert.NoError(t, res.Err)
145 + newCid := res.Stdout.Trimmed()
146 +
147 + // verify new CID does NOT use identity
148 + res = node.IPFS("cid", "format", "-f", "%h", newCid)
149 + assert.NoError(t, res.Err)
150 + assert.NotEqual(t, "identity", res.Stdout.Trimmed())
151 +
152 + // verify it switched to a cryptographic hash
153 + assert.Equal(t, config.DefaultHashFunction, res.Stdout.Trimmed())
154 + })
155 +
156 + t.Run("ipfs files write --hash=identity with small write creates identity CID", func(t *testing.T) {
157 + t.Parallel()
158 + node := harness.NewT(t).NewNode().Init().StartDaemon()
159 + defer node.StopDaemon()
160 +
161 + // create a small file with identity hash directly in MFS
162 + smallData := "small"
163 + tempFile := filepath.Join(node.Dir, "small.txt")
164 + err := os.WriteFile(tempFile, []byte(smallData), 0644)
165 + require.NoError(t, err)
166 +
167 + // write to MFS with identity hash
168 + res := node.IPFS("files", "write", "--create", "--hash=identity", "/mfs-identity", tempFile)
169 + assert.NoError(t, res.Err)
170 +
171 + // verify using identity CID
172 + res = node.IPFS("files", "stat", "--hash", "/mfs-identity")
173 + assert.NoError(t, res.Err)
174 + cid := res.Stdout.Trimmed()
175 +
176 + // verify CID uses identity hash
177 + res = node.IPFS("cid", "format", "-f", "%h", cid)
178 + assert.NoError(t, res.Err)
179 + assert.Equal(t, "identity", res.Stdout.Trimmed())
180 +
181 + // verify content
182 + res = node.IPFS("files", "read", "/mfs-identity")
183 + assert.NoError(t, res.Err)
184 + assert.Equal(t, smallData, res.Stdout.Trimmed())
185 + })
186 +
187 + t.Run("raw node with identity CID converts to UnixFS when appending", func(t *testing.T) {
188 + t.Parallel()
189 + node := harness.NewT(t).NewNode().Init().StartDaemon()
190 + defer node.StopDaemon()
191 +
192 + // create raw block with identity CID
193 + rawData := "raw"
194 + tempFile := filepath.Join(node.Dir, "raw.txt")
195 + err := os.WriteFile(tempFile, []byte(rawData), 0644)
196 + require.NoError(t, err)
197 +
198 + res := node.IPFS("block", "put", "--format=raw", "--mhtype=identity", tempFile)
199 + assert.NoError(t, res.Err)
200 + rawCid := res.Stdout.Trimmed()
201 +
202 + // verify initial CID uses identity hash and raw codec
203 + res = node.IPFS("cid", "format", "-f", "%h", rawCid)
204 + assert.NoError(t, res.Err)
205 + assert.Equal(t, "identity", res.Stdout.Trimmed())
206 +
207 + res = node.IPFS("cid", "format", "-f", "%c", rawCid)
208 + assert.NoError(t, res.Err)
209 + assert.Equal(t, "raw", res.Stdout.Trimmed())
210 +
211 + // copy to MFS
212 + res = node.IPFS("files", "cp", fmt.Sprintf("/ipfs/%s", rawCid), "/raw-identity")
213 + assert.NoError(t, res.Err)
214 +
215 + // append data
216 + appendData := "appended"
217 + appendFile := filepath.Join(node.Dir, "append-raw.txt")
218 + err = os.WriteFile(appendFile, []byte(appendData), 0644)
219 + require.NoError(t, err)
220 +
221 + // get current data size for appending
222 + res = node.IPFS("files", "stat", "--format", "<size>", "/raw-identity")
223 + assert.NoError(t, res.Err)
224 + size := res.Stdout.Trimmed()
225 + res = node.IPFS("files", "write", "--hash=identity", "--offset="+size, "/raw-identity", appendFile)
226 + assert.NoError(t, res.Err)
227 +
228 + // verify content
229 + res = node.IPFS("files", "read", "/raw-identity")
230 + assert.NoError(t, res.Err)
231 + assert.Equal(t, rawData+appendData, res.Stdout.Trimmed())
232 +
233 + // check that it's now a UnixFS structure (dag-pb)
234 + res = node.IPFS("files", "stat", "--hash", "/raw-identity")
235 + assert.NoError(t, res.Err)
236 + newCid := res.Stdout.Trimmed()
237 +
238 + res = node.IPFS("cid", "format", "-f", "%c", newCid)
239 + assert.NoError(t, res.Err)
240 + assert.Equal(t, "dag-pb", res.Stdout.Trimmed())
241 +
242 + res = node.IPFS("files", "stat", "/raw-identity")
243 + assert.NoError(t, res.Err)
244 + assert.Contains(t, res.Stdout.String(), "Type: file")
245 + })
246 +
247 + t.Run("ipfs add --inline-limit at exactly max size succeeds", func(t *testing.T) {
248 + t.Parallel()
249 + node := harness.NewT(t).NewNode().Init().StartDaemon()
250 + defer node.StopDaemon()
251 +
252 + // create small data that will be inlined
253 + smallData := "test data for inline"
254 + tempFile := filepath.Join(node.Dir, "exact.txt")
255 + err := os.WriteFile(tempFile, []byte(smallData), 0644)
256 + require.NoError(t, err)
257 +
258 + // exactly at the limit should succeed
259 + res := node.IPFS("add", "--inline", fmt.Sprintf("--inline-limit=%d", verifcid.DefaultMaxIdentityDigestSize), tempFile)
260 + assert.NoError(t, res.Err)
261 + cid := strings.Fields(res.Stdout.String())[1]
262 +
263 + // verify it uses identity hash (inline) since data is small enough
264 + res = node.IPFS("cid", "format", "-f", "%h", cid)
265 + assert.NoError(t, res.Err)
266 + assert.Equal(t, "identity", res.Stdout.Trimmed())
267 + })
268 +
269 + t.Run("ipfs add --inline-limit one byte over max fails", func(t *testing.T) {
270 + t.Parallel()
271 + node := harness.NewT(t).NewNode().Init().StartDaemon()
272 + defer node.StopDaemon()
273 +
274 + smallData := "test"
275 + tempFile := filepath.Join(node.Dir, "oneover.txt")
276 + err := os.WriteFile(tempFile, []byte(smallData), 0644)
277 + require.NoError(t, err)
278 +
279 + // one byte over should fail
280 + overLimit := verifcid.DefaultMaxIdentityDigestSize + 1
281 + res := node.RunIPFS("add", "--inline", fmt.Sprintf("--inline-limit=%d", overLimit), tempFile)
282 + assert.NotEqual(t, 0, res.ExitErr.ExitCode())
283 + assert.Contains(t, res.Stderr.String(), fmt.Sprintf("inline-limit %d exceeds maximum allowed size of %d bytes", overLimit, verifcid.DefaultMaxIdentityDigestSize))
284 + })
285 +
286 + t.Run("ipfs add --inline with data larger than limit uses configured hash", func(t *testing.T) {
287 + t.Parallel()
288 + node := harness.NewT(t).NewNode().Init().StartDaemon()
289 + defer node.StopDaemon()
290 +
291 + // data larger than inline limit
292 + largeData := strings.Repeat("y", 100)
293 + tempFile := filepath.Join(node.Dir, "toolarge.txt")
294 + err := os.WriteFile(tempFile, []byte(largeData), 0644)
295 + require.NoError(t, err)
296 +
297 + // set inline limit smaller than data
298 + res := node.IPFS("add", "--inline", "--inline-limit=50", tempFile)
299 + assert.NoError(t, res.Err)
300 + cid := strings.Fields(res.Stdout.String())[1]
301 +
302 + // verify it's NOT using identity hash (data too large for inline)
303 + res = node.IPFS("cid", "format", "-f", "%h", cid)
304 + assert.NoError(t, res.Err)
305 + assert.NotEqual(t, "identity", res.Stdout.Trimmed())
306 +
307 + // should use configured hash
308 + assert.Equal(t, config.DefaultHashFunction, res.Stdout.Trimmed())
309 + })
310 +}
test/dependencies/go.mod
+1 -1
@@ -134,7 +134,7 @@ require (
134 github.com/huin/goupnp v1.3.0 // indirect
135 github.com/inconshreveable/mousetrap v1.1.0 // indirect
136 github.com/ipfs/bbloom v0.0.4 // indirect
137 - github.com/ipfs/boxo v0.34.1-0.20250908170437-7d2493027364 // indirect
137 + github.com/ipfs/boxo v0.34.1-0.20250909170220-e69f67e94c11 // indirect
138 github.com/ipfs/go-bitfield v1.1.0 // indirect
139 github.com/ipfs/go-block-format v0.2.2 // indirect
140 github.com/ipfs/go-cid v0.5.0 // indirect
test/dependencies/go.sum
+2 -2
@@ -332,8 +332,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
332 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
333 github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
334 github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
335 -github.com/ipfs/boxo v0.34.1-0.20250908170437-7d2493027364 h1:VdRdPlosNYdlENC0UsCxapHala/Q1Me6yBY5ChKUw7s=
336 -github.com/ipfs/boxo v0.34.1-0.20250908170437-7d2493027364/go.mod h1:rXql6ncaLZZfLqDG3Cuw9ZYQKd3rMU5bk1TGXF0+ZL0=
335 +github.com/ipfs/boxo v0.34.1-0.20250909170220-e69f67e94c11 h1:EsCbOKE+giLtrFTysnbTzIRQENOiLdcpOY3kV3y6wlU=
336 +github.com/ipfs/boxo v0.34.1-0.20250909170220-e69f67e94c11/go.mod h1:rXql6ncaLZZfLqDG3Cuw9ZYQKd3rMU5bk1TGXF0+ZL0=
337 github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
338 github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=
339 github.com/ipfs/go-block-format v0.2.2 h1:uecCTgRwDIXyZPgYspaLXoMiMmxQpSx2aq34eNc4YvQ=
test/sharness/t0275-cid-security.sh
+4 -4
@@ -15,7 +15,7 @@ test_expect_success "adding using unsafe function fails with error" '
15 '
16
17 test_expect_success "error reason is pointed out" '
18 - grep "insecure hash functions not allowed" add_out || test_fsh cat add_out
18 + grep "potentially insecure hash functions not allowed" add_out || test_fsh cat add_out
19 '
20
21 test_expect_success "adding using too short of a hash function gives out an error" '
@@ -23,7 +23,7 @@ test_expect_success "adding using too short of a hash function gives out an erro
23 '
24
25 test_expect_success "error reason is pointed out" '
26 - grep "hashes must be at least 20 bytes long" block_out
26 + grep "digest too small" block_out
27 '
28
29
@@ -35,7 +35,7 @@ test_cat_get() {
35
36
37 test_expect_success "error reason is pointed out" '
38 - grep "insecure hash functions not allowed" ipfs_cat
38 + grep "potentially insecure hash functions not allowed" ipfs_cat
39 '
40
41
@@ -45,7 +45,7 @@ test_cat_get() {
45 '
46
47 test_expect_success "error reason is pointed out" '
48 - grep "hashes must be at least 20 bytes long" ipfs_get
48 + grep "digest too small" ipfs_get
49 '
50 }
51