diff --git a/server/src/dev/resources/application.yml b/server/src/dev/resources/application.yml index 1108173c3..530003da2 100644 --- a/server/src/dev/resources/application.yml +++ b/server/src/dev/resources/application.yml @@ -206,7 +206,7 @@ ovsx: "contact": "infrastructure@eclipse-foundation.org" } scanning: - enabled: false + enabled: true # Shared archive limits for all scanning checks (secret detection, blocklist, etc.) max-archive-size-bytes: 1073741824 # 1 GB total archive limit max-single-file-bytes: 268435456 # 256 MB per-file limit diff --git a/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandler.java b/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandler.java index ed2a2280f..484cd37fd 100644 --- a/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandler.java +++ b/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandler.java @@ -20,11 +20,8 @@ import org.eclipse.openvsx.UserService; import org.eclipse.openvsx.adapter.VSCodeIdNewExtensionJobRequest; import org.eclipse.openvsx.entities.*; -import org.eclipse.openvsx.entities.ScanCheckResult.CheckCategory; -import org.eclipse.openvsx.entities.ScanCheckResult.CheckResult; import org.eclipse.openvsx.extension_control.ExtensionControlService; import org.eclipse.openvsx.repositories.RepositoryService; -import org.eclipse.openvsx.scanning.ExtensionScanPersistenceService; import org.eclipse.openvsx.scanning.ExtensionScanService; import org.eclipse.openvsx.util.*; import org.jobrunr.scheduling.JobRequestScheduler; @@ -58,7 +55,6 @@ public class PublishExtensionVersionHandler { private final ExtensionValidator validator; private final ExtensionControlService extensionControl; private final ExtensionScanService scanService; - private final ExtensionScanPersistenceService scanPersistenceService; public PublishExtensionVersionHandler( PublishExtensionVersionService service, @@ -69,8 +65,7 @@ public PublishExtensionVersionHandler( UserService users, ExtensionValidator validator, ExtensionControlService extensionControl, - ExtensionScanService scanService, - ExtensionScanPersistenceService scanPersistenceService + ExtensionScanService scanService ) { this.service = service; this.integrityService = integrityService; @@ -81,7 +76,6 @@ public PublishExtensionVersionHandler( this.validator = validator; this.extensionControl = extensionControl; this.scanService = scanService; - this.scanPersistenceService = scanPersistenceService; } public boolean isLicenseRequired() { @@ -276,48 +270,15 @@ private void doPublish(TempFile extensionFile, ExtensionService extensionService service.storeResource(extensionFile); service.persistResource(download); - try(var processor = new ExtensionProcessor(extensionFile)) { - extVersion.setPotentiallyMalicious(processor.isPotentiallyMalicious()); - if (extVersion.isPotentiallyMalicious()) { + try (var processor = new ExtensionProcessor(extensionFile)) { + // to keep backwards compatibility, mark extension versions as potentially malicious + // if no scan service is enabled and the vsix file contains entries with extra fields. + if (!scanService.isEnabled() && processor.isPotentiallyMalicious()) { + service.markExtensionAsPotentiallyMalicious(extVersion); logger.atWarn() .setMessage("Extension version is potentially malicious: {}") .addArgument(() -> NamingUtil.toLogFormat(extVersion)) .log(); - - // Record as a publish check failure and reject the extension - if (scan != null) { - var now = TimeUtil.getCurrentUTC(); - var checkType = "MALICIOUS_ZIP_CHECK"; - var reason = "VSIX contains zip entries with potentially harmful extra fields"; - - // Record the check result for audit trail - scanPersistenceService.recordCheckResult( - scan, - checkType, - CheckCategory.PUBLISH_CHECK, - CheckResult.REJECT, - now, // startedAt - now, // completedAt - 1, // filesScanned - the vsix file - 1, // findingsCount - reason, - null, // errorMessage - null, // scannerJobId - not a scanner job - true - ); - - // Also record as validation failure for the failures list - scanPersistenceService.recordValidationFailure( - scan, - checkType, - "EXTRA_FIELDS_DETECTED", // ruleName - reason, - true // enforced - ); - - scanService.rejectScan(scan); - logger.info("Scan {} rejected due to potentially malicious extension", scan.getId()); - } return; } @@ -326,7 +287,7 @@ private void doPublish(TempFile extensionFile, ExtensionService extensionService service.persistResource(tempFile.getResource()); }; - if(integrityService.isEnabled()) { + if (integrityService.isEnabled()) { var keyPair = extVersion.getSignatureKeyPair(); if(keyPair != null) { try(var signature = integrityService.generateSignature(extensionFile, keyPair)) { diff --git a/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionService.java b/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionService.java index ce74beb9c..7f062c061 100644 --- a/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionService.java +++ b/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionService.java @@ -66,6 +66,12 @@ public void persistResource(FileResource resource) { entityManager.persist(resource); } + @Transactional + public void markExtensionAsPotentiallyMalicious(ExtensionVersion extVersion) { + extVersion = entityManager.merge(extVersion); + extVersion.setPotentiallyMalicious(true); + } + @Transactional @CacheEvict(value = CACHE_SITEMAP, allEntries = true) public void activateExtension(ExtensionVersion extVersion, ExtensionService extensions) { diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/BlocklistCheckService.java b/server/src/main/java/org/eclipse/openvsx/scanning/BlocklistCheckService.java index 08089d6dd..44c17da23 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/BlocklistCheckService.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/BlocklistCheckService.java @@ -97,10 +97,6 @@ public String getUserFacingMessage(List failures) { @Override public Result check(Context context) { - if (context.extensionFile() == null) { - return Result.pass(); - } - var blockedFiles = checkForBlockedFiles(context.extensionFile()); if (blockedFiles.isEmpty()) { return Result.pass(); diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanCompletionService.java b/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanCompletionService.java index bf53a4bb0..cb7fd4989 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanCompletionService.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanCompletionService.java @@ -35,6 +35,7 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -457,8 +458,8 @@ private boolean completeExtensionScan(String scanId, List jobs) { if (!failedJobs.isEmpty()) { // Check if any REQUIRED scanners failed // Optional scanners (typically external) can fail without blocking activation - List requiredFailedJobs = new java.util.ArrayList<>(); - List optionalFailedJobs = new java.util.ArrayList<>(); + List requiredFailedJobs = new ArrayList<>(); + List optionalFailedJobs = new ArrayList<>(); for (ScannerJob failedJob : failedJobs) { // Record ERROR result for audit trail (if not already recorded) diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanService.java b/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanService.java index dc207ef6f..5f6513078 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanService.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanService.java @@ -22,12 +22,10 @@ import org.jobrunr.scheduling.JobRequestScheduler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Component; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; -import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; @@ -111,7 +109,7 @@ private ExtensionScan initializeScan( /** * Run validation checks and record results. - * + *

