diff --git a/go.mod b/go.mod index 8ed9ccd71..2f2afd68e 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/jedib0t/go-pretty/v6 v6.4.7 github.com/jfrog/build-info-go v1.9.10 github.com/jfrog/gofrog v1.3.0 - github.com/jfrog/jfrog-client-go v1.32.1 + github.com/jfrog/jfrog-client-go v1.32.2 github.com/magiconair/properties v1.8.7 github.com/manifoldco/promptui v0.9.0 github.com/owenrumney/go-sarif/v2 v2.2.0 @@ -43,13 +43,14 @@ require ( github.com/andybalholm/brotli v1.0.1 // indirect github.com/cloudflare/circl v1.3.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.4.1 // indirect - github.com/go-git/go-git/v5 v5.8.1 // indirect + github.com/go-git/go-billy/v5 v5.5.0 // indirect + github.com/go-git/go-git/v5 v5.9.0 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.2 // indirect @@ -92,7 +93,3 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) - -// replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20230905120411-62d1bdd4eb38 - -// replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.2.6-0.20230418122323-2bf299dd6d27 diff --git a/go.sum b/go.sum index c0825b56f..02c23bd61 100644 --- a/go.sum +++ b/go.sum @@ -84,14 +84,15 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= -github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -108,11 +109,11 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= -github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= +github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= +github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= -github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A= -github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo= +github.com/go-git/go-git/v5 v5.9.0 h1:cD9SFA7sHVRdJ7AYck1ZaAa/yeuBvGPxwXDL8cxrObY= +github.com/go-git/go-git/v5 v5.9.0/go.mod h1:RKIqga24sWdMGZF+1Ekv9kylsDz6LzdTSI2s/OsZWE0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -198,8 +199,8 @@ github.com/jfrog/build-info-go v1.9.10 h1:uXnDLVxpqxoAMpXcki00QaBB+M2BoGMMpHODPk github.com/jfrog/build-info-go v1.9.10/go.mod h1:ujJ8XQZMdT2tMkLSMJNyDd1pCY+duwHdjV+9or9FLIg= github.com/jfrog/gofrog v1.3.0 h1:o4zgsBZE4QyDbz2M7D4K6fXPTBJht+8lE87mS9bw7Gk= github.com/jfrog/gofrog v1.3.0/go.mod h1:IFMc+V/yf7rA5WZ74CSbXe+Lgf0iApEQLxRZVzKRUR0= -github.com/jfrog/jfrog-client-go v1.32.1 h1:RQmuPSLsF5222vZJzwkgHSZMMJF83ExS7SwIvh4P+H8= -github.com/jfrog/jfrog-client-go v1.32.1/go.mod h1:362+oa7uTTYurzBs1L0dmUTlLo7uhpAU/pwM5Zb9clg= +github.com/jfrog/jfrog-client-go v1.32.2 h1:t0ceWCtFri+xsa0D2ESqD/itcovlxBXCky1A1MJ4P2I= +github.com/jfrog/jfrog-client-go v1.32.2/go.mod h1:UewnwkIf/77HzBgwCPzOHZCK6V/Nw5/JwdzN/tRb4aU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= @@ -215,12 +216,10 @@ github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= @@ -248,9 +247,9 @@ github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dz github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/owenrumney/go-sarif v1.1.1/go.mod h1:dNDiPlF04ESR/6fHlPyq7gHKmrM0sHUvAGjsoh8ZH0U= github.com/owenrumney/go-sarif/v2 v2.2.0 h1:1DmZaijK0HBZCR1fgcDSGa7VzYkU9NDmbZ7qC2QfUjE= github.com/owenrumney/go-sarif/v2 v2.2.0/go.mod h1:MSqMMx9WqlBSY7pXoOZWgEsVB4FDNfhcaXDA1j6Sr+w= @@ -275,7 +274,7 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= @@ -684,7 +683,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/utils/config/tokenrefresh.go b/utils/config/tokenrefresh.go index 31ed2433e..298a3917d 100644 --- a/utils/config/tokenrefresh.go +++ b/utils/config/tokenrefresh.go @@ -31,11 +31,11 @@ const ( type TokenType string func AccessTokenRefreshPreRequestInterceptor(fields *auth.CommonConfigFields, httpClientDetails *httputils.HttpClientDetails) (err error) { - return tokenRefreshPreRequestInterceptor(fields, httpClientDetails, AccessToken, auth.InviteRefreshBeforeExpiryMinutes) + return tokenRefreshPreRequestInterceptor(fields, httpClientDetails, AccessToken, auth.RefreshPlatformTokenBeforeExpiryMinutes) } func ArtifactoryTokenRefreshPreRequestInterceptor(fields *auth.CommonConfigFields, httpClientDetails *httputils.HttpClientDetails) (err error) { - return tokenRefreshPreRequestInterceptor(fields, httpClientDetails, ArtifactoryToken, auth.RefreshBeforeExpiryMinutes) + return tokenRefreshPreRequestInterceptor(fields, httpClientDetails, ArtifactoryToken, auth.RefreshArtifactoryTokenBeforeExpiryMinutes) } func tokenRefreshPreRequestInterceptor(fields *auth.CommonConfigFields, httpClientDetails *httputils.HttpClientDetails, tokenType TokenType, refreshBeforeExpiryMinutes int64) (err error) { diff --git a/xray/commands/audit/audit.go b/xray/commands/audit/audit.go index 41eca4ae7..2303ef0a2 100644 --- a/xray/commands/audit/audit.go +++ b/xray/commands/audit/audit.go @@ -91,7 +91,8 @@ func (auditCmd *AuditCommand) Run() (err error) { SetWorkingDirs(workingDirs). SetMinSeverityFilter(auditCmd.minSeverityFilter). SetFixableOnly(auditCmd.fixableOnly). - SetGraphBasicParams(auditCmd.AuditBasicParams) + SetGraphBasicParams(auditCmd.AuditBasicParams). + SetThirdPartyApplicabilityScan(auditCmd.thirdPartyApplicabilityScan) auditResults, err := RunAudit(auditParams) if err != nil { return @@ -186,7 +187,7 @@ func RunAudit(auditParams *AuditParams) (results *Results, err error) { // Run scanners only if the user is entitled for Advanced Security if results.ExtendedScanResults.EntitledForJas { - results.JasError = runJasScannersAndSetResults(results.ExtendedScanResults, auditParams.DirectDependencies(), serverDetails, auditParams.workingDirs, auditParams.Progress(), auditParams.xrayGraphScanParams.MultiScanId) + results.JasError = runJasScannersAndSetResults(results.ExtendedScanResults, auditParams.DirectDependencies(), serverDetails, auditParams.workingDirs, auditParams.Progress(), auditParams.xrayGraphScanParams.MultiScanId, auditParams.thirdPartyApplicabilityScan) } return } diff --git a/xray/commands/audit/auditparams.go b/xray/commands/audit/auditparams.go index 69c83a55f..9dc42e3c2 100644 --- a/xray/commands/audit/auditparams.go +++ b/xray/commands/audit/auditparams.go @@ -13,6 +13,8 @@ type AuditParams struct { minSeverityFilter string *xrayutils.AuditBasicParams xrayVersion string + // Include third party dependencies source code in the applicability scan. + thirdPartyApplicabilityScan bool } func NewAuditParams() *AuditParams { @@ -75,3 +77,8 @@ func (params *AuditParams) SetMinSeverityFilter(minSeverityFilter string) *Audit params.minSeverityFilter = minSeverityFilter return params } + +func (params *AuditParams) SetThirdPartyApplicabilityScan(includeThirdPartyDeps bool) *AuditParams { + params.thirdPartyApplicabilityScan = includeThirdPartyDeps + return params +} diff --git a/xray/commands/audit/jas/applicability/applicabilitymanager.go b/xray/commands/audit/jas/applicability/applicabilitymanager.go index 8c46f4865..b038e4371 100644 --- a/xray/commands/audit/jas/applicability/applicabilitymanager.go +++ b/xray/commands/audit/jas/applicability/applicabilitymanager.go @@ -25,6 +25,7 @@ type ApplicabilityScanManager struct { directDependenciesCves []string xrayResults []services.ScanResponse scanner *jas.JasScanner + thirdPartyScan bool } // The getApplicabilityScanResults function runs the applicability scan flow, which includes the following steps: @@ -37,8 +38,8 @@ type ApplicabilityScanManager struct { // bool: true if the user is entitled to the applicability scan, false otherwise. // error: An error object (if any). func RunApplicabilityScan(xrayResults []services.ScanResponse, directDependencies []string, - scannedTechnologies []coreutils.Technology, scanner *jas.JasScanner) (results []*sarif.Run, err error) { - applicabilityScanManager := newApplicabilityScanManager(xrayResults, directDependencies, scanner) + scannedTechnologies []coreutils.Technology, scanner *jas.JasScanner, thirdPartyContextualAnalysis bool) (results []*sarif.Run, err error) { + applicabilityScanManager := newApplicabilityScanManager(xrayResults, directDependencies, scanner, thirdPartyContextualAnalysis) if !applicabilityScanManager.shouldRunApplicabilityScan(scannedTechnologies) { log.Debug("The technologies that have been scanned are currently not supported for contextual analysis scanning, or we couldn't find any vulnerable direct dependencies. Skipping....") return @@ -51,13 +52,14 @@ func RunApplicabilityScan(xrayResults []services.ScanResponse, directDependencie return } -func newApplicabilityScanManager(xrayScanResults []services.ScanResponse, directDependencies []string, scanner *jas.JasScanner) (manager *ApplicabilityScanManager) { +func newApplicabilityScanManager(xrayScanResults []services.ScanResponse, directDependencies []string, scanner *jas.JasScanner, thirdPartyScan bool) (manager *ApplicabilityScanManager) { directDependenciesCves := extractDirectDependenciesCvesFromScan(xrayScanResults, directDependencies) return &ApplicabilityScanManager{ applicabilityScanResults: []*sarif.Run{}, directDependenciesCves: directDependenciesCves, xrayResults: xrayScanResults, scanner: scanner, + thirdPartyScan: thirdPartyScan, } } @@ -140,6 +142,11 @@ type scanConfiguration struct { } func (asm *ApplicabilityScanManager) createConfigFile(workingDir string) error { + skipDirs := jas.SkippedDirs + if asm.thirdPartyScan { + log.Info("Including node modules folder in applicability scan") + skipDirs = removeElementFromSlice(skipDirs, jas.NodeModulesPattern) + } configFileContent := applicabilityScanConfig{ Scans: []scanConfiguration{ { @@ -148,7 +155,7 @@ func (asm *ApplicabilityScanManager) createConfigFile(workingDir string) error { Type: applicabilityScanType, GrepDisable: false, CveWhitelist: asm.directDependenciesCves, - SkippedDirs: jas.SkippedDirs, + SkippedDirs: skipDirs, }, }, } @@ -160,3 +167,11 @@ func (asm *ApplicabilityScanManager) createConfigFile(workingDir string) error { func (asm *ApplicabilityScanManager) runAnalyzerManager() error { return asm.scanner.AnalyzerManager.Exec(asm.scanner.ConfigFileName, applicabilityScanCommand, filepath.Dir(asm.scanner.AnalyzerManager.AnalyzerManagerFullPath), asm.scanner.ServerDetails) } + +func removeElementFromSlice(skipDirs []string, element string) []string { + deleteIndex := slices.Index(skipDirs, element) + if deleteIndex == -1 { + return skipDirs + } + return slices.Delete(skipDirs, deleteIndex, deleteIndex+1) +} diff --git a/xray/commands/audit/jas/applicability/applicabilitymanager_test.go b/xray/commands/audit/jas/applicability/applicabilitymanager_test.go index f887763c5..e4a232c44 100644 --- a/xray/commands/audit/jas/applicability/applicabilitymanager_test.go +++ b/xray/commands/audit/jas/applicability/applicabilitymanager_test.go @@ -17,7 +17,7 @@ func TestNewApplicabilityScanManager_InputIsValid(t *testing.T) { scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() // Act - applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockDirectDependencies, scanner) + applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockDirectDependencies, scanner, false) // Assert if assert.NotNil(t, applicabilityManager) { @@ -31,7 +31,7 @@ func TestNewApplicabilityScanManager_DependencyTreeDoesntExist(t *testing.T) { scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() // Act - applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, nil, scanner) + applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, nil, scanner, false) // Assert if assert.NotNil(t, applicabilityManager) { @@ -68,9 +68,14 @@ func TestNewApplicabilityScanManager_NoDirectDependenciesInScan(t *testing.T) { // Act scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() - applicabilityManager := newApplicabilityScanManager(noDirectDependenciesResults, mockDirectDependencies, scanner) + applicabilityManager := newApplicabilityScanManager(noDirectDependenciesResults, mockDirectDependencies, scanner, false) + assertApplicabilityScanner(t, applicabilityManager) + // ThirdPartyContextual shouldn't change anything here as this is not npm. + applicabilityManager = newApplicabilityScanManager(noDirectDependenciesResults, mockDirectDependencies, scanner, true) + assertApplicabilityScanner(t, applicabilityManager) +} - // Assert +func assertApplicabilityScanner(t *testing.T, applicabilityManager *ApplicabilityScanManager) { if assert.NotNil(t, applicabilityManager) { assert.NotEmpty(t, applicabilityManager.scanner.ConfigFileName) assert.NotEmpty(t, applicabilityManager.scanner.ResultsFileName) @@ -84,7 +89,7 @@ func TestNewApplicabilityScanManager_MultipleDependencyTrees(t *testing.T) { scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() // Act - applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockMultiRootDirectDependencies, scanner) + applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockMultiRootDirectDependencies, scanner, false) // Assert if assert.NotNil(t, applicabilityManager) { @@ -110,7 +115,7 @@ func TestNewApplicabilityScanManager_ViolationsDontExistInResults(t *testing.T) defer cleanUp() // Act - applicabilityManager := newApplicabilityScanManager(noViolationScanResponse, mockDirectDependencies, scanner) + applicabilityManager := newApplicabilityScanManager(noViolationScanResponse, mockDirectDependencies, scanner, false) // Assert if assert.NotNil(t, applicabilityManager) { @@ -136,7 +141,7 @@ func TestNewApplicabilityScanManager_VulnerabilitiesDontExist(t *testing.T) { defer cleanUp() // Act - applicabilityManager := newApplicabilityScanManager(noVulnerabilitiesScanResponse, mockDirectDependencies, scanner) + applicabilityManager := newApplicabilityScanManager(noVulnerabilitiesScanResponse, mockDirectDependencies, scanner, false) // Assert if assert.NotNil(t, applicabilityManager) { @@ -150,8 +155,7 @@ func TestApplicabilityScanManager_ShouldRun_TechnologiesNotEligibleForScan(t *te scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() - results, err := RunApplicabilityScan(jas.FakeBasicXrayResults, mockDirectDependencies, - []coreutils.Technology{coreutils.Nuget, coreutils.Go}, scanner) + results, err := RunApplicabilityScan(jas.FakeBasicXrayResults, mockDirectDependencies, []coreutils.Technology{coreutils.Nuget, coreutils.Go}, scanner, false) // Assert assert.Nil(t, results) @@ -163,7 +167,7 @@ func TestApplicabilityScanManager_ShouldRun_ScanResultsAreEmpty(t *testing.T) { scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() - applicabilityManager := newApplicabilityScanManager(nil, mockDirectDependencies, scanner) + applicabilityManager := newApplicabilityScanManager(nil, mockDirectDependencies, scanner, false) // Assert eligible := applicabilityManager.shouldRunApplicabilityScan([]coreutils.Technology{coreutils.Npm}) @@ -247,7 +251,7 @@ func TestCreateConfigFile_VerifyFileWasCreated(t *testing.T) { scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() - applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, []string{"issueId_1_direct_dependency", "issueId_2_direct_dependency"}, scanner) + applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, []string{"issueId_1_direct_dependency", "issueId_2_direct_dependency"}, scanner, false) currWd, err := coreutils.GetWorkingDirectory() assert.NoError(t, err) @@ -271,7 +275,7 @@ func TestParseResults_EmptyResults_AllCvesShouldGetUnknown(t *testing.T) { scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() - applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockDirectDependencies, scanner) + applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockDirectDependencies, scanner, false) applicabilityManager.scanner.ResultsFileName = filepath.Join(jas.GetTestDataPath(), "applicability-scan", "empty-results.sarif") // Act @@ -288,7 +292,7 @@ func TestParseResults_ApplicableCveExist(t *testing.T) { // Arrange scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() - applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockDirectDependencies, scanner) + applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockDirectDependencies, scanner, false) applicabilityManager.scanner.ResultsFileName = filepath.Join(jas.GetTestDataPath(), "applicability-scan", "applicable-cve-results.sarif") // Act @@ -305,7 +309,7 @@ func TestParseResults_AllCvesNotApplicable(t *testing.T) { // Arrange scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() - applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockDirectDependencies, scanner) + applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockDirectDependencies, scanner, false) applicabilityManager.scanner.ResultsFileName = filepath.Join(jas.GetTestDataPath(), "applicability-scan", "no-applicable-cves-results.sarif") // Act diff --git a/xray/commands/audit/jas/common.go b/xray/commands/audit/jas/common.go index 3814e06d0..1f4082bfe 100644 --- a/xray/commands/audit/jas/common.go +++ b/xray/commands/audit/jas/common.go @@ -19,8 +19,12 @@ import ( "gopkg.in/yaml.v3" ) +const ( + NodeModulesPattern = "**/*node_modules*/**" +) + var ( - SkippedDirs = []string{"**/*test*/**", "**/*venv*/**", "**/*node_modules*/**", "**/*target*/**"} + SkippedDirs = []string{"**/*test*/**", "**/*venv*/**", NodeModulesPattern, "**/*target*/**"} mapSeverityToScore = map[string]string{ "": "0.0", diff --git a/xray/commands/audit/jasrunner.go b/xray/commands/audit/jasrunner.go index 9f917004b..76a59ff8e 100644 --- a/xray/commands/audit/jasrunner.go +++ b/xray/commands/audit/jasrunner.go @@ -14,7 +14,7 @@ import ( ) func runJasScannersAndSetResults(scanResults *utils.ExtendedScanResults, directDependencies []string, - serverDetails *config.ServerDetails, workingDirs []string, progress io.ProgressMgr, multiScanId string) (err error) { + serverDetails *config.ServerDetails, workingDirs []string, progress io.ProgressMgr, multiScanId string, thirdPartyApplicabilityScan bool) (err error) { if serverDetails == nil || len(serverDetails.Url) == 0 { log.Warn("To include 'Advanced Security' scan as part of the audit output, please run the 'jf c add' command before running this command.") return @@ -30,10 +30,14 @@ func runJasScannersAndSetResults(scanResults *utils.ExtendedScanResults, directD if progress != nil { progress.SetHeadlineMsg("Running applicability scanning") } - scanResults.ApplicabilityScanResults, err = applicability.RunApplicabilityScan(scanResults.XrayResults, directDependencies, scanResults.ScannedTechnologies, scanner) + scanResults.ApplicabilityScanResults, err = applicability.RunApplicabilityScan(scanResults.XrayResults, directDependencies, scanResults.ScannedTechnologies, scanner, thirdPartyApplicabilityScan) if err != nil { return } + // Don't execute other scanners when scanning third party dependencies. + if thirdPartyApplicabilityScan { + return + } if progress != nil { progress.SetHeadlineMsg("Running secrets scanning") } diff --git a/xray/commands/audit/jasrunner_test.go b/xray/commands/audit/jasrunner_test.go index b6bd121df..0d92f1b18 100644 --- a/xray/commands/audit/jasrunner_test.go +++ b/xray/commands/audit/jasrunner_test.go @@ -22,14 +22,14 @@ func TestGetExtendedScanResults_AnalyzerManagerDoesntExist(t *testing.T) { assert.NoError(t, os.Unsetenv(coreutils.HomeDir)) }() scanResults := &utils.ExtendedScanResults{XrayResults: jas.FakeBasicXrayResults, ScannedTechnologies: []coreutils.Technology{coreutils.Yarn}} - err = runJasScannersAndSetResults(scanResults, []string{"issueId_1_direct_dependency", "issueId_2_direct_dependency"}, &jas.FakeServerDetails, nil, nil, "") + err = runJasScannersAndSetResults(scanResults, []string{"issueId_1_direct_dependency", "issueId_2_direct_dependency"}, &jas.FakeServerDetails, nil, nil, "", false) // Expect error: assert.Error(t, err) } func TestGetExtendedScanResults_ServerNotValid(t *testing.T) { scanResults := &utils.ExtendedScanResults{XrayResults: jas.FakeBasicXrayResults, ScannedTechnologies: []coreutils.Technology{coreutils.Pip}} - err := runJasScannersAndSetResults(scanResults, []string{"issueId_1_direct_dependency", "issueId_2_direct_dependency"}, nil, nil, nil, "") + err := runJasScannersAndSetResults(scanResults, []string{"issueId_1_direct_dependency", "issueId_2_direct_dependency"}, nil, nil, nil, "", false) assert.NoError(t, err) } @@ -37,7 +37,7 @@ func TestGetExtendedScanResults_AnalyzerManagerReturnsError(t *testing.T) { mockDirectDependencies := []string{"issueId_2_direct_dependency", "issueId_1_direct_dependency"} assert.NoError(t, rtutils.DownloadAnalyzerManagerIfNeeded()) scanResults := &utils.ExtendedScanResults{XrayResults: jas.FakeBasicXrayResults, ScannedTechnologies: []coreutils.Technology{coreutils.Yarn}} - err := runJasScannersAndSetResults(scanResults, mockDirectDependencies, &jas.FakeServerDetails, nil, nil, "") + err := runJasScannersAndSetResults(scanResults, mockDirectDependencies, &jas.FakeServerDetails, nil, nil, "", false) // Expect error: assert.ErrorContains(t, err, "failed to run Applicability scan") diff --git a/xray/commands/audit/sca/npm/npm.go b/xray/commands/audit/sca/npm/npm.go index 7a646e349..269c6993e 100644 --- a/xray/commands/audit/sca/npm/npm.go +++ b/xray/commands/audit/sca/npm/npm.go @@ -5,14 +5,14 @@ import ( buildinfo "github.com/jfrog/build-info-go/entities" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/sca" + "github.com/jfrog/jfrog-cli-core/v2/xray/utils" "github.com/jfrog/jfrog-client-go/utils/log" xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils" "golang.org/x/exp/slices" ) const ( - npmPackageTypeIdentifier = "npm://" - ignoreScriptsFlag = "--ignore-scripts" + ignoreScriptsFlag = "--ignore-scripts" ) func BuildDependencyTree(npmArgs []string) (dependencyTrees []*xrayUtils.GraphNode, uniqueDeps []string, err error) { @@ -58,9 +58,9 @@ func addIgnoreScriptsFlag(npmArgs []string) []string { func parseNpmDependenciesList(dependencies []buildinfo.Dependency, packageInfo *biutils.PackageInfo) (*xrayUtils.GraphNode, []string) { treeMap := make(map[string][]string) for _, dependency := range dependencies { - dependencyId := npmPackageTypeIdentifier + dependency.Id + dependencyId := utils.NpmPackageTypeIdentifier + dependency.Id for _, requestedByNode := range dependency.RequestedBy { - parent := npmPackageTypeIdentifier + requestedByNode[0] + parent := utils.NpmPackageTypeIdentifier + requestedByNode[0] if children, ok := treeMap[parent]; ok { treeMap[parent] = appendUniqueChild(children, dependencyId) } else { @@ -68,7 +68,7 @@ func parseNpmDependenciesList(dependencies []buildinfo.Dependency, packageInfo * } } } - return sca.BuildXrayDependencyTree(treeMap, npmPackageTypeIdentifier+packageInfo.BuildInfoModuleId()) + return sca.BuildXrayDependencyTree(treeMap, utils.NpmPackageTypeIdentifier+packageInfo.BuildInfoModuleId()) } func appendUniqueChild(children []string, candidateDependency string) []string { diff --git a/xray/commands/audit/sca/npm/npm_test.go b/xray/commands/audit/sca/npm/npm_test.go index aaa9ea47c..ff4d0aa01 100644 --- a/xray/commands/audit/sca/npm/npm_test.go +++ b/xray/commands/audit/sca/npm/npm_test.go @@ -3,6 +3,7 @@ package npm import ( "encoding/json" "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/sca" + "github.com/jfrog/jfrog-cli-core/v2/xray/utils" xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils" "os" "testing" @@ -102,7 +103,7 @@ func TestParseNpmDependenciesList(t *testing.T) { } expectedUniqueDeps := []string{xrayDependenciesTree.Id} for _, dep := range dependencies { - expectedUniqueDeps = append(expectedUniqueDeps, npmPackageTypeIdentifier+dep.Id) + expectedUniqueDeps = append(expectedUniqueDeps, utils.NpmPackageTypeIdentifier+dep.Id) } assert.ElementsMatch(t, uniqueDeps, expectedUniqueDeps, "First is actual, Second is Expected") diff --git a/xray/commands/audit/sca/yarn/yarn.go b/xray/commands/audit/sca/yarn/yarn.go index 9df1333c9..ed15121dc 100644 --- a/xray/commands/audit/sca/yarn/yarn.go +++ b/xray/commands/audit/sca/yarn/yarn.go @@ -4,15 +4,12 @@ import ( biUtils "github.com/jfrog/build-info-go/build/utils" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/sca" + "github.com/jfrog/jfrog-cli-core/v2/xray/utils" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/jfrog/jfrog-client-go/utils/log" xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils" ) -const ( - npmPackageTypeIdentifier = "npm://" -) - func BuildDependencyTree() (dependencyTrees []*xrayUtils.GraphNode, uniqueDeps []string, err error) { currentDir, err := coreutils.GetWorkingDirectory() if err != nil { @@ -55,5 +52,5 @@ func parseYarnDependenciesMap(dependencies map[string]*biUtils.YarnDependency, r } func getXrayDependencyId(yarnDependency *biUtils.YarnDependency) string { - return npmPackageTypeIdentifier + yarnDependency.Name() + ":" + yarnDependency.Details.Version + return utils.NpmPackageTypeIdentifier + yarnDependency.Name() + ":" + yarnDependency.Details.Version } diff --git a/xray/commands/audit/sca/yarn/yarn_test.go b/xray/commands/audit/sca/yarn/yarn_test.go index bb24f3c0d..aadcdc814 100644 --- a/xray/commands/audit/sca/yarn/yarn_test.go +++ b/xray/commands/audit/sca/yarn/yarn_test.go @@ -1,6 +1,7 @@ package yarn import ( + "github.com/jfrog/jfrog-cli-core/v2/xray/utils" xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils" "github.com/stretchr/testify/assert" "testing" @@ -18,30 +19,30 @@ func TestParseYarnDependenciesList(t *testing.T) { "pack5@npm:5.0.0": {Value: "pack5@npm:5.0.0", Details: biutils.YarnDepDetails{Version: "5.0.0", Dependencies: []biutils.YarnDependencyPointer{{Locator: "pack2@npm:2.0.0"}}}}, } - rootXrayId := npmPackageTypeIdentifier + "@jfrog/pack3:3.0.0" + rootXrayId := utils.NpmPackageTypeIdentifier + "@jfrog/pack3:3.0.0" expectedTree := &xrayUtils.GraphNode{ Id: rootXrayId, Nodes: []*xrayUtils.GraphNode{ - {Id: npmPackageTypeIdentifier + "pack1:1.0.0", + {Id: utils.NpmPackageTypeIdentifier + "pack1:1.0.0", Nodes: []*xrayUtils.GraphNode{ - {Id: npmPackageTypeIdentifier + "pack4:4.0.0", + {Id: utils.NpmPackageTypeIdentifier + "pack4:4.0.0", Nodes: []*xrayUtils.GraphNode{}}, }}, - {Id: npmPackageTypeIdentifier + "pack2:2.0.0", + {Id: utils.NpmPackageTypeIdentifier + "pack2:2.0.0", Nodes: []*xrayUtils.GraphNode{ - {Id: npmPackageTypeIdentifier + "pack4:4.0.0", + {Id: utils.NpmPackageTypeIdentifier + "pack4:4.0.0", Nodes: []*xrayUtils.GraphNode{}}, - {Id: npmPackageTypeIdentifier + "pack5:5.0.0", + {Id: utils.NpmPackageTypeIdentifier + "pack5:5.0.0", Nodes: []*xrayUtils.GraphNode{}}, }}, }, } expectedUniqueDeps := []string{ - npmPackageTypeIdentifier + "pack1:1.0.0", - npmPackageTypeIdentifier + "pack2:2.0.0", - npmPackageTypeIdentifier + "pack4:4.0.0", - npmPackageTypeIdentifier + "pack5:5.0.0", - npmPackageTypeIdentifier + "@jfrog/pack3:3.0.0", + utils.NpmPackageTypeIdentifier + "pack1:1.0.0", + utils.NpmPackageTypeIdentifier + "pack2:2.0.0", + utils.NpmPackageTypeIdentifier + "pack4:4.0.0", + utils.NpmPackageTypeIdentifier + "pack5:5.0.0", + utils.NpmPackageTypeIdentifier + "@jfrog/pack3:3.0.0", } xrayDependenciesTree, uniqueDeps := parseYarnDependenciesMap(yarnDependencies, rootXrayId) diff --git a/xray/commands/audit/scarunner.go b/xray/commands/audit/scarunner.go index 7f209fa29..f78ac8dc2 100644 --- a/xray/commands/audit/scarunner.go +++ b/xray/commands/audit/scarunner.go @@ -96,15 +96,14 @@ func runScaScanOnWorkingDir(params *AuditParams, results *Results, workingDir, r continue } techResults = sca.BuildImpactPathsForScanResponse(techResults, fullDependencyTrees) - var directDependencies []string - if tech == coreutils.Pip { - // When building pip dependency tree using pipdeptree, some of the direct dependencies are recognized as transitive and missed by the CA scanner. - // Our solution for this case is to send all dependencies to the CA scanner. - directDependencies = getDirectDependenciesFromTree([]*xrayCmdUtils.GraphNode{flattenTree}) + + var dependenciesForApplicabilityScan []string + if shouldUseAllDependencies(params.thirdPartyApplicabilityScan, tech) { + dependenciesForApplicabilityScan = getDirectDependenciesFromTree([]*xrayCmdUtils.GraphNode{flattenTree}) } else { - directDependencies = getDirectDependenciesFromTree(fullDependencyTrees) + dependenciesForApplicabilityScan = getDirectDependenciesFromTree(fullDependencyTrees) } - params.AppendDirectDependencies(directDependencies) + params.AppendDependenciesForApplicabilityScan(dependenciesForApplicabilityScan) results.ExtendedScanResults.XrayResults = append(results.ExtendedScanResults.XrayResults, techResults...) if !results.IsMultipleRootProject { @@ -115,6 +114,14 @@ func runScaScanOnWorkingDir(params *AuditParams, results *Results, workingDir, r return } +// When building pip dependency tree using pipdeptree, some of the direct dependencies are recognized as transitive and missed by the CA scanner. +// Our solution for this case is to send all dependencies to the CA scanner. +// When thirdPartyApplicabilityScan is true, use flatten graph to include all the dependencies in applicability scanning. +// Only npm is supported for this flag. +func shouldUseAllDependencies(thirdPartyApplicabilityScan bool, tech coreutils.Technology) bool { + return tech == coreutils.Pip || (thirdPartyApplicabilityScan && tech == coreutils.Npm) +} + // This function retrieves the dependency trees of the scanned project and extracts a set that contains only the direct dependencies. func getDirectDependenciesFromTree(dependencyTrees []*xrayCmdUtils.GraphNode) []string { directDependencies := datastructures.MakeSet[string]() diff --git a/xray/utils/auditbasicparams.go b/xray/utils/auditbasicparams.go index f8e3abba3..ff990e9fc 100644 --- a/xray/utils/auditbasicparams.go +++ b/xray/utils/auditbasicparams.go @@ -6,26 +6,26 @@ import ( ) type AuditBasicParams struct { - serverDetails *config.ServerDetails - outputFormat OutputFormat - progress ioUtils.ProgressMgr - directDependencies []string - excludeTestDependencies bool - useWrapper bool - insecureTls bool - pipRequirementsFile string - technologies []string - args []string - depsRepo string - ignoreConfigFile bool + serverDetails *config.ServerDetails + outputFormat OutputFormat + progress ioUtils.ProgressMgr + dependenciesForApplicabilityScan []string + excludeTestDependencies bool + useWrapper bool + insecureTls bool + pipRequirementsFile string + technologies []string + args []string + depsRepo string + ignoreConfigFile bool } func (abp *AuditBasicParams) DirectDependencies() []string { - return abp.directDependencies + return abp.dependenciesForApplicabilityScan } -func (abp *AuditBasicParams) AppendDirectDependencies(directDependencies []string) *AuditBasicParams { - abp.directDependencies = append(abp.directDependencies, directDependencies...) +func (abp *AuditBasicParams) AppendDependenciesForApplicabilityScan(directDependencies []string) *AuditBasicParams { + abp.dependenciesForApplicabilityScan = append(abp.dependenciesForApplicabilityScan, directDependencies...) return abp } diff --git a/xray/utils/resultstable.go b/xray/utils/resultstable.go index 6a61fb1ad..4f098e114 100644 --- a/xray/utils/resultstable.go +++ b/xray/utils/resultstable.go @@ -3,6 +3,7 @@ package utils import ( "fmt" "os" + "path/filepath" "sort" "strconv" "strings" @@ -26,6 +27,8 @@ const ( rootIndex = 0 directDependencyIndex = 1 directDependencyPathLength = 2 + nodeModules = "node_modules" + NpmPackageTypeIdentifier = "npm://" ) // PrintViolationsTable prints the violations in 4 tables: security violations, license compliance violations, operational risk violations and ignore rule URLs. @@ -88,13 +91,13 @@ func prepareViolations(violations []services.Violation, extendedResults *Extende switch violation.ViolationType { case "security": cves := convertCves(violation.Cves) - applicableValue := getApplicableCveValue(extendedResults, cves) if extendedResults.EntitledForJas { for i := range cves { - cves[i].Applicability = getCveApplicability(cves[i], extendedResults.ApplicabilityScanResults) + cves[i].Applicability = getCveApplicabilityField(cves[i], extendedResults.ApplicabilityScanResults, violation.Components) } } - currSeverity := GetSeverity(violation.Severity, applicableValue) + applicabilityStatus := getApplicableCveStatus(extendedResults.EntitledForJas, extendedResults.ApplicabilityScanResults, cves) + currSeverity := GetSeverity(violation.Severity, applicabilityStatus) jfrogResearchInfo := convertJfrogResearchInformation(violation.ExtendedInformation) for compIndex := 0; compIndex < len(impactedPackagesNames); compIndex++ { securityViolationsRows = append(securityViolationsRows, @@ -113,7 +116,7 @@ func prepareViolations(violations []services.Violation, extendedResults *Extende JfrogResearchInformation: jfrogResearchInfo, ImpactPaths: impactPaths[compIndex], Technology: coreutils.Technology(violation.Technology), - Applicable: printApplicableCveValue(applicableValue, isTable), + Applicable: printApplicableCveValue(applicabilityStatus, isTable), }, ) } @@ -210,13 +213,13 @@ func prepareVulnerabilities(vulnerabilities []services.Vulnerability, extendedRe return nil, err } cves := convertCves(vulnerability.Cves) - applicableValue := getApplicableCveValue(extendedResults, cves) if extendedResults.EntitledForJas { for i := range cves { - cves[i].Applicability = getCveApplicability(cves[i], extendedResults.ApplicabilityScanResults) + cves[i].Applicability = getCveApplicabilityField(cves[i], extendedResults.ApplicabilityScanResults, vulnerability.Components) } } - currSeverity := GetSeverity(vulnerability.Severity, applicableValue) + applicabilityStatus := getApplicableCveStatus(extendedResults.EntitledForJas, extendedResults.ApplicabilityScanResults, cves) + currSeverity := GetSeverity(vulnerability.Severity, applicabilityStatus) jfrogResearchInfo := convertJfrogResearchInformation(vulnerability.ExtendedInformation) for compIndex := 0; compIndex < len(impactedPackagesNames); compIndex++ { vulnerabilitiesRows = append(vulnerabilitiesRows, @@ -235,7 +238,7 @@ func prepareVulnerabilities(vulnerabilities []services.Vulnerability, extendedRe JfrogResearchInformation: jfrogResearchInfo, ImpactPaths: impactPaths[compIndex], Technology: coreutils.Technology(vulnerability.Technology), - Applicable: printApplicableCveValue(applicableValue, isTable), + Applicable: printApplicableCveValue(applicabilityStatus, isTable), }, ) } @@ -925,71 +928,75 @@ func convertCves(cves []services.Cve) []formats.CveRow { // If at least one cve is applicable - final value is applicable // Else if at least one cve is undetermined - final value is undetermined // Else (case when all cves aren't applicable) -> final value is not applicable -func getApplicableCveValue(extendedResults *ExtendedScanResults, xrayCves []formats.CveRow) ApplicabilityStatus { - if !extendedResults.EntitledForJas || len(extendedResults.ApplicabilityScanResults) == 0 { +func getApplicableCveStatus(entitledForJas bool, applicabilityScanResults []*sarif.Run, cves []formats.CveRow) ApplicabilityStatus { + if !entitledForJas || len(applicabilityScanResults) == 0 { return NotScanned } - if len(xrayCves) == 0 { + if len(cves) == 0 { return ApplicabilityUndetermined } - cveExistsInResult := false - finalApplicableValue := NotApplicable - for _, applicabilityRun := range extendedResults.ApplicabilityScanResults { - for _, cve := range xrayCves { - relatedResults := GetResultsByRuleId(applicabilityRun, CveToApplicabilityRuleId(cve.Id)) - if len(relatedResults) == 0 { - finalApplicableValue = ApplicabilityUndetermined + foundUndetermined := false + for _, cve := range cves { + if cve.Applicability != nil { + if cve.Applicability.Status == string(Applicable) { + return Applicable } - for _, relatedResult := range relatedResults { - cveExistsInResult = true - if IsApplicableResult(relatedResult) { - return Applicable - } + if cve.Applicability.Status == string(ApplicabilityUndetermined) { + foundUndetermined = true } } } - if cveExistsInResult { - return finalApplicableValue + if foundUndetermined { + return ApplicabilityUndetermined } - return ApplicabilityUndetermined + return NotApplicable } -func getCveApplicability(cve formats.CveRow, applicabilityScanResults []*sarif.Run) *formats.Applicability { - applicability := &formats.Applicability{Status: string(ApplicabilityUndetermined)} +func getCveApplicabilityField(cve formats.CveRow, applicabilityScanResults []*sarif.Run, components map[string]services.Component) *formats.Applicability { + if len(applicabilityScanResults) == 0 { + return nil + } + + applicability := formats.Applicability{} + resultFound := false for _, applicabilityRun := range applicabilityScanResults { - foundResult, _ := applicabilityRun.GetResultByRuleId(CveToApplicabilityRuleId(cve.Id)) - if foundResult == nil { + result, _ := applicabilityRun.GetResultByRuleId(CveToApplicabilityRuleId(cve.Id)) + if result == nil { continue } - applicability = &formats.Applicability{} - if IsApplicableResult(foundResult) { - applicability.Status = string(Applicable) - } else { - applicability.Status = string(NotApplicable) - } - - foundRule, _ := applicabilityRun.GetRuleById(CveToApplicabilityRuleId(cve.Id)) - if foundRule != nil { - applicability.ScannerDescription = GetRuleFullDescription(foundRule) + resultFound = true + rule, _ := applicabilityRun.GetRuleById(CveToApplicabilityRuleId(cve.Id)) + if rule != nil { + applicability.ScannerDescription = GetRuleFullDescription(rule) } - // Add new evidences from locations - for _, location := range foundResult.Locations { + for _, location := range result.Locations { + fileName := GetRelativeLocationFileName(location, applicabilityRun.Invocations) + if shouldDisqualifyEvidence(components, fileName) { + continue + } applicability.Evidence = append(applicability.Evidence, formats.Evidence{ Location: formats.Location{ - File: GetRelativeLocationFileName(location, applicabilityRun.Invocations), + File: fileName, StartLine: GetLocationStartLine(location), StartColumn: GetLocationStartColumn(location), EndLine: GetLocationEndLine(location), EndColumn: GetLocationEndColumn(location), Snippet: GetLocationSnippet(location), }, - Reason: GetResultMsgText(foundResult), + Reason: GetResultMsgText(result), }) } - break } - return applicability + switch { + case !resultFound: + applicability.Status = string(ApplicabilityUndetermined) + case len(applicability.Evidence) == 0: + applicability.Status = string(NotApplicable) + default: + applicability.Status = string(Applicable) + } + return &applicability } func printApplicableCveValue(applicableValue ApplicabilityStatus, isTable bool) string { @@ -1002,3 +1009,39 @@ func printApplicableCveValue(applicableValue ApplicabilityStatus, isTable bool) } return string(applicableValue) } + +// Relevant only when "third-party-contextual-analysis" flag is on, +// which mean we scan the environment folders as well (node_modules for example...) +// When a certain package is reported applicable, and the evidence found +// is inside the source code of the same package, we should disqualify it. +// +// For example, +// Cve applicability was found inside the 'mquery' package. +// filePath = myProject/node_modules/mquery/badCode.js , disqualify = True. +// Disqualify the above evidence, as the reported applicability is used inside its own package. +// +// filePath = myProject/node_modules/mpath/badCode.js , disqualify = False. +// Found use of a badCode inside the node_modules from a different package, report applicable. +func shouldDisqualifyEvidence(components map[string]services.Component, evidenceFilePath string) (disqualify bool) { + for key := range components { + if !strings.HasPrefix(key, NpmPackageTypeIdentifier) { + return + } + dependencyName := extractDependencyNameFromComponent(key, NpmPackageTypeIdentifier) + // Check both Unix & Windows paths. + if strings.Contains(evidenceFilePath, nodeModules+"/"+dependencyName) || strings.Contains(evidenceFilePath, filepath.Join(nodeModules, dependencyName)) { + return true + } + } + return +} + +func extractDependencyNameFromComponent(key string, techIdentifier string) (dependencyName string) { + packageAndVersion := strings.TrimPrefix(key, techIdentifier) + split := strings.Split(packageAndVersion, ":") + if len(split) < 2 { + return + } + dependencyName = split[0] + return +} diff --git a/xray/utils/resultstable_test.go b/xray/utils/resultstable_test.go index 7ed50f24f..d7b7c5056 100644 --- a/xray/utils/resultstable_test.go +++ b/xray/utils/resultstable_test.go @@ -520,10 +520,10 @@ func TestGetApplicableCveValue(t *testing.T) { for _, testCase := range testCases { cves := convertCves(testCase.cves) - applicableValue := getApplicableCveValue(testCase.scanResults, cves) for i := range cves { - cves[i].Applicability = getCveApplicability(cves[i], testCase.scanResults.ApplicabilityScanResults) + cves[i].Applicability = getCveApplicabilityField(cves[i], testCase.scanResults.ApplicabilityScanResults, nil) } + applicableValue := getApplicableCveStatus(testCase.scanResults.EntitledForJas, testCase.scanResults.ApplicabilityScanResults, cves) assert.Equal(t, testCase.expectedResult, applicableValue) if assert.True(t, len(testCase.expectedCves) == len(cves)) { for i := range cves { @@ -637,6 +637,43 @@ func TestSortVulnerabilityOrViolationRows(t *testing.T) { } } +func TestShouldDisqualifyEvidence(t *testing.T) { + testCases := []struct { + name string + component map[string]services.Component + filePath string + disqualify bool + }{ + { + name: "package folders", + component: map[string]services.Component{"npm://protobufjs:6.11.2": {}}, + filePath: "file:///Users/jfrog/test/node_modules/protobufjs/src/badCode.js", + disqualify: true, + }, { + name: "nested folders", + component: map[string]services.Component{"npm://protobufjs:6.11.2": {}}, + filePath: "file:///Users/jfrog/test/node_modules/someDep/node_modules/protobufjs/src/badCode.js", + disqualify: true, + }, { + name: "applicability in node modules", + component: map[string]services.Component{"npm://protobufjs:6.11.2": {}}, + filePath: "file:///Users/jfrog/test/node_modules/mquery/src/badCode.js", + disqualify: false, + }, { + // Only npm supported + name: "not npm", + component: map[string]services.Component{"yarn://protobufjs:6.11.2": {}}, + filePath: "file:///Users/jfrog/test/node_modules/protobufjs/src/badCode.js", + disqualify: false, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.disqualify, shouldDisqualifyEvidence(tc.component, tc.filePath)) + }) + } +} + func newBoolPtr(v bool) *bool { return &v } diff --git a/xray/utils/resultwriter.go b/xray/utils/resultwriter.go index dc611d057..80433a734 100644 --- a/xray/utils/resultwriter.go +++ b/xray/utils/resultwriter.go @@ -208,7 +208,7 @@ func addXrayCveIssueToSarifRun(cves []formats.CveRow, issueId, severity, file st if err != nil { return err } - cveId := getCves(cves, issueId) + cveId := GetIssueIdentifier(cves, issueId) msg := getVulnerabilityOrViolationSarifHeadline(impactedDependencyName, impactedDependencyVersion, cveId) location := sarif.NewLocation().WithPhysicalLocation(sarif.NewPhysicalLocation().WithArtifactLocation(sarif.NewArtifactLocation().WithUri(file))) @@ -292,20 +292,20 @@ func convertScanToSimpleJson(extendedResults *ExtendedScanResults, errors []form return jsonTable, nil } -func getCves(cvesRow []formats.CveRow, issueId string) string { - var cvesStr string +func GetIssueIdentifier(cvesRow []formats.CveRow, issueId string) string { + var identifier string if len(cvesRow) != 0 { var cvesBuilder strings.Builder for _, cve := range cvesRow { cvesBuilder.WriteString(cve.Id + ", ") } - cvesStr = strings.TrimSuffix(cvesBuilder.String(), ", ") + identifier = strings.TrimSuffix(cvesBuilder.String(), ", ") } - if cvesStr == "" { - cvesStr = issueId + if identifier == "" { + identifier = issueId } - return cvesStr + return identifier } func getVulnerabilityOrViolationSarifHeadline(depName, version, key string) string { diff --git a/xray/utils/resultwriter_test.go b/xray/utils/resultwriter_test.go index 6f13d6a03..57458ea34 100644 --- a/xray/utils/resultwriter_test.go +++ b/xray/utils/resultwriter_test.go @@ -12,13 +12,13 @@ func TestGetVulnerabilityOrViolationSarifHeadline(t *testing.T) { assert.NotEqual(t, "[CVE-2022-1234] loadsh 1.4.1", getVulnerabilityOrViolationSarifHeadline("loadsh", "1.2.1", "CVE-2022-1234")) } -func TestGetCves(t *testing.T) { +func TestGetIssueIdentifier(t *testing.T) { issueId := "XRAY-123456" cvesRow := []formats.CveRow{{Id: "CVE-2022-1234"}} - assert.Equal(t, "CVE-2022-1234", getCves(cvesRow, issueId)) + assert.Equal(t, "CVE-2022-1234", GetIssueIdentifier(cvesRow, issueId)) cvesRow = append(cvesRow, formats.CveRow{Id: "CVE-2019-1234"}) - assert.Equal(t, "CVE-2022-1234, CVE-2019-1234", getCves(cvesRow, issueId)) - assert.Equal(t, issueId, getCves(nil, issueId)) + assert.Equal(t, "CVE-2022-1234, CVE-2019-1234", GetIssueIdentifier(cvesRow, issueId)) + assert.Equal(t, issueId, GetIssueIdentifier(nil, issueId)) } func TestGetDirectDependenciesFormatted(t *testing.T) {