Skip to content
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

feat(npm): add v3 lock file support #191

Merged
merged 6 commits into from
Mar 15, 2023

Conversation

DmitriyLewen
Copy link
Collaborator

@DmitriyLewen DmitriyLewen self-assigned this Mar 13, 2023
@DmitriyLewen DmitriyLewen marked this pull request as ready for review March 13, 2023 09:00

directDeps := map[string]string{}
for name, version := range packages[""].Dependencies {
pkgPath := filepath.Join(nodeModulesFolder, name)
Copy link
Collaborator

@knqyf263 knqyf263 Mar 14, 2023

Choose a reason for hiding this comment

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

I think we should use path.Join here because the joined path will be node_modules\\name with filepath.Join on Windows. I guess the path separator in package-lock.json is always / regardless of the platform. Otherwise, generating a lockfile results in different files, depending on the platform. But this is my guess. Please correct me if I'm wrong.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for tip, fixed.

// lock file contains path to dependency in `node_modules` folder. e.g.:
// node_modules/string-width
// node_modules/string-width/node_modules/strip-ansi
return filepath.Base(path)
Copy link
Collaborator

Choose a reason for hiding this comment

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

ditto

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines 243 to 251
// project can contain 2 different version of dependency
// we need to check that we choose version for direct dependency
// also the direct dependency version can use range
// e.g. "body-parser": "^1.18.3"
// we need to compare versions using npm comparator
match, err := matchVersion(directVersion, libVersionRange)
if err != nil {
log.Logger.Warnf("unable to compare version for %s@%s", libName, libVersionRange)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this really needed? I suppose direct dependencies are always located just under node_modules.

For example, there are two versions of strip-ansi, and it is marked as a direct dependency.

    "": {
      "name": "node_v1",
      "version": "1.0.0",
      "license": "ISC",
      "dependencies": {
        "strip-ansi": "^1.18.3"
      }
    },
  • node_modules/strip-ansi
  • node_modules/string-width/node_modules/strip-ansi

In this case, is it possible that node_modules/string-width/node_modules/strip-ansi is a direct dependency and node_modules/string-ansi is indirect as below?

  • node_modules/strip-ansi (indirect)
  • node_modules/string-width/node_modules/strip-ansi (direct)

I thought the direct dependency was always under node_modules.

  • node_modules/strip-ansi (direct)
  • node_modules/string-width/node_modules/strip-ansi (indirect)

If it is the case, we can just compare the package paths rather than the version constraint. If strip-ansi is written under dependencies, it means node_modules/strip-ansi is direct. I might be wrong.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

direct dependencies are always located just under node_modules.

You are right! i didn't think about this.
Changed this logic.

Comment on lines 123 to 145
// Try to resolve the version with nested dependencies first
depPath := filepath.Join(pkgPath, nodeModulesFolder, depName)
if dep, ok := packages[depPath]; ok {
depID := utils.PackageID(depName, dep.Version)
dependsOn = append(dependsOn, depID)
continue
}

// Try to resolve the version with dependencies same folder
depPath = filepath.Join(filepath.Dir(pkgPath), depName)
if dep, ok := packages[depPath]; ok {
depID := utils.PackageID(depName, dep.Version)
dependsOn = append(dependsOn, depID)
continue
}

// Try to resolve the version with the higher level dependencies
depPath = filepath.Join(nodeModulesFolder, depName)
if dep, ok := packages[depPath]; ok {
depID := utils.PackageID(depName, dep.Version)
dependsOn = append(dependsOn, depID)
continue
}
Copy link
Collaborator

@knqyf263 knqyf263 Mar 14, 2023

Choose a reason for hiding this comment

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

Isn't it possible to have more than two nests like node_modules/body-parser/node_modules/debug/node_modules/ms? Looks like it happens theoretically.

What about this code?

Suggested change
// Try to resolve the version with nested dependencies first
depPath := filepath.Join(pkgPath, nodeModulesFolder, depName)
if dep, ok := packages[depPath]; ok {
depID := utils.PackageID(depName, dep.Version)
dependsOn = append(dependsOn, depID)
continue
}
// Try to resolve the version with dependencies same folder
depPath = filepath.Join(filepath.Dir(pkgPath), depName)
if dep, ok := packages[depPath]; ok {
depID := utils.PackageID(depName, dep.Version)
dependsOn = append(dependsOn, depID)
continue
}
// Try to resolve the version with the higher level dependencies
depPath = filepath.Join(nodeModulesFolder, depName)
if dep, ok := packages[depPath]; ok {
depID := utils.PackageID(depName, dep.Version)
dependsOn = append(dependsOn, depID)
continue
}
depPath := path.Join(pkgPath, nodeModulesFolder)
paths := strings.Split(depPath, "/")
// Try to resolve the version with the nearest directory
// In the case of "ms", try the following paths in order.
// - "node_modules/body-parser/node_modules/debug/node_moduls/ms"
// - "node_modules/body-parser/node_modules/ms"
// - "node_modules/ms"
for i := len(paths) - 1; i >= 0; i-- {
if paths[i] != nodeModulesFolder {
continue
}
p := path.Join(paths[:i+1]...)
p = path.Join(p, depName)
if dep, ok := packages[p]; ok {
depID := utils.PackageID(depName, dep.Version)
dependsOn = append(dependsOn, depID)
continue
}
}

Here is my test: https://go.dev/play/p/UPQN11T8qJ_m

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good idea! thanks, used this.

@knqyf263 knqyf263 merged commit 2c62bb5 into aquasecurity:main Mar 15, 2023
Sq34sy pushed a commit to Sq34sy/go-dep-parser that referenced this pull request Jul 28, 2023
Co-authored-by: knqyf263 <knqyf263@gmail.com>
Sq34sy pushed a commit to Sq34sy/go-dep-parser that referenced this pull request Jul 28, 2023
Co-authored-by: knqyf263 <knqyf263@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants