feat(security): prevent dev mode activation on production flavor builds

This commit is contained in:
Claudecio Martins
2026-10-20 15:03:30 -03:00
parent 7aa33a4789
commit 300944697f
4 changed files with 73 additions and 2 deletions
+18
View File
@@ -108,6 +108,23 @@
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>build-info.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>build-info.properties</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.openjfx</groupId>
@@ -138,6 +155,7 @@
<Implementation-Title>CyberCore Signer</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
<Implementation-Vendor>CyberCore</Implementation-Vendor>
<Build-Flavor>${cybercore.build.flavor}</Build-Flavor>
</manifestEntries>
</transformer>
</transformers>
@@ -19,12 +19,39 @@ public class CyberCoreSignerApplication {
private static final Logger logger = LoggerFactory.getLogger(CyberCoreSignerApplication.class);
private static final String BUILD_INFO_RESOURCE = "/build-info.properties";
private static String buildFlavor = "production";
private static boolean developmentMode = false;
private static boolean headlessMode = false;
private static boolean noWaitMode = false;
private static int configuredPort = CcspServer.DEFAULT_PORT;
private static volatile CcspServer runningServer = null;
static {
loadBuildInfo();
}
private static void loadBuildInfo() {
try (var is = CyberCoreSignerApplication.class.getResourceAsStream(BUILD_INFO_RESOURCE)) {
if (is != null) {
var props = new java.util.Properties();
props.load(is);
String flavor = props.getProperty("build.flavor");
if (flavor != null && !flavor.isBlank()) {
buildFlavor = flavor.trim().toLowerCase();
}
}
} catch (Exception ignored) {}
}
public static String getBuildFlavor() {
return buildFlavor;
}
public static void setBuildFlavorForTesting(String flavor) {
buildFlavor = flavor;
}
public static CcspServer getRunningServer() {
return runningServer;
}
@@ -147,6 +174,10 @@ public class CyberCoreSignerApplication {
for (int i = 0; i < args.length; i++) {
String arg = args[i];
if ("--dev".equalsIgnoreCase(arg) || "-dev".equalsIgnoreCase(arg)) {
if ("production".equalsIgnoreCase(buildFlavor)) {
logger.error("ALERTA DE SEGURANÇA: Modo de desenvolvimento (--dev) não é permitido em compilações de produção (flavor: production).");
throw new SecurityException("Modo de desenvolvimento (--dev) bloqueado em compilações de produção.");
}
developmentMode = true;
} else if ("--headless".equalsIgnoreCase(arg) || "-headless".equalsIgnoreCase(arg)
|| "--cli".equalsIgnoreCase(arg)) {
@@ -0,0 +1,2 @@
build.version=${project.version}
build.flavor=${cybercore.build.flavor}
@@ -8,16 +8,36 @@ import static org.assertj.core.api.Assertions.assertThatCode;
class CyberCoreSignerApplicationTest {
@Test
@DisplayName("Deve inicializar a aplicação com argumentos sem erros")
@DisplayName("Deve inicializar a aplicação com argumentos sem erros em compilação normal")
void shouldBootstrapWithoutErrors() {
try {
assertThatCode(() -> CyberCoreSignerApplication.main(new String[]{"--dev", "--headless", "--no-wait", "--port", "29999"}))
assertThatCode(() -> CyberCoreSignerApplication.main(new String[]{"--headless", "--no-wait", "--port", "29999"}))
.doesNotThrowAnyException();
} finally {
CyberCoreSignerApplication.stopServer();
}
}
@Test
@DisplayName("Deve bloquear o modo dev em compilações de produção")
void shouldBlockDevModeInProductionFlavor() {
CyberCoreSignerApplication.setBuildFlavorForTesting("production");
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
CyberCoreSignerApplication.parseArguments(new String[]{"--dev"})
).isInstanceOf(SecurityException.class)
.hasMessageContaining("bloqueado em compilações de produção");
}
@Test
@DisplayName("Deve permitir o modo dev quando o flavor for development")
void shouldAllowDevModeInDevelopmentFlavor() {
CyberCoreSignerApplication.setBuildFlavorForTesting("development");
org.assertj.core.api.Assertions.assertThatCode(() ->
CyberCoreSignerApplication.parseArguments(new String[]{"--dev"})
).doesNotThrowAnyException();
org.assertj.core.api.Assertions.assertThat(CyberCoreSignerApplication.isDevelopmentMode()).isTrue();
}
@Test
@DisplayName("Deve realizar o parse correto do argumento --port")
void shouldParsePortArgument() {