Skip to content

Conversation

@eyalk007
Copy link
Collaborator

@eyalk007 eyalk007 commented Dec 28, 2025

  • All tests passed. If this feature is not already covered by the tests, I added new tests.
  • This pull request is on the dev branch.
  • I used gofmt for formatting the code before submitting the pull request.
  • Update documentation about new features / new supported technologies

- Update MavenPackageUpdater to handle multiple Components
- Loop through all Components and update each pom.xml file
- Extracts file paths from Component.Location.File
- Handles same vulnerability in multiple modules (e.g., backend + frontend)
- All changes go into ONE PR (same vuln, same fix)
- Update all tests to populate Components array with Location
- Backward compatible: falls back to 'pom.xml' if no Components
- Remove backward compatibility fallback that masked bugs
- If Components array is empty or missing Location data, fail explicitly
- Better error message explains the issue is with scan results
- Forces engine to always populate Components with Location
- Prevents silent failures in multi-module projects
- Add constants for magic strings (separator, property prefix/suffix)
- Extract parseMavenCoordinate() and toMavenCoordinate() helpers
- Extract extractPropertyName() for property placeholder parsing
- Remove all comments (code is self-documenting)
- Simplify debug logs (no fmt.Sprintf)
- DRY: no repetition anywhere
- All tests still pass
- Better naming: matches ImpactedDependencyName field
- parseDependencyName() / toDependencyName()
- More accurate: we parse dependency names (groupId:artifactId), not coordinates (which include version)
- Replace encoding/xml with github.com/beevik/etree
- Use DOM-based XML manipulation instead of unmarshal/marshal
- Should preserve formatting, namespaces, and all XML attributes
- All tests still pass
- Need to test on real repo to verify formatting preservation
- Parse XML to understand structure (encoding/xml)
- Use regex to replace ONLY the version text
- Preserves ALL formatting, namespaces, blank lines, comments
- Minimal diffs - only version numbers change
- All tests pass
- Ready for real-world testing
- Document all 5 test cases (simple, property, parent, depMgmt, multi-module)
- Document engine limitations (parent POM resolution, non-standard pom names)
- Document text-based replacement approach
- Document multi-module support
- Recommendations for engine team
- Testing checklist
@eyalk007 eyalk007 self-assigned this Dec 28, 2025
@eyalk007 eyalk007 added the improvement Automatically generated release notes label Dec 28, 2025
Comment on lines +83 to +91
func (mpu *MavenPackageUpdater) getPomPaths(vulnDetails *utils.VulnerabilityDetails) []string {
var pomPaths []string
for _, component := range vulnDetails.Components {
if component.Location != nil && component.Location.File != "" {
pomPaths = append(pomPaths, component.Location.File)
}
}
return pomPaths
}
Copy link
Collaborator Author

@eyalk007 eyalk007 Dec 28, 2025

Choose a reason for hiding this comment

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

this gets all occurrences of a vulnerability (the exact package and version)
can be moved to a utils because multiple package updaters will use this logic

Copy link
Contributor

@eranturgeman eranturgeman Jan 6, 2026

Choose a reason for hiding this comment

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

I will move it an insert it to a dictionary first so we filter any duplicates if any exist.
please use GetVulnerabilityLocations once npm new updater is merged

)

