@@ -23,6 +23,9 @@ import (
2323
2424 "github.com/danieljoos/wincred"
2525 "github.com/stretchr/testify/assert"
26+ "github.com/stretchr/testify/require"
27+
28+ "github.com/docker/secrets-engine/store/mocks"
2629)
2730
2831func TestChunkBlob (t * testing.T ) {
@@ -72,6 +75,81 @@ func TestChunkBlob(t *testing.T) {
7275 })
7376}
7477
78+ func TestEncodeDecodeSecret (t * testing.T ) {
79+ t .Run ("roundtrip small credential" , func (t * testing.T ) {
80+ cred := & mocks.MockCredential {
81+ Username : "bob" ,
82+ Password : "secret" ,
83+ }
84+ blob , err := encodeSecret (cred )
85+ require .NoError (t , err )
86+
87+ result := & mocks.MockCredential {}
88+ require .NoError (t , decodeSecret (blob , result ))
89+ assert .Equal (t , cred .Username , result .Username )
90+ assert .Equal (t , cred .Password , result .Password )
91+ })
92+
93+ t .Run ("roundtrip large JWT credential exceeding maxBlobSize" , func (t * testing.T ) {
94+ // Construct a fake JWT large enough to exceed maxBlobSize (2560 bytes)
95+ // when UTF-16 encoded. Each ASCII character becomes 2 bytes in UTF-16,
96+ // so the marshaled string must be longer than 1280 characters.
97+ largePayload := strings .Repeat ("eyJzdWIiOiJ1c2VyMTIzNDU2Nzg5MCIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTUxNjIzOTAyMn0" , 20 )
98+ largeJWT := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9." + largePayload + ".SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
99+
100+ cred := & mocks.MockCredential {
101+ Username : "alice" ,
102+ Password : largeJWT ,
103+ }
104+ blob , err := encodeSecret (cred )
105+ require .NoError (t , err )
106+ assert .Greater (t , len (blob ), maxBlobSize , "JWT credential should exceed maxBlobSize when UTF-16 encoded" )
107+
108+ // Verify that chunkBlob properly splits the oversized blob.
109+ chunks := chunkBlob (blob , maxBlobSize )
110+ assert .Greater (t , len (chunks ), 1 )
111+
112+ // Reassemble chunks and decode back to verify no data is lost.
113+ var reassembled []byte
114+ for _ , chunk := range chunks {
115+ reassembled = append (reassembled , chunk ... )
116+ }
117+ result := & mocks.MockCredential {}
118+ require .NoError (t , decodeSecret (reassembled , result ))
119+ assert .Equal (t , cred .Username , result .Username )
120+ assert .Equal (t , cred .Password , result .Password )
121+ })
122+
123+ t .Run ("roundtrip multiple large JWTs as separate credentials" , func (t * testing.T ) {
124+ largePayload := strings .Repeat ("eyJzdWIiOiJ1c2VyMTIzNDU2Nzg5MCIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTUxNjIzOTAyMn0" , 40 )
125+ veryLargeJWT := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9." + largePayload + ".SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
126+
127+ for _ , tc := range []struct {
128+ username string
129+ password string
130+ }{
131+ {"user1" , veryLargeJWT },
132+ {"user2" , veryLargeJWT },
133+ } {
134+ cred := & mocks.MockCredential {Username : tc .username , Password : tc .password }
135+ blob , err := encodeSecret (cred )
136+ require .NoError (t , err )
137+ assert .Greater (t , len (blob ), maxBlobSize )
138+
139+ chunks := chunkBlob (blob , maxBlobSize )
140+ var reassembled []byte
141+ for _ , chunk := range chunks {
142+ reassembled = append (reassembled , chunk ... )
143+ }
144+
145+ result := & mocks.MockCredential {}
146+ require .NoError (t , decodeSecret (reassembled , result ))
147+ assert .Equal (t , tc .username , result .Username )
148+ assert .Equal (t , tc .password , result .Password )
149+ }
150+ })
151+ }
152+
75153func TestIsChunkCredential (t * testing.T ) {
76154 t .Run ("returns true when chunk:index attribute is present" , func (t * testing.T ) {
77155 attrs := []wincred.CredentialAttribute {
0 commit comments