* Delegates the actual check execution to ExtensionScanner, * then records findings and manages state transitions. */ diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/GitleaksRulesService.java b/server/src/main/java/org/eclipse/openvsx/scanning/GitleaksRulesService.java index 1c6bbc13b..9c3eee43f 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/GitleaksRulesService.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/GitleaksRulesService.java @@ -243,29 +243,29 @@ private void startRedisSubscriber() { private void subscribeLoop() { AtomicInteger backoffMs = new AtomicInteger(1000); - ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor( - new NamedThreadFactory("gitleaks-rules-reconnect")); - while (running && !Thread.currentThread().isInterrupted()) { - ScheduledFuture resetTask = null; - try { - resetTask = executor.schedule(() -> backoffMs.set(1000), 10, TimeUnit.SECONDS); - logger.debug("Subscribing to gitleaks rules update channel"); - jedisCluster.subscribe(this, RULES_UPDATE_CHANNEL); - } catch (Exception e) { - if (!running) break; - logger.warn("Gitleaks rules subscriber disconnected, reconnecting in {}s: {}", - backoffMs.get() / 1000, e.getMessage()); - if (resetTask != null) resetTask.cancel(true); + try (var executor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("gitleaks-rules-reconnect"))) { + while (running && !Thread.currentThread().isInterrupted()) { + ScheduledFuture resetTask = null; try { - Thread.sleep(backoffMs.get()); - backoffMs.set(Math.min(backoffMs.get() * 2, 30000)); - } catch (InterruptedException ignored) { - break; + resetTask = executor.schedule(() -> backoffMs.set(1000), 10, TimeUnit.SECONDS); + logger.debug("Subscribing to gitleaks rules update channel"); + jedisCluster.subscribe(this, RULES_UPDATE_CHANNEL); + } catch (Exception e) { + if (!running) break; + logger.warn("Gitleaks rules subscriber disconnected, reconnecting in {}s: {}", + backoffMs.get() / 1000, e.getMessage()); + if (resetTask != null) resetTask.cancel(true); + try { + Thread.sleep(backoffMs.get()); + backoffMs.set(Math.min(backoffMs.get() * 2, 30000)); + } catch (InterruptedException ignored) { + break; + } } } + executor.shutdownNow(); } - executor.shutdownNow(); } @Override diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java b/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java index d93ecdb4d..b37d2a4a0 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java @@ -36,10 +36,10 @@ /** * Executes HTTP requests based on configuration. - * + *

* Handles different HTTP methods, body types (JSON, multipart, form-urlencoded), * headers, query parameters, file uploads, and authentication. - * + *

* Use static factory methods to create instances with scanner-specific configs. */ public class HttpClientExecutor { diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/HttpTemplateEngine.java b/server/src/main/java/org/eclipse/openvsx/scanning/HttpTemplateEngine.java index 74bb78c76..9c9987c05 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/HttpTemplateEngine.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/HttpTemplateEngine.java @@ -48,7 +48,7 @@ public String process(String template, Map placeholders) { } Matcher matcher = PLACEHOLDER_PATTERN.matcher(template); - StringBuffer result = new StringBuffer(); + StringBuilder result = new StringBuilder(); while (matcher.find()) { String placeholderName = matcher.group(1); diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/MaliciousZipCheckService.java b/server/src/main/java/org/eclipse/openvsx/scanning/MaliciousZipCheckService.java new file mode 100644 index 000000000..24b69fe7f --- /dev/null +++ b/server/src/main/java/org/eclipse/openvsx/scanning/MaliciousZipCheckService.java @@ -0,0 +1,67 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ +package org.eclipse.openvsx.scanning; + +import org.eclipse.openvsx.ExtensionProcessor; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * Service for checking extension files for potentially malicious zip extra fields. + *

+ * Implements PublishCheck to be auto-discovered by PublishCheckRunner. + * Always enabled and enforced. + */ +@Service +@Order(0) +public class MaliciousZipCheckService implements PublishCheck { + + public static final String CHECK_TYPE = "MALICIOUS_ZIP_CHECK"; + private static final String RULE_NAME = "EXTRA_FIELDS_DETECTED"; + private static final String MESSAGE = "extension file contains zip entries with potentially harmful extra fields"; + private static final String USER_MESSAGE = "Extension contains zip entries with unsupported extra fields"; + + @Override + public String getCheckType() { + return CHECK_TYPE; + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public boolean isEnforced() { + return true; + } + + @Override + public String getUserFacingMessage(List failures) { + return USER_MESSAGE; + } + + @Override + public PublishCheck.Result check(Context context) { + try (var processor = new ExtensionProcessor(context.extensionFile())) { + var potentiallyMalicious = processor.isPotentiallyMalicious(); + if (potentiallyMalicious) { + return PublishCheck.Result.fail(RULE_NAME, MESSAGE); + } + } + + return PublishCheck.Result.pass(); + } +} diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/ScannerFileProvider.java b/server/src/main/java/org/eclipse/openvsx/scanning/ScannerFileProvider.java index ea1a67db9..15851a9d5 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/ScannerFileProvider.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/ScannerFileProvider.java @@ -83,9 +83,7 @@ public TempFile getExtensionFile(long extensionVersionId) throws ScannerExceptio TempFile extensionFile = storageUtil.downloadFile(download); if (extensionFile == null) { - throw new ScannerException( - "Failed to download file for extension version: " + extensionVersionId - ); + throw new ScannerException("Failed to download file for extension version: " + extensionVersionId); } logger.debug("Extension file ready for scanning: {} (extension version: {})", @@ -96,15 +94,9 @@ public TempFile getExtensionFile(long extensionVersionId) throws ScannerExceptio } catch (ScannerException e) { throw e; } catch (IOException e) { - throw new ScannerException( - "Failed to download file for extension version " + extensionVersionId, - e - ); + throw new ScannerException("Failed to download file for extension version " + extensionVersionId, e); } catch (Exception e) { - throw new ScannerException( - "Failed to retrieve file for extension version " + extensionVersionId, - e - ); + throw new ScannerException("Failed to retrieve file for extension version " + extensionVersionId, e); } } } diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/SecretCheckService.java b/server/src/main/java/org/eclipse/openvsx/scanning/SecretCheckService.java index fa3deab86..5a69c48d0 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/SecretCheckService.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/SecretCheckService.java @@ -23,7 +23,6 @@ import org.springframework.stereotype.Service; import jakarta.validation.constraints.NotNull; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -104,10 +103,6 @@ public String getCheckType() { @Override public PublishCheck.Result check(PublishCheck.Context context) { - if (context.extensionFile() == null) { - return PublishCheck.Result.pass(); - } - var scanResult = scanForSecrets(context.extensionFile()); if (!scanResult.isSecretsFound()) { return PublishCheck.Result.pass(); @@ -232,9 +227,6 @@ private SecretDetector.Result scanForSecrets(@NotNull TempFile extensionFile) { } catch (ZipException e) { logger.error("Failed to open extension file as zip: {}", e.getMessage()); throw new SecretScanningException("Failed to scan extension file: invalid zip format", e); - } catch (IOException e) { - logger.error("Failed to scan extension file: {}", e.getMessage()); - throw new SecretScanningException("Failed to scan extension file: " + e.getMessage(), e); } catch (SecretScanningException e) { throw e; } catch (Exception e) { diff --git a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java index 40bf514e8..61f6b5574 100644 --- a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java @@ -2627,8 +2627,7 @@ PublishExtensionVersionHandler publishExtensionVersionHandler( UserService users, ExtensionValidator validator, ExtensionControlService extensionControl, - ExtensionScanService extensionScanService, - ExtensionScanPersistenceService scanPersistenceService + ExtensionScanService extensionScanService ) { return new PublishExtensionVersionHandler( service, @@ -2639,8 +2638,7 @@ PublishExtensionVersionHandler publishExtensionVersionHandler( users, validator, extensionControl, - extensionScanService, - scanPersistenceService + extensionScanService ); } diff --git a/server/src/test/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandlerTest.java b/server/src/test/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandlerTest.java index fffc8900b..b1fe8341c 100644 --- a/server/src/test/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandlerTest.java +++ b/server/src/test/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandlerTest.java @@ -22,10 +22,8 @@ import org.eclipse.openvsx.entities.PersonalAccessToken; import org.eclipse.openvsx.extension_control.ExtensionControlService; import org.eclipse.openvsx.repositories.RepositoryService; -import org.eclipse.openvsx.scanning.ExtensionScanPersistenceService; import org.eclipse.openvsx.scanning.ExtensionScanService; import org.eclipse.openvsx.util.ErrorResultException; -import org.eclipse.openvsx.util.TempFile; import org.jobrunr.scheduling.JobRequestScheduler; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -74,9 +72,6 @@ class PublishExtensionVersionHandlerTest { @Mock ExtensionScanService scanService; - @Mock - ExtensionScanPersistenceService scanPersistenceService; - private PublishExtensionVersionHandler handler; @BeforeEach @@ -90,8 +85,7 @@ void setUp() throws Exception { users, validator, extensionControl, - scanService, - scanPersistenceService + scanService ); // Lenient: not all tests need this mock diff --git a/server/src/test/java/org/eclipse/openvsx/scanning/MaliciousZipCheckServiceTest.java b/server/src/test/java/org/eclipse/openvsx/scanning/MaliciousZipCheckServiceTest.java new file mode 100644 index 000000000..31cfcc547 --- /dev/null +++ b/server/src/test/java/org/eclipse/openvsx/scanning/MaliciousZipCheckServiceTest.java @@ -0,0 +1,112 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ +package org.eclipse.openvsx.scanning; + +import org.apache.commons.compress.archivers.zip.ExtraFieldUtils; +import org.apache.commons.compress.archivers.zip.UnicodeCommentExtraField; +import org.apache.commons.compress.archivers.zip.ZipExtraField; +import org.eclipse.openvsx.entities.ExtensionScan; +import org.eclipse.openvsx.entities.UserData; +import org.eclipse.openvsx.util.TempFile; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.FileOutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for MaliciousZipCheckService. + */ +class MaliciousZipCheckServiceTest { + + @TempDir + Path tempDir; + + private MaliciousZipCheckService service; + + @BeforeEach + void setUp() { + service = new MaliciousZipCheckService(); + } + + @Test + void check_passesWhenNoExtraFields() throws Exception { + // Create a test zip with clean files + TempFile extensionFile = createTestZip("clean.txt", "This is clean content"); + + var context = createContext(extensionFile); + var result = service.check(context); + + assertTrue(result.passed()); + assertTrue(result.failures().isEmpty()); + } + + @Test + void check_failsWhenExtraFieldsAreFound() throws Exception { + // Create a test zip with a file that contains extra fields + TempFile extensionFile = createTestZipWithExtraField("extra.txt", "The content is clean"); + + var context = createContext(extensionFile); + var result = service.check(context); + + assertFalse(result.passed()); + assertEquals(1, result.failures().size()); + assertEquals("EXTRA_FIELDS_DETECTED", result.failures().getFirst().ruleName()); + assertTrue(result.failures().getFirst().reason().contains("extension file contains zip entries")); + } + + // --- Helper methods --- + + private TempFile createTestZip(String fileName, String content) throws Exception { + Path zipPath = tempDir.resolve("test-extension.vsix"); + try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipPath.toFile()))) { + ZipEntry entry = new ZipEntry(fileName); + zos.putNextEntry(entry); + zos.write(content.getBytes(StandardCharsets.UTF_8)); + zos.closeEntry(); + } + return new TempFile(zipPath); + } + + private TempFile createTestZipWithExtraField(String fileName, String content) throws Exception { + Path zipPath = tempDir.resolve("test-extension-extra.vsix"); + try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipPath.toFile()))) { + ZipEntry entry = new ZipEntry(fileName); + var field = new UnicodeCommentExtraField("sample data", "sample data".getBytes()); + var data = ExtraFieldUtils.mergeLocalFileDataData(new ZipExtraField[] { field }); + entry.setExtra(data); + zos.putNextEntry(entry); + zos.write(content.getBytes(StandardCharsets.UTF_8)); + zos.closeEntry(); + } + return new TempFile(zipPath); + } + + private PublishCheck.Context createContext(TempFile extensionFile) { + ExtensionScan scan = new ExtensionScan(); + scan.setNamespaceName("test-namespace"); + scan.setExtensionName("test-extension"); + scan.setExtensionVersion("1.0.0"); + + UserData user = new UserData(); + user.setLoginName("testuser"); + + return new PublishCheck.Context(scan, extensionFile, user); + } +} diff --git a/server/src/test/java/org/eclipse/openvsx/scanning/PublishCheckRunnerTest.java b/server/src/test/java/org/eclipse/openvsx/scanning/PublishCheckRunnerTest.java index d53e8c09a..bd252cd16 100644 --- a/server/src/test/java/org/eclipse/openvsx/scanning/PublishCheckRunnerTest.java +++ b/server/src/test/java/org/eclipse/openvsx/scanning/PublishCheckRunnerTest.java @@ -110,7 +110,7 @@ void runChecks_capturesCheckError() { assertFalse(result.passed()); assertTrue(result.hasError()); - assertEquals("ERROR_CHECK", result.getRequiredErrors().get(0).checkType()); + assertEquals("ERROR_CHECK", result.getRequiredErrors().getFirst().checkType()); assertNotNull(result.getErrorMessage()); } diff --git a/server/src/test/java/org/eclipse/openvsx/scanning/ScannerInvocationTest.java b/server/src/test/java/org/eclipse/openvsx/scanning/ScannerInvocationTest.java index 81e1185ab..9fe2ed55d 100644 --- a/server/src/test/java/org/eclipse/openvsx/scanning/ScannerInvocationTest.java +++ b/server/src/test/java/org/eclipse/openvsx/scanning/ScannerInvocationTest.java @@ -47,6 +47,6 @@ void completed_withThreats() { assertFalse(invocation.result().isClean()); assertEquals(1, invocation.result().getThreats().size()); - assertEquals("virus", invocation.result().getThreats().get(0).getName()); + assertEquals("virus", invocation.result().getThreats().getFirst().getName()); } } diff --git a/server/src/test/java/org/eclipse/openvsx/scanning/ScannerRegistryTest.java b/server/src/test/java/org/eclipse/openvsx/scanning/ScannerRegistryTest.java index 80e274513..e3b1f2916 100644 --- a/server/src/test/java/org/eclipse/openvsx/scanning/ScannerRegistryTest.java +++ b/server/src/test/java/org/eclipse/openvsx/scanning/ScannerRegistryTest.java @@ -47,8 +47,7 @@ void registerScanner_throwsOnDuplicate() { registry.registerScanner(scanner1); - assertThrows(IllegalArgumentException.class, () -> - registry.registerScanner(scanner2)); + assertThrows(IllegalArgumentException.class, () -> registry.registerScanner(scanner2)); } @Test @@ -120,8 +119,7 @@ void getRegisteredTypes_returnsImmutableSet() { var types = registry.getRegisteredTypes(); - assertThrows(UnsupportedOperationException.class, () -> - types.add("TYPE_B")); + assertThrows(UnsupportedOperationException.class, () -> types.add("TYPE_B")); } @Test