Skip to content
Open
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions commands/audit/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/jfrog/jfrog-cli-security/jas"
"github.com/jfrog/jfrog-cli-security/jas/applicability"
"github.com/jfrog/jfrog-cli-security/jas/runner"
"github.com/jfrog/jfrog-cli-security/jas/sast"
"github.com/jfrog/jfrog-cli-security/jas/secrets"
"github.com/jfrog/jfrog-cli-security/policy"
"github.com/jfrog/jfrog-cli-security/policy/enforcer"
Expand Down Expand Up @@ -739,6 +740,7 @@ func createJasScansTask(auditParallelRunner *utils.SecurityParallelRunner, scanR
ApplicableScanType: applicability.ApplicabilityScannerType,
SignedDescriptions: getSignedDescriptions(auditParams.OutputFormat()),
SastRules: auditParams.SastRules(),
SastChangedFiles: sast.SastChangedFilesForTarget(scanResults.GitContext, targetResult.Target, scanResults.GetCommonParentPath()),
ScanResults: targetResult,
TargetCount: len(scanResults.Targets),
TargetOutputDir: auditParams.scanResultsOutputDir,
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ require (
gopkg.in/warnings.v0 v0.1.2 // indirect
)

// replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go master
// attiasas:add_chagned_files_to_git_ctx
replace github.com/jfrog/jfrog-client-go => github.com/attiasas/jfrog-client-go v0.0.0-20260420064147-858bbdf9ec5b

// replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 master

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/attiasas/jfrog-client-go v0.0.0-20260420064147-858bbdf9ec5b h1:JGfUd7uoxR6VcYtEzYPW7vFkA6VJ6wnL4mAkrcxEkHM=
github.com/attiasas/jfrog-client-go v0.0.0-20260420064147-858bbdf9ec5b/go.mod h1:sCE06+GngPoyrGO0c+vmhgMoVSP83UMNiZnIuNPzU8U=
github.com/beevik/etree v1.6.0 h1:u8Kwy8pp9D9XeITj2Z0XtA5qqZEmtJtuXZRQi+j03eE=
github.com/beevik/etree v1.6.0/go.mod h1:bh4zJxiIr62SOf9pRzN7UUYaEDa9HEKafK25+sLc0Gc=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
Expand Down Expand Up @@ -173,8 +175,6 @@ github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260408205330-fb3f40fbcd22 h1:X
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260408205330-fb3f40fbcd22/go.mod h1:KSJZO+tguFpGG4TE2Ut2rmOk1j03RrqHQ7E33FrsEt4=
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260402104745-7a0bc2c11d63 h1:rvEiuETYgy7VbQFmf1QeYTcG0Sp4Lr+1QgrVQzLV58Q=
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260402104745-7a0bc2c11d63/go.mod h1:RLLUO+oGDq88e5DPtP/KK2sVgMF32OuoRdVMxSFfb30=
github.com/jfrog/jfrog-client-go v1.55.1-0.20260401130923-f5a15b584a0d h1:H9orUmxbClfYcVwP3yefNJxc6XJIvZp0k3fSBaMOOCc=
github.com/jfrog/jfrog-client-go v1.55.1-0.20260401130923-f5a15b584a0d/go.mod h1:sCE06+GngPoyrGO0c+vmhgMoVSP83UMNiZnIuNPzU8U=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY=
Expand Down
3 changes: 2 additions & 1 deletion jas/runner/jasrunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type JasRunnerParams struct {
ScansToPerform []utils.SubScanType
// Diff mode flags
SourceResultsToCompare *results.TargetResults
SastChangedFiles []string
DiffMode bool
// Secret scan flags
SecretsScanType secrets.SecretsScanType
Expand Down Expand Up @@ -175,7 +176,7 @@ func runSastScan(params *JasRunnerParams) parallel.TaskFunc {
defer func() {
params.Runner.JasScannersWg.Done()
}()
vulnerabilitiesResults, violationsResults, err := sast.RunSastScan(params.Scanner, params.Module, params.SignedDescriptions, params.SastRules, params.TargetCount, threadId, getSourceRunsToCompare(params, jasutils.Sast)...)
vulnerabilitiesResults, violationsResults, err := sast.RunSastScan(params.Scanner, params.Module, params.SignedDescriptions, params.SastRules, params.SastChangedFiles, params.TargetCount, threadId, getSourceRunsToCompare(params, jasutils.Sast)...)
params.Runner.ResultsMu.Lock()
defer params.Runner.ResultsMu.Unlock()
// We first add the scan results and only then check for errors, so we can store the exit code in order to report it in the end
Expand Down
96 changes: 90 additions & 6 deletions jas/sast/sastscanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package sast

import (
"fmt"
"os"
"path/filepath"
"strings"
"time"

jfrogappsconfig "github.com/jfrog/jfrog-apps-config/go"
Expand All @@ -11,6 +13,7 @@ import (
"github.com/jfrog/jfrog-cli-security/utils/formats/sarifutils"
"github.com/jfrog/jfrog-cli-security/utils/jasutils"
"github.com/jfrog/jfrog-client-go/utils/log"
xscservices "github.com/jfrog/jfrog-client-go/xsc/services"
"github.com/owenrumney/go-sarif/v3/pkg/report/v210/sarif"
"golang.org/x/exp/maps"
)
Expand All @@ -19,10 +22,86 @@ const (
sastScannerType = "sast"
sastScanCommand = "zd"
sastDocsUrlSuffix = "sast-1"

// ChangedFilesModeEnvVar enables using GitContext changed files (scoped per target) as SAST scan roots.
ChangedFilesModeEnvVar = "JAS_SAST_CHANGED_FILES_MODE"
)

// SastChangedFilesForTarget returns absolute paths of git changed files that belong to targetPath
// (relative to commonParent), when ChangedFilesModeEnvVar is "true". Returns nil if nothing matches
// or if gitCtx, commonParent, or targetPath are unusable.
func SastChangedFilesForTarget(gitCtx *xscservices.XscGitInfoContext, targetPath, commonParent string) []string {
if gitCtx == nil || os.Getenv(ChangedFilesModeEnvVar) != "true" {
return nil
}
if len(gitCtx.ChangedFiles) == 0 {
return nil
}
if strings.TrimSpace(commonParent) == "" || strings.TrimSpace(targetPath) == "" {
return nil
}
commonAbs, err := filepath.Abs(filepath.Clean(commonParent))
if err != nil {
return nil
}
targetRel := filepath.ToSlash(utils.GetRelativePath(targetPath, commonParent))

var out []string
for _, cf := range gitCtx.ChangedFiles {
cfSlash, ok := normalizeRepoRelativeChangedPath(commonAbs, cf)
if !ok {
continue
}
if !changedFileBelongsToTarget(targetRel, cfSlash) {
continue
}
joined := filepath.Join(commonAbs, filepath.FromSlash(cfSlash))
absPath, err := filepath.Abs(filepath.Clean(joined))
if err != nil {
continue
}
out = append(out, absPath)
}
if len(out) == 0 {
return nil
}
return out
}

func normalizeRepoRelativeChangedPath(commonAbs, cf string) (slashPath string, ok bool) {
cf = strings.TrimSpace(cf)
if cf == "" {
return "", false
}
if filepath.IsAbs(cf) {
cleaned := filepath.Clean(cf)
r, err := filepath.Rel(commonAbs, cleaned)
if err != nil {
return "", false
}
r = filepath.ToSlash(filepath.Clean(r))
if r == ".." || strings.HasPrefix(r, "../") {
return "", false
}
return r, true
}
return filepath.ToSlash(filepath.Clean(cf)), true
}

func changedFileBelongsToTarget(targetRel, cfSlash string) bool {
if targetRel == "" {
return true
}
if cfSlash == targetRel {
return true
}
return strings.HasPrefix(cfSlash, targetRel+"/")
}

type SastScanManager struct {
scanner *jas.JasScanner
scanner *jas.JasScanner

sastChangedFiles []string
signedDescriptions bool
sastRules string

Expand All @@ -31,12 +110,12 @@ type SastScanManager struct {
resultsFileName string
}

func RunSastScan(scanner *jas.JasScanner, module jfrogappsconfig.Module, signedDescriptions bool, sastRules string, targetCount, threadId int, resultsToCompare ...*sarif.Run) (vulnerabilitiesResults []*sarif.Run, violationsResults []*sarif.Run, err error) {
func RunSastScan(scanner *jas.JasScanner, module jfrogappsconfig.Module, signedDescriptions bool, sastRules string, sastChangedFiles []string, targetCount, threadId int, resultsToCompare ...*sarif.Run) (vulnerabilitiesResults []*sarif.Run, violationsResults []*sarif.Run, err error) {
var scannerTempDir string
if scannerTempDir, err = jas.CreateScannerTempDirectory(scanner, jasutils.Sast.String(), threadId); err != nil {
return
}
sastScanManager, err := newSastScanManager(scanner, scannerTempDir, signedDescriptions, sastRules, resultsToCompare...)
sastScanManager, err := newSastScanManager(scanner, scannerTempDir, signedDescriptions, sastRules, sastChangedFiles, resultsToCompare...)
if err != nil {
return
}
Expand All @@ -49,11 +128,12 @@ func RunSastScan(scanner *jas.JasScanner, module jfrogappsconfig.Module, signedD
return
}

func newSastScanManager(scanner *jas.JasScanner, scannerTempDir string, signedDescriptions bool, sastRules string, resultsToCompare ...*sarif.Run) (manager *SastScanManager, err error) {
func newSastScanManager(scanner *jas.JasScanner, scannerTempDir string, signedDescriptions bool, sastRules string, sastChangedFiles []string, resultsToCompare ...*sarif.Run) (manager *SastScanManager, err error) {
manager = &SastScanManager{
scanner: scanner,
signedDescriptions: signedDescriptions,
sastRules: sastRules,
sastChangedFiles: sastChangedFiles,
configFileName: filepath.Join(scannerTempDir, "config.yaml"),
resultsFileName: filepath.Join(scannerTempDir, "results.sarif"),
}
Expand All @@ -69,7 +149,7 @@ func newSastScanManager(scanner *jas.JasScanner, scannerTempDir string, signedDe
}

func (ssm *SastScanManager) Run(module jfrogappsconfig.Module) (vulnerabilitiesSarifRuns []*sarif.Run, violationsSarifRuns []*sarif.Run, err error) {
if err = ssm.createConfigFile(module, ssm.signedDescriptions, ssm.scanner.ScannersExclusions.SastExcludePatterns, ssm.scanner.Exclusions...); err != nil {
if err = ssm.createConfigFile(module, ssm.signedDescriptions, ssm.sastChangedFiles, ssm.scanner.ScannersExclusions.SastExcludePatterns, ssm.scanner.Exclusions...); err != nil {
return
}
if err = ssm.runAnalyzerManager(filepath.Dir(ssm.scanner.AnalyzerManager.AnalyzerManagerFullPath)); err != nil {
Expand Down Expand Up @@ -104,7 +184,7 @@ type sastParameters struct {
SignedDescriptions bool `yaml:"signed_descriptions,omitempty"`
}

func (ssm *SastScanManager) createConfigFile(module jfrogappsconfig.Module, signedDescriptions bool, centralConfigExclusions []string, exclusions ...string) error {
func (ssm *SastScanManager) createConfigFile(module jfrogappsconfig.Module, signedDescriptions bool, sastChangedFiles []string, centralConfigExclusions []string, exclusions ...string) error {
sastScanner := module.Scanners.Sast
if sastScanner == nil {
sastScanner = &jfrogappsconfig.SastScanner{}
Expand All @@ -113,6 +193,10 @@ func (ssm *SastScanManager) createConfigFile(module jfrogappsconfig.Module, sign
if err != nil {
return err
}
if len(sastChangedFiles) > 0 && os.Getenv(ChangedFilesModeEnvVar) == "true" {
log.Debug(fmt.Sprintf("Using SAST Changed Files mode with %d changed files", len(sastChangedFiles)))
roots = sastChangedFiles
}
configFileContent := sastScanConfig{
Scans: []scanConfiguration{
{
Expand Down
Loading
Loading