Skip to content

Commit cca14ae

Browse files
committed
XRAY-128408 - Add Sast rules flag to audit command
1 parent 015f1b4 commit cca14ae

File tree

7 files changed

+75
-10
lines changed

7 files changed

+75
-10
lines changed

cli/docs/flags.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ const (
5656
Sast = "sast"
5757
Secrets = "secrets"
5858
WithoutCA = "without-contextual-analysis"
59+
60+
// Sast related flags
61+
SastRules = "sast-rules"
5962
)
6063

6164
const (
@@ -170,7 +173,7 @@ var commandFlags = map[string][]string{
170173
useWrapperAudit, DepType, RequirementsFile, Fail, ExtendedTable, WorkingDirs, ExclusionsAudit, Mvn, Gradle, Npm,
171174
Pnpm, Yarn, Go, Swift, Cocoapods, Nuget, Pip, Pipenv, Poetry, MinSeverity, FixableOnly, ThirdPartyContextualAnalysis, Threads,
172175
Sca, Iac, Sast, Secrets, WithoutCA, ScanVuln, SecretValidation, OutputDir, SkipAutoInstall, AllowPartialResults, MaxTreeDepth,
173-
StaticSca, XrayLibPluginBinaryCustomPath, AnalyzerManagerCustomPath,
176+
StaticSca, XrayLibPluginBinaryCustomPath, AnalyzerManagerCustomPath, SastRules,
174177
},
175178
UploadCdx: {
176179
UploadRepoPath, uploadProjectKey,
@@ -186,7 +189,7 @@ var commandFlags = map[string][]string{
186189
// Output params
187190
Licenses, OutputFormat, ExtendedTable, OutputDir,
188191
// Scan Logic params
189-
StaticSca, XrayLibPluginBinaryCustomPath, AnalyzerManagerCustomPath,
192+
StaticSca, XrayLibPluginBinaryCustomPath, AnalyzerManagerCustomPath, SastRules,
190193
},
191194
CurationAudit: {
192195
CurationOutput, WorkingDirs, Threads, RequirementsFile, InsecureTls, useWrapperAudit,
@@ -305,6 +308,8 @@ var flagsMap = map[string]components.Flag{
305308
WithoutCA: components.NewBoolFlag(WithoutCA, fmt.Sprintf("Selective scanners mode: Disable Contextual Analysis scanner after SCA. Relevant only with --%s flag.", Sca)),
306309
SecretValidation: components.NewBoolFlag(SecretValidation, fmt.Sprintf("Selective scanners mode: Triggers token validation on found secrets. Relevant only with --%s flag.", Secrets)),
307310

311+
SastRules: components.NewStringFlag(SastRules, "Absolute Path to a local custom rules file. The file should be in JSON format and contain the additional custom rules to be applied during the scan."),
312+
308313
// Git flags
309314
InputFile: components.NewStringFlag(InputFile, "Path to an input file in YAML format contains multiple git providers. With this option, all other scm flags will be ignored and only git servers mentioned in the file will be examined.."),
310315
ScmType: components.NewStringFlag(ScmType, fmt.Sprintf("SCM type. Possible values are: %s.", contributors.NewScmType().GetValidScmTypeString()), components.SetMandatory()),

cli/scancommands.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package cli
33
import (
44
"errors"
55
"fmt"
6+
"io/fs"
67
"os"
8+
"slices"
79
"strings"
810

911
buildInfoUtils "github.com/jfrog/build-info-go/utils"
@@ -27,6 +29,7 @@ import (
2729
dockerScanDocs "github.com/jfrog/jfrog-cli-security/cli/docs/scan/dockerscan"
2830
scanDocs "github.com/jfrog/jfrog-cli-security/cli/docs/scan/scan"
2931
uploadCdxDocs "github.com/jfrog/jfrog-cli-security/cli/docs/upload"
32+
"github.com/jfrog/jfrog-cli-security/utils"
3033

3134
"github.com/jfrog/jfrog-cli-security/commands/enrich"
3235
"github.com/jfrog/jfrog-cli-security/commands/source_mcp"
@@ -407,6 +410,27 @@ func AuditCmd(c *components.Context) error {
407410
auditCmd.SetScansToPerform(subScans)
408411
}
409412

413+
// Validate that there is a sast scan before setting the sast rules
414+
if c.GetStringFlagValue(flags.SastRules) != "" {
415+
if !slices.Contains(auditCmd.ScansToPerform(), utils.SubScanType(flags.Sast)) {
416+
return pluginsCommon.PrintHelpAndReturnError(fmt.Sprintf("flag '--%s' can only be used with '--%s'", flags.SastRules, flags.Sast), c)
417+
}
418+
419+
sastRules := c.GetStringFlagValue(flags.SastRules)
420+
if _, err := os.Stat(sastRules); err != nil {
421+
switch {
422+
case errors.Is(err, fs.ErrNotExist):
423+
return pluginsCommon.PrintHelpAndReturnError(fmt.Sprintf("file '%s' does not exist", sastRules), c)
424+
case errors.Is(err, fs.ErrPermission):
425+
return pluginsCommon.PrintHelpAndReturnError(fmt.Sprintf("permission denied to read sast rules file '%s': %v", sastRules, err), c)
426+
default:
427+
return pluginsCommon.PrintHelpAndReturnError(fmt.Sprintf("error reading sast rules file '%s': %v", sastRules, err), c)
428+
}
429+
}
430+
431+
auditCmd.SetSastRules(sastRules)
432+
}
433+
410434
threads, err := pluginsCommon.GetThreadsCount(c)
411435
if err != nil {
412436
return err

commands/audit/audit.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,7 @@ func createJasScansTask(auditParallelRunner *utils.SecurityParallelRunner, scanR
625625
ThirdPartyApplicabilityScan: auditParams.thirdPartyApplicabilityScan,
626626
ApplicableScanType: applicability.ApplicabilityScannerType,
627627
SignedDescriptions: getSignedDescriptions(auditParams.OutputFormat()),
628+
SastRules: auditParams.SastRules(),
628629
ScanResults: targetResult,
629630
TargetOutputDir: auditParams.scanResultsOutputDir,
630631
AllowPartialResults: auditParams.AllowPartialResults(),

commands/audit/auditbasicparams.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ type AuditParamsInterface interface {
4949
AllowPartialResults() bool
5050
GetXrayVersion() string
5151
GetConfigProfile() *xscservices.ConfigProfile
52+
SastRules() string
53+
SetSastRules(sastRules string) *AuditBasicParams
5254
}
5355

5456
type AuditBasicParams struct {
@@ -79,6 +81,7 @@ type AuditBasicParams struct {
7981
xrayVersion string
8082
xscVersion string
8183
configProfile *xscservices.ConfigProfile
84+
sastRules string
8285
}
8386

8487
func (abp *AuditBasicParams) DirectDependencies() *[]string {
@@ -330,3 +333,12 @@ func (abp *AuditBasicParams) SetConfigProfile(profile *xscservices.ConfigProfile
330333
func (abp *AuditBasicParams) GetConfigProfile() *xscservices.ConfigProfile {
331334
return abp.configProfile
332335
}
336+
337+
func (abp *AuditBasicParams) SetSastRules(sastRules string) *AuditBasicParams {
338+
abp.sastRules = sastRules
339+
return abp
340+
}
341+
342+
func (abp *AuditBasicParams) SastRules() string {
343+
return abp.sastRules
344+
}

jas/runner/jasrunner.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type JasRunnerParams struct {
4343
ThirdPartyApplicabilityScan bool
4444
// SAST scan flags
4545
SignedDescriptions bool
46+
SastRules string
4647
// Outputs
4748
ScanResults *results.TargetResults
4849
TargetOutputDir string
@@ -177,7 +178,7 @@ func runSastScan(params *JasRunnerParams) parallel.TaskFunc {
177178
defer func() {
178179
params.Runner.JasScannersWg.Done()
179180
}()
180-
vulnerabilitiesResults, violationsResults, err := sast.RunSastScan(params.Scanner, params.Module, params.SignedDescriptions, threadId, getSourceRunsToCompare(params, jasutils.Sast)...)
181+
vulnerabilitiesResults, violationsResults, err := sast.RunSastScan(params.Scanner, params.Module, params.SignedDescriptions, params.SastRules, threadId, getSourceRunsToCompare(params, jasutils.Sast)...)
181182
params.Runner.ResultsMu.Lock()
182183
defer params.Runner.ResultsMu.Unlock()
183184
// 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

jas/sast/sastscanner.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,19 @@ const (
2424
type SastScanManager struct {
2525
scanner *jas.JasScanner
2626
signedDescriptions bool
27+
sastRules string
2728

2829
resultsToCompareFileName string
2930
configFileName string
3031
resultsFileName string
3132
}
3233

33-
func RunSastScan(scanner *jas.JasScanner, module jfrogappsconfig.Module, signedDescriptions bool, threadId int, resultsToCompare ...*sarif.Run) (vulnerabilitiesResults []*sarif.Run, violationsResults []*sarif.Run, err error) {
34+
func RunSastScan(scanner *jas.JasScanner, module jfrogappsconfig.Module, signedDescriptions bool, sastRules string, threadId int, resultsToCompare ...*sarif.Run) (vulnerabilitiesResults []*sarif.Run, violationsResults []*sarif.Run, err error) {
3435
var scannerTempDir string
3536
if scannerTempDir, err = jas.CreateScannerTempDirectory(scanner, jasutils.Sast.String(), threadId); err != nil {
3637
return
3738
}
38-
sastScanManager, err := newSastScanManager(scanner, scannerTempDir, signedDescriptions, resultsToCompare...)
39+
sastScanManager, err := newSastScanManager(scanner, scannerTempDir, signedDescriptions, sastRules, resultsToCompare...)
3940
if err != nil {
4041
return
4142
}
@@ -47,10 +48,11 @@ func RunSastScan(scanner *jas.JasScanner, module jfrogappsconfig.Module, signedD
4748
return
4849
}
4950

50-
func newSastScanManager(scanner *jas.JasScanner, scannerTempDir string, signedDescriptions bool, resultsToCompare ...*sarif.Run) (manager *SastScanManager, err error) {
51+
func newSastScanManager(scanner *jas.JasScanner, scannerTempDir string, signedDescriptions bool, sastRules string, resultsToCompare ...*sarif.Run) (manager *SastScanManager, err error) {
5152
manager = &SastScanManager{
5253
scanner: scanner,
5354
signedDescriptions: signedDescriptions,
55+
sastRules: sastRules,
5456
configFileName: filepath.Join(scannerTempDir, "config.yaml"),
5557
resultsFileName: filepath.Join(scannerTempDir, "results.sarif"),
5658
}
@@ -94,6 +96,7 @@ type scanConfiguration struct {
9496
ExcludePatterns []string `yaml:"exclude_patterns,omitempty"`
9597
ExcludedRules []string `yaml:"excluded-rules,omitempty"`
9698
SastParameters sastParameters `yaml:"sast_parameters,omitempty"`
99+
UserRules string `yaml:"user_rules,omitempty"`
97100
}
98101

99102
type sastParameters struct {
@@ -122,6 +125,7 @@ func (ssm *SastScanManager) createConfigFile(module jfrogappsconfig.Module, sign
122125
SignedDescriptions: signedDescriptions,
123126
},
124127
ExcludePatterns: jas.GetExcludePatterns(module, &sastScanner.Scanner, exclusions...),
128+
UserRules: ssm.sastRules,
125129
},
126130
},
127131
}

jas/sast/sastscanner_test.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func TestNewSastScanManager(t *testing.T) {
2323
jfrogAppsConfigForTest, err := jas.CreateJFrogAppsConfig([]string{"currentDir"})
2424
assert.NoError(t, err)
2525
// Act
26-
sastScanManager, err := newSastScanManager(scanner, "temoDirPath", true)
26+
sastScanManager, err := newSastScanManager(scanner, "temoDirPath", true, "")
2727
assert.NoError(t, err)
2828

2929
// Assert
@@ -33,6 +33,7 @@ func TestNewSastScanManager(t *testing.T) {
3333
assert.NotEmpty(t, sastScanManager.resultsFileName)
3434
assert.NotEmpty(t, jfrogAppsConfigForTest.Modules[0].SourceRoot)
3535
assert.Equal(t, &jas.FakeServerDetails, sastScanManager.scanner.ServerDetails)
36+
assert.Empty(t, sastScanManager.sastRules)
3637
}
3738
}
3839

@@ -46,7 +47,7 @@ func TestNewSastScanManagerWithFilesToCompare(t *testing.T) {
4647
scannerTempDir, err := jas.CreateScannerTempDirectory(scanner, jasutils.Secrets.String(), 0)
4748
require.NoError(t, err)
4849

49-
sastScanManager, err := newSastScanManager(scanner, scannerTempDir, false, sarifutils.CreateRunWithDummyResults(sarifutils.CreateDummyResult("test-markdown", "test-msg", "test-rule-id", "note")))
50+
sastScanManager, err := newSastScanManager(scanner, scannerTempDir, false, "", sarifutils.CreateRunWithDummyResults(sarifutils.CreateDummyResult("test-markdown", "test-msg", "test-rule-id", "note")))
5051
require.NoError(t, err)
5152

5253
// Check if path value exists and file is created
@@ -61,7 +62,7 @@ func TestSastParseResults_EmptyResults(t *testing.T) {
6162
assert.NoError(t, err)
6263

6364
// Arrange
64-
sastScanManager, err := newSastScanManager(scanner, "temoDirPath", true)
65+
sastScanManager, err := newSastScanManager(scanner, "temoDirPath", true, "")
6566
assert.NoError(t, err)
6667
sastScanManager.resultsFileName = filepath.Join(jas.GetTestDataPath(), "sast-scan", "no-violations.sarif")
6768

@@ -84,7 +85,7 @@ func TestSastParseResults_ResultsContainIacViolations(t *testing.T) {
8485
jfrogAppsConfigForTest, err := jas.CreateJFrogAppsConfig([]string{})
8586
assert.NoError(t, err)
8687
// Arrange
87-
sastScanManager, err := newSastScanManager(scanner, "temoDirPath", false)
88+
sastScanManager, err := newSastScanManager(scanner, "temoDirPath", false, "")
8889
assert.NoError(t, err)
8990
sastScanManager.resultsFileName = filepath.Join(jas.GetTestDataPath(), "sast-scan", "contains-sast-violations.sarif")
9091

@@ -186,3 +187,20 @@ func TestGroupResultsByLocation(t *testing.T) {
186187
assert.ElementsMatch(t, test.expectedOutput.Results, test.run.Results)
187188
}
188189
}
190+
191+
func TestSastRules(t *testing.T) {
192+
scanner, cleanUp := jas.InitJasTest(t)
193+
defer cleanUp()
194+
tempDir, cleanUpTempDir := coreTests.CreateTempDirWithCallbackAndAssert(t)
195+
defer cleanUpTempDir()
196+
197+
scanner.TempDir = tempDir
198+
scannerTempDir, err := jas.CreateScannerTempDirectory(scanner, jasutils.Sast.String(), 0)
199+
require.NoError(t, err)
200+
201+
sastScanManager, err := newSastScanManager(scanner, scannerTempDir, false, "test-rules.json")
202+
require.NoError(t, err)
203+
assert.Equal(t, "test-rules.json", sastScanManager.sastRules)
204+
assert.Equal(t, filepath.Join(scannerTempDir, "config.yaml"), sastScanManager.configFileName)
205+
assert.Equal(t, filepath.Join(scannerTempDir, "results.sarif"), sastScanManager.resultsFileName)
206+
}

0 commit comments

Comments
 (0)