diff --git a/assets/icons/about.svg b/assets/icons/about.svg index f6aac90..de19102 100644 --- a/assets/icons/about.svg +++ b/assets/icons/about.svg @@ -1,6 +1,5 @@ - - - - - + + + + \ No newline at end of file diff --git a/assets/icons/arrow-right.svg b/assets/icons/arrow-right.svg index 1db6d0c..7cb8f33 100644 --- a/assets/icons/arrow-right.svg +++ b/assets/icons/arrow-right.svg @@ -1,3 +1,3 @@ - + diff --git a/assets/icons/attached.svg b/assets/icons/attached.svg index 7d894e7..07596ef 100644 --- a/assets/icons/attached.svg +++ b/assets/icons/attached.svg @@ -1,12 +1,4 @@ - - - - - - - - - - + + diff --git a/assets/icons/batch.svg b/assets/icons/batch.svg index 5ced311..03b691f 100644 --- a/assets/icons/batch.svg +++ b/assets/icons/batch.svg @@ -1,13 +1,6 @@ - - - - - - - - - - - - + + + + + diff --git a/assets/icons/cert.svg b/assets/icons/cert.svg index 14cc2b4..5088055 100644 --- a/assets/icons/cert.svg +++ b/assets/icons/cert.svg @@ -1,11 +1,7 @@ - - - - - - - - - - + + + + + + diff --git a/assets/icons/check.svg b/assets/icons/check.svg index c379af0..f1debcd 100644 --- a/assets/icons/check.svg +++ b/assets/icons/check.svg @@ -1,4 +1,4 @@ - - - + + + diff --git a/assets/icons/close.svg b/assets/icons/close.svg index 3f6cc79..57f3e07 100644 --- a/assets/icons/close.svg +++ b/assets/icons/close.svg @@ -1,3 +1,3 @@ - + diff --git a/assets/icons/co-sign.svg b/assets/icons/co-sign.svg index 283c24f..3516c7a 100644 --- a/assets/icons/co-sign.svg +++ b/assets/icons/co-sign.svg @@ -1,12 +1,6 @@ - - - - - - - - - - - + + + + diff --git a/assets/icons/error.svg b/assets/icons/error.svg index 5a36ff3..91ccfe6 100644 --- a/assets/icons/error.svg +++ b/assets/icons/error.svg @@ -1,5 +1,5 @@ - - - - + + + + diff --git a/assets/icons/exit.svg b/assets/icons/exit.svg index d209a6b..dd4ea0d 100644 --- a/assets/icons/exit.svg +++ b/assets/icons/exit.svg @@ -1,8 +1,6 @@ - - - - - - + + + + diff --git a/assets/icons/file.svg b/assets/icons/file.svg index 023885f..94ce44c 100644 --- a/assets/icons/file.svg +++ b/assets/icons/file.svg @@ -1,13 +1,7 @@ - - - - - - - - - - - - + + + + + diff --git a/assets/icons/logs.svg b/assets/icons/logs.svg index 0dd89e3..79f1f6f 100644 --- a/assets/icons/logs.svg +++ b/assets/icons/logs.svg @@ -1,13 +1,6 @@ - - - - - - - - - - - - + + + + + diff --git a/assets/icons/ltv.svg b/assets/icons/ltv.svg index dbc162a..9392d27 100644 --- a/assets/icons/ltv.svg +++ b/assets/icons/ltv.svg @@ -1,11 +1,6 @@ - - - - - - - - - - LTV + + + + diff --git a/assets/icons/moon.svg b/assets/icons/moon.svg index 9eb6f1a..6687394 100644 --- a/assets/icons/moon.svg +++ b/assets/icons/moon.svg @@ -1,6 +1,3 @@ - - - - - + + diff --git a/assets/icons/pdf.svg b/assets/icons/pdf.svg index d3bc5e9..e003bac 100644 --- a/assets/icons/pdf.svg +++ b/assets/icons/pdf.svg @@ -1,13 +1,7 @@ - - - - - - - - - - - - PDF + + + + + diff --git a/assets/icons/refresh.svg b/assets/icons/refresh.svg index 8ee09d9..c7694f3 100644 --- a/assets/icons/refresh.svg +++ b/assets/icons/refresh.svg @@ -1,5 +1,5 @@ - - - + + + diff --git a/assets/icons/seal-icp-brasil.svg b/assets/icons/seal-icp-brasil.svg new file mode 100644 index 0000000..139606c --- /dev/null +++ b/assets/icons/seal-icp-brasil.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/assets/icons/seal.svg b/assets/icons/seal.svg index 1aa3a5f..2f3a1fb 100644 --- a/assets/icons/seal.svg +++ b/assets/icons/seal.svg @@ -1,11 +1,5 @@ - - - - - - - - - - + + + + diff --git a/assets/icons/settings.svg b/assets/icons/settings.svg index 0a8dbc2..0d63eed 100644 --- a/assets/icons/settings.svg +++ b/assets/icons/settings.svg @@ -1,4 +1,5 @@ - - - + + + diff --git a/assets/icons/shield.svg b/assets/icons/shield.svg index 1bc2539..c6f5415 100644 --- a/assets/icons/shield.svg +++ b/assets/icons/shield.svg @@ -1,6 +1,5 @@ - - - - - + + + diff --git a/assets/icons/star.svg b/assets/icons/star.svg index ac2213a..08515ae 100644 --- a/assets/icons/star.svg +++ b/assets/icons/star.svg @@ -1,4 +1,4 @@ - - + + diff --git a/assets/icons/sun.svg b/assets/icons/sun.svg index 0327476..e444c96 100644 --- a/assets/icons/sun.svg +++ b/assets/icons/sun.svg @@ -1,15 +1,11 @@ - - - - - - - - - - - - - - + + + + + + + + + + diff --git a/assets/icons/timestamp.svg b/assets/icons/timestamp.svg index 16238f4..86f4f0b 100644 --- a/assets/icons/timestamp.svg +++ b/assets/icons/timestamp.svg @@ -1,13 +1,5 @@ - - - - - - - - - - - - + + + + diff --git a/assets/icons/tutorial.svg b/assets/icons/tutorial.svg index 32d13d2..5eabb2f 100644 --- a/assets/icons/tutorial.svg +++ b/assets/icons/tutorial.svg @@ -1,8 +1,5 @@ - - - - - - - + + + diff --git a/assets/icons/validate-timestamp.svg b/assets/icons/validate-timestamp.svg index dc0f7c8..36a9505 100644 --- a/assets/icons/validate-timestamp.svg +++ b/assets/icons/validate-timestamp.svg @@ -1,10 +1,6 @@ - - - - - - - - - + + + + + diff --git a/assets/icons/validate.svg b/assets/icons/validate.svg index f654ce8..e10612d 100644 --- a/assets/icons/validate.svg +++ b/assets/icons/validate.svg @@ -1,11 +1,6 @@ - - - - - - - - - - + + + + + diff --git a/assets/icons/xml.svg b/assets/icons/xml.svg index f728b5f..711ec86 100644 --- a/assets/icons/xml.svg +++ b/assets/icons/xml.svg @@ -1,13 +1,8 @@ - - - - - - - - - - - - <XML> + + + + + + diff --git a/assets/icons/zoom-fit.svg b/assets/icons/zoom-fit.svg index 201271c..aac60cb 100644 --- a/assets/icons/zoom-fit.svg +++ b/assets/icons/zoom-fit.svg @@ -1,3 +1,6 @@ - + + + + diff --git a/assets/icons/zoom-in.svg b/assets/icons/zoom-in.svg index 6783075..854e624 100644 --- a/assets/icons/zoom-in.svg +++ b/assets/icons/zoom-in.svg @@ -1,5 +1,5 @@ - - - + + + diff --git a/assets/icons/zoom-out.svg b/assets/icons/zoom-out.svg index 3882ea9..2c59873 100644 --- a/assets/icons/zoom-out.svg +++ b/assets/icons/zoom-out.svg @@ -1,5 +1,5 @@ - - - + + + diff --git a/modules/cybercore-ui/src/main/java/br/com/cybercore/signer/ui/CyberCoreDesktopApplication.java b/modules/cybercore-ui/src/main/java/br/com/cybercore/signer/ui/CyberCoreDesktopApplication.java index 52c18cc..1db2a18 100644 --- a/modules/cybercore-ui/src/main/java/br/com/cybercore/signer/ui/CyberCoreDesktopApplication.java +++ b/modules/cybercore-ui/src/main/java/br/com/cybercore/signer/ui/CyberCoreDesktopApplication.java @@ -95,6 +95,10 @@ public class CyberCoreDesktopApplication extends Application { developmentMode = devMode; } + public static boolean isDevelopmentMode() { + return developmentMode; + } + public static MainAppView getMainAppView() { return mainAppView; } diff --git a/modules/cybercore-ui/src/main/java/br/com/cybercore/signer/ui/assets/AssetManager.java b/modules/cybercore-ui/src/main/java/br/com/cybercore/signer/ui/assets/AssetManager.java index 04d7e10..c74663e 100644 --- a/modules/cybercore-ui/src/main/java/br/com/cybercore/signer/ui/assets/AssetManager.java +++ b/modules/cybercore-ui/src/main/java/br/com/cybercore/signer/ui/assets/AssetManager.java @@ -39,15 +39,15 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import br.com.cybercore.signer.ui.CyberCoreDesktopApplication; import br.com.cybercore.signer.ui.theme.ThemeManager; import java.util.Optional; import java.util.function.Supplier; /** * Gerenciador centralizado de recursos visuais (ícones e imagens). - * Permite que o usuário customize ou substitua facilmente qualquer ícone ou imagem (SVG/PNG) - * do CyberCore Signer simplesmente colocando os arquivos na pasta 'assets/' - * na raiz do projeto ou no diretório ~/.cybercore/assets/. + * Centraliza o carregamento de recursos do classpath em produção e permite + * substituição via pasta externa 'assets/' exclusivamente em modo de desenvolvimento. */ public final class AssetManager { @@ -55,9 +55,33 @@ public final class AssetManager { private static final String[] RASTER_EXTENSIONS = {".png", ".jpg", ".jpeg"}; + private static volatile Boolean externalOverridesOverride = null; + private AssetManager() { } + /** + * Define explicitamente se o carregamento de assets externos está habilitado (útil para testes). + */ + public static void setExternalOverridesEnabled(Boolean enabled) { + externalOverridesOverride = enabled; + } + + /** + * Indica se o carregamento de assets externos (./assets ou ~/.cybercore/assets) é permitido. + * Em ambiente de produção, este método retorna false por padrão para garantir + * integridade e isolamento estrito de recursos empacotados. + */ + public static boolean isExternalOverridesAllowed() { + if (externalOverridesOverride != null) { + return externalOverridesOverride; + } + if (CyberCoreDesktopApplication.isDevelopmentMode()) { + return true; + } + return Boolean.getBoolean("cybercore.dev") || Boolean.getBoolean("cybercore.assets.external"); + } + /** * Retorna o caminho do diretório externo prioritário de assets. */ @@ -82,31 +106,33 @@ public final class AssetManager { String cleanName = cleanBaseName(baseName); - // 1. Procura no diretório local ./assets/{category}/ - Path localDir = Path.of("assets", category); - for (String ext : RASTER_EXTENSIONS) { - Path file = localDir.resolve(cleanName + ext); - if (Files.isRegularFile(file)) { - try (InputStream in = new FileInputStream(file.toFile())) { - logger.debug("Carregando imagem bitmap do diretório local: {}", file); - return Optional.of(new Image(in, width, height, true, true)); - } catch (Exception e) { - logger.warn("Falha ao ler imagem customizada {}: {}", file, e.getMessage()); + if (isExternalOverridesAllowed()) { + // 1. Procura no diretório local ./assets/{category}/ + Path localDir = Path.of("assets", category); + for (String ext : RASTER_EXTENSIONS) { + Path file = localDir.resolve(cleanName + ext); + if (Files.isRegularFile(file)) { + try (InputStream in = new FileInputStream(file.toFile())) { + logger.debug("Carregando imagem bitmap do diretório local: {}", file); + return Optional.of(new Image(in, width, height, true, true)); + } catch (Exception e) { + logger.warn("Falha ao ler imagem customizada {}: {}", file, e.getMessage()); + } } } - } - // 2. Procura em ~/.cybercore/assets/{category}/ - String userHome = System.getProperty("user.home", "."); - Path userDir = Path.of(userHome, ".cybercore", "assets", category); - for (String ext : RASTER_EXTENSIONS) { - Path file = userDir.resolve(cleanName + ext); - if (Files.isRegularFile(file)) { - try (InputStream in = new FileInputStream(file.toFile())) { - logger.debug("Carregando imagem bitmap de ~/.cybercore: {}", file); - return Optional.of(new Image(in, width, height, true, true)); - } catch (Exception e) { - logger.warn("Falha ao ler imagem do usuário {}: {}", file, e.getMessage()); + // 2. Procura em ~/.cybercore/assets/{category}/ + String userHome = System.getProperty("user.home", "."); + Path userDir = Path.of(userHome, ".cybercore", "assets", category); + for (String ext : RASTER_EXTENSIONS) { + Path file = userDir.resolve(cleanName + ext); + if (Files.isRegularFile(file)) { + try (InputStream in = new FileInputStream(file.toFile())) { + logger.debug("Carregando imagem bitmap de ~/.cybercore: {}", file); + return Optional.of(new Image(in, width, height, true, true)); + } catch (Exception e) { + logger.warn("Falha ao ler imagem do usuário {}: {}", file, e.getMessage()); + } } } } @@ -141,17 +167,19 @@ public final class AssetManager { if (iconName != null && !iconName.isBlank()) { String cleanName = cleanBaseName(iconName); - // 1. Prioridade para SVG em ./assets/icons/ - Optional localSvg = loadSvgIconFromPath(Path.of("assets", "icons", cleanName + ".svg"), size, tint); - if (localSvg.isPresent()) { - return localSvg.get(); - } + if (isExternalOverridesAllowed()) { + // 1. Prioridade para SVG em ./assets/icons/ + Optional localSvg = loadSvgIconFromPath(Path.of("assets", "icons", cleanName + ".svg"), size, tint); + if (localSvg.isPresent()) { + return localSvg.get(); + } - // 2. Procura SVG em ~/.cybercore/assets/icons/ - String userHome = System.getProperty("user.home", "."); - Optional userSvg = loadSvgIconFromPath(Path.of(userHome, ".cybercore", "assets", "icons", cleanName + ".svg"), size, tint); - if (userSvg.isPresent()) { - return userSvg.get(); + // 2. Procura SVG em ~/.cybercore/assets/icons/ + String userHome = System.getProperty("user.home", "."); + Optional userSvg = loadSvgIconFromPath(Path.of(userHome, ".cybercore", "assets", "icons", cleanName + ".svg"), size, tint); + if (userSvg.isPresent()) { + return userSvg.get(); + } } // 3. Procura SVG no Classpath interno @@ -188,32 +216,34 @@ public final class AssetManager { if (imageName != null && !imageName.isBlank()) { String cleanName = cleanBaseName(imageName); - // 1. Prioridade para SVG em ./assets/images/ - Path localSvg = Path.of("assets", "images", cleanName + ".svg"); - if (Files.isRegularFile(localSvg)) { - try (InputStream in = new FileInputStream(localSvg.toFile())) { - Node node = parseSvgToNode(in, width, height, null); - if (node != null) { - logger.debug("Carregada imagem SVG local: {}", localSvg); - return node; + if (isExternalOverridesAllowed()) { + // 1. Prioridade para SVG em ./assets/images/ + Path localSvg = Path.of("assets", "images", cleanName + ".svg"); + if (Files.isRegularFile(localSvg)) { + try (InputStream in = new FileInputStream(localSvg.toFile())) { + Node node = parseSvgToNode(in, width, height, null); + if (node != null) { + logger.debug("Carregada imagem SVG local: {}", localSvg); + return node; + } + } catch (Exception e) { + logger.warn("Falha ao ler imagem SVG local {}: {}", localSvg, e.getMessage()); } - } catch (Exception e) { - logger.warn("Falha ao ler imagem SVG local {}: {}", localSvg, e.getMessage()); } - } - // 2. Procura SVG em ~/.cybercore/assets/images/ - String userHome = System.getProperty("user.home", "."); - Path userSvg = Path.of(userHome, ".cybercore", "assets", "images", cleanName + ".svg"); - if (Files.isRegularFile(userSvg)) { - try (InputStream in = new FileInputStream(userSvg.toFile())) { - Node node = parseSvgToNode(in, width, height, null); - if (node != null) { - logger.debug("Carregada imagem SVG do usuário: {}", userSvg); - return node; + // 2. Procura SVG em ~/.cybercore/assets/images/ + String userHome = System.getProperty("user.home", "."); + Path userSvg = Path.of(userHome, ".cybercore", "assets", "images", cleanName + ".svg"); + if (Files.isRegularFile(userSvg)) { + try (InputStream in = new FileInputStream(userSvg.toFile())) { + Node node = parseSvgToNode(in, width, height, null); + if (node != null) { + logger.debug("Carregada imagem SVG do usuário: {}", userSvg); + return node; + } + } catch (Exception e) { + logger.warn("Falha ao ler imagem SVG do usuário {}: {}", userSvg, e.getMessage()); } - } catch (Exception e) { - logger.warn("Falha ao ler imagem SVG do usuário {}: {}", userSvg, e.getMessage()); } } diff --git a/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/assets/AssetManagerTest.java b/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/assets/AssetManagerTest.java index f741338..fd785de 100644 --- a/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/assets/AssetManagerTest.java +++ b/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/assets/AssetManagerTest.java @@ -82,20 +82,26 @@ class AssetManagerTest { } @Test - @DisplayName("Deve interpretar atributos style inline no padrão de exportação do Inkscape/Figma") - void shouldParseSvgWithInlineStyles() { - String svgWithStyle = """ - - - OK - - """; - try (var in = new java.io.ByteArrayInputStream(svgWithStyle.getBytes(java.nio.charset.StandardCharsets.UTF_8))) { - Node node = AssetManager.parseSvgToNode(in, 48, 48); - assertThat(node).isNotNull(); - assertThat(node).isInstanceOf(javafx.scene.layout.Pane.class); - } catch (Exception e) { - org.junit.jupiter.api.Assertions.fail("Falha ao analisar SVG com estilo inline: " + e.getMessage()); + @DisplayName("Deve restringir overrides externos ao modo de desenvolvimento e isolar recursos em produção") + void shouldIsolateAssetsInProductionMode() { + try { + // Em produção (overrides desabilitados): + AssetManager.setExternalOverridesEnabled(false); + assertThat(AssetManager.isExternalOverridesAllowed()).isFalse(); + + // Ícones devem carregar do classpath mesmo sem diretório externo + Node certIcon = AssetManager.getIconOrDefault("cert", 24, null); + assertThat(certIcon).isNotNull(); + + Node logoNode = AssetManager.getImageOrDefault("app-logo", 200, 60, null); + assertThat(logoNode).isNotNull(); + + // Em modo desenvolvimento explicitamente habilitado: + AssetManager.setExternalOverridesEnabled(true); + assertThat(AssetManager.isExternalOverridesAllowed()).isTrue(); + + } finally { + AssetManager.setExternalOverridesEnabled(null); } } }