const (
mavenCoordinateSeparator = ":"
Copy link
Collaborator Author

@eyalk007 eyalk007 Dec 28, 2025

Choose a reason for hiding this comment

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

can rename to dependency instead of coordinate

updated := false
newContent := content

if updated, newContent = mpu.updateInParent(&project, groupId, artifactId, fixedVersion, newContent); updated {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

currently not working because of the engine

- When a config profile is fetched from XSC, explicitly set CreateAutoFixPr = false
- This ensures autofix is disabled for repositories using config profiles
- Autofix behavior can still be controlled via environment variables when not using config profiles
- Restore the check that respects CreateAutoFixPr configuration
- When CreateAutoFixPr is false, the scan runs in detection mode only
- No fix PRs are created when this flag is disabled
- This change should only be in v3_er branch, not in refactor-maven-xml-updater
- The autofix behavior is controlled elsewhere
Comment on lines +107 to +132
if updated, newContent = mpu.updateInParent(&project, groupId, artifactId, fixedVersion, newContent); updated {
if err := os.WriteFile(pomPath, newContent, 0644); err != nil {
return fmt.Errorf("failed to write %s: %w", pomPath, err)
}
log.Debug("Successfully updated", pomPath)
return nil
}

if updated, newContent = mpu.updateInDependencies(&project, project.Dependencies, groupId, artifactId, fixedVersion, newContent); updated {
if err := os.WriteFile(pomPath, newContent, 0644); err != nil {
return fmt.Errorf("failed to write %s: %w", pomPath, err)
}
log.Debug("Successfully updated", pomPath)
return nil
}

if project.DependencyManagement != nil {
if updated, newContent = mpu.updateInDependencies(&project, project.DependencyManagement.Dependencies, groupId, artifactId, fixedVersion, newContent); updated {
if err := os.WriteFile(pomPath, newContent, 0644); err != nil {
return fmt.Errorf("failed to write %s: %w", pomPath, err)
}
log.Debug("Successfully updated", pomPath)
return nil
}
}

Copy link
Collaborator Author

@eyalk007 eyalk007 Jan 6, 2026

Choose a reason for hiding this comment

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

maybe need to rethink the pecking order

in maven if you were bold enough to have the same dep, in multiple contexts
in practice the closest one in hierarchy wins version wise

but here im not checking according to hierarchy and fixing only one instance (i am searching for same package and same version though)

so maybe do it -
update in deps -> update dep management -> update parent

and not exit early
in practice updating all is not needed for the vuln fix, but its the safest approach

}

if project.Parent.GroupId == groupId && project.Parent.ArtifactId == artifactId {
pattern := regexp.MustCompile(`(?s)(<parent>\s*<groupId>` + regexp.QuoteMeta(groupId) + `</groupId>\s*<artifactId>` + regexp.QuoteMeta(artifactId) + `</artifactId>\s*<version>)[^<]+(</version>)`)
Copy link
Contributor

Choose a reason for hiding this comment

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

please see if you can use GetVulnerabilityRegexCompiler that is found in commonpackagehandler.go
I fixed it to fix all escaping chars, and we can reuse this code.
Also please define the patterns as consts
Screenshot 2026-01-06 at 11 42 37

return mpu.updateProperty(project, propertyName, fixedVersion, content)
}

pattern := regexp.MustCompile(`(?s)(<groupId>` + regexp.QuoteMeta(groupId) + `</groupId>\s*<artifactId>` + regexp.QuoteMeta(artifactId) + `</artifactId>\s*<version>)[^<]+(</version>)`)
Copy link
Contributor

Choose a reason for hiding this comment

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

please see if you can use GetVulnerabilityRegexCompiler that is found in commonpackagehandler.go
I fixed it to fix all escaping chars, and we can reuse this code.
Also please define the patterns as consts


for _, prop := range project.Properties.Props {
if prop.XMLName.Local == propertyName {
pattern := regexp.MustCompile(`(<` + regexp.QuoteMeta(propertyName) + `>)[^<]+(</` + regexp.QuoteMeta(propertyName) + `>)`)
Copy link
Contributor

Choose a reason for hiding this comment

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

please see if you can use GetVulnerabilityRegexCompiler that is found in commonpackagehandler.go
I fixed it to fix all escaping chars, and we can reuse this code.
Also please define the patterns as consts

<scope> compile </scope>
</dependency>`,
}
func TestMavenUpdateRegularDependency(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

please move all teste related to maven that are not for functions in commonpackagehandler_test.go to a new file: mavenpackagehandler_test.go

Copy link
Contributor

Choose a reason for hiding this comment

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

+1

"Note: Frogbot currently cannot address certain vulnerabilities in some package managers, which may result in the absence of changes", err.PackageName)
}

// VulnerabilityDetails serves as a container for essential information regarding a vulnerability that is going to be addressed and resolved
Copy link
Contributor

Choose a reason for hiding this comment

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

this comment also can be removed

Dependencies []mavenDep `xml:"dependencies>dependency"`
}

func (mpu *MavenPackageUpdater) UpdateDependency(vulnDetails *utils.VulnerabilityDetails) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

to my opinion mpu is not clear, and it should be changed to "m" or "maven" , but just a suggestion

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

was just following hte foramt you set
can keep it you prefer mavenPackageUpdater
or mavenpu?

return nil
}

func (mpu *MavenPackageUpdater) getPomPaths(vulnDetails *utils.VulnerabilityDetails) []string {
Copy link
Contributor

@orto17 orto17 Jan 12, 2026

Choose a reason for hiding this comment

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

even tough we are trying to avoid comments i think for this function we need to add an explanation of what it returns or change the signature to something like:

getPomPaths(vulnDetails *utils.VulnerabilityDetails) (occurrences []string) 
getVulnerabilityOccurences(vulnDetails *utils.VulnerabilityDetails) []string 

because it was unclear to me until i saw your comments

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

i mean we can just change hte name to get descriptor paths

get vulnerability occurrences is also an option

dont think a comment is needed just need to find the right function name

}

if len(errors) > 0 {
return fmt.Errorf("failed to update pom.xml files:\n%s", strings.Join(errors, "\n"))
Copy link
Contributor

Choose a reason for hiding this comment

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

regarding errors & fix flow in general, i think this entire flow should not raise an error. meaning if the fix is failed frogbot will add a warn log and finishes execution without an error. i need to take it with Barb, but WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

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

regardless of the decision i think we need to add Warn logs in case of errors during the fix, it will be easier to read the logs this way in case we are looping and have several fixes at once.

func (mpu *MavenPackageUpdater) updatePomFile(pomPath, groupId, artifactId, fixedVersion string) error {
content, err := os.ReadFile(pomPath)
if err != nil {
return fmt.Errorf("failed to read %s: %w", pomPath, err)
Copy link
Contributor

@orto17 orto17 Jan 12, 2026

Choose a reason for hiding this comment

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

logs will be easier to understand if we'll add the step we are currently at: fmt.Errorf("failed to update Pom file: failed to read %s: %w", pomPath, err)

}

var project mavenProject
if err := xml.Unmarshal(content, &project); err != nil {
Copy link
Contributor

@orto17 orto17 Jan 12, 2026

Choose a reason for hiding this comment

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

you can make it if err = xml.Unmarshal(content, &project); since err var exist (and same goes to the other err vars in this function)

if err := os.WriteFile(pomPath, newContent, 0644); err != nil {
return fmt.Errorf("failed to write %s: %w", pomPath, err)
}
log.Debug("Successfully updated", pomPath)
Copy link
Contributor

Choose a reason for hiding this comment

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

log.Debug("Successfully updated Pom Parent", pomPath)
log.Debug("Successfully updated Dependency Management", pomPath)

return parts[0], parts[1], nil
}

func toDependencyName(groupId, artifactId string) string {
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe this func can also move to utils file and get coordinateSeparator as an argument?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

why? its specific to maven (maybe gradle will use it but this format is maven only for now

return fmt.Errorf("failed to write %s: %w", pomPath, err)
}
log.Debug("Successfully updated", pomPath)
return nil
Copy link
Contributor

Choose a reason for hiding this comment

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

there is no option for "two" changes needed? for example both in Dependencies and in DependencyManagement?

return mpu.updateProperty(project, propertyName, fixedVersion, content)
}

pattern := regexp.MustCompile(`(?s)(<groupId>` + regexp.QuoteMeta(groupId) + `</groupId>\s*<artifactId>` + regexp.QuoteMeta(artifactId) + `</artifactId>\s*<version>)[^<]+(</version>)`)
Copy link
Contributor

Choose a reason for hiding this comment

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

what do you think about having this regexes as consts? they are repeating for parent fix and for dependencies fix , plus it will make it more readable

`</artifactId>\s*<version>)[^<]+(</version>)`
`</groupId>\s*<artifactId>`


for _, prop := range project.Properties.Props {
if prop.XMLName.Local == propertyName {
pattern := regexp.MustCompile(`(<` + regexp.QuoteMeta(propertyName) + `>)[^<]+(</` + regexp.QuoteMeta(propertyName) + `>)`)
Copy link
Contributor

Choose a reason for hiding this comment

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

i think this one must be a const even tough it appears only once: >)[^<]+(</


if project.DependencyManagement != nil {
if updated, newContent = mpu.updateInDependencies(&project, project.DependencyManagement.Dependencies, groupId, artifactId, fixedVersion, newContent); updated {
if err := os.WriteFile(pomPath, newContent, 0644); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

no sure it will make sense but maybe those line can be extracted to utils since they are duplicated and implement a file update utility:

			if err := os.WriteFile(pomPath, newContent, 0644); err != nil {
				return fmt.Errorf("failed to write %s: %w", pomPath, err)
			}
			log.Debug("Successfully updated", pomPath)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

im against making thing generic until we see them in mutlipe uses
the next pcakge manager that does it need to move it to utils

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Automatically generated release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants