diff --git a/providers/os/resources/npm.go b/providers/os/resources/npm.go new file mode 100644 index 0000000000..193f54fa43 --- /dev/null +++ b/providers/os/resources/npm.go @@ -0,0 +1,530 @@ +// Copyright (c) Mondoo, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package resources + +import ( + "cmp" + "errors" + "fmt" + "path/filepath" + "slices" + "strings" + "sync" + + "github.com/rs/zerolog/log" + "github.com/spf13/afero" + "go.mondoo.com/cnquery/v10/llx" + "go.mondoo.com/cnquery/v10/providers-sdk/v1/plugin" + "go.mondoo.com/cnquery/v10/providers/os/connection/shared" + "go.mondoo.com/cnquery/v10/providers/os/resources/npm" + "go.mondoo.com/cnquery/v10/types" +) + +func initNpmPackages(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) { + if x, ok := args["path"]; ok { + _, ok := x.Value.(string) + if !ok { + return nil, nil, errors.New("Wrong type for 'path' in npm initialization, it must be a string") + } + } else { + // empty path means search through default locations + args["path"] = llx.StringData("") + } + + return args, nil, nil +} + +func (r *mqlNpmPackages) id() (string, error) { + path := r.Path.Data + if path == "" { + return "npm.packages", nil + } + + return "npm.packages/" + path, nil +} + +func getFileContent(runtime *plugin.Runtime, path string) (*mqlFile, error) { + f, err := CreateResource(runtime, "file", map[string]*llx.RawData{ + "path": llx.StringData(path), + }) + if err != nil { + return nil, err + } + file := f.(*mqlFile) + return file, nil +} + +var ( + // we need to add extra testing for windows paths + //windowsDefaultNpmPaths = []string{ + // "C:\\Users\\%\\AppData\\Roaming\\npm", + //} + linuxDefaultNpmPaths = []string{ + "/usr/local/lib", + "/opt/homebrew/lib", + "/usr/lib", + "/home/%/.npm-global/lib", + "/Users/%/.npm-global/lib", + } +) + +func (r *mqlNpmPackages) gatherPackagesFromSystemDefaults(conn shared.Connection) ([]*npm.Package, []*npm.Package, []string, error) { + var directPackageList []*npm.Package + var transitivePackageList []*npm.Package + evidenceFiles := []string{} + log.Debug().Msg("searching for npm packages in default locations") + afs := &afero.Afero{Fs: conn.FileSystem()} + // we search through default system locations + for _, pattern := range linuxDefaultNpmPaths { + log.Debug().Str("path", pattern).Msg("searching for npm packages") + m, err := afero.Glob(conn.FileSystem(), pattern) + if err != nil { + log.Info().Err(err).Str("path", pattern).Msg("could not search for npm packages") + // nothing to do, we just ignore it + } + for _, walkPath := range m { + // 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) + if err != nil { + continue + } + for i := range files { + f := files[i] + p := f.Name() + log.Debug().Str("path", p).Msg("checking for package-lock.json or package.json file") + + if !f.IsDir() { + continue + } + + // check if there is a package-lock.json or package.json file + packageLockPath := filepath.Join(nodeModulesPath, p, "/package-lock.json") + packageJsonPath := filepath.Join(nodeModulesPath, p, "/package.json") + + packageLockExists, _ := afs.Exists(packageLockPath) + packageJsonExists, _ := afs.Exists(packageJsonPath) + + // add files to evidence + if packageLockExists { + evidenceFiles = append(evidenceFiles, packageLockPath) + } + if packageJsonExists { + evidenceFiles = append(evidenceFiles, packageJsonPath) + } + + // parse npm files + if packageLockExists { + log.Debug().Str("path", packageLockPath).Msg("found package-lock.json file") + f, err := getFileContent(r.MqlRuntime, packageLockPath) + if err != nil { + continue + } + content := f.GetContent() + if content.Error != nil { + continue + } + + p := &npm.PackageLockParser{} + info, err := p.Parse(strings.NewReader(content.Data)) + if err != nil { + log.Error().Err(err).Str("path", packageLockPath).Msg("could not parse package-lock.json file") + } + root := info.Root() + if root != nil { + directPackageList = append(directPackageList, root) + } + transitive := info.Transitive() + if transitive != nil { + transitivePackageList = append(transitivePackageList, transitive...) + } + + } else if packageJsonExists { + log.Debug().Str("path", packageJsonPath).Msg("found package.json file") + f, err := getFileContent(r.MqlRuntime, packageJsonPath) + if err != nil { + continue + } + content := f.GetContent() + if content.Error != nil { + continue + } + + p := &npm.PackageJsonParser{} + info, err := p.Parse(strings.NewReader(content.Data)) + if err != nil { + log.Error().Err(err).Str("path", packageJsonPath).Msg("could not parse package.json file") + } + root := info.Root() + if root != nil { + directPackageList = append(directPackageList, root) + } + transitive := info.Transitive() + if transitive != nil { + transitivePackageList = append(transitivePackageList, transitive...) + } + } + } + } + } + return directPackageList, transitivePackageList, evidenceFiles, nil +} + +func (r *mqlNpmPackages) gatherPackagesFromLocation(conn shared.Connection, path string) (*npm.Package, []*npm.Package, []*npm.Package, []string, error) { + evidenceFiles := []string{} + + // specific path was provided + afs := &afero.Afero{Fs: conn.FileSystem()} + isDir, err := afs.IsDir(path) + if err != nil { + return nil, nil, nil, nil, err + } + + loadPackageLock := false + packageLockPath := "" + loadPackageJson := false + packageJsonPath := "" + + if isDir { + // check if there is a package-lock.json or package.json file + packageLockPath = filepath.Join(path, "/package-lock.json") + packageJsonPath = filepath.Join(path, "/package.json") + } else { + loadPackageJson = strings.HasSuffix(path, "package-lock.json") + if loadPackageJson { + packageLockPath = path + } + loadPackageLock = strings.HasSuffix(path, "package.json") + if loadPackageLock { + packageJsonPath = path + } + + if !loadPackageJson && !loadPackageLock { + return nil, nil, nil, nil, fmt.Errorf("path %s is not a package.json or package-lock.json file", path) + } + } + + loadPackageLock, _ = afs.Exists(packageLockPath) + loadPackageJson, _ = afs.Exists(packageJsonPath) + + if !loadPackageLock && !loadPackageJson { + return nil, nil, nil, nil, fmt.Errorf("path %s does not contain a package-lock.json or package.json file", path) + } + + // add source files as evidence to files list + if loadPackageLock { + evidenceFiles = append(evidenceFiles, packageLockPath) + } + if loadPackageJson { + evidenceFiles = append(evidenceFiles, packageJsonPath) + } + + // parse npm files + var info npm.NpmPackageInfo + if loadPackageLock { + // if there is a package-lock.json file, we use it + f, err := getFileContent(r.MqlRuntime, packageLockPath) + if err != nil { + return nil, nil, nil, nil, err + } + content := f.GetContent() + if content.Error != nil { + return nil, nil, nil, nil, content.Error + } + + p := &npm.PackageLockParser{} + info, err = p.Parse(strings.NewReader(content.Data)) + if err != nil { + return nil, nil, nil, nil, err + } + } else if loadPackageJson { + // if there is a package.json file, we use it + f, err := getFileContent(r.MqlRuntime, packageJsonPath) + if err != nil { + return nil, nil, nil, nil, err + } + content := f.GetContent() + if content.Error != nil { + return nil, nil, nil, nil, content.Error + } + + p := &npm.PackageJsonParser{} + info, err = p.Parse(strings.NewReader(content.Data)) + if err != nil { + return nil, nil, nil, nil, err + } + } else { + return nil, nil, nil, nil, errors.New("could not parse package-lock.json or package.json file") + } + + return info.Root(), info.Direct(), info.Transitive(), evidenceFiles, nil +} + +type mqlNpmPackagesInternal struct { + mutex sync.Mutex +} + +func (r *mqlNpmPackages) gatherData() error { + r.mutex.Lock() + defer r.mutex.Unlock() + + if r.Path.Error != nil { + return r.Path.Error + } + path := r.Path.Data + + // we check if the path is a directory or a file + // if it is a directory, we check if there is a package-lock.json or package.json file + conn := r.MqlRuntime.Connection.(shared.Connection) + + var root *npm.Package + var directDependencies []*npm.Package + var transitiveDependencies []*npm.Package + var filePaths []string + var err error + if path == "" { + // no specific path was provided, we search through default locations + // here we are not going to have a root package, only direct and transitive dependencies + directDependencies, transitiveDependencies, filePaths, err = r.gatherPackagesFromSystemDefaults(conn) + } else { + // specific path was provided and most likely it is a package-lock.json or package.json file or a directory + // that contains one of those files. We will have a root package direct and transitive dependencies + root, directDependencies, transitiveDependencies, filePaths, err = r.gatherPackagesFromLocation(conn, path) + } + + if err != nil { + return err + } + + // sort packages by name + sortFn := func(a, b *npm.Package) int { + if n := cmp.Compare(a.Name, b.Name); n != 0 { + return n + } + // if names are equal, order by version + return cmp.Compare(a.Version, b.Version) + } + slices.SortFunc(directDependencies, sortFn) + slices.SortFunc(transitiveDependencies, sortFn) + + if root != nil { + mqlPkg, err := newNpmPackages(r.MqlRuntime, root) + if err != nil { + return err + } + r.Root = plugin.TValue[*mqlNpmPackage]{Data: mqlPkg, State: plugin.StateIsSet} + } else { + r.Root = plugin.TValue[*mqlNpmPackage]{State: plugin.StateIsSet | plugin.StateIsNull} + } + + // create a resource for each package + transitiveResources := []interface{}{} + for i := range transitiveDependencies { + newNpmPackages, err := newNpmPackages(r.MqlRuntime, transitiveDependencies[i]) + if err != nil { + return err + } + transitiveResources = append(transitiveResources, newNpmPackages) + } + r.List = plugin.TValue[[]interface{}]{Data: transitiveResources, State: plugin.StateIsSet} + + directResources := []interface{}{} + for i := range directDependencies { + newNpmPackages, err := newNpmPackages(r.MqlRuntime, directDependencies[i]) + if err != nil { + return err + } + directResources = append(directResources, newNpmPackages) + } + r.DirectDependencies = plugin.TValue[[]interface{}]{Data: directResources, State: plugin.StateIsSet} + + // create files for each path + mqlFiles := []interface{}{} + for i := range filePaths { + path := filePaths[i] + lf, err := CreateResource(r.MqlRuntime, "pkgFileInfo", map[string]*llx.RawData{ + "path": llx.StringData(path), + }) + if err != nil { + return err + } + mqlFiles = append(mqlFiles, lf) + } + r.Files = plugin.TValue[[]interface{}]{Data: mqlFiles, State: plugin.StateIsSet} + + return nil +} + +func newNpmPackages(runtime *plugin.Runtime, pkg *npm.Package) (*mqlNpmPackage, error) { + cpes := []interface{}{} + for i := range pkg.Cpes { + cpe, err := runtime.CreateSharedResource("cpe", map[string]*llx.RawData{ + "uri": llx.StringData(pkg.Cpes[i]), + }) + if err != nil { + return nil, err + } + cpes = append(cpes, cpe) + } + + mqlPkg, err := CreateResource(runtime, "npm.package", map[string]*llx.RawData{ + "id": llx.StringData(pkg.Name), + "name": llx.StringData(pkg.Name), + "version": llx.StringData(pkg.Version), + "license": llx.StringData(""), + "description": llx.StringData(pkg.Description), + "purl": llx.StringData(pkg.Purl), + "cpes": llx.ArrayData(cpes, types.Resource("cpe")), + }) + if err != nil { + return nil, err + } + return mqlPkg.(*mqlNpmPackage), nil +} + +func (r *mqlNpmPackages) root() (*mqlNpmPackage, error) { + return nil, r.gatherData() +} + +func (r *mqlNpmPackages) directDependencies() ([]interface{}, error) { + return nil, r.gatherData() +} + +func (r *mqlNpmPackages) list() ([]interface{}, error) { + return nil, r.gatherData() +} + +func (r *mqlNpmPackages) files() ([]interface{}, error) { + return nil, r.gatherData() +} + +type mqlNpmPackageInternal struct { +} + +func (k *mqlNpmPackage) id() (string, error) { + return k.Id.Data, nil +} + +func initNpmPackage(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) { + if len(args) > 1 { + return args, nil, nil + } + if x, ok := args["path"]; ok { + path, ok := x.Value.(string) + if !ok { + return nil, nil, errors.New("Wrong type for 'path' in npm.package initialization, it must be a string") + } + + file, err := CreateResource(runtime, "file", map[string]*llx.RawData{ + "path": llx.StringData(path), + }) + if err != nil { + return nil, nil, err + } + args["id"] = llx.StringData(path) + args["file"] = llx.ResourceData(file, "file") + + delete(args, "path") + } + return args, nil, nil +} + +func (r *mqlNpmPackage) name() (string, error) { + err := r.populateData() + if err != nil { + return "", err + } + return r.Name.Data, nil +} + +func (r *mqlNpmPackage) version() (string, error) { + err := r.populateData() + if err != nil { + return "", err + } + return r.Version.Data, nil +} + +func (r *mqlNpmPackage) license() (string, error) { + err := r.populateData() + if err != nil { + return "", err + } + return r.License.Data, nil +} + +func (r *mqlNpmPackage) author() (interface{}, error) { + return nil, errors.New("not implemented") +} + +func (r *mqlNpmPackage) description() (string, error) { + err := r.populateData() + if err != nil { + return "", err + } + return r.Description.Data, nil +} + +func (r *mqlNpmPackage) purl() (string, error) { + err := r.populateData() + if err != nil { + return "", err + } + return r.Purl.Data, nil +} + +func (r *mqlNpmPackage) cpes() ([]interface{}, error) { + err := r.populateData() + if err != nil { + return nil, err + } + return r.Cpes.Data, nil +} + +func (r *mqlNpmPackage) files() ([]interface{}, error) { + return nil, errors.New("not implemented") +} + +func (r *mqlNpmPackage) populateData() error { + //file := k.GetFile() + //if file.Error != nil { + // return file.Error + //} + // + //if file.Data == nil || file.Data.Path.Data == "" { + // return fmt.Errorf("file path is empty") + //} + + // load the package data from the file + + //// TODO: parse package json + //// TODO: use file.Data.Path.Data to select the right parser + //parser := &npm.PackageJsonParser{} + //pkg, err := parser.Parse(strings.NewReader(file.Data.Content.Data)) + //if err != nil { + // return err + //} + // + //k.Name = plugin.TValue[string]{Data: pkg.Name, State: plugin.StateIsSet} + //k.Version = plugin.TValue[string]{Data: pkg.Version, State: plugin.StateIsSet} + //k.Author = plugin.TValue[string]{Data: pkg.Author, State: plugin.StateIsSet} + //k.Description = plugin.TValue[string]{Data: pkg.Summary, State: plugin.StateIsSet} + //k.License = plugin.TValue[string]{Data: pkg.License, State: plugin.StateIsSet} + // + //cpes := []interface{}{} + //for i := range pkg.Cpes { + // cpe, err := k.MqlRuntime.CreateSharedResource("cpe", map[string]*llx.RawData{ + // "uri": llx.StringData(pkg.Cpes[i]), + // }) + // if err != nil { + // return err + // } + // cpes = append(cpes, cpe) + //} + // + //k.Cpes = plugin.TValue[[]interface{}]{Data: cpes, State: plugin.StateIsSet} + //k.Purl = plugin.TValue[string]{Data: pkg.Purl, State: plugin.StateIsSet} + return nil +} diff --git a/providers/os/resources/os.lr b/providers/os/resources/os.lr index d7f0a04a5c..93998252dc 100644 --- a/providers/os/resources/os.lr +++ b/providers/os/resources/os.lr @@ -1170,6 +1170,46 @@ python.package @defaults("name version") { dependencies() []python.package } +// npm packages +npm.packages { + []npm.package + + init(path? string) + + // optional path to search for packages + path string + + // Root Package (may not exist) + root() npm.package + + // List of direct dependencies + directDependencies() []npm.package + + // Files used to determine the packages + files() []pkgFileInfo +} + +npm.package @defaults("name version") { + // ID is the npm.package unique identifier + id string + // Name of the package + name() string + // Version of the package + version() string + // License of the package + license() string + // Author of the package + author() dict + // Short package description + description() string + // Package URL + purl() string + // Common Platform Enumeration (CPE) for the package + cpes() []core.cpe + // Package files + files() []pkgFileInfo +} + // macOS specific resources macos { // macOS user defaults diff --git a/providers/os/resources/os.lr.go b/providers/os/resources/os.lr.go index d2dcc64f7e..929da9ed8b 100644 --- a/providers/os/resources/os.lr.go +++ b/providers/os/resources/os.lr.go @@ -366,6 +366,14 @@ func init() { Init: initPythonPackage, Create: createPythonPackage, }, + "npm.packages": { + Init: initNpmPackages, + Create: createNpmPackages, + }, + "npm.package": { + Init: initNpmPackage, + Create: createNpmPackage, + }, "macos": { // to override args, implement: initMacos(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) Create: createMacos, @@ -1691,6 +1699,48 @@ var getDataFields = map[string]func(r plugin.Resource) *plugin.DataRes{ "python.package.dependencies": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlPythonPackage).GetDependencies()).ToDataRes(types.Array(types.Resource("python.package"))) }, + "npm.packages.path": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlNpmPackages).GetPath()).ToDataRes(types.String) + }, + "npm.packages.root": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlNpmPackages).GetRoot()).ToDataRes(types.Resource("npm.package")) + }, + "npm.packages.directDependencies": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlNpmPackages).GetDirectDependencies()).ToDataRes(types.Array(types.Resource("npm.package"))) + }, + "npm.packages.files": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlNpmPackages).GetFiles()).ToDataRes(types.Array(types.Resource("pkgFileInfo"))) + }, + "npm.packages.list": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlNpmPackages).GetList()).ToDataRes(types.Array(types.Resource("npm.package"))) + }, + "npm.package.id": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlNpmPackage).GetId()).ToDataRes(types.String) + }, + "npm.package.name": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlNpmPackage).GetName()).ToDataRes(types.String) + }, + "npm.package.version": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlNpmPackage).GetVersion()).ToDataRes(types.String) + }, + "npm.package.license": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlNpmPackage).GetLicense()).ToDataRes(types.String) + }, + "npm.package.author": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlNpmPackage).GetAuthor()).ToDataRes(types.Dict) + }, + "npm.package.description": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlNpmPackage).GetDescription()).ToDataRes(types.String) + }, + "npm.package.purl": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlNpmPackage).GetPurl()).ToDataRes(types.String) + }, + "npm.package.cpes": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlNpmPackage).GetCpes()).ToDataRes(types.Array(types.Resource("cpe"))) + }, + "npm.package.files": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlNpmPackage).GetFiles()).ToDataRes(types.Array(types.Resource("pkgFileInfo"))) + }, "macos.userPreferences": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlMacos).GetUserPreferences()).ToDataRes(types.Map(types.String, types.Dict)) }, @@ -3977,6 +4027,70 @@ var setDataFields = map[string]func(r plugin.Resource, v *llx.RawData) bool { r.(*mqlPythonPackage).Dependencies, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) return }, + "npm.packages.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlNpmPackages).__id, ok = v.Value.(string) + return + }, + "npm.packages.path": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlNpmPackages).Path, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "npm.packages.root": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlNpmPackages).Root, ok = plugin.RawToTValue[*mqlNpmPackage](v.Value, v.Error) + return + }, + "npm.packages.directDependencies": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlNpmPackages).DirectDependencies, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) + return + }, + "npm.packages.files": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlNpmPackages).Files, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) + return + }, + "npm.packages.list": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlNpmPackages).List, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) + return + }, + "npm.package.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlNpmPackage).__id, ok = v.Value.(string) + return + }, + "npm.package.id": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlNpmPackage).Id, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "npm.package.name": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlNpmPackage).Name, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "npm.package.version": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlNpmPackage).Version, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "npm.package.license": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlNpmPackage).License, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "npm.package.author": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlNpmPackage).Author, ok = plugin.RawToTValue[interface{}](v.Value, v.Error) + return + }, + "npm.package.description": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlNpmPackage).Description, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "npm.package.purl": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlNpmPackage).Purl, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "npm.package.cpes": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlNpmPackage).Cpes, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) + return + }, + "npm.package.files": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlNpmPackage).Files, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) + return + }, "macos.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { r.(*mqlMacos).__id, ok = v.Value.(string) return @@ -11480,6 +11594,248 @@ func (c *mqlPythonPackage) GetDependencies() *plugin.TValue[[]interface{}] { }) } +// mqlNpmPackages for the npm.packages resource +type mqlNpmPackages struct { + MqlRuntime *plugin.Runtime + __id string + mqlNpmPackagesInternal + Path plugin.TValue[string] + Root plugin.TValue[*mqlNpmPackage] + DirectDependencies plugin.TValue[[]interface{}] + Files plugin.TValue[[]interface{}] + List plugin.TValue[[]interface{}] +} + +// createNpmPackages creates a new instance of this resource +func createNpmPackages(runtime *plugin.Runtime, args map[string]*llx.RawData) (plugin.Resource, error) { + res := &mqlNpmPackages{ + MqlRuntime: runtime, + } + + err := SetAllData(res, args) + if err != nil { + return res, err + } + + if res.__id == "" { + res.__id, err = res.id() + if err != nil { + return nil, err + } + } + + if runtime.HasRecording { + args, err = runtime.ResourceFromRecording("npm.packages", res.__id) + if err != nil || args == nil { + return res, err + } + return res, SetAllData(res, args) + } + + return res, nil +} + +func (c *mqlNpmPackages) MqlName() string { + return "npm.packages" +} + +func (c *mqlNpmPackages) MqlID() string { + return c.__id +} + +func (c *mqlNpmPackages) GetPath() *plugin.TValue[string] { + return &c.Path +} + +func (c *mqlNpmPackages) GetRoot() *plugin.TValue[*mqlNpmPackage] { + return plugin.GetOrCompute[*mqlNpmPackage](&c.Root, func() (*mqlNpmPackage, error) { + if c.MqlRuntime.HasRecording { + d, err := c.MqlRuntime.FieldResourceFromRecording("npm.packages", c.__id, "root") + if err != nil { + return nil, err + } + if d != nil { + return d.Value.(*mqlNpmPackage), nil + } + } + + return c.root() + }) +} + +func (c *mqlNpmPackages) GetDirectDependencies() *plugin.TValue[[]interface{}] { + return plugin.GetOrCompute[[]interface{}](&c.DirectDependencies, func() ([]interface{}, error) { + if c.MqlRuntime.HasRecording { + d, err := c.MqlRuntime.FieldResourceFromRecording("npm.packages", c.__id, "directDependencies") + if err != nil { + return nil, err + } + if d != nil { + return d.Value.([]interface{}), nil + } + } + + return c.directDependencies() + }) +} + +func (c *mqlNpmPackages) GetFiles() *plugin.TValue[[]interface{}] { + return plugin.GetOrCompute[[]interface{}](&c.Files, func() ([]interface{}, error) { + if c.MqlRuntime.HasRecording { + d, err := c.MqlRuntime.FieldResourceFromRecording("npm.packages", c.__id, "files") + if err != nil { + return nil, err + } + if d != nil { + return d.Value.([]interface{}), nil + } + } + + return c.files() + }) +} + +func (c *mqlNpmPackages) GetList() *plugin.TValue[[]interface{}] { + return plugin.GetOrCompute[[]interface{}](&c.List, func() ([]interface{}, error) { + if c.MqlRuntime.HasRecording { + d, err := c.MqlRuntime.FieldResourceFromRecording("npm.packages", c.__id, "list") + if err != nil { + return nil, err + } + if d != nil { + return d.Value.([]interface{}), nil + } + } + + return c.list() + }) +} + +// mqlNpmPackage for the npm.package resource +type mqlNpmPackage struct { + MqlRuntime *plugin.Runtime + __id string + mqlNpmPackageInternal + Id plugin.TValue[string] + Name plugin.TValue[string] + Version plugin.TValue[string] + License plugin.TValue[string] + Author plugin.TValue[interface{}] + Description plugin.TValue[string] + Purl plugin.TValue[string] + Cpes plugin.TValue[[]interface{}] + Files plugin.TValue[[]interface{}] +} + +// createNpmPackage creates a new instance of this resource +func createNpmPackage(runtime *plugin.Runtime, args map[string]*llx.RawData) (plugin.Resource, error) { + res := &mqlNpmPackage{ + MqlRuntime: runtime, + } + + err := SetAllData(res, args) + if err != nil { + return res, err + } + + if res.__id == "" { + res.__id, err = res.id() + if err != nil { + return nil, err + } + } + + if runtime.HasRecording { + args, err = runtime.ResourceFromRecording("npm.package", res.__id) + if err != nil || args == nil { + return res, err + } + return res, SetAllData(res, args) + } + + return res, nil +} + +func (c *mqlNpmPackage) MqlName() string { + return "npm.package" +} + +func (c *mqlNpmPackage) MqlID() string { + return c.__id +} + +func (c *mqlNpmPackage) GetId() *plugin.TValue[string] { + return &c.Id +} + +func (c *mqlNpmPackage) GetName() *plugin.TValue[string] { + return plugin.GetOrCompute[string](&c.Name, func() (string, error) { + return c.name() + }) +} + +func (c *mqlNpmPackage) GetVersion() *plugin.TValue[string] { + return plugin.GetOrCompute[string](&c.Version, func() (string, error) { + return c.version() + }) +} + +func (c *mqlNpmPackage) GetLicense() *plugin.TValue[string] { + return plugin.GetOrCompute[string](&c.License, func() (string, error) { + return c.license() + }) +} + +func (c *mqlNpmPackage) GetAuthor() *plugin.TValue[interface{}] { + return plugin.GetOrCompute[interface{}](&c.Author, func() (interface{}, error) { + return c.author() + }) +} + +func (c *mqlNpmPackage) GetDescription() *plugin.TValue[string] { + return plugin.GetOrCompute[string](&c.Description, func() (string, error) { + return c.description() + }) +} + +func (c *mqlNpmPackage) GetPurl() *plugin.TValue[string] { + return plugin.GetOrCompute[string](&c.Purl, func() (string, error) { + return c.purl() + }) +} + +func (c *mqlNpmPackage) GetCpes() *plugin.TValue[[]interface{}] { + return plugin.GetOrCompute[[]interface{}](&c.Cpes, func() ([]interface{}, error) { + if c.MqlRuntime.HasRecording { + d, err := c.MqlRuntime.FieldResourceFromRecording("npm.package", c.__id, "cpes") + if err != nil { + return nil, err + } + if d != nil { + return d.Value.([]interface{}), nil + } + } + + return c.cpes() + }) +} + +func (c *mqlNpmPackage) GetFiles() *plugin.TValue[[]interface{}] { + return plugin.GetOrCompute[[]interface{}](&c.Files, func() ([]interface{}, error) { + if c.MqlRuntime.HasRecording { + d, err := c.MqlRuntime.FieldResourceFromRecording("npm.package", c.__id, "files") + if err != nil { + return nil, err + } + if d != nil { + return d.Value.([]interface{}), nil + } + } + + return c.files() + }) +} + // mqlMacos for the macos resource type mqlMacos struct { MqlRuntime *plugin.Runtime diff --git a/providers/os/resources/os.lr.manifest.yaml b/providers/os/resources/os.lr.manifest.yaml index 1a7f6dc58c..d221528d98 100644 --- a/providers/os/resources/os.lr.manifest.yaml +++ b/providers/os/resources/os.lr.manifest.yaml @@ -489,6 +489,30 @@ resources: options: {} path: {} min_mondoo_version: 5.15.0 + npm.package: + fields: + author: {} + cpes: {} + dependencies: {} + description: {} + file: {} + files: {} + id: {} + license: {} + name: {} + purl: {} + version: {} + min_mondoo_version: latest + npm.packages: + fields: + "": {} + directDependencies: {} + files: {} + list: {} + path: {} + root: {} + transitive: {} + min_mondoo_version: latest ntp.conf: fields: content: {}