Skip to content

Commit de51f30

Browse files
committed
Merge remote-tracking branch 'upstream/dev' into add_pnpm_log_if_err_install
2 parents a58241e + 68eb00c commit de51f30

File tree

11 files changed

+243
-60
lines changed

11 files changed

+243
-60
lines changed

cli/docs/flags.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ const (
120120
RequirementsFile = "requirements-file"
121121
WorkingDirs = "working-dirs"
122122
OutputDir = "output-dir"
123+
SkipAutoInstall = "skip-auto-install"
123124

124125
// Unique curation flags
125126
CurationOutput = "curation-format"
@@ -156,7 +157,7 @@ var commandFlags = map[string][]string{
156157
url, user, password, accessToken, ServerId, InsecureTls, Project, Watches, RepoPath, Licenses, OutputFormat, ExcludeTestDeps,
157158
useWrapperAudit, DepType, RequirementsFile, Fail, ExtendedTable, WorkingDirs, ExclusionsAudit, Mvn, Gradle, Npm,
158159
Pnpm, Yarn, Go, Nuget, Pip, Pipenv, Poetry, MinSeverity, FixableOnly, ThirdPartyContextualAnalysis, Threads,
159-
Sca, Iac, Sast, Secrets, WithoutCA, ScanVuln, SecretValidation, OutputDir,
160+
Sca, Iac, Sast, Secrets, WithoutCA, ScanVuln, SecretValidation, OutputDir, SkipAutoInstall,
160161
},
161162
CurationAudit: {
162163
CurationOutput, WorkingDirs, Threads, RequirementsFile,
@@ -231,8 +232,9 @@ var flagsMap = map[string]components.Flag{
231232
"Set to false if you wish to not use the gradle or maven wrapper.",
232233
components.WithBoolDefaultValue(true),
233234
),
234-
WorkingDirs: components.NewStringFlag(WorkingDirs, "A comma-separated list of relative working directories, to determine audit targets locations."),
235-
OutputDir: components.NewStringFlag(OutputDir, "Target directory to save partial results to.", components.SetHiddenStrFlag()),
235+
WorkingDirs: components.NewStringFlag(WorkingDirs, "A comma-separated list of relative working directories, to determine audit targets locations."),
236+
OutputDir: components.NewStringFlag(OutputDir, "Target directory to save partial results to.", components.SetHiddenStrFlag()),
237+
SkipAutoInstall: components.NewBoolFlag(SkipAutoInstall, "Set to true to skip auto-install of dependencies in un-built modules. Currently supported for Yarn and NPM only.", components.SetHiddenBoolFlag()),
236238
ExclusionsAudit: components.NewStringFlag(
237239
Exclusions,
238240
"List of exclusions separated by semicolons, utilized to skip sub-projects from undergoing an audit. These exclusions may incorporate the * and ? wildcards.",

cli/scancommands.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,8 @@ func CreateAuditCmd(c *components.Context) (*audit.AuditCommand, error) {
477477
SetMinSeverityFilter(minSeverity).
478478
SetFixableOnly(c.GetBoolFlagValue(flags.FixableOnly)).
479479
SetThirdPartyApplicabilityScan(c.GetBoolFlagValue(flags.ThirdPartyContextualAnalysis)).
480-
SetScansResultsOutputDir(scansOutputDir)
480+
SetScansResultsOutputDir(scansOutputDir).
481+
SetSkipAutoInstall(c.GetBoolFlagValue(flags.SkipAutoInstall))
481482

482483
if c.GetStringFlagValue(flags.Watches) != "" {
483484
auditCmd.SetWatches(splitByCommaAndTrim(c.GetStringFlagValue(flags.Watches)))

commands/audit/sca/npm/npm.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package npm
33
import (
44
"errors"
55
"fmt"
6-
76
biutils "github.com/jfrog/build-info-go/build/utils"
87
buildinfo "github.com/jfrog/build-info-go/entities"
98
"github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/npm"
@@ -48,7 +47,7 @@ func BuildDependencyTree(params utils.AuditParams) (dependencyTrees []*xrayUtils
4847
}()
4948

5049
// Calculate npm dependencies
51-
dependenciesMap, err := biutils.CalculateDependenciesMap(npmExecutablePath, currentDir, packageInfo.BuildInfoModuleId(), treeDepsParam, log.Logger)
50+
dependenciesMap, err := biutils.CalculateDependenciesMap(npmExecutablePath, currentDir, packageInfo.BuildInfoModuleId(), treeDepsParam, log.Logger, params.SkipAutoInstall())
5251
if err != nil {
5352
log.Info("Used npm version:", npmVersion.GetVersion())
5453
return

commands/audit/sca/npm/npm_test.go

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@ package npm
22

33
import (
44
"encoding/json"
5-
"os"
6-
"path/filepath"
7-
"testing"
8-
5+
bibuildutils "github.com/jfrog/build-info-go/build/utils"
6+
buildinfo "github.com/jfrog/build-info-go/entities"
7+
biutils "github.com/jfrog/build-info-go/utils"
8+
"github.com/jfrog/jfrog-cli-core/v2/utils/tests"
99
"github.com/jfrog/jfrog-cli-security/commands/audit/sca"
1010
"github.com/jfrog/jfrog-cli-security/utils"
11+
"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
1112
xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils"
12-
13-
biutils "github.com/jfrog/build-info-go/build/utils"
14-
buildinfo "github.com/jfrog/build-info-go/entities"
15-
"github.com/jfrog/jfrog-cli-core/v2/utils/tests"
1613
"github.com/stretchr/testify/assert"
14+
"os"
15+
"path/filepath"
16+
"strings"
17+
"testing"
1718
)
1819

1920
func TestParseNpmDependenciesList(t *testing.T) {
@@ -25,7 +26,7 @@ func TestParseNpmDependenciesList(t *testing.T) {
2526
var dependencies []buildinfo.Dependency
2627
err = json.Unmarshal(dependenciesJson, &dependencies)
2728
assert.NoError(t, err)
28-
packageInfo := &biutils.PackageInfo{Name: "npmexmaple", Version: "0.1.0"}
29+
packageInfo := &bibuildutils.PackageInfo{Name: "npmexmaple", Version: "0.1.0"}
2930
looseEnvifyJsTokens := []*xrayUtils.GraphNode{{Id: "npm://loose-envify:1.4.0", Nodes: []*xrayUtils.GraphNode{{Id: "npm://js-tokens:4.0.0"}}}}
3031
expectedTree := &xrayUtils.GraphNode{
3132
Id: "npm://npmexmaple:0.1.0",
@@ -122,3 +123,66 @@ func TestIgnoreScripts(t *testing.T) {
122123
_, _, err := BuildDependencyTree(params)
123124
assert.NoError(t, err)
124125
}
126+
127+
// This test checks that the tree construction is skipped when the project is not installed and the user prohibited installation
128+
func TestSkipBuildDepTreeWhenInstallForbidden(t *testing.T) {
129+
testCases := []struct {
130+
name string
131+
testDir string
132+
installCommand string
133+
shouldBeInstalled bool
134+
successfulTreeBuiltExpected bool
135+
}{
136+
{
137+
name: "not installed | install required - install command",
138+
testDir: filepath.Join("projects", "package-managers", "npm", "npm-no-lock"),
139+
installCommand: "npm install",
140+
shouldBeInstalled: false,
141+
successfulTreeBuiltExpected: true,
142+
},
143+
{
144+
name: "not installed | install required - install forbidden",
145+
testDir: filepath.Join("projects", "package-managers", "npm", "npm-no-lock"),
146+
shouldBeInstalled: false,
147+
successfulTreeBuiltExpected: false,
148+
},
149+
{
150+
name: "installed | install not required",
151+
testDir: filepath.Join("projects", "package-managers", "npm", "npm-project"),
152+
shouldBeInstalled: true,
153+
successfulTreeBuiltExpected: true,
154+
},
155+
}
156+
157+
for _, test := range testCases {
158+
t.Run(test.name, func(t *testing.T) {
159+
dirPath, cleanUp := sca.CreateTestWorkspace(t, test.testDir)
160+
defer cleanUp()
161+
162+
exists, err := fileutils.IsFileExists(filepath.Join(dirPath, "package-lock.json"), false)
163+
assert.NoError(t, err)
164+
165+
if !test.shouldBeInstalled && exists {
166+
err = os.Remove(filepath.Join(dirPath, "package-lock.json"))
167+
assert.NoError(t, err)
168+
}
169+
170+
params := (&utils.AuditBasicParams{}).SetSkipAutoInstall(true)
171+
if test.installCommand != "" {
172+
splitInstallCommand := strings.Split(test.installCommand, " ")
173+
params = params.SetInstallCommandName(splitInstallCommand[0]).SetInstallCommandArgs(splitInstallCommand[1:])
174+
}
175+
dependencyTrees, uniqueDeps, err := BuildDependencyTree(params)
176+
if !test.successfulTreeBuiltExpected {
177+
assert.Nil(t, dependencyTrees)
178+
assert.Nil(t, uniqueDeps)
179+
assert.Error(t, err)
180+
assert.IsType(t, &biutils.ErrProjectNotInstalled{}, err)
181+
} else {
182+
assert.NotNil(t, dependencyTrees)
183+
assert.NotNil(t, uniqueDeps)
184+
assert.NoError(t, err)
185+
}
186+
})
187+
}
188+
}

commands/audit/sca/yarn/yarn.go

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ package yarn
33
import (
44
"errors"
55
"fmt"
6+
biutils "github.com/jfrog/build-info-go/utils"
67
"path/filepath"
78

89
"golang.org/x/exp/maps"
910

1011
"github.com/jfrog/build-info-go/build"
11-
biutils "github.com/jfrog/build-info-go/build/utils"
12+
bibuildutils "github.com/jfrog/build-info-go/build/utils"
1213
"github.com/jfrog/gofrog/version"
1314
"github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/yarn"
1415
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
@@ -46,17 +47,17 @@ func BuildDependencyTree(params utils.AuditParams) (dependencyTrees []*xrayUtils
4647
if err != nil {
4748
return
4849
}
49-
executablePath, err := biutils.GetYarnExecutable()
50+
executablePath, err := bibuildutils.GetYarnExecutable()
5051
if errorutils.CheckError(err) != nil {
5152
return
5253
}
5354

54-
packageInfo, err := biutils.ReadPackageInfoFromPackageJsonIfExists(currentDir, nil)
55+
packageInfo, err := bibuildutils.ReadPackageInfoFromPackageJsonIfExists(currentDir, nil)
5556
if errorutils.CheckError(err) != nil {
5657
return
5758
}
5859

59-
installRequired, err := isInstallRequired(currentDir, params.InstallCommandArgs())
60+
installRequired, err := isInstallRequired(currentDir, params.InstallCommandArgs(), params.SkipAutoInstall())
6061
if err != nil {
6162
return
6263
}
@@ -70,7 +71,7 @@ func BuildDependencyTree(params utils.AuditParams) (dependencyTrees []*xrayUtils
7071
}
7172

7273
// Calculate Yarn dependencies
73-
dependenciesMap, root, err := biutils.GetYarnDependencies(executablePath, currentDir, packageInfo, log.Logger)
74+
dependenciesMap, root, err := bibuildutils.GetYarnDependencies(executablePath, currentDir, packageInfo, log.Logger)
7475
if err != nil {
7576
return
7677
}
@@ -89,7 +90,7 @@ func configureYarnResolutionServerAndRunInstall(params utils.AuditParams, curWd,
8990
return runYarnInstallAccordingToVersion(curWd, yarnExecPath, params.InstallCommandArgs())
9091
}
9192

92-
executableYarnVersion, err := biutils.GetVersion(yarnExecPath, curWd)
93+
executableYarnVersion, err := bibuildutils.GetVersion(yarnExecPath, curWd)
9394
if err != nil {
9495
return
9596
}
@@ -136,19 +137,23 @@ func configureYarnResolutionServerAndRunInstall(params utils.AuditParams, curWd,
136137
return runYarnInstallAccordingToVersion(curWd, yarnExecPath, params.InstallCommandArgs())
137138
}
138139

139-
func isInstallRequired(currentDir string, installCommandArgs []string) (installRequired bool, err error) {
140+
// We verify the project's installation status by examining the presence of the yarn.lock file and the presence of an installation command provided by the user.
141+
// If install command was provided - we install
142+
// If yarn.lock is missing, we should install unless the user has explicitly disabled auto-install. In this case we return an error
143+
// Notice!: If alterations are made manually in the package.json file, it necessitates a manual update to the yarn.lock file as well.
144+
func isInstallRequired(currentDir string, installCommandArgs []string, skipAutoInstall bool) (installRequired bool, err error) {
140145
yarnLockExits, err := fileutils.IsFileExists(filepath.Join(currentDir, yarn.YarnLockFileName), false)
141146
if err != nil {
142147
err = fmt.Errorf("failed to check the existence of '%s' file: %s", filepath.Join(currentDir, yarn.YarnLockFileName), err.Error())
143148
return
144149
}
145150

146-
// We verify the project's installation status by examining the presence of the yarn.lock file and the presence of an installation command provided by the user.
147-
// Notice!: If alterations are made manually in the package.json file, it necessitates a manual update to the yarn.lock file as well.
148-
if len(installCommandArgs) > 0 || !yarnLockExits {
149-
installRequired = true
151+
if len(installCommandArgs) > 0 {
152+
return true, nil
153+
} else if !yarnLockExits && skipAutoInstall {
154+
return false, &biutils.ErrProjectNotInstalled{UninstalledDir: currentDir}
150155
}
151-
return
156+
return !yarnLockExits, nil
152157
}
153158

154159
// Executes the user-defined 'install' command; if absent, defaults to running an 'install' command with specific flags suited to the current yarn version.
@@ -162,7 +167,7 @@ func runYarnInstallAccordingToVersion(curWd, yarnExecPath string, installCommand
162167
}
163168

164169
installCommandArgs = []string{"install"}
165-
executableVersionStr, err := biutils.GetVersion(yarnExecPath, curWd)
170+
executableVersionStr, err := bibuildutils.GetVersion(yarnExecPath, curWd)
166171
if err != nil {
167172
return
168173
}
@@ -200,13 +205,13 @@ func runYarnInstallAccordingToVersion(curWd, yarnExecPath string, installCommand
200205
}
201206

202207
// Parse the dependencies into a Xray dependency tree format
203-
func parseYarnDependenciesMap(dependencies map[string]*biutils.YarnDependency, rootXrayId string) (*xrayUtils.GraphNode, []string) {
208+
func parseYarnDependenciesMap(dependencies map[string]*bibuildutils.YarnDependency, rootXrayId string) (*xrayUtils.GraphNode, []string) {
204209
treeMap := make(map[string]xray.DepTreeNode)
205210
for _, dependency := range dependencies {
206211
xrayDepId := getXrayDependencyId(dependency)
207212
var subDeps []string
208213
for _, subDepPtr := range dependency.Details.Dependencies {
209-
subDeps = append(subDeps, getXrayDependencyId(dependencies[biutils.GetYarnDependencyKeyFromLocator(subDepPtr.Locator)]))
214+
subDeps = append(subDeps, getXrayDependencyId(dependencies[bibuildutils.GetYarnDependencyKeyFromLocator(subDepPtr.Locator)]))
210215
}
211216
if len(subDeps) > 0 {
212217
treeMap[xrayDepId] = xray.DepTreeNode{Children: subDeps}
@@ -216,6 +221,6 @@ func parseYarnDependenciesMap(dependencies map[string]*biutils.YarnDependency, r
216221
return graph, maps.Keys(uniqDeps)
217222
}
218223

219-
func getXrayDependencyId(yarnDependency *biutils.YarnDependency) string {
224+
func getXrayDependencyId(yarnDependency *bibuildutils.YarnDependency) string {
220225
return utils.NpmPackageTypeIdentifier + yarnDependency.Name() + ":" + yarnDependency.Details.Version
221226
}

0 commit comments

Comments
 (0)