Skip to content

Commit

Permalink
npm-aliases: handle aliases for package-lock.json
Browse files Browse the repository at this point in the history
Signed-off-by: mikcl <mikesmikes400@gmail.com>
  • Loading branch information
Mikcl committed Nov 18, 2022
1 parent da4b2df commit 0bd18b4
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 4 deletions.
6 changes: 3 additions & 3 deletions syft/pkg/cataloger/javascript/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ func newPackageJSONPackage(u packageJSON, locations ...source.Location) pkg.Pack
return p
}

func newPackageLockV1Package(resolver source.FileResolver, location source.Location, name string, u lockDependency) pkg.Package {
func newPackageLockV1Package(resolver source.FileResolver, location source.Location, name, version string, u lockDependency) pkg.Package {
return finalizeLockPkg(
resolver,
location,
pkg.Package{
Name: name,
Version: u.Version,
Version: version,
Locations: source.NewLocationSet(location),
PURL: packageURL(name, u.Version),
PURL: packageURL(name, version),
Language: pkg.JavaScript,
Type: pkg.NpmPkg,
},
Expand Down
28 changes: 27 additions & 1 deletion syft/pkg/cataloger/javascript/parse_package_lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,25 @@ type lockPackage struct {
License string `json:"license"`
}

// Handles type aliases https://github.com/npm/rfcs/blob/main/implemented/0001-package-aliases.md
// for npm package-lock.json version 1
func resolvePackageLockNameAliases(name, version string) (string, string) {
aliasSeparator := "npm:"

if !strings.HasPrefix(version, aliasSeparator) {
// not an alias
return name, version
}

canonicalPackageAndVersion := version[len(aliasSeparator):]
versionSeparator := strings.LastIndex(canonicalPackageAndVersion, "@")

canonicalName := canonicalPackageAndVersion[:versionSeparator]
canonicalVersion := canonicalPackageAndVersion[versionSeparator+1:]

return canonicalName, canonicalVersion
}

// parsePackageLock parses a package-lock.json and returns the discovered JavaScript packages.
func parsePackageLock(resolver source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
// in the case we find package-lock.json files in the node_modules directories, skip those
Expand All @@ -61,7 +80,9 @@ func parsePackageLock(resolver source.FileResolver, _ *generic.Environment, read

if lock.LockfileVersion == 1 {
for name, pkgMeta := range lock.Dependencies {
pkgs = append(pkgs, newPackageLockV1Package(resolver, reader.Location, name, pkgMeta))
canonicalName, canonicalVersion := resolvePackageLockNameAliases(name, pkgMeta.Version)

pkgs = append(pkgs, newPackageLockV1Package(resolver, reader.Location, canonicalName, canonicalVersion, pkgMeta))
}
}

Expand All @@ -75,6 +96,11 @@ func parsePackageLock(resolver source.FileResolver, _ *generic.Environment, read
}
}

// handles alias names
if pkgMeta.Name != "" {
name = pkgMeta.Name
}

pkgs = append(pkgs, newPackageLockV2Package(resolver, reader.Location, getNameFromPath(name), pkgMeta))
}
}
Expand Down
88 changes: 88 additions & 0 deletions syft/pkg/cataloger/javascript/parse_package_lock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,61 @@ package javascript
import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
"github.com/anchore/syft/syft/source"
)

func Test_resolveNameAliases(t *testing.T) {

tests := []struct {
testName string
name string
version string
expectedName string
expectedVersion string
}{
{
testName: "alias",
name: "case-alias",
version: "npm:case@1.6.3",
expectedName: "case",
expectedVersion: "1.6.3",
},
{
testName: "alias-with-@",
name: "case-alias",
version: "npm:@case@1.6.3",
expectedName: "@case",
expectedVersion: "1.6.3",
},
{
testName: "simple-name-version",
name: "case",
version: "1.6.3",
expectedName: "case",
expectedVersion: "1.6.3",
},
{
testName: "simple-@name-version",
name: "@case",
version: "1.6.3",
expectedName: "@case",
expectedVersion: "1.6.3",
},
}
for _, tt := range tests {
t.Run(tt.testName, func(t *testing.T) {
resolvedName, resolvedVersion := resolvePackageLockNameAliases(tt.name, tt.version)
assert.Equal(t, tt.expectedName, resolvedName)
assert.Equal(t, tt.expectedVersion, resolvedVersion)
})
}
}

func TestParsePackageLock(t *testing.T) {
var expectedRelationships []artifact.Relationship
expectedPkgs := []pkg.Package{
Expand Down Expand Up @@ -193,3 +242,42 @@ func TestParsePackageLockV3(t *testing.T) {
}
pkgtest.TestFileParser(t, fixture, parsePackageLock, expectedPkgs, expectedRelationships)
}

func TestParsePackageLockAlias(t *testing.T) {
var expectedRelationships []artifact.Relationship
expectedPkgs := []pkg.Package{
{
Name: "case",
Version: "1.6.2",
PURL: "pkg:npm/case@1.6.2",
Language: pkg.JavaScript,
Type: pkg.NpmPkg,
},
{
Name: "case",
Version: "1.6.3",
PURL: "pkg:npm/case@1.6.3",
Language: pkg.JavaScript,
Type: pkg.NpmPkg,
},
{
Name: "@bundled-es-modules/chai",
Version: "4.2.2",
PURL: "pkg:npm/%40bundled-es-modules/chai@1.6.3",
Language: pkg.JavaScript,
Type: pkg.NpmPkg,
},
}
packageLockV1 := "test-fixtures/pkg-lock/alias-package-lock-1.json"
packageLockV2 := "test-fixtures/pkg-lock/alias-package-lock-2.json"
packageLocks := []string{packageLockV1, packageLockV2}

for _, packageLock := range packageLocks {
expected := make([]pkg.Package, len(expectedPkgs))
copy(expectedPkgs, expected)
for i := range expected {
expected[i].Locations.Add(source.NewLocation(packageLock))
}
pkgtest.TestFileParser(t, packageLock, parsePackageLock, expected, expectedRelationships)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "alias-check",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"case": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/case/-/case-1.6.2.tgz",
"integrity": "sha512-ll380ZRoraT7mUK2G92UbH+FJVD5AwdVIAYk9xhV1tauh0carDgYByUD1HhjCWsWgxrfQvCeHvtfj7IYR6TKeg=="
},
"case-alias": {
"version": "npm:case@1.6.3",
"resolved": "https://registry.npmjs.org/case/-/case-1.6.3.tgz",
"integrity": "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ=="
},
"chai": {
"version": "npm:@bundled-es-modules/chai@4.2.2",
"resolved": "https://registry.npmjs.org/@bundled-es-modules/chai/-/chai-4.2.2.tgz",
"integrity": "sha512-iGmVYw2/zJCoqyKTtWEYCtFmMyi8WmACQKtky0lpNyEKWX0YIOpKWGD7saMXL+tPpllss0otilxV0SLwyi3Ytg=="
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"name": "alias-check",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "alias-check",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"case": "1.6.2",
"case-alias": "npm:case@^1.6.3",
"chai": "npm:@bundled-es-modules/chai@^4.2.2"
}
},
"node_modules/case": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/case/-/case-1.6.2.tgz",
"integrity": "sha512-ll380ZRoraT7mUK2G92UbH+FJVD5AwdVIAYk9xhV1tauh0carDgYByUD1HhjCWsWgxrfQvCeHvtfj7IYR6TKeg==",
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/case-alias": {
"name": "case",
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/case/-/case-1.6.3.tgz",
"integrity": "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==",
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/chai": {
"name": "@bundled-es-modules/chai",
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@bundled-es-modules/chai/-/chai-4.2.2.tgz",
"integrity": "sha512-iGmVYw2/zJCoqyKTtWEYCtFmMyi8WmACQKtky0lpNyEKWX0YIOpKWGD7saMXL+tPpllss0otilxV0SLwyi3Ytg=="
}
},
"dependencies": {
"case": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/case/-/case-1.6.2.tgz",
"integrity": "sha512-ll380ZRoraT7mUK2G92UbH+FJVD5AwdVIAYk9xhV1tauh0carDgYByUD1HhjCWsWgxrfQvCeHvtfj7IYR6TKeg=="
},
"case-alias": {
"version": "npm:case@1.6.3",
"resolved": "https://registry.npmjs.org/case/-/case-1.6.3.tgz",
"integrity": "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ=="
},
"chai": {
"version": "npm:@bundled-es-modules/chai@4.2.2",
"resolved": "https://registry.npmjs.org/@bundled-es-modules/chai/-/chai-4.2.2.tgz",
"integrity": "sha512-iGmVYw2/zJCoqyKTtWEYCtFmMyi8WmACQKtky0lpNyEKWX0YIOpKWGD7saMXL+tPpllss0otilxV0SLwyi3Ytg=="
}
}
}

0 comments on commit 0bd18b4

Please sign in to comment.