Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7ebdf6c
Auto install Cocoapods when Podfile.lock not exist
attiasas Apr 12, 2026
282c55c
Add tests
attiasas Apr 12, 2026
48e9e60
add hidden flag desc change
attiasas Apr 12, 2026
6a4ae3a
only run new tests on MAC, show hint on err
attiasas Apr 12, 2026
50230bc
fix static
attiasas Apr 12, 2026
b874485
Merge remote-tracking branch 'upstream/dev' into fix_cocoapods_auto_i…
attiasas Apr 12, 2026
d3ceb61
try fix tests for all os
attiasas Apr 12, 2026
88ead38
fix tests
attiasas Apr 12, 2026
56a1f07
verify right format
attiasas Apr 12, 2026
3ea40cb
Merge remote-tracking branch 'upstream/dev' into fix_cocoapods_auto_i…
attiasas Apr 13, 2026
bea61aa
Merge remote-tracking branch 'upstream/dev' into fix_cocoapods_auto_i…
attiasas Apr 19, 2026
5422e98
Merge remote-tracking branch 'upstream/dev' into fix_cocoapods_auto_i…
attiasas Apr 20, 2026
35a1e94
Merge remote-tracking branch 'upstream/dev' into fix_cocoapods_auto_i…
attiasas May 4, 2026
e504e79
Merge remote-tracking branch 'upstream/dev' into fix_cocoapods_auto_i…
attiasas May 11, 2026
a8400b0
CR changes
attiasas May 11, 2026
634ead7
fix tests
attiasas May 19, 2026
ea5047d
Merge remote-tracking branch 'upstream/dev' into fix_cocoapods_auto_i…
attiasas May 27, 2026
27dde4c
Merge remote-tracking branch 'upstream/dev' into fix_cocoapods_auto_i…
attiasas May 31, 2026
741586c
Merge remote-tracking branch 'upstream/dev' into fix_cocoapods_auto_i…
attiasas Jun 3, 2026
75a99e3
fix(ci): use Go module proxy to avoid VCS shallow fetch races in tests
attiasas Jun 3, 2026
53cb522
Merge remote-tracking branch 'upstream/dev' into fix_cocoapods_auto_i…
attiasas Jun 4, 2026
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
33 changes: 29 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ env:
JFROG_SECURITY_CLI_TESTS_JFROG_PLATFORM_PROJECT_KEY: ${{ vars.JFROG_TEST_PROJECT_KEY }}
# Repository variable ENABLE_FASTCI_BOOST (default: true). Set to 'false' to skip jfrog/boost@v0.
ENABLE_FASTCI_BOOST: ${{ vars.ENABLE_FASTCI_BOOST != 'false' }}

jobs:
Pretest:
if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push'
Expand Down Expand Up @@ -58,9 +58,16 @@ jobs:
with:
go-version-file: go.mod

- name: Download Go modules
run: go mod download
env:
GOPROXY: https://proxy.golang.org,direct

- name: Run Go vet
run: go vet -v ./...

env:
GOPROXY: https://proxy.golang.org,direct

Unit_Tests:
name: "[${{ matrix.os }}] Unit Tests"
needs: Pretest
Expand Down Expand Up @@ -89,6 +96,8 @@ jobs:

# Test and generate code coverage
- name: Run tests
env:
GOPROXY: https://proxy.golang.org,direct
run: go test ${{ env.GO_COMMON_TEST_ARGS }} --ci.runId=${{ runner.os }}-sec-test -covermode atomic -coverprofile=cover-unit-tests --test.unit

Audit_Command_Integration_Tests:
Expand Down Expand Up @@ -141,6 +150,8 @@ jobs:

# Test
- name: Run tests
env:
GOPROXY: https://proxy.golang.org,direct
run: go test ${{ env.GO_COMMON_TEST_ARGS }} ${{ matrix.suite.testFlags }} --ci.runId=${{ runner.os }}-sec-test

Artifactory_Integration_Tests:
Expand Down Expand Up @@ -169,6 +180,8 @@ jobs:

# Test
- name: Run tests
env:
GOPROXY: https://proxy.golang.org,direct
run: go test ${{ env.GO_COMMON_TEST_ARGS }} --test.artifactory --ci.runId=${{ runner.os }}-sec-test

Xray_Commands_Integration_Tests:
Expand All @@ -195,6 +208,8 @@ jobs:

# Test
- name: Run tests
env:
GOPROXY: https://proxy.golang.org,direct
run: go test ${{ env.GO_COMMON_TEST_ARGS }} --test.xray --ci.runId=${{ runner.os }}-sec-test

Xsc_Integration_Tests:
Expand All @@ -221,6 +236,8 @@ jobs:

# Test
- name: Run tests
env:
GOPROXY: https://proxy.golang.org,direct
run: go test ${{ env.GO_COMMON_TEST_ARGS }} --test.xsc --ci.runId=${{ runner.os }}-sec-test

