Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion providers/os/connection/fs/filesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,18 @@ func NewFileSystemConnectionWithClose(id uint32, conf *inventory.Config, asset *

log.Debug().Str("path", path).Msg("load filesystem")

return NewFileSystemConnectionWithFs(id, conf, asset, path, closeFN, fs.NewMountedFs(path))
}

func NewFileSystemConnectionWithFs(id uint32, conf *inventory.Config, asset *inventory.Asset, path string, closeFN func(), fs afero.Fs) (*FileSystemConnection, error) {
return &FileSystemConnection{
Connection: plugin.NewConnection(id, asset),
Conf: conf,
asset: asset,
MountedDir: path,
closeFN: closeFN,
tcPlatformId: conf.PlatformId,
fs: fs.NewMountedFs(path),
fs: fs,
}, nil
}

Expand Down
46 changes: 25 additions & 21 deletions providers/os/resources/npm.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,23 @@ import (
"go.mondoo.com/cnquery/v11/types"
)

var (
defaultNpmPaths = []string{
// Linux
"/usr/local/lib",
"/opt/homebrew/lib",
"/usr/lib",
"/home/*/.npm-global/lib",
// Windows
"C:\\Users\\*\\AppData\\Roaming\\npm",
"C:\\Program Files\\nodejs\\node_modules\\npm",
"C:\\Users\\*\\node_modules",
// macOS
"/Users/*/.npm-global/lib",
// Container app paths
"/app",
"/home/node/app",
"/usr/src/app",
}
)
var defaultNpmPaths = []string{
// Linux
"/usr/local/lib",
"/opt/homebrew/lib",
"/usr/lib",
"/home/*/.npm-global/lib",
// Windows
"C:\\Users\\*\\AppData\\Roaming\\npm",
"C:\\Program Files\\nodejs\\node_modules\\npm",
"C:\\Users\\*\\node_modules",
// macOS
"/Users/*/.npm-global/lib",
// Container app paths
"/app",
"/home/node/app",
"/usr/src/app",
}

