-
Notifications
You must be signed in to change notification settings - Fork 89
Refactor maven xml updater #1011
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v3_er
Are you sure you want to change the base?
Conversation
- 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
| 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 | ||
| } |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 = ":" |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
6c8a76c to
c2727a7
Compare
c2727a7 to
d10294c
Compare
- 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
b39f15b to
2ba32ea
Compare
fb4c056 to
0cb9521
Compare
- 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
0cb9521 to
cd2e6df
Compare
- This change should only be in v3_er branch, not in refactor-maven-xml-updater - The autofix behavior is controlled elsewhere
| 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 | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
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>)`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| 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>)`) |
There was a problem hiding this comment.
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) + `>)`) |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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")) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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>)`) |
There was a problem hiding this comment.
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) + `>)`) |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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

Uh oh!
There was an error while loading. Please reload this page.