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

Enable conan support #781

Open
wants to merge 19 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
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
5 changes: 5 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ jobs:
distribution: "adopt"
java-version: "11"

- name: Install Conan
run: |
python -m pip install conan
conan profile detect

# Generate mocks
- name: Generate mocks
run: go generate ./...
Expand Down
2 changes: 2 additions & 0 deletions packagehandlers/commonpackagehandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ func GetCompatiblePackageHandler(vulnDetails *utils.VulnerabilityDetails, detail
handler = &GradlePackageHandler{}
case techutils.Pnpm:
handler = &PnpmPackageHandler{}
case techutils.Conan:
handler = &ConanPackageHandler{}
default:
handler = &UnsupportedPackageHandler{}
}
Expand Down
79 changes: 79 additions & 0 deletions packagehandlers/conanpackagehandler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package packagehandlers

import (
"fmt"
"github.com/jfrog/frogbot/v2/utils"
"github.com/jfrog/jfrog-client-go/utils/log"
"os"
"strings"
)

const (
conanFileTxt = "conanfile.txt"
conanFilePy = "conanfile.py"
)

type ConanPackageHandler struct {
CommonPackageHandler
}

func (conan *ConanPackageHandler) UpdateDependency(vulnDetails *utils.VulnerabilityDetails) error {
if vulnDetails.IsDirectDependency {
return conan.updateDirectDependency(vulnDetails)
} else {
return &utils.ErrUnsupportedFix{
PackageName: vulnDetails.ImpactedDependencyName,
FixedVersion: vulnDetails.SuggestedFixedVersion,
ErrorType: utils.IndirectDependencyFixNotSupported,
}
}
}

func (conan *ConanPackageHandler) updateDirectDependency(vulnDetails *utils.VulnerabilityDetails) (err error) {
orto17 marked this conversation as resolved.
Show resolved Hide resolved
conanDescriptors, err := conan.CommonPackageHandler.GetAllDescriptorFilesFullPaths([]string{conanFileTxt, conanFilePy})
if err != nil {
err = fmt.Errorf("failed while searching for Conan descriptor files in project: %s", err.Error())
return
}
isAnyDescriptorFileChanged := false
for _, descriptor := range conanDescriptors {
var isFileChanged bool
isFileChanged, err = conan.updateConanFile(descriptor, vulnDetails)
if err != nil {
return
}
isAnyDescriptorFileChanged = isAnyDescriptorFileChanged || isFileChanged
}
if !isAnyDescriptorFileChanged {
err = fmt.Errorf("impacted package '%s' was not found or could not be fixed in all descriptor files", vulnDetails.ImpactedDependencyName)
} else {
conan.logNoInstallationMessage()
}
return
}

func (conan *ConanPackageHandler) updateConanFile(conanFile string, vulnDetails *utils.VulnerabilityDetails) (isFileChanged bool, err error) {
orto17 marked this conversation as resolved.
Show resolved Hide resolved
data, err := os.ReadFile(conanFile)
if err != nil {
return false, fmt.Errorf("an error occurred while attempting to read the requirements file '%s': %s\n", conanFile, err.Error())
}
currentFile := string(data)
fixedPackage := vulnDetails.ImpactedDependencyName + "/" + vulnDetails.SuggestedFixedVersion
impactedDependency := vulnDetails.ImpactedDependencyName + "/" + vulnDetails.ImpactedDependencyVersion
fixedFile := strings.Replace(currentFile, impactedDependency, strings.ToLower(fixedPackage), 1)

if fixedFile == currentFile {
log.Info(fmt.Sprintf("impacted dependency '%s' not found in descriptor '%s', moving to the next descriptor if exists...", impactedDependency, conanFile))
orto17 marked this conversation as resolved.
Show resolved Hide resolved
return false, nil
}
if err = os.WriteFile(conanFile, []byte(fixedFile), 0600); err != nil {
err = fmt.Errorf("an error occured while writing the fixed version of %s to the requirements file '%s': %s", conanFile, vulnDetails.ImpactedDependencyName, err.Error())
orto17 marked this conversation as resolved.
Show resolved Hide resolved
}
isFileChanged = true
return
}

func (conan *ConanPackageHandler) logNoInstallationMessage() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is redundant and is being called only once, we can simple move this to the function where it is called.

log.Info("Requirements file was updated with a suggested fix version, but no installation was performed. " +
"In order to update the dependencies, please run 'conan install' command")
}
37 changes: 37 additions & 0 deletions packagehandlers/packagehandlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,43 @@ func TestUpdateDependency(t *testing.T) {
descriptorsToCheck: []string{"package.json"},
},
},

// Conan test cases
orto17 marked this conversation as resolved.
Show resolved Hide resolved
{
orto17 marked this conversation as resolved.
Show resolved Hide resolved
{
vulnDetails: &utils.VulnerabilityDetails{
IsDirectDependency: true,
SuggestedFixedVersion: "3.0.14",
VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: techutils.Conan, ImpactedDependencyDetails: formats.ImpactedDependencyDetails{ImpactedDependencyName: "openssl", ImpactedDependencyVersion: "3.0.9"}},
},
scanDetails: scanDetails,
fixSupported: true,
testDirName: "conan",
descriptorsToCheck: []string{"conanfile.txt"},
},
{
vulnDetails: &utils.VulnerabilityDetails{
SuggestedFixedVersion: "3.0.14",
IsDirectDependency: true,
VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: techutils.Conan, ImpactedDependencyDetails: formats.ImpactedDependencyDetails{ImpactedDependencyName: "openssl", ImpactedDependencyVersion: "3.0.9"}},
},
scanDetails: scanDetails,
fixSupported: true,
testDirName: "conan",
descriptorsToCheck: []string{"conanfile.py"},
},
{
vulnDetails: &utils.VulnerabilityDetails{
SuggestedFixedVersion: "3.0.14",
IsDirectDependency: true,
VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: techutils.Conan, ImpactedDependencyDetails: formats.ImpactedDependencyDetails{ImpactedDependencyName: "openssl", ImpactedDependencyVersion: "3.0.9"}},
},
scanDetails: scanDetails,
fixSupported: true,
testDirName: "conan",
descriptorsToCheck: []string{"conanfile.py", "conanfile.txt"},
},
},
orto17 marked this conversation as resolved.
Show resolved Hide resolved
}

for _, testBatch := range testCases {
Expand Down
23 changes: 23 additions & 0 deletions testdata/projects/conan/conanfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from conan import ConanFile

class MyPackage(ConanFile):
name = "my_package"
version = "1.0.0"

requires = [
"zlib/1.3.1",
"openssl/3.0.9",
"meson/1.4.1"
]

def build_requirements(self):
self.build_requires("meson/1.4.1")

def build(self):
pass

def package(self):
pass

def package_info(self):
pass
4 changes: 4 additions & 0 deletions testdata/projects/conan/conanfile.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[requires]
zlib/1.3.1
openssl/3.0.9
meson/1.4.1
8 changes: 8 additions & 0 deletions testdata/projects/conan/profile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=gnu17
compiler.libcxx=libstdc++11
compiler.version=11
os=Linux
Loading