Skip to content

Conversation

@eranturgeman
Copy link
Contributor

@eranturgeman eranturgeman commented Jan 7, 2026

  • 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

Missing testcase:
We need to add a new integration test test case to TestScanRepositoryCmd_Run that verifies we do not regenerate a lock file if it doesnt exists in remote. since the TestScanRepositoryCmd_Run is not passing right now and we need to fix it I added it to the overall test plan for future addition

@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
@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

@eranturgeman
Copy link
Contributor Author

eranturgeman commented Jan 19, 2026

please provide a link to a fix pr

https://github.com/eranturgeman/npm-small/pull/14

Screenshot 2026-01-19 at 13 13 27

@eranturgeman eranturgeman added the safe to test Approve running integration tests on a pull request label Jan 19, 2026
@github-actions github-actions bot removed the safe to test Approve running integration tests on a pull request label Jan 19, 2026
@eranturgeman eranturgeman added the safe to test Approve running integration tests on a pull request label Jan 25, 2026
@github-actions github-actions bot removed the safe to test Approve running integration tests on a pull request label Jan 25, 2026
@eranturgeman eranturgeman added the safe to test Approve running integration tests on a pull request label Jan 25, 2026
@github-actions github-actions bot removed the safe to test Approve running integration tests on a pull request label Jan 25, 2026
@github-actions
Copy link
Contributor

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


Comment on lines +130 to +181
func (npm *NpmPackageUpdater) fixVulnerabilityAndRegenerateLockIfNeeded(vulnDetails *utils.VulnerabilityDetails, descriptorPath string, originalWd string, vulnRegexp *regexp.Regexp) (err error) {
descriptorContent, err := os.ReadFile(descriptorPath)
if err != nil {
return fmt.Errorf("failed to read file '%s': %w", descriptorPath, err)
}

if !vulnRegexp.MatchString(strings.ToLower(string(descriptorContent))) {
return fmt.Errorf("dependency '%s' not found in descriptor '%s' despite lock file evidence", vulnDetails.ImpactedDependencyName, descriptorPath)
}

backupContent := make([]byte, len(descriptorContent))
copy(backupContent, descriptorContent)
updatedContent, err := npm.updateVersionInDescriptor(descriptorContent, vulnDetails.ImpactedDependencyName, vulnDetails.SuggestedFixedVersion, descriptorPath)
if err != nil {
return fmt.Errorf("failed to update version in descriptor: %w", err)
}

if err = os.WriteFile(descriptorPath, updatedContent, 0644); err != nil {
return fmt.Errorf("failed to write updated descriptor '%s': %w", descriptorPath, err)
}

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))
}
}()

lockFileTracked, checkErr := utils.IsFileTrackedByGit(npmLockFileName, originalWd)
if checkErr != nil {
log.Debug(fmt.Sprintf("Failed to check if lock file is tracked in git: %s. Proceeding with lock file regeneration.", checkErr.Error()))
lockFileTracked = true
}

if !lockFileTracked {
log.Debug(fmt.Sprintf("Lock file '%s' does not exist in remote, skipping lock file regeneration", npmLockFileName))
log.Debug(fmt.Sprintf("Successfully updated '%s' from version '%s' to '%s' in descriptor '%s' without regenerating lock file", vulnDetails.ImpactedDependencyName, vulnDetails.ImpactedDependencyVersion, vulnDetails.SuggestedFixedVersion, descriptorPath))
return
}

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' in descriptor '%s'", vulnDetails.ImpactedDependencyName, vulnDetails.ImpactedDependencyVersion, vulnDetails.SuggestedFixedVersion, descriptorPath))
return nil
Copy link
Collaborator

@eyalk007 eyalk007 Jan 26, 2026

Choose a reason for hiding this comment

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

as i said before this fucntion does multiple things at once, and is 60 lines long and that is making it hard to read, also this is seperation of concerns
separate them

something like this

func (npm *NpmPackageUpdater) updateDirectDependency(vulnDetails *utils.VulnerabilityDetails) error {
    descriptorPaths, err := npm.getDescriptorsToFixFromVulnerability(vulnDetails)
    if err != nil {
        return err
    }

    initialWd, err := os.Getwd()
    if err != nil {
        return fmt.Errorf("failed to get current working directory: %w", err)
    }

    vulnRegexp := BuildPackageRegex(vulnDetails.ImpactedDependencyName, npmDependencyRegexpPattern)

    for _, descriptorPath := range descriptorPaths {
        // 1. Fix the descriptor
        backup, err := npm.fixDescriptor(vulnDetails, descriptorPath, vulnRegexp)
        if err != nil {
            log.Warn(...)
            continue
        }

        // 2. Check if lockfile is tracked
        descriptorDir := filepath.Dir(descriptorPath)
        if isTracked, _ := npm.isLockfileTracked(descriptorDir, initialWd); !isTracked {
            log.Debug("Lockfile not tracked, skipping regeneration")
            continue
        }

        // 3. Regenerate lockfile (with rollback on failure)
        if err := npm.regenerateLockFile(descriptorPath, descriptorDir, backup); err != nil {
            log.Warn(...)
            continue
        }
    }

    return nil
}

this is just pseudo code but there should be a concept of separation not just one big function

return nil
}

func (npm *NpmPackageUpdater) updateVersionInDescriptor(content []byte, packageName, newVersion, descriptorPath string) ([]byte, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider replacing manual JSON parsing with tidwall/sjson library:
will remove all the edge cases handling and around 70 lines from file


escapedName := regexp.QuoteMeta(packageName)
replacePattern := fmt.Sprintf(npmDependencyReplacePattern, escapedName)
replaceRegex := regexp.MustCompile("(?i)" + replacePattern)
Copy link
Collaborator

Choose a reason for hiding this comment

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

npm is case sensitive not sure this is correct

}

// Creates an environment slice with npm isolation variables that override user's .npmrc settings for specific options while allowing registry configuration to pass through
func (npm *NpmPackageUpdater) buildIsolatedEnv() []string {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Image

please check this

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.

looks a lot better please look at my comments

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