Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions config/native/no-ldap.java.security
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
security.provider.1=SUN
security.provider.2=SunRsaSign
security.provider.3=SunEC
security.provider.4=SunJSSE
security.provider.5=SunJCE
security.provider.6=SunJGSS
security.provider.7=SunSASL
security.provider.8=XMLDSig
security.provider.9=SunPCSC
security.provider.10=JdkSASL
security.provider.11=SunPKCS11
keystore.type=pkcs12
ssl.KeyManagerFactory.algorithm=SunX509
ssl.TrustManagerFactory.algorithm=PKIX
securerandom.source=file:/dev/random
securerandom.strongAlgorithms=NativePRNGBlocking:SUN,DRBG:SUN
Comment on lines +1 to +16

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if there are other security configuration files that might provide algorithm restrictions,
# and verify if native-image build configuration references additional security properties.

echo "=== Searching for other java.security or security-related config files ==="
fd -e security -e properties . config/

echo ""
echo "=== Checking native-image configuration for security property overrides ==="
rg -n "disabledAlgorithms|security\.provider|java\.security" --type-add 'native:*.json' --type native config/
rg -n "disabledAlgorithms|java\.security" pom.xml

Repository: ayagmar/quarkus-forge

Length of output: 376


🏁 Script executed:

#!/bin/bash
# Check for native-image configuration files
echo "=== Searching for native-image configuration files ==="
fd -e properties -e json . | rg -i "native.image|native-image"

echo ""
echo "=== Checking pom.xml context around the security properties setting ==="
rg -B 5 -A 5 "java.security.properties" pom.xml

echo ""
echo "=== Looking for comments or documentation about security config ==="
cat config/native/no-ldap.java.security

Repository: ayagmar/quarkus-forge

Length of output: 1510


Add jdk.tls.disabledAlgorithms and jdk.certpath.disabledAlgorithms to the security configuration.

