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

Add Security content from core and CLI #3

Merged
merged 19 commits into from
Jan 18, 2024
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 0 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ jobs:
env:
GOPROXY: direct
GRADLE_OPTS: -Dorg.gradle.daemon=false
JFROG_CLI_LOG_LEVEL: "DEBUG"
steps:
# Install dependencies
- name: Install Go
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@

## General

**jfrog-cli-security** is a go module which contains the security code components (Xray, JAS) used by the [JFrog CLI source code](https://github.com/jfrog/jfrog-cli).
**jfrog-cli-security** is a Go module that encompasses the security commands of [JFrog CLI](https://docs.jfrog-applications.jfrog.io/jfrog-applications/jfrog-cli). This module is an Embedded JFrog CLI Plugins and is referenced as a Go module within the [JFrog CLI codebase](https://github.com/jfrog/jfrog-cli).

## 🫱🏻‍🫲🏼 Contributions

We welcome pull requests from the community. To help us improve this project, please read
our [Contribution](./CONTRIBUTING.md) guide.
We welcome contributions from the community through pull requests. To assist in enhancing this project, please review our [Plugin Contribution](https://github.com/jfrog/jfrog-cli-core/blob/dev/plugins/README.md) guide.
244 changes: 244 additions & 0 deletions artifactory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
package main

import (
"errors"
"github.com/stretchr/testify/require"
"os"
"os/exec"
"path/filepath"
"testing"

"github.com/jfrog/jfrog-cli-core/v2/utils/dependencies"
"github.com/jfrog/jfrog-client-go/utils/io/fileutils"

"github.com/stretchr/testify/assert"

biutils "github.com/jfrog/build-info-go/utils"

securityTests "github.com/jfrog/jfrog-cli-security/tests"
securityTestUtils "github.com/jfrog/jfrog-cli-security/tests/utils"
"github.com/jfrog/jfrog-cli-security/utils"

"github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/generic"
commonCommands "github.com/jfrog/jfrog-cli-core/v2/common/commands"
"github.com/jfrog/jfrog-cli-core/v2/common/project"
"github.com/jfrog/jfrog-cli-core/v2/common/spec"
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
coreTests "github.com/jfrog/jfrog-cli-core/v2/utils/tests"

clientTests "github.com/jfrog/jfrog-client-go/utils/tests"
)

// We perform validation on dependency resolution from an Artifactory server during the construction of the dependency tree during 'audit' flow.
// This process involves resolving all dependencies required by the project.
func TestDependencyResolutionFromArtifactory(t *testing.T) {
testCases := []struct {
testProjectPath []string
resolveRepoName string
cacheRepoName string
projectType project.ProjectType
}{
{
testProjectPath: []string{"npm", "npm-no-lock"},
resolveRepoName: securityTests.NpmRemoteRepo,
cacheRepoName: securityTests.NpmRemoteRepo,
projectType: project.Npm,
},
{
testProjectPath: []string{"dotnet", "dotnet-single"},
resolveRepoName: securityTests.NugetRemoteRepo,
cacheRepoName: securityTests.NugetRemoteRepo,
projectType: project.Dotnet,
},
{
testProjectPath: []string{"yarn", "yarn-v2"},
resolveRepoName: securityTests.YarnRemoteRepo,
cacheRepoName: securityTests.YarnRemoteRepo,
projectType: project.Yarn,
},
{
testProjectPath: []string{"gradle", "gradleproject"},
resolveRepoName: securityTests.GradleRemoteRepo,
cacheRepoName: securityTests.GradleRemoteRepo,
projectType: project.Gradle,
},
{
testProjectPath: []string{"maven", "mavenproject"},
resolveRepoName: securityTests.MvnRemoteRepo,
cacheRepoName: securityTests.MvnRemoteRepo,
projectType: project.Maven,
},
{
testProjectPath: []string{"go", "simple-project"},
resolveRepoName: securityTests.GoVirtualRepo,
cacheRepoName: securityTests.GoRemoteRepo,
projectType: project.Go,
},
{
testProjectPath: []string{"python", "pipenv", "pipenv", "pipenvproject"},
resolveRepoName: securityTests.PypiRemoteRepo,
cacheRepoName: securityTests.PypiRemoteRepo,
projectType: project.Pipenv,
},
{
testProjectPath: []string{"python", "pip", "pip", "setuppyproject"},
resolveRepoName: securityTests.PypiRemoteRepo,
cacheRepoName: securityTests.PypiRemoteRepo,
projectType: project.Pip,
},
{
testProjectPath: []string{"python", "poetry", "poetry"},
resolveRepoName: securityTests.PypiRemoteRepo,
cacheRepoName: securityTests.PypiRemoteRepo,
projectType: project.Poetry,
},
}
securityTestUtils.CreateJfrogHomeConfig(t, true)
defer securityTestUtils.CleanTestsHomeEnv()

for _, testCase := range testCases {
t.Run(testCase.projectType.String(), func(t *testing.T) {
testSingleTechDependencyResolution(t, testCase.testProjectPath, testCase.resolveRepoName, testCase.cacheRepoName, testCase.projectType)
})
}
}

func testSingleTechDependencyResolution(t *testing.T, testProjectPartialPath []string, resolveRepoName string, cacheRepoName string, projectType project.ProjectType) {
tempDirPath, createTempDirCallback := coreTests.CreateTempDirWithCallbackAndAssert(t)
defer createTempDirCallback()
testProjectPath := filepath.Join(append([]string{filepath.FromSlash(securityTestUtils.GetTestResourcesPath()), "projects", "package-managers"}, testProjectPartialPath...)...)
assert.NoError(t, biutils.CopyDir(testProjectPath, tempDirPath, true, nil))
rootDir, err := os.Getwd()
assert.NoError(t, err)
assert.NoError(t, os.Chdir(tempDirPath))
defer func() {
assert.NoError(t, os.Chdir(rootDir))
}()

server := &config.ServerDetails{
Url: *securityTests.JfrogUrl,
ArtifactoryUrl: *securityTests.JfrogUrl + securityTests.ArtifactoryEndpoint,
XrayUrl: *securityTests.JfrogUrl + securityTests.XrayEndpoint,
AccessToken: *securityTests.JfrogAccessToken,
ServerId: securityTests.ServerId,
}
configCmd := commonCommands.NewConfigCommand(commonCommands.AddOrEdit, securityTests.ServerId).SetDetails(server).SetUseBasicAuthOnly(true).SetInteractive(false)
assert.NoError(t, configCmd.Run())
// Create build config
assert.NoError(t, commonCommands.CreateBuildConfigWithOptions(false, projectType,
commonCommands.WithResolverServerId(server.ServerId),
commonCommands.WithResolverRepo(resolveRepoName),
))

artifactoryPathToSearch := cacheRepoName + "-cache/*"
// To ensure a clean state between test cases, we need to verify that the cache remains clear for remote directories shared across multiple test cases.
deleteCmd := generic.NewDeleteCommand()
deleteCmd.SetServerDetails(server).SetRetries(3).SetSpec(spec.NewBuilder().Pattern(artifactoryPathToSearch).Recursive(true).BuildSpec())
assert.NoError(t, deleteCmd.Run())

callbackFunc := clearOrRedirectLocalCacheIfNeeded(t, projectType)
if callbackFunc != nil {
defer func() {
callbackFunc()
}()
}

// Executing the 'audit' command on an uninstalled project, we anticipate the resolution of dependencies from the configured Artifactory server and repository.
assert.NoError(t, securityTests.PlatformCli.WithoutCredentials().Exec("audit"))

// Following resolution from Artifactory, we anticipate the repository's cache to contain data.
output := coreTests.RunCmdWithOutput(t, func() error {
searchCmd := generic.NewSearchCommand()
searchCmd.SetServerDetails(server).SetRetries(3).SetSpec(spec.NewBuilder().Pattern(artifactoryPathToSearch).Recursive(true).BuildSpec())
err := searchCmd.Run()
if err != nil {
return err
}
// After the resolution from Artifactory, we verify whether the repository's cache is filled with artifacts.
result := searchCmd.Result()
require.NotNil(t, result)
reader := result.Reader()
require.NotNil(t, reader)
defer func() {
err = errors.Join(err, reader.Close())
}()
readerLen, e := reader.Length()
if err = errors.Join(err, e); err != nil {
return err
}
assert.NotEqual(t, 0, readerLen)
return err
})
assert.NotEqual(t, "[]\n", output)
}

// To guarantee that dependencies are resolved from Artifactory, certain package managers may need their local cache to be cleared.
func clearOrRedirectLocalCacheIfNeeded(t *testing.T, projectType project.ProjectType) (callbackFunc func()) {
switch projectType {
case project.Dotnet:
_, err := exec.Command("dotnet", "nuget", "locals", "all", "--clear").CombinedOutput()
assert.NoError(t, err)
case project.Maven:
mavenCacheTempPath, createTempDirCallback := coreTests.CreateTempDirWithCallbackAndAssert(t)
envVarCallbackFunc := clientTests.SetEnvWithCallbackAndAssert(t, securityTests.JvmLaunchEnvVar, securityTests.MavenCacheRedirectionVal+mavenCacheTempPath)
callbackFunc = func() {
envVarCallbackFunc()
createTempDirCallback()
}
case project.Go:
goTempCachePath, createTempDirCallback := coreTests.CreateTempDirWithCallbackAndAssert(t)
envVarCallbackFunc := clientTests.SetEnvWithCallbackAndAssert(t, securityTests.GoCacheEnvVar, goTempCachePath)

callbackFunc = func() {
envVarCallbackFunc()
// To remove the temporary cache in Go and all its contents, appropriate deletion permissions are required.
assert.NoError(t, coreutils.SetPermissionsRecursively(goTempCachePath, 0755))
createTempDirCallback()
}
case project.Pip:
pipTempCachePath, createTempDirCallback := coreTests.CreateTempDirWithCallbackAndAssert(t)
envVarCallbackFunc := clientTests.SetEnvWithCallbackAndAssert(t, securityTests.PipCacheEnvVar, pipTempCachePath)
callbackFunc = func() {
envVarCallbackFunc()
createTempDirCallback()
}
}
return
}

func TestDownloadAnalyzerManagerIfNeeded(t *testing.T) {
// Configure a new JFrog CLI home dir.
tempDirPath, createTempDirCallback := coreTests.CreateTempDirWithCallbackAndAssert(t)
defer createTempDirCallback()
setEnvCallBack := clientTests.SetEnvWithCallbackAndAssert(t, coreutils.HomeDir, tempDirPath)
defer setEnvCallBack()

// Download
err := utils.DownloadAnalyzerManagerIfNeeded()
assert.NoError(t, err)

// Validate Analyzer manager app & checksum.sh2 file exist
path, err := utils.GetAnalyzerManagerDirAbsolutePath()
assert.NoError(t, err)
amPath := filepath.Join(path, utils.GetAnalyzerManagerExecutableName())
exists, err := fileutils.IsFileExists(amPath, false)
assert.NoError(t, err)
assert.True(t, exists)
checksumPath := filepath.Join(path, dependencies.ChecksumFileName)
exists, err = fileutils.IsFileExists(checksumPath, false)
assert.NoError(t, err)
assert.True(t, exists)
checksumFileStat, err := os.Stat(checksumPath)
assert.NoError(t, err)
assert.True(t, checksumFileStat.Size() > 0)

// Validate no second download occurred
firstFileStat, err := os.Stat(amPath)
assert.NoError(t, err)
err = utils.DownloadAnalyzerManagerIfNeeded()
assert.NoError(t, err)
secondFileStat, err := os.Stat(amPath)
assert.NoError(t, err)
assert.Equal(t, firstFileStat.ModTime(), secondFileStat.ModTime())
}
Loading
Loading