func initNpmPackages(_ *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) {
if x, ok := args["path"]; ok {
Expand Down Expand Up @@ -82,7 +80,7 @@ func collectNpmPackagesInPaths(runtime *plugin.Runtime, fs afero.Fs, paths []str
// we walk through the directories and check if there is a node_modules directory
log.Debug().Str("path", walkPath).Msg("found npm package")
nodeModulesPath := filepath.Join(walkPath, "node_modules")
var files, err = afs.ReadDir(nodeModulesPath)
files, err := afs.ReadDir(nodeModulesPath)
if err != nil {
// we ignore the error, it is expected that there is no node_modules directory
return nil
Expand Down Expand Up @@ -322,8 +320,14 @@ func newNpmPackage(runtime *plugin.Runtime, pkg *languages.Package) (*mqlNpmPack
mqlFiles = append(mqlFiles, lf)
}

path := ""
if len(mqlFiles) > 0 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can we make mqlFiles to be a []*mqlPkgFileInfo{} list? that way we avoid the casting. not sure if this causes a problem later in the code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No,
L335:

"files":   llx.ArrayData(mqlFiles, types.Resource("pkgFileInfo")),

This requires []interfac{}.

if fi, ok := mqlFiles[0].(*mqlPkgFileInfo); ok {
path = fi.Path.Data
}
}
mqlPkg, err := CreateResource(runtime, "npm.package", map[string]*llx.RawData{
"id": llx.StringData(pkg.Name),
"id": llx.StringData(pkg.Name + path),
"name": llx.StringData(pkg.Name),
"version": llx.StringData(pkg.Version),
"purl": llx.StringData(pkg.Purl),
Expand Down
98 changes: 98 additions & 0 deletions providers/os/resources/npm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (c) Mondoo, Inc.
// SPDX-License-Identifier: BUSL-1.1

package resources

import (
"os"
"path/filepath"
"testing"

"github.com/spf13/afero"
"github.com/stretchr/testify/require"
"go.mondoo.com/cnquery/v11/llx"
"go.mondoo.com/cnquery/v11/providers-sdk/v1/inventory"
"go.mondoo.com/cnquery/v11/providers-sdk/v1/plugin"
"go.mondoo.com/cnquery/v11/providers/os/connection/fs"
"go.mondoo.com/cnquery/v11/types"
"go.mondoo.com/cnquery/v11/utils/syncx"
)

func TestNpmPackage_unique(t *testing.T) {
mockFS := afero.NewMemMapFs()
// create test files and directories
err := mockFS.MkdirAll("/usr/local/lib/node_modules/generator-code", 0o755)
require.NoError(t, err)
err = mockFS.MkdirAll("/usr/local/lib/node_modules/yo", 0o755)
require.NoError(t, err)

// Read package.json files from testdata
yoPkg, err := os.ReadFile(filepath.Join("packages", "testdata", "yo_package.json"))
require.NoError(t, err)
require.NotNil(t, yoPkg)

gcPkg, err := os.ReadFile(filepath.Join("packages", "testdata", "gc_package.json"))
require.NoError(t, err)
require.NotNil(t, gcPkg)

err = afero.WriteFile(mockFS, "/usr/local/lib/node_modules/generator-code/package.json", gcPkg, 0o644)
require.NoError(t, err)
err = afero.WriteFile(mockFS, "/usr/local/lib/node_modules/yo/package.json", yoPkg, 0o644)
require.NoError(t, err)

conn, err := fs.NewFileSystemConnectionWithFs(0, &inventory.Config{}, &inventory.Asset{}, "", nil, mockFS)
require.NoError(t, err)

r := &plugin.Runtime{
Resources: &syncx.Map[plugin.Resource]{},
Connection: conn,
Callback: &providerCallbacks{},
}
mqlNpm := &mqlNpmPackages{
MqlRuntime: r,
}

// Create resources from filesystem
err = mqlNpm.gatherData()
require.NoError(t, err)

// Check that we have 4 packages
require.Equal(t, 4, len(mqlNpm.List.Data))

// Check that the first package is yosay
pkg2 := mqlNpm.List.Data[2].(*mqlNpmPackage)
require.Equal(t, "yosay", pkg2.Name.Data)
require.Equal(t, "^2.0.2", pkg2.Version.Data)

// Check that the third package is also yosay, but with a different version
pkg3 := mqlNpm.List.Data[3].(*mqlNpmPackage)
require.Equal(t, "yosay", pkg3.Name.Data)
require.Equal(t, "^3.0.0", pkg3.Version.Data)

// To get the correct data, we need distinct IDs
require.NotEqual(t, pkg2.MqlID(), pkg3.MqlID())
require.Equal(t, "yosay/usr/local/lib/node_modules/yo/package.json", pkg2.MqlID())
require.Equal(t, "yosay/usr/local/lib/node_modules/generator-code/package.json", pkg3.MqlID())
}

// Mock callbacks for testing
// These are needed during calls to CreateSharedResource
type providerCallbacks struct{}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can we extract this in a test util file so it can be used elsewhere too?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I gave it a try.
When we extract this to the testutils, it results in an import cycle.
When we move it into the mockprovider, the k8s tests fail.

Seem like they are to specific to the tests.


func (p *providerCallbacks) GetData(req *plugin.DataReq) (*plugin.DataRes, error) {
return &plugin.DataRes{
Data: &llx.Primitive{
Type: string(types.Resource(req.Resource)),
Value: []byte("not of interest"),
},
}, nil
}

func (p *providerCallbacks) GetRecording(req *plugin.DataReq) (*plugin.ResourceData, error) {
res := plugin.ResourceData{}
return &res, nil
}

func (p *providerCallbacks) Collect(req *plugin.DataRes) error {
return nil
}
40 changes: 40 additions & 0 deletions providers/os/resources/packages/testdata/gc_package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "generator-code",
"version": "1.11.9",
"description": "Yeoman generator for Visual Studio Code extensions.",
"keywords": [
"yeoman-generator",
"vscode",
"visual studio",
"visual studio code",
"vs code",
"extensions"
],
"type": "module",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/vscode-generator-code.git"
},
"bugs": {
"url": "https://github.com/Microsoft/vscode-generator-code/issues"
},
"main": "./generators/app/index.js",
"homepage": "http://code.visualstudio.com",
"license": "MIT",
"author": {
"name": "VS Code Team",
"url": "https://github.com/Microsoft"
},
"engines": {
"node": "^18.17.0 || >=20.5.0"
},
"scripts": {
"test": "mocha",
"prepublishOnly": "npm test",
"preversion": "npm test",
"postversion": "git push && git push --tags"
},
"dependencies": {
"yosay": "^3.0.0"
}
}
76 changes: 76 additions & 0 deletions providers/os/resources/packages/testdata/yo_package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{
"name": "yo",
"version": "5.1.0",
"description": "CLI tool for running Yeoman generators",
"license": "BSD-2-Clause",
"repository": "yeoman/yo",
"homepage": "http://yeoman.io",
"author": "Yeoman",
"main": "lib",
"bin": {
"yo": "lib/cli.js",
"yo-complete": "lib/completion/index.js"
},
"engines": {
"node": "^18.17.0 || >=20.5.0"
},
"scripts": {
"postinstall": "yodoctor",
"postupdate": "yodoctor",
"pretest": "xo",
"test": "nyc mocha --timeout=30000",
"coverage": "nyc report --reporter=text-lcov | coveralls"
},
"files": [
"lib"
],
"keywords": [
"cli-app",
"cli",
"front-end",
"development",
"dev",
"build",
"web",
"tool",
"scaffold",
"stack",
"yeoman",
"generator",
"generate",
"app",
"boilerplate"
],
"dependencies": {
"yosay": "^2.0.2"
},
"resolutions": {
"natives": "1.1.3"
},
"tabtab": {
"yo": [
"-f",
"--force",
"--version",
"--no-color",
"--generators",
"--local-only"
]
},
"xo": {
"space": true,
"overrides": [
{
"files": "test/**",
"envs": [
"node",
"mocha"
]
}
],
"rules": {
"promise/prefer-await-to-then": 0,
"unicorn/no-array-reduce": "off"
}
}
}
Loading