diff --git a/internal/services/realtimeengine/ossrealtime/oss-realtime.go b/internal/services/realtimeengine/ossrealtime/oss-realtime.go index 9316b1a7..bf65b0c3 100644 --- a/internal/services/realtimeengine/ossrealtime/oss-realtime.go +++ b/internal/services/realtimeengine/ossrealtime/oss-realtime.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "os" + "path/filepath" "strings" "github.com/Checkmarx/manifest-parser/pkg/parser" @@ -74,6 +75,10 @@ func (o *OssRealtimeService) RunOssRealtimeScan(filePath, ignoredFilePath string return nil, errorconstants.NewRealtimeEngineError("invalid file path").Error() } + if err := validateSupportedManifestFile(filePath); err != nil { + return nil, err + } + pkgs, err := parseManifest(filePath) if err != nil { logger.PrintfIfVerbose("Failed to parse manifest file %s: %v", filePath, err) @@ -174,6 +179,55 @@ func getPackageEntryFromPackageMap( return &entry } +// validateSupportedManifestFile checks if the manifest file format is supported by OSS realtime scanner. +func validateSupportedManifestFile(filePath string) error { + manifestFileName := filepath.Base(filePath) + manifestFileExtension := filepath.Ext(manifestFileName) + + // Check supported extensions + supportedExtensions := map[string]bool{ + ".csproj": true, + ".sbt": true, + } + + // Check supported filenames + supportedFilenames := map[string]bool{ + "pom.xml": true, + "package.json": true, + "Directory.Packages.props": true, + "packages.config": true, + "go.mod": true, + "build.gradle": true, + "build.gradle.kts": true, + "libs.versions.toml": true, + "setup.cfg": true, + "setup.py": true, + "pyproject.toml": true, + } + + // Check by extension + if supportedExtensions[manifestFileExtension] { + return nil + } + + // Check by filename + if supportedFilenames[manifestFileName] { + return nil + } + + // Special handling for .txt files (check prefix) + if manifestFileExtension == ".txt" { + if strings.HasPrefix(manifestFileName, "requirement") || + strings.HasPrefix(manifestFileName, "packages") || + strings.HasPrefix(manifestFileName, "constraint") { + return nil + } + } + + // Manifest format is not supported + return errorconstants.NewRealtimeEngineError(fmt.Sprintf("OSS Realtime scanner doesn't currently support scanning '%s' file.", manifestFileName)).Error() +} + // parseManifest parses the manifest file and returns a list of packages. func parseManifest(filePath string) ([]models.Package, error) { manifestParser := parser.ParsersFactory(filePath) diff --git a/internal/services/realtimeengine/ossrealtime/oss-realtime_test.go b/internal/services/realtimeengine/ossrealtime/oss-realtime_test.go index 3fc4e975..a3868cd5 100644 --- a/internal/services/realtimeengine/ossrealtime/oss-realtime_test.go +++ b/internal/services/realtimeengine/ossrealtime/oss-realtime_test.go @@ -388,3 +388,145 @@ func TestOssRealtimeScan_CsprojFile_ReturnsLocations(t *testing.T) { "Location %d endIndex should be %d", i, expected.endIndex) } } + +// Tests for validateSupportedManifestFile function +func TestValidateSupportedManifestFile_ValidExtensions(t *testing.T) { + tests := []struct { + name string + filePath string + isValid bool + }{ + { + name: "CsprojExtension", + filePath: "project.csproj", + isValid: true, + }, + { + name: "SbtExtension", + filePath: "build.sbt", + isValid: true, + }, + { + name: "CsprojWithPath", + filePath: "/path/to/project.csproj", + isValid: true, + }, + { + name: "SbtWithPath", + filePath: "src/build.sbt", + isValid: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateSupportedManifestFile(tt.filePath) + if tt.isValid { + assert.Nil(t, err, "Should accept %s file", tt.filePath) + } else { + assert.NotNil(t, err, "Should reject %s file", tt.filePath) + } + }) + } +} + +func TestValidateSupportedManifestFile_ValidFilenames(t *testing.T) { + tests := []struct { + name string + filePath string + }{ + {name: "PomXml", filePath: "pom.xml"}, + {name: "PackageJson", filePath: "package.json"}, + {name: "GoMod", filePath: "go.mod"}, + {name: "BuildGradle", filePath: "build.gradle"}, + {name: "BuildGradleKts", filePath: "build.gradle.kts"}, + {name: "LibsVersionsToml", filePath: "libs.versions.toml"}, + {name: "SetupCfg", filePath: "setup.cfg"}, + {name: "SetupPy", filePath: "setup.py"}, + {name: "PyprojectToml", filePath: "pyproject.toml"}, + {name: "PackagesConfig", filePath: "packages.config"}, + {name: "DirectoryPackagesProps", filePath: "Directory.Packages.props"}, + {name: "PomXmlWithPath", filePath: "/path/to/pom.xml"}, + {name: "PackageJsonWithPath", filePath: "src/package.json"}, + {name: "GoModWithPath", filePath: "project/go.mod"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateSupportedManifestFile(tt.filePath) + assert.Nil(t, err, "Should accept %s file", tt.filePath) + }) + } +} + +func TestValidateSupportedManifestFile_ValidTxtFilesWithPrefixes(t *testing.T) { + tests := []struct { + name string + filePath string + }{ + {name: "RequirementsTxt", filePath: "requirements.txt"}, + {name: "RequirementsDevTxt", filePath: "requirements-dev.txt"}, + {name: "PackagesTxt", filePath: "packages.txt"}, + {name: "ConstraintsTxt", filePath: "constraints.txt"}, + {name: "RequirementsWithPath", filePath: "/path/to/requirements.txt"}, + {name: "PackagesWithPath", filePath: "config/packages.txt"}, + {name: "ConstraintsWithPath", filePath: "src/constraints.txt"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateSupportedManifestFile(tt.filePath) + assert.Nil(t, err, "Should accept %s file", tt.filePath) + }) + } +} + +func TestValidateSupportedManifestFile_InvalidTxtFilesWithoutPrefixes(t *testing.T) { + tests := []struct { + name string + filePath string + }{ + {name: "UnrelatedTxt", filePath: "readme.txt"}, + {name: "DataTxt", filePath: "data.txt"}, + {name: "ConfigTxt", filePath: "config.txt"}, + {name: "InfoTxt", filePath: "info.txt"}, + {name: "UnrelatedTxtWithPath", filePath: "/path/to/random.txt"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateSupportedManifestFile(tt.filePath) + assert.NotNil(t, err, "Should reject %s file (missing prefix)", tt.filePath) + // Verify error message indicates unsupported format + assert.Contains(t, err.Error(), "OSS Realtime scanner doesn't currently support scanning") + }) + } +} + +func TestValidateSupportedManifestFile_UnsupportedFormats(t *testing.T) { + tests := []struct { + name string + filePath string + }{ + {name: "RubyGemfile", filePath: "Gemfile"}, + {name: "PHPComposer", filePath: "composer.json"}, + {name: "RustCargo", filePath: "Cargo.toml"}, + {name: "PythonPipfile", filePath: "Pipfile"}, + {name: "JavaGradleProperties", filePath: "gradle.properties"}, + {name: "NodeNpmLock", filePath: "package-lock.json"}, + {name: "YarnLock", filePath: "yarn.lock"}, + {name: "UnsupportedExtension", filePath: "manifest.xml"}, + {name: "RandomFile", filePath: "random.txt"}, + {name: "NoPyproject", filePath: "pyproject.yaml"}, + {name: "PomWithDifferentName", filePath: "maven.xml"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateSupportedManifestFile(tt.filePath) + assert.NotNil(t, err, "Should reject %s file (unsupported format)", tt.filePath) + // Verify error message indicates unsupported format + assert.Contains(t, err.Error(), "OSS Realtime scanner doesn't currently support scanning") + }) + } +} diff --git a/test/integration/oss-realtime_test.go b/test/integration/oss-realtime_test.go index 0ccfcff5..cdd7971d 100644 --- a/test/integration/oss-realtime_test.go +++ b/test/integration/oss-realtime_test.go @@ -71,6 +71,20 @@ func TestOssRealtimeScan_PackageJsonFile_Success(t *testing.T) { defer deleteCacheFile() } +func TestOssRealtimeScan_UnsupportedManifestFormat_ReturnsError(t *testing.T) { + // Test with unsupported file format - should return error (using a .py file which is not a supported manifest) + args := []string{ + "scan", "oss-realtime", + flag(commonParams.SourcesFlag), "data/python-vul-file.py", + } + err, _ := executeCommand(t, args...) + // Should fail with error + assert.NotNil(t, err, "Should fail with unsupported manifest format") + // Error message should indicate unsupported format + assert.Contains(t, err.Error(), "OSS Realtime scanner doesn't currently support scanning", + "Error message should indicate that format is not supported") +} + func validateCacheFileExist() bool { cacheFilePath := os.TempDir() + "/oss-realtime-cache.json" if _, err := os.Stat(cacheFilePath); os.IsNotExist(err) {