Binary_Scan_Command_Integration_Tests:
Expand Down Expand Up @@ -249,6 +266,8 @@ jobs:

# Test
- name: Run tests
env:
GOPROXY: https://proxy.golang.org,direct
run: go test ${{ env.GO_COMMON_TEST_ARGS }} --test.scan --ci.runId=${{ runner.os }}-sec-test

Docker_Scan_Commands_Integration_Tests:
Expand Down Expand Up @@ -277,6 +296,8 @@ jobs:

# Test
- name: Run tests
env:
GOPROXY: https://proxy.golang.org,direct
run: go test ${{ env.GO_COMMON_TEST_ARGS }} --test.dockerScan --ci.runId=${{ runner.os }}-sec-test

Other_Commands_Integration_Tests:
Expand Down Expand Up @@ -305,8 +326,10 @@ jobs:

# Test
- name: Run tests
run: go test ${{ env.GO_COMMON_TEST_ARGS }} --test.curation --test.enrich --test.maliciousScan --ci.runId=${{ runner.os }}-sec-test

env:
GOPROXY: https://proxy.golang.org,direct
run: go test ${{ env.GO_COMMON_TEST_ARGS }} --test.curation --test.enrich --test.maliciousScan --ci.runId=${{ runner.os }}-sec-test

Git_Commands_Integration_Tests:
name: "[${{ matrix.os }}] Git Commands Integration Tests"
needs: Pretest
Expand All @@ -333,4 +356,6 @@ jobs:

