Bug Analysis: maven-help-plugin
These were all pulled out by throwing the code at an LLM. This issue lists multiple independent bugs. If anyone wants to work on any of these, note it here and open a new issue just for that bug. Be careful that some of these are likely false reports, though closing those is also useful work.
All 26 unit tests pass. Below are bugs found by code inspection, ordered by severity.
HIGH Severity
1. EffectivePomMojo.cleanModel() mutates the live project's Model in-place
File: src/main/java/org/apache/maven/plugins/help/EffectivePomMojo.java:210-213
private static void cleanModel(Model pom) {
Properties properties = new SortedProperties();
properties.putAll(pom.getProperties());
pom.setProperties(properties);
}
Called from writeEffectivePom() at line 182 on project.getModel(). This permanently replaces the project's Properties with a SortedProperties instance, altering property iteration order for the remainder of the build. This is a data corruption side effect in what should be a read-only display operation.
2. EffectiveSettingsMojo mutates the original Settings object when showPasswords=true
File: src/main/java/org/apache/maven/plugins/help/EffectiveSettingsMojo.java:87-94
if (showPasswords) {
copySettings = settings; // uses original, not a copy
} else {
copySettings = copySettings(settings);
}
writeEffectiveSettings(copySettings, writer); // calls cleanSettings() which modifies profiles
When -DshowPasswords=true, the original Maven Settings object is used directly. writeEffectiveSettings → cleanSettings() then replaces each profile's Properties with a SortedProperties instance (same pattern as bug #1), mutating the global shared settings object.
3. DescribeMojo.lookupPluginDescriptor() error message references null mojo fields instead of PluginInfo
File: src/main/java/org/apache/maven/plugins/help/DescribeMojo.java:333-338
throw new MojoExecutionException(
"Error retrieving plugin descriptor for:" + LS + LS + "groupId: '"
+ groupId + "'" + LS + "artifactId: '" + artifactId + "'" + LS + "version: '" + version + "'" + LS + LS, e);
When the plugin was specified by prefix (e.g., -Dplugin=help), the mojo fields groupId, artifactId, version are all null. The method parameter PluginInfo pi contains the actual values but the error message ignores them. Output: groupId: 'null'\nartifactId: 'null'\nversion: 'null' — misleading and unhelpful for debugging.
MEDIUM Severity
4. AllProfilesMojo.addProjectPomProfiles() uses parent build context for active profiles
File: src/main/java/org/apache/maven/plugins/help/AllProfilesMojo.java:153-156
if (project.getActiveProfiles() != null) {
for (Profile profile : project.getActiveProfiles()) {
activeProfiles.put(profile.getId(), profile);
}
}
project = project.getParent(); // walks up to parent
When the loop reaches a parent POM, project.getActiveProfiles() returns profiles active in the parent's build context. These may not be active for the child project (different JDK, OS, properties). The output can incorrectly mark parent profiles as active when they do not apply to the child.
5. EffectiveSettingsMojo.copySettings() does not deep-copy profiles
File: src/main/java/org/apache/maven/plugins/help/EffectiveSettingsMojo.java:156-198
Only servers and proxies are manually deep-copied. The profiles list is shared with the original via the shallow SettingsUtils.copySettings(). Profiles can contain sensitive data (passwords in properties) that would be exposed if the original settings object is later queried expecting the copy to be isolated.
6. ListDependencyTypesMojo, ListLifecyclePhasesMojo, ListPackagingMojo write to console AND file simultaneously
Files:
ListDependencyTypesMojo.java:95-96
ListLifecyclePhasesMojo.java:76-77
ListPackagingMojo.java:110-111
getLog().info(LS + "Maven Dependency Types defined:" + LS + LS + descriptionBuffer);
writeFile(output, descriptionBuffer); // always called, even when output is null
These three mojos always log to console AND unconditionally call writeFile(output, ...) (no-op when null). Every other mojo uses if (output != null) { ... } else { getLog().info(...); }. The dual-write means content is always printed to console even when writing to file. They also do not prepend the "Generated by Maven Help Plugin" header that ActiveProfilesMojo and SystemMojo add.
LOW Severity
7. DescribeMojo.toLines() delegates to generated HelpMojo via fragile reflection
File: src/main/java/org/apache/maven/plugins/help/DescribeMojo.java:747-778
Method m = HelpMojo.class.getDeclaredMethod("toLines", String.class, Integer.TYPE, Integer.TYPE, Integer.TYPE);
m.setAccessible(true);
List<String> output = (List<String>) m.invoke(HelpMojo.class, text, indent, indentSize, lineLength);
HelpMojo is generated by maven-plugin-tools at compile time (in target/generated-sources/). It IS available at runtime, but this reflection is brittle: any change in the generated code's toLines signature silently breaks all describe output formatting. The HelpMojo.toLines internally calls repeat() with new StringBuilder(repeat * str.length()) — a classic integer overflow risk that can throw NegativeArraySizeException.
8. AbstractHelpMojo.LS uses System.getProperty("line.separator")
File: src/main/java/org/apache/maven/plugins/help/AbstractHelpMojo.java:55
protected static final String LS = System.getProperty("line.separator");
Should use System.lineSeparator() (Java 7+), which falls back to "\n" if the property is missing. DescribeMojoTest inconsistently uses both System.getProperty("line.separator") (line 88) and System.lineSeparator() (line 110).
9. SortedProperties uses raw types
File: src/main/java/org/apache/maven/plugins/help/AbstractEffectiveMojo.java:147-148
List list = new ArrayList(keynames);
Collections.sort(list);
The @SuppressWarnings({"rawtypes", "unchecked"}) annotation masks the issue rather than using List<Object>.
10. AbstractHelpMojo.getAetherArtifact() uses deprecated Artifact.LATEST_VERSION
File: src/main/java/org/apache/maven/plugins/help/AbstractHelpMojo.java:147
version = Artifact.LATEST_VERSION;
Deprecated in favor of null (which means "latest") in the Aether API.
11. EffectivePomMojo encoding fallback can return null
File: src/main/java/org/apache/maven/plugins/help/EffectivePomMojo.java:110
String encoding = output != null ? project.getModel().getModelEncoding() : System.getProperty("file.encoding");
System.getProperty("file.encoding") can theoretically return null.
12. AllProfilesMojo — settings profiles never tracked for active status
File: src/main/java/org/apache/maven/plugins/help/AllProfilesMojo.java:167-173
private void addSettingsProfiles(Map<String, Profile> allProfiles) {
for (org.apache.maven.settings.Profile settingsProfile : settingsProfiles) {
Profile profile = SettingsUtils.convertFromSettingsProfile(settingsProfile);
allProfiles.put(profile.getId(), profile);
}
}
addSettingsProfiles() only populates allProfilesByIds, never activeProfilesByIds. Active detection relies on project.getActiveProfiles() which may or may not include converted settings profiles depending on Maven version and activation mechanisms. This can under-report active settings profiles.
Bug Analysis: maven-help-plugin
These were all pulled out by throwing the code at an LLM. This issue lists multiple independent bugs. If anyone wants to work on any of these, note it here and open a new issue just for that bug. Be careful that some of these are likely false reports, though closing those is also useful work.
All 26 unit tests pass. Below are bugs found by code inspection, ordered by severity.
HIGH Severity
1.
EffectivePomMojo.cleanModel()mutates the live project's Model in-placeFile:
src/main/java/org/apache/maven/plugins/help/EffectivePomMojo.java:210-213Called from
writeEffectivePom()at line 182 onproject.getModel(). This permanently replaces the project'sPropertieswith aSortedPropertiesinstance, altering property iteration order for the remainder of the build. This is a data corruption side effect in what should be a read-only display operation.2.
EffectiveSettingsMojomutates the original Settings object whenshowPasswords=trueFile:
src/main/java/org/apache/maven/plugins/help/EffectiveSettingsMojo.java:87-94When
-DshowPasswords=true, the original MavenSettingsobject is used directly.writeEffectiveSettings→cleanSettings()then replaces each profile'sPropertieswith aSortedPropertiesinstance (same pattern as bug #1), mutating the global shared settings object.3.
DescribeMojo.lookupPluginDescriptor()error message references null mojo fields instead ofPluginInfoFile:
src/main/java/org/apache/maven/plugins/help/DescribeMojo.java:333-338When the plugin was specified by prefix (e.g.,
-Dplugin=help), the mojo fieldsgroupId,artifactId,versionare all null. The method parameterPluginInfo picontains the actual values but the error message ignores them. Output:groupId: 'null'\nartifactId: 'null'\nversion: 'null'— misleading and unhelpful for debugging.MEDIUM Severity
4.
AllProfilesMojo.addProjectPomProfiles()uses parent build context for active profilesFile:
src/main/java/org/apache/maven/plugins/help/AllProfilesMojo.java:153-156When the loop reaches a parent POM,
project.getActiveProfiles()returns profiles active in the parent's build context. These may not be active for the child project (different JDK, OS, properties). The output can incorrectly mark parent profiles as active when they do not apply to the child.5.
EffectiveSettingsMojo.copySettings()does not deep-copy profilesFile:
src/main/java/org/apache/maven/plugins/help/EffectiveSettingsMojo.java:156-198Only
serversandproxiesare manually deep-copied. Theprofileslist is shared with the original via the shallowSettingsUtils.copySettings(). Profiles can contain sensitive data (passwords in properties) that would be exposed if the original settings object is later queried expecting the copy to be isolated.6.
ListDependencyTypesMojo,ListLifecyclePhasesMojo,ListPackagingMojowrite to console AND file simultaneouslyFiles:
ListDependencyTypesMojo.java:95-96ListLifecyclePhasesMojo.java:76-77ListPackagingMojo.java:110-111These three mojos always log to console AND unconditionally call
writeFile(output, ...)(no-op when null). Every other mojo usesif (output != null) { ... } else { getLog().info(...); }. The dual-write means content is always printed to console even when writing to file. They also do not prepend the "Generated by Maven Help Plugin" header thatActiveProfilesMojoandSystemMojoadd.LOW Severity
7.
DescribeMojo.toLines()delegates to generatedHelpMojovia fragile reflectionFile:
src/main/java/org/apache/maven/plugins/help/DescribeMojo.java:747-778HelpMojois generated bymaven-plugin-toolsat compile time (intarget/generated-sources/). It IS available at runtime, but this reflection is brittle: any change in the generated code'stoLinessignature silently breaks all describe output formatting. TheHelpMojo.toLinesinternally callsrepeat()withnew StringBuilder(repeat * str.length())— a classic integer overflow risk that can throwNegativeArraySizeException.8.
AbstractHelpMojo.LSusesSystem.getProperty("line.separator")File:
src/main/java/org/apache/maven/plugins/help/AbstractHelpMojo.java:55Should use
System.lineSeparator()(Java 7+), which falls back to"\n"if the property is missing.DescribeMojoTestinconsistently uses bothSystem.getProperty("line.separator")(line 88) andSystem.lineSeparator()(line 110).9.
SortedPropertiesuses raw typesFile:
src/main/java/org/apache/maven/plugins/help/AbstractEffectiveMojo.java:147-148The
@SuppressWarnings({"rawtypes", "unchecked"})annotation masks the issue rather than usingList<Object>.10.
AbstractHelpMojo.getAetherArtifact()uses deprecatedArtifact.LATEST_VERSIONFile:
src/main/java/org/apache/maven/plugins/help/AbstractHelpMojo.java:147Deprecated in favor of
null(which means "latest") in the Aether API.11.
EffectivePomMojoencoding fallback can return nullFile:
src/main/java/org/apache/maven/plugins/help/EffectivePomMojo.java:110System.getProperty("file.encoding")can theoretically return null.12.
AllProfilesMojo— settings profiles never tracked for active statusFile:
src/main/java/org/apache/maven/plugins/help/AllProfilesMojo.java:167-173addSettingsProfiles()only populatesallProfilesByIds, neveractiveProfilesByIds. Active detection relies onproject.getActiveProfiles()which may or may not include converted settings profiles depending on Maven version and activation mechanisms. This can under-report active settings profiles.