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
54 changes: 54 additions & 0 deletions internal/services/realtimeengine/ossrealtime/oss-realtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/Checkmarx/manifest-parser/pkg/parser"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
142 changes: 142 additions & 0 deletions internal/services/realtimeengine/ossrealtime/oss-realtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
})
}
}
14 changes: 14 additions & 0 deletions test/integration/oss-realtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down