From 4ed80373073aae9011d623d25a4f641f00abef47 Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Mon, 20 Apr 2026 11:14:14 +0200 Subject: [PATCH 1/2] fix: validate that all file resources are available during sync publishing call --- .../publish/PublishExtensionVersionHandler.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) 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 d40ad7f18..b11d86b26 100644 --- a/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandler.java +++ b/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandler.java @@ -115,7 +115,7 @@ public ExtensionVersion createExtensionVersion(ExtensionProcessor processor, Per .map(id -> parseExtensionId(id, "extensionDependencies")) .toList(); - if(!parsedDependencies.isEmpty()) { + if (!parsedDependencies.isEmpty()) { checkDependencies(parsedDependencies); } bundledExtensions.forEach(id -> parseExtensionId(id, "extensionPack")); @@ -123,7 +123,7 @@ public ExtensionVersion createExtensionVersion(ExtensionProcessor processor, Per extVersion.setDependencies(dependencies); extVersion.setBundledExtensions(bundledExtensions); - if(integrityService.isEnabled()) { + if (integrityService.isEnabled()) { extVersion.setSignatureKeyPair(repositories.findActiveKeyPair()); } @@ -179,6 +179,7 @@ private ExtensionVersion createExtensionVersion(ExtensionProcessor processor, Us validateLicense(processor, extVersion); validateIcon(processor, extVersion); + validateFileResources(processor, extVersion); validateMetadata(extVersion); entityManager.persist(extVersion); return extVersion; @@ -231,6 +232,16 @@ private void validateIcon(ExtensionProcessor processor, ExtensionVersion extVers } } + private void validateFileResources(ExtensionProcessor processor, ExtensionVersion extVersion) { + try { + // validate that all file resources are readable/accessible during synchronous publishing + // to avoid failing during async publishing and report errors directly back to publishers + processor.getFileResources(extVersion, _ -> {}); + } catch (ErrorResultException exc) { + throw new ErrorResultException("Validation failed: " + exc.getMessage()); + } + } + private void validateMetadata(ExtensionVersion extVersion) { var metadataIssues = validator.validateMetadata(extVersion); if (!metadataIssues.isEmpty()) { From 8b2dd33c13ce59e46a768bf98d7d48338517fc21 Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Mon, 20 Apr 2026 11:37:57 +0200 Subject: [PATCH 2/2] fix: validate file resources already during synchronous publishing --- .../eclipse/openvsx/ExtensionProcessor.java | 12 +++++++++- .../PublishExtensionVersionHandler.java | 24 +++++++++---------- .../PublishExtensionVersionHandlerTest.java | 4 +--- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/server/src/main/java/org/eclipse/openvsx/ExtensionProcessor.java b/server/src/main/java/org/eclipse/openvsx/ExtensionProcessor.java index 2f74efdd0..35db6180e 100644 --- a/server/src/main/java/org/eclipse/openvsx/ExtensionProcessor.java +++ b/server/src/main/java/org/eclipse/openvsx/ExtensionProcessor.java @@ -473,8 +473,9 @@ private String tryGetAssetPath(String type) { return null; } - public @Nullable TempFile getIcon(ExtensionVersion extVersion) throws IOException { + public @Nullable String getIconPath() { var iconPath = tryGetAssetPath(ExtensionQueryResult.ExtensionFile.FILE_ICON); + if (StringUtils.isEmpty(iconPath)) { loadPackageJson(); var iconPathNode = packageJson.get("icon"); @@ -489,6 +490,15 @@ private String tryGetAssetPath(String type) { iconPath = "extension/" + iconPath; } + return iconPath; + } + + public @Nullable TempFile getIcon(ExtensionVersion extVersion) throws IOException { + var iconPath = getIconPath(); + if (iconPath == null) { + return null; + } + var entryFile = ArchiveUtil.readEntry(zipFile, iconPath); if (entryFile == null) { throw new ErrorResultException("Icon " + entryNotFoundMessage(iconPath)); 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 b11d86b26..857c37fa4 100644 --- a/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandler.java +++ b/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandler.java @@ -10,7 +10,6 @@ package org.eclipse.openvsx.publish; import java.io.IOException; -import java.nio.file.Path; import java.time.LocalDateTime; import java.util.List; import java.util.function.Consumer; @@ -65,7 +64,7 @@ public class PublishExtensionVersionHandler { private final ExtensionControlService extensionControl; private final ExtensionScanService scanService; - private final Predicate unsupportedIconExtensions; + private final Predicate unsupportedIconExtensions; public PublishExtensionVersionHandler( PublishingConfig config, @@ -95,7 +94,7 @@ public PublishExtensionVersionHandler( return false; } - var fileExtension = FilenameUtils.getExtension(path.toString()); + var fileExtension = FilenameUtils.getExtension(path); return config.getUnsupportedIconFormats().stream().anyMatch(ext -> ext.equalsIgnoreCase(fileExtension)); }; } @@ -178,7 +177,7 @@ private ExtensionVersion createExtensionVersion(ExtensionProcessor processor, Us extVersion.setExtension(extension); validateLicense(processor, extVersion); - validateIcon(processor, extVersion); + validateIcon(processor); validateFileResources(processor, extVersion); validateMetadata(extVersion); entityManager.persist(extVersion); @@ -222,20 +221,19 @@ private void checkLicense(ExtensionVersion extVersion, TempFile licenseFile) { } } - private void validateIcon(ExtensionProcessor processor, ExtensionVersion extVersion) { - try (var iconFile = processor.getIcon(extVersion)) { - if (iconFile != null && unsupportedIconExtensions.test(iconFile.getPath())) { - throw new ErrorResultException("This extension cannot be accepted as it uses an unsupported icon format."); - } - } catch (IOException e) { - throw new ServerErrorException("Failed to read icon file", e); + private void validateIcon(ExtensionProcessor processor) { + var iconPath = processor.getIconPath(); + if (iconPath != null && unsupportedIconExtensions.test(iconPath)) { + throw new ErrorResultException("This extension cannot be accepted as it uses an unsupported icon format."); } } private void validateFileResources(ExtensionProcessor processor, ExtensionVersion extVersion) { try { - // validate that all file resources are readable/accessible during synchronous publishing - // to avoid failing during async publishing and report errors directly back to publishers + // Validate that all file resources are readable/accessible during synchronous publishing + // to avoid failing during async publishing and report errors directly back to publishers. + // This creates unnecessary temp files, however these are usually small and thus it is + // acceptable in this case. processor.getFileResources(extVersion, _ -> {}); } catch (ErrorResultException exc) { throw new ErrorResultException("Validation failed: " + exc.getMessage()); 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 4eb3761ee..0ca67f9c8 100644 --- a/server/src/test/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandlerTest.java +++ b/server/src/test/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandlerTest.java @@ -18,7 +18,6 @@ import static org.mockito.Mockito.when; import java.io.IOException; -import java.nio.file.Path; import java.time.LocalDateTime; import java.util.Collections; import java.util.List; @@ -35,7 +34,6 @@ import org.eclipse.openvsx.repositories.RepositoryService; 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; @@ -207,7 +205,7 @@ private ExtensionVersion mockExtensionVersion(String namespace, String name, Str when(processor.getExtensionName()).thenReturn(name); when(processor.getVersion()).thenReturn(version); if (iconPath != null) { - when(processor.getIcon(ArgumentMatchers.any())).thenReturn(new TempFile(Path.of(iconPath))); + when(processor.getIconPath()).thenReturn(iconPath); } var ev = new ExtensionVersion();