The custom security properties file replaces JDK defaults entirely when set via -Djava.security.properties. Without explicitly disabling weak algorithms (MD5, SHA1withRSA, TLSv1, TLSv1.1), the native image will accept deprecated protocols and signatures. These restrictions must be added to this configuration file to prevent use of insecure algorithms.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@config/native/no-ldap.java.security` around lines 1 - 16, Add two properties
to this security properties set: jdk.tls.disabledAlgorithms and
jdk.certpath.disabledAlgorithms. Set jdk.tls.disabledAlgorithms to disable
legacy protocols and weak ciphers/signatures (e.g. TLSv1, TLSv1.1, SSLv3, MD5,
SHA1, anon, NULL) and set jdk.certpath.disabledAlgorithms to disable weak
signature/hash algorithms and short keys (e.g. MD2, MD5, SHA1, DSA, RSA keySize
< 2048) so the custom file preserves the JDK’s default algorithm restrictions;
include these property names exactly (jdk.tls.disabledAlgorithms and
jdk.certpath.disabledAlgorithms) in the file alongside the existing entries such
as security.provider.* and keystore.type.

30 changes: 27 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -157,15 +157,13 @@
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>version.properties</include>
<include>META-INF/native-image/dev.ayagmar/quarkusforge/resource-config.json</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>version.properties</exclude>
<exclude>META-INF/native-image/dev.ayagmar/quarkusforge/resource-config.json</exclude>
</excludes>
</resource>
Expand Down Expand Up @@ -207,6 +205,8 @@
</annotationProcessorPaths>
<compilerArgs>
<arg>-Aproject=${project.groupId}/${project.artifactId}</arg>
<arg>-Adisable.reflect.config</arg>
<arg>-Adisable.resource.config</arg>
<arg>-Adisable.proxy.config</arg>
</compilerArgs>
</configuration>
Expand Down Expand Up @@ -424,7 +424,7 @@
<buildArg>-Os</buildArg>
<buildArg>--emit build-report</buildArg>
<buildArg>-H:+ReportExceptionStackTraces</buildArg>
<buildArg>-H:+SharedArenaSupport</buildArg>
<buildArg>-Djava.security.properties==${project.basedir}/config/native/no-ldap.java.security</buildArg>
<buildArg>--enable-url-protocols=http,https</buildArg>
<buildArg>${native.access.flag}</buildArg>
</buildArgs>
Expand All @@ -434,6 +434,30 @@
</build>
</profile>

<profile>
<id>unix-native-jline-trim</id>
<activation>
<os>
<family>unix</family>
</os>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<configuration>
<buildArgs combine.children="append">
<buildArg>--exclude-config</buildArg>
<buildArg>jline-.*\.jar</buildArg>
<buildArg>META-INF/native-image/org\.jline/(jansi|jline-native|jline-terminal-jansi|jline-terminal-jna|jline-terminal-jni)/.*</buildArg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>

<profile>
<id>headless</id>
<properties>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package dev.ayagmar.quarkusforge.cli;

import picocli.CommandLine;

final class CliCommandLineFactory {
private CliCommandLineFactory() {}

static <T extends HeadlessRunner> CommandLine create(T command) {
CommandLine commandLine = new CommandLine(command);
addRequestOptionsMixin(commandLine, command);

GenerateCommand generateCommand = new GenerateCommand(command);
CommandLine generateCommandLine = new CommandLine(generateCommand);
generateCommandLine.addMixin("requestOptions", generateCommand.requestOptions());
commandLine.addSubcommand("generate", generateCommandLine);

commandLine.getCommandSpec().version(CliVersionProvider.resolveVersion());
commandLine.setExecutionStrategy(
parseResult -> {
recordMatchedRequestOptions(parseResult);
return new CommandLine.RunLast().execute(parseResult);
});
return commandLine;
}

private static void addRequestOptionsMixin(CommandLine commandLine, HeadlessRunner command) {
if (command instanceof QuarkusForgeCli quarkusForgeCli) {
commandLine.addMixin("requestOptions", quarkusForgeCli.requestOptions());
}
}

private static void recordMatchedRequestOptions(CommandLine.ParseResult parseResult) {
for (CommandLine.ParseResult current = parseResult;
current != null;
current = current.subcommand()) {
Object userObject = current.commandSpec().userObject();
if (userObject instanceof QuarkusForgeCli quarkusForgeCli) {
quarkusForgeCli.requestOptions().recordMatchedOptions(current);
} else if (userObject instanceof GenerateCommand generateCommand) {
generateCommand.requestOptions().recordMatchedOptions(current);
}
}
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,47 +1,18 @@
package dev.ayagmar.quarkusforge.cli;

import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
import java.util.Properties;
import picocli.CommandLine;

final class CliVersionProvider implements CommandLine.IVersionProvider {
private static final String VERSION_RESOURCE = "version.properties";
private static final String VERSION_KEY = "version";
final class CliVersionProvider {
private static final String UNKNOWN_VERSION = "unknown";

@Override
public String[] getVersion() {
return new String[] {resolveVersion()};
}

static String resolveVersion() {
String implementationVersion = CliVersionProvider.class.getPackage().getImplementationVersion();
if (isUsableVersion(implementationVersion)) {
return implementationVersion.strip();
}
String resourceVersion = readVersionFromResource();
if (isUsableVersion(resourceVersion)) {
return resourceVersion.strip();
}
return UNKNOWN_VERSION;
}

private static String readVersionFromResource() {
try (InputStream inputStream =
CliVersionProvider.class.getClassLoader().getResourceAsStream(VERSION_RESOURCE)) {
if (inputStream == null) {
return null;
}
Properties properties = new Properties();
properties.load(inputStream);
return properties.getProperty(VERSION_KEY);
} catch (IOException ignored) {
return null;
}
}

private static boolean isUsableVersion(String value) {
return value != null && !value.isBlank() && !Objects.equals(value.strip(), UNKNOWN_VERSION);
}
Expand Down
22 changes: 18 additions & 4 deletions src/main/java/dev/ayagmar/quarkusforge/cli/GenerateCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,19 @@
import dev.ayagmar.quarkusforge.forge.Forgefile;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Callable;
import picocli.CommandLine.Command;
import picocli.CommandLine.Mixin;
import picocli.CommandLine.Option;
import picocli.CommandLine.ParentCommand;

@Command(
name = "generate",
mixinStandardHelpOptions = true,
description = "Generate a Quarkus project without starting the TUI")
public final class GenerateCommand implements Callable<Integer> {
@ParentCommand private HeadlessRunner rootCommand;
private final HeadlessRunner rootCommand;

@Mixin private RequestOptions requestOptions = new RequestOptions();
private RequestOptions requestOptions = new RequestOptions();

@Option(
names = {"-e", "--extension"},
Expand Down Expand Up @@ -64,6 +63,17 @@ public final class GenerateCommand implements Callable<Integer> {
description = "Validate full generation request without writing files")
private boolean dryRun;

public GenerateCommand() {
this(
command -> {
throw new IllegalStateException("Generate command runner not configured");
});
}

GenerateCommand(HeadlessRunner rootCommand) {
this.rootCommand = Objects.requireNonNull(rootCommand);
}

@Override
public Integer call() {
return rootCommand.runHeadlessGenerate(this);
Expand All @@ -84,6 +94,10 @@ public void setCliPrefill(CliPrefill prefill) {
requestOptions = RequestOptions.explicitFromCliPrefill(prefill);
}

RequestOptions requestOptions() {
return requestOptions;
}

public List<String> extensions() {
return extensions;
}
Expand Down
10 changes: 2 additions & 8 deletions src/main/java/dev/ayagmar/quarkusforge/cli/HeadlessCli.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,16 @@
import dev.ayagmar.quarkusforge.runtime.RuntimeServices;
import java.util.Objects;
import java.util.concurrent.Callable;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Spec;

@Command(
name = "quarkus-forge",
mixinStandardHelpOptions = true,
versionProvider = CliVersionProvider.class,
subcommands = {GenerateCommand.class},
description = "Quarkus forge headless CLI")
public final class HeadlessCli implements Callable<Integer>, HeadlessRunner {
private final RuntimeConfig runtimeConfig;

@Spec CommandLine.Model.CommandSpec spec;

@Option(
names = "--verbose",
defaultValue = "false",
Expand All @@ -43,7 +37,7 @@ public final class HeadlessCli implements Callable<Integer>, HeadlessRunner {

@Override
public Integer call() {
spec.commandLine().usage(System.out);
CliCommandLineFactory.create(this).usage(System.out);
return ExitCodes.OK;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

Expand All @@ -60,7 +54,7 @@ public static int runWithArgs(String[] args) {
}

static int runWithArgs(String[] args, RuntimeConfig runtimeConfig) {
return new CommandLine(new HeadlessCli(runtimeConfig)).execute(args);
return CliCommandLineFactory.create(new HeadlessCli(runtimeConfig)).execute(args);
}

public static void main(String[] args) {
Expand Down
21 changes: 3 additions & 18 deletions src/main/java/dev/ayagmar/quarkusforge/cli/QuarkusForgeCli.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,19 @@
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.function.Function;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Mixin;
import picocli.CommandLine.Option;

@Command(
name = "quarkus-forge",
versionProvider = CliVersionProvider.class,
subcommands = {GenerateCommand.class},
mixinStandardHelpOptions = true,
description = "Quarkus forge terminal UI")
public final class QuarkusForgeCli implements Callable<Integer>, HeadlessRunner {

private static final PostTuiActionExecutor POST_TUI_ACTION_EXECUTOR = new PostTuiActionExecutor();
private static final TuiBootstrapService TUI_BOOTSTRAP_SERVICE = new TuiBootstrapService();

@Mixin private RequestOptions requestOptions = new RequestOptions();
private RequestOptions requestOptions = new RequestOptions();

private final RuntimeConfig runtimeConfig;
private final Function<java.net.URI, QuarkusApiClient> apiClientFactory;
Expand All @@ -65,18 +62,6 @@ public final class QuarkusForgeCli implements Callable<Integer>, HeadlessRunner
@Option(names = "--interactive-smoke-test", hidden = true, defaultValue = "false")
private boolean interactiveSmokeTest;

@Option(
names = {"-h", "--help"},
usageHelp = true,
description = "Show this help message and exit.")
private boolean helpRequested;

@Option(
names = {"-V", "--version"},
versionHelp = true,
description = "Print version information and exit.")
private boolean versionRequested;

@Option(
names = "--post-generate-hook",
defaultValue = "",
Expand Down Expand Up @@ -158,7 +143,7 @@ public static int runWithArgs(String[] args) {
}

static int runWithArgs(String[] args, RuntimeConfig runtimeConfig) {
return new CommandLine(new QuarkusForgeCli(runtimeConfig)).execute(args);
return CliCommandLineFactory.create(new QuarkusForgeCli(runtimeConfig)).execute(args);
}

public static void main(String[] args) {
Expand Down
27 changes: 16 additions & 11 deletions src/main/java/dev/ayagmar/quarkusforge/cli/RequestOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,12 @@
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Option;
import picocli.CommandLine.Spec;
import picocli.CommandLine.ParseResult;

final class RequestOptions {
private final Set<String> prefilledOptions = new HashSet<>();

/**
* Injected by Picocli after parsing; null when the object is constructed manually (e.g. in tests
* or when calling {@link #defaults()}).
*/
@Spec CommandSpec spec;
private final Set<String> matchedOptions = new HashSet<>();

static final String DEFAULT_GROUP_ID = ProjectInputDefaults.GROUP_ID;
static final String DEFAULT_ARTIFACT_ID = ProjectInputDefaults.ARTIFACT_ID;
Expand Down Expand Up @@ -181,15 +175,16 @@ private static RequestOptions fromCliPrefill(CliPrefill prefill, boolean markPre

/**
* Returns {@code true} when the user explicitly supplied the option on the command line. Falls
* back to value-equality detection when {@link #spec} is not available (tests, defaults()).
* back to value-equality detection when no parse result has been recorded yet (tests,
* defaults()).
*
* @param optionName canonical option name, e.g. {@code "--group-id"}
* @param currentValue the current field value
* @param defaultValue the compiled-in default value
*/
boolean isExplicitlySet(String optionName, String currentValue, String defaultValue) {
if (spec != null && spec.commandLine() != null && spec.commandLine().getParseResult() != null) {
return spec.commandLine().getParseResult().hasMatchedOption(optionName);
if (matchedOptions.contains(optionName)) {
return true;
}
if (prefilledOptions.contains(optionName)) {
return false;
Expand All @@ -205,6 +200,16 @@ private String explicitValue(String optionName, String currentValue, String defa
return currentValue;
}

void recordMatchedOptions(ParseResult parseResult) {
matchedOptions.clear();
if (parseResult == null) {
return;
}
parseResult
.matchedOptions()
.forEach(optionSpec -> matchedOptions.add(optionSpec.longestName()));
}

private String applyCliValue(
String optionName, String cliValue, String fallbackValue, boolean markPrefilled) {
if (cliValue == null || cliValue.isBlank()) {
Expand Down
Loading
Loading