# Test
- name: Run tests
env:
GOPROXY: https://proxy.golang.org,direct
run: go test ${{ env.GO_COMMON_TEST_ARGS }} --test.git --ci.runId=${{ runner.os }}-sec-test
16 changes: 13 additions & 3 deletions audit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (

"github.com/jfrog/jfrog-cli-core/v2/common/format"
"github.com/jfrog/jfrog-cli-core/v2/common/progressbar"
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
coreTests "github.com/jfrog/jfrog-cli-core/v2/utils/tests"

"github.com/jfrog/jfrog-cli-security/sca/bom/buildinfo"
Expand Down Expand Up @@ -578,12 +579,21 @@ func testXrayAuditPip(t *testing.T, outFormat format.OutputFormat, requirementsF

func TestXrayAuditCocoapods(t *testing.T) {
securityIntegrationTestUtils.InitAuditCocoapodsTest(t, scangraph.CocoapodsScanMinXrayVersion)
output := testXrayAuditCocoapods(t, format.Json)
output := testXrayAuditCocoapods(t, format.Json, "cocoapods-project")
validations.VerifyJsonResults(t, output, validations.ValidationParams{Total: &validations.TotalCount{Vulnerabilities: 1}})
}

func testXrayAuditCocoapods(t *testing.T, format format.OutputFormat) string {
_, cleanUp := securityTestUtils.CreateTestProjectEnvAndChdir(t, filepath.Join(filepath.FromSlash(securityTests.GetTestResourcesPath()), "projects", "package-managers", "cocoapods"))
func TestXrayAuditCocoapodsNoLockFile(t *testing.T) {
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.

TestXrayAuditCocoapodsNoLockFile will fail noisily on Linux CI without CocoaPods — add a pod binary availability skip guard.

securityIntegrationTestUtils.InitAuditCocoapodsTest(t, scangraph.CocoapodsScanMinXrayVersion)
if coreutils.IsWindows() {
t.Skip("Skipping: CocoaPods auto-install (pod install) requires macOS/Linux with Xcode.")
}
output := testXrayAuditCocoapods(t, format.SimpleJson, "cocoapods-no-lock-file")
validations.VerifySimpleJsonResults(t, output, validations.ValidationParams{Total: &validations.TotalCount{Vulnerabilities: 1}})
}

func testXrayAuditCocoapods(t *testing.T, format format.OutputFormat, projectName string) string {
_, cleanUp := securityTestUtils.CreateTestProjectEnvAndChdir(t, filepath.Join(filepath.FromSlash(securityTests.GetTestResourcesPath()), "projects", "package-managers", "cocoapods", projectName))
defer cleanUp()
cleanUpHome := securityIntegrationTestUtils.UseTestHomeWithDefaultXrayConfig(t)
defer cleanUpHome()
Expand Down
2 changes: 1 addition & 1 deletion cli/docs/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ var flagsMap = map[string]components.Flag{
WorkingDirs: components.NewStringFlag(WorkingDirs, "A comma-separated(,) list of relative working directories, to determine the audit targets locations. If flag isn't provided, a recursive scan is triggered from the root directory of the project."),
OutputDir: components.NewStringFlag(OutputDir, "Target directory to save partial results to.", components.SetHiddenStrFlag()),
UploadRepoPath: components.NewStringFlag(UploadRepoPath, "Artifactory repository name or path to upload the cyclonedx file to. If no name or path are provided, a local generic repository will be created which will automatically be indexed by Xray.", components.WithStrDefaultValue("import-cdx-scan-results")),
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()),
SkipAutoInstall: components.NewBoolFlag(SkipAutoInstall, "Set to true to skip auto-install of dependencies in un-built modules. Currently supported only for some package managers.", components.SetHiddenBoolFlag()),
Comment thread
attiasas marked this conversation as resolved.
AllowPartialResults: components.NewBoolFlag(AllowPartialResults, "Set to true to allow partial results and continuance of the scan in case of certain errors.", components.SetHiddenBoolFlag()),
ExclusionsAudit: components.NewStringFlag(
Exclusions,
Expand Down
30 changes: 24 additions & 6 deletions sca/bom/buildinfo/technologies/cocoapods/cocoapods.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package cocoapods

import (
"fmt"
"golang.org/x/exp/slices"
"os"
"path/filepath"
"regexp"
"strings"

"golang.org/x/exp/slices"

biutils "github.com/jfrog/build-info-go/utils"
"github.com/jfrog/gofrog/datastructures"
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
"github.com/jfrog/jfrog-cli-security/sca/bom/buildinfo/technologies"
Expand All @@ -22,6 +24,9 @@ import (
// dependencies.
const (
VersionForMainModule = "0.0.0"

descriptorFileName = "Podfile"
lockFileName = "Podfile.lock"
)

var (
Expand All @@ -34,7 +39,7 @@ func GetTechDependencyLocation(directDependencyName, directDependencyVersion str
var podPositions []*sarif.Location
for _, descriptorPath := range descriptorPaths {
descriptorPath = filepath.Clean(descriptorPath)
if !strings.HasSuffix(descriptorPath, "Podfile") {
if !strings.HasSuffix(descriptorPath, descriptorFileName) {
log.Logger.Warn("Cannot support other files besides Podfile: %s", descriptorPath)
continue
}
Expand Down Expand Up @@ -92,7 +97,7 @@ func parsePodLine(line, directDependencyName, directDependencyVersion, descripto
func FixTechDependency(dependencyName, dependencyVersion, fixVersion string, descriptorPaths ...string) error {
for _, descriptorPath := range descriptorPaths {
descriptorPath = filepath.Clean(descriptorPath)
if !strings.HasSuffix(descriptorPath, "Podfile") {
if !strings.HasSuffix(descriptorPath, descriptorFileName) {
log.Logger.Warn("Cannot support other files besides Podfile: %s", descriptorPath)
continue
}
Expand Down Expand Up @@ -180,11 +185,11 @@ func extractPodsSection(filePath string) (string, error) {
}

func GetDependenciesData(currentDir string) (string, error) {
_, err := os.Stat(filepath.Join(currentDir, "Podfile.lock"))
_, err := os.Stat(filepath.Join(currentDir, lockFileName))
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.

Is this necessary? we called it and checked the existence of the lock file prior to the call to GetDependenciesData

if err != nil {
return "", err
}
result, err := extractPodsSection(filepath.Join(currentDir, "Podfile.lock"))
result, err := extractPodsSection(filepath.Join(currentDir, lockFileName))
if err != nil {
return "", err
}
Expand All @@ -199,11 +204,24 @@ func BuildDependencyTree(params technologies.BuildInfoBomGeneratorParams) (depen

packageName := filepath.Base(currentDir)
packageInfo := fmt.Sprintf("%s:%s", packageName, VersionForMainModule)
_, _, err = getPodVersionAndExecPath()
_, podExecPath, err := getPodVersionAndExecPath()
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.

This is used only inside the 'if' and if !SkipAutoInstall
we can call it inside the 'if' statement and spare the call if not required

if err != nil {
err = fmt.Errorf("failed while retrieving pod path: %s", err.Error())
return
}
// Check if lock file exists, if not run 'pod install'
Comment thread
attiasas marked this conversation as resolved.
lockFilePath := filepath.Join(currentDir, lockFileName)
if _, err := os.Stat(lockFilePath); os.IsNotExist(err) {
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.

Error variable shadowing — permission errors silently swallowed
The os.Stat result uses := in an inner scope, so when Stat returns a non-IsNotExist error (e.g. permission denied), the else if err != nil branch reads the outer err (which is nil), silently dropping
the error. Fix by using a separate statErr variable.

if params.SkipAutoInstall {
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.

No unit tests for the new auto-install / SkipAutoInstall code paths. Yarn and NuGet both test SkipAutoInstall at the unit level; at minimum, add a test asserting ErrProjectNotInstalled is returned
when SkipAutoInstall: true and no lock file exists.

return nil, nil, &biutils.ErrProjectNotInstalled{UninstalledDir: currentDir}
}
log.Debug("Running 'pod install' command to install dependencies...")
if _, err = runPodCmd(podExecPath, currentDir, []string{"install"}); err != nil {
Comment thread
attiasas marked this conversation as resolved.
return nil, nil, fmt.Errorf("failed to run 'pod install': %w", err)
}
} else if err != nil {
return nil, nil, fmt.Errorf("failed to check if lock file exists: %w", err)
}
// Calculate pod dependencies
data, err := GetDependenciesData(currentDir)
if err != nil {
Expand Down
10 changes: 5 additions & 5 deletions sca/bom/buildinfo/technologies/cocoapods/cocoapods_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (

func TestBuildCocoapodsDependencyList(t *testing.T) {
// Create and change directory to test workspace
_, cleanUp := technologies.CreateTestWorkspace(t, filepath.Join("projects", "package-managers", "cocoapods"))
_, cleanUp := technologies.CreateTestWorkspace(t, filepath.Join("projects", "package-managers", "cocoapods", "cocoapods-project"))
defer cleanUp()

// Run getModulesDependencyTrees
Expand Down Expand Up @@ -62,7 +62,7 @@ func TestBuildCocoapodsDependencyList(t *testing.T) {
}

func TestGetTechDependencyLocation(t *testing.T) {
_, cleanUp := technologies.CreateTestWorkspace(t, filepath.Join("projects", "package-managers", "cocoapods"))
_, cleanUp := technologies.CreateTestWorkspace(t, filepath.Join("projects", "package-managers", "cocoapods", "cocoapods-project"))
defer cleanUp()
currentDir, err := coreutils.GetWorkingDirectory()
assert.NoError(t, err)
Expand Down Expand Up @@ -93,7 +93,7 @@ func TestPodLineParseFoundOnlyDependencyName(t *testing.T) {
}

func TestFixTechDependencySingleLocation(t *testing.T) {
_, cleanUp := technologies.CreateTestWorkspace(t, filepath.Join("projects", "package-managers", "cocoapods"))
_, cleanUp := technologies.CreateTestWorkspace(t, filepath.Join("projects", "package-managers", "cocoapods", "cocoapods-project"))
defer cleanUp()
currentDir, err := coreutils.GetWorkingDirectory()
assert.NoError(t, err)
Expand All @@ -105,7 +105,7 @@ func TestFixTechDependencySingleLocation(t *testing.T) {
}

func TestFixTechDependencyMultipleLocations(t *testing.T) {
_, cleanUp := technologies.CreateTestWorkspace(t, filepath.Join("projects", "package-managers", "cocoapods"))
_, cleanUp := technologies.CreateTestWorkspace(t, filepath.Join("projects", "package-managers", "cocoapods", "cocoapods-project"))
defer cleanUp()
currentDir, err := coreutils.GetWorkingDirectory()
assert.NoError(t, err)
Expand All @@ -118,7 +118,7 @@ func TestFixTechDependencyMultipleLocations(t *testing.T) {
}

func TestFixTechDependencyNoLocations(t *testing.T) {
_, cleanUp := technologies.CreateTestWorkspace(t, filepath.Join("projects", "package-managers", "cocoapods"))
_, cleanUp := technologies.CreateTestWorkspace(t, filepath.Join("projects", "package-managers", "cocoapods", "cocoapods-project"))
defer cleanUp()
currentDir, err := coreutils.GetWorkingDirectory()
assert.NoError(t, err)
Expand Down
14 changes: 11 additions & 3 deletions sca/bom/buildinfo/technologies/cocoapods/podcommand.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,23 @@ type PodCommand struct {
executablePath string
}

func getPodVersionAndExecPath() (*version.Version, string, error) {
func getPodExecPath() (string, error) {
podExecPath, err := exec.LookPath("pod")
if err != nil {
return nil, "", fmt.Errorf("could not find the 'pod' executable in the system PATH %w", err)
return "", fmt.Errorf("could not find the 'pod' executable in the system PATH %w", err)
}
return podExecPath, nil
}

func getPodVersionAndExecPath() (*version.Version, string, error) {
podExecPath, err := getPodExecPath()
if err != nil {
return nil, "", err
}
log.Debug("Using pod executable:", podExecPath)
versionData, err := runPodCmd(podExecPath, "", []string{"--version"})
if err != nil {
return nil, "", err
return nil, "", fmt.Errorf("failed to get pod version: %w", err)
}
return version.NewVersion(strings.TrimSpace(string(versionData))), podExecPath, nil
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
platform :ios, '9.0'

target 'Test' do
use_frameworks!
pod 'GoogleSignIn', '~> 6.2.4'
pod 'AppAuth', '~> 1.7.5'
pod 'nanopb', '~> 0.3.0'

end
Loading
Loading