Skip to content

Commit cf844d4

Browse files
authored
Fix X509SVID hint deduplication for empty hints (spiffe#385)
Only non-empty X509SVID hints participate in deduplication, in accordance with the SPIFFE Workload API specification. SVIDs without a hint are no longer incorrectly filtered out. Signed-off-by: Max Lambrecht <maxlambrecht@gmail.com>
1 parent 9cc2e7c commit cf844d4

12 files changed

Lines changed: 99 additions & 9 deletions

File tree

java-spiffe-core/src/main/java/io/spiffe/workloadapi/GrpcConversionUtils.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -129,21 +129,25 @@ private static X509Bundle createX509Bundle(Workload.X509SVID x509Svid) throws X5
129129
return parseX509Bundle(trustDomain, bundleBytes);
130130
}
131131

132-
private static List<X509Svid> getListOfX509Svid(final Workload.X509SVIDResponse x509SvidResponse) throws X509ContextException{
132+
static List<X509Svid> getListOfX509Svid(final Workload.X509SVIDResponse x509SvidResponse) throws X509ContextException{
133133

134134
final List<X509Svid> result = new ArrayList<>();
135-
HashSet<String> hints = new HashSet<>();
135+
final Set<String> seenHints = new HashSet<>();
136136

137137
for (Workload.X509SVID x509SVID : x509SvidResponse.getSvidsList()) {
138-
// In the event of more than one X509SVID message with the same hint value set, then the first message in the
139-
// list SHOULD be selected.
140-
if (hints.contains(x509SVID.getHint())) {
141-
continue;
138+
139+
final String hint = x509SVID.getHint();
140+
141+
if (!hint.isEmpty()) {
142+
if (seenHints.contains(hint)) {
143+
continue;
144+
}
145+
seenHints.add(hint);
142146
}
143-
final X509Svid svid = createAndValidateX509Svid(x509SVID);
144-
hints.add(svid.getHint());
145-
result.add(svid);
147+
148+
result.add(createAndValidateX509Svid(x509SVID));
146149
}
150+
147151
return result;
148152
}
149153

java-spiffe-core/src/test/java/io/spiffe/workloadapi/GrpcConversionUtilsTest.java

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,20 @@
88
import io.spiffe.exception.X509BundleException;
99
import io.spiffe.exception.X509ContextException;
1010
import io.spiffe.spiffeid.TrustDomain;
11+
import io.spiffe.svid.x509svid.X509Svid;
1112
import io.spiffe.workloadapi.grpc.Workload;
1213
import org.junit.jupiter.api.Test;
1314

15+
import java.io.FileNotFoundException;
1416
import java.io.IOException;
17+
import java.io.InputStream;
1518
import java.net.URISyntaxException;
1619
import java.nio.file.Files;
1720
import java.nio.file.Path;
1821
import java.nio.file.Paths;
1922
import java.util.Collections;
2023
import java.util.Iterator;
24+
import java.util.List;
2125

2226
import static io.spiffe.utils.TestUtils.toUri;
2327
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -128,4 +132,86 @@ private Workload.X509BundlesResponse createX509BundlesResponse() throws URISynta
128132
.putBundles(TrustDomain.parse("domain.test").getName(), federatedByteString)
129133
.build();
130134
}
135+
136+
@Test
137+
void getListOfX509Svid_dedupesOnlyNonEmptyHints() throws Exception {
138+
139+
ByteString certA = loadTestResource("testdata/certs/leaf-a.crt.der");
140+
ByteString keyA = loadTestResource("testdata/certs/leaf-a.key.der");
141+
142+
ByteString certB = loadTestResource("testdata/certs/leaf-b.crt.der");
143+
ByteString keyB = loadTestResource("testdata/certs/leaf-b.key.der");
144+
145+
ByteString certC = loadTestResource("testdata/certs/leaf-c.crt.der");
146+
ByteString keyC = loadTestResource("testdata/certs/leaf-c.key.der");
147+
148+
ByteString certD = loadTestResource("testdata/certs/leaf-d.crt.der");
149+
ByteString keyD = loadTestResource("testdata/certs/leaf-d.key.der");
150+
151+
ByteString certE = loadTestResource("testdata/certs/leaf-e.crt.der");
152+
ByteString keyE = loadTestResource("testdata/certs/leaf-e.key.der");
153+
154+
Workload.X509SVID svidA = Workload.X509SVID.newBuilder()
155+
.setHint("")
156+
.setSpiffeId("spiffe://test/a")
157+
.setX509Svid(certA)
158+
.setX509SvidKey(keyA)
159+
.build();
160+
161+
Workload.X509SVID svidB = Workload.X509SVID.newBuilder()
162+
.setHint("")
163+
.setSpiffeId("spiffe://test/b")
164+
.setX509Svid(certB)
165+
.setX509SvidKey(keyB)
166+
.build();
167+
168+
Workload.X509SVID svidC = Workload.X509SVID.newBuilder()
169+
.setHint("hintX")
170+
.setSpiffeId("spiffe://test/c")
171+
.setX509Svid(certC)
172+
.setX509SvidKey(keyC)
173+
.build();
174+
175+
Workload.X509SVID svidD = Workload.X509SVID.newBuilder()
176+
.setHint("hintX")
177+
.setSpiffeId("spiffe://test/d")
178+
.setX509Svid(certD)
179+
.setX509SvidKey(keyD)
180+
.build();
181+
182+
Workload.X509SVID svidE = Workload.X509SVID.newBuilder()
183+
.setHint("hintY")
184+
.setSpiffeId("spiffe://test/e")
185+
.setX509Svid(certE)
186+
.setX509SvidKey(keyE)
187+
.build();
188+
189+
Workload.X509SVIDResponse resp = Workload.X509SVIDResponse.newBuilder()
190+
.addSvids(svidA)
191+
.addSvids(svidB)
192+
.addSvids(svidC)
193+
.addSvids(svidD)
194+
.addSvids(svidE)
195+
.build();
196+
197+
// Act
198+
List<X509Svid> out = GrpcConversionUtils.getListOfX509Svid(resp);
199+
200+
// Assert: B must NOT be removed; D must be removed; order preserved
201+
assertEquals(4, out.size());
202+
assertEquals("spiffe://test/a", out.get(0).getSpiffeId().toString());
203+
assertEquals("spiffe://test/b", out.get(1).getSpiffeId().toString());
204+
assertEquals("spiffe://test/c", out.get(2).getSpiffeId().toString());
205+
assertEquals("spiffe://test/e", out.get(3).getSpiffeId().toString());
206+
207+
}
208+
209+
private static ByteString loadTestResource(String resourcePath) throws IOException {
210+
try (InputStream in = GrpcConversionUtilsTest.class.getResourceAsStream("/" + resourcePath)) {
211+
if (in == null) {
212+
throw new FileNotFoundException("Resource not found on classpath: " + resourcePath);
213+
}
214+
return ByteString.copyFrom(in.readAllBytes());
215+
}
216+
}
131217
}
428 Bytes
Binary file not shown.
138 Bytes
Binary file not shown.
429 Bytes
Binary file not shown.
138 Bytes
Binary file not shown.
428 Bytes
Binary file not shown.
138 Bytes
Binary file not shown.
429 Bytes
Binary file not shown.
138 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)