Skip to content

Conversation

@eranturgeman
Copy link
Contributor

  • 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

This PR changes the NPM package handler to test-based fixes instead of cli command fixes.
As past of the change we ease the installation after a fix is performed to only regenerate the lock file, hence reducing the strict build process we used to have and make the process less error prone

@eranturgeman eranturgeman added safe to test Approve running integration tests on a pull request improvement Automatically generated release notes labels Jan 7, 2026
@github-actions github-actions bot removed the safe to test Approve running integration tests on a pull request label Jan 7, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Jan 7, 2026

👍 Frogbot scanned this pull request and did not find any new security issues.


@eyalk007
Copy link
Collaborator

eyalk007 commented Jan 7, 2026

please provide a link to a fix pr

Copy link
Collaborator

@eyalk007 eyalk007 left a comment

Choose a reason for hiding this comment

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

reviewed most of files
please tell me you are done so i can rereveiw

regexpCompleteFormat := fmt.Sprintf(strings.ToLower(dependencyLineFormat), regexpFitImpactedName, regexpFitImpactedVersion)
return regexp.MustCompile(regexpCompleteFormat)
}

// Extracts unique file paths from the vulnerability's component locations.
Copy link
Collaborator

Choose a reason for hiding this comment

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

remove comment

Comment on lines +35 to +38
// Matches: "package-name": "version" with optional ^ or ~ prefix
npmDependencyRegexpPattern = `\s*"%s"\s*:\s*"[~^]?%s"`
// Regex pattern for replacement - captures the groups for reconstruction
npmDependencyReplacePattern = `(\s*"%s"\s*:\s*")[~^]?[^"]+(")`
Copy link
Collaborator

Choose a reason for hiding this comment

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

regex patterns are self explanatory
no need for comments

Comment on lines +91 to +92
func (npm *NpmPackageHandler) getDescriptorsToFixFromVulnerability(vulnDetails *utils.VulnerabilityDetails) ([]string, error) {
lockFilePaths := GetVulnerabilityLocations(vulnDetails)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would rename it to getDescriptorsPaths
Also we said its a bug, so that's why we need to do this
so basically we need to delete this function once they fix the bug?
if so add a todo of deleting the function
also delete comment after rename as it will be self explanatory
So something like:

// TODO: This is a workaround. Engine provides lock file paths but we need descriptor paths.
// Delete this function once engine provides descriptor paths directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes this would be changed once the bug will be fixed. I coded it before we knew its a but and I maintained it since we dont know when the bug will be fixed. Ill add the comment

Comment on lines +133 to +153
// Change to the descriptor directory for the regeneration of the lock file
descriptorDir := filepath.Dir(descriptorPath)
if err = os.Chdir(descriptorDir); err != nil {
return fmt.Errorf("failed to change directory to '%s': %w", descriptorDir, err)
}
defer func() {
if chErr := os.Chdir(originalWd); chErr != nil {
err = errors.Join(err, fmt.Errorf("failed to return to original directory: %w", chErr))
}
}()

if err = npm.regenerateLockFileWithRetry(); err != nil {
log.Warn(fmt.Sprintf("Failed to regenerate lock file after updating '%s' to version '%s': %s. Rolling back...", vulnDetails.ImpactedDependencyName, vulnDetails.SuggestedFixedVersion, err.Error()))
if rollbackErr := os.WriteFile(descriptorPath, backupContent, 0644); rollbackErr != nil {
return fmt.Errorf("failed to rollback descriptor after lock file regeneration failure: %w (original error: %v)", rollbackErr, err)
}
return err
}

log.Debug(fmt.Sprintf("Successfully updated '%s' from version '%s' to '%s'", vulnDetails.ImpactedDependencyName, vulnDetails.ImpactedDependencyVersion, vulnDetails.SuggestedFixedVersion))
return nil
Copy link
Collaborator

Choose a reason for hiding this comment

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

separation of concerns
the goal of this func is to update the descriptor

this func is both updating the descriptor and tidying up the lockfile

I would separate to 2 function calls in the loop
and would check with product regarding backup

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not really. the function that is updating the descriptor is called updateVersionInDescriptor. this is why I called this function fixVulnerabilityInDescriptor as it fixes vulnerability and performing all the steps around it.
we can change it to 'fixVulnerability' if you still think it is not clear enough

Comment on lines +134 to +153
descriptorDir := filepath.Dir(descriptorPath)
if err = os.Chdir(descriptorDir); err != nil {
return fmt.Errorf("failed to change directory to '%s': %w", descriptorDir, err)
}
defer func() {
if chErr := os.Chdir(originalWd); chErr != nil {
err = errors.Join(err, fmt.Errorf("failed to return to original directory: %w", chErr))
}
}()

if err = npm.regenerateLockFileWithRetry(); err != nil {
log.Warn(fmt.Sprintf("Failed to regenerate lock file after updating '%s' to version '%s': %s. Rolling back...", vulnDetails.ImpactedDependencyName, vulnDetails.SuggestedFixedVersion, err.Error()))
if rollbackErr := os.WriteFile(descriptorPath, backupContent, 0644); rollbackErr != nil {
return fmt.Errorf("failed to rollback descriptor after lock file regeneration failure: %w (original error: %v)", rollbackErr, err)
}
return err
}

log.Debug(fmt.Sprintf("Successfully updated '%s' from version '%s' to '%s'", vulnDetails.ImpactedDependencyName, vulnDetails.ImpactedDependencyVersion, vulnDetails.SuggestedFixedVersion))
return nil
Copy link
Collaborator

Choose a reason for hiding this comment

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

what if user doesnt have lockfile in git, are we taking this into consideration?
@orto17

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree - we talked about it before and as far as I remember this is what we decided.
In V2 we have a logic of 'cleaning' whatever that is not in the remote (like node_modules for example).
we can apply same logic here

@@ -0,0 +1,297 @@
package packagehandlers
Copy link
Collaborator

@eyalk007 eyalk007 Jan 8, 2026

Choose a reason for hiding this comment

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

missing test cases:

  1. Version Range Mismatch - meaning lockfile resolves to diff version

  2. Multiple Occurrences - multiple occurrence of the same version once as lets say a dep and once as a dev dep

  3. Diff types of deps - we have peer deps, deps, dev deps, peerDeps, optionalDeps
    plus we have overrides

  4. Maybe a test for the rollback functionality if we keep it

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.

2 participants