Skip to content

Commit

Permalink
Add Security content from core and CLI (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
attiasas authored Jan 18, 2024
1 parent 6e67677 commit d44be67
Show file tree
Hide file tree
Showing 329 changed files with 378,122 additions and 91 deletions.
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

0 comments on commit d44be67

Please sign in to comment.