diff --git a/.trivyignore b/.trivyignore index 6b0e1b69..2cbbafcb 100644 --- a/.trivyignore +++ b/.trivyignore @@ -48,3 +48,11 @@ GHSA-vh4v-2xq2-g5cg exp:2026-12-31 # Risk: Low - requires attacker-controlled registry, container scanning only # Impact: Credential exfiltration in container registry operations CVE-2026-48978 exp:2026-12-31 + +# CVE-2026-6791 (UNKNOWN): glibc vulnerability +# Libraries: glibc, glibc-locale-posix, ld-linux, libcrypt1 +# Image: wolfi 20230201 (base image) +# Status: Fixed in glibc 2.43-r10 (current: 2.43-r8) +# Risk: Low - affects base OS image, not application code +# Impact: Minimal - only affects base OS components, application uses glibc runtime only +CVE-2026-6791 exp:2026-12-31 diff --git a/internal/commands/agenthooks/guardrails/asca/delta.go b/internal/commands/agenthooks/guardrails/asca/delta.go index 03c917f6..9032ff2a 100644 --- a/internal/commands/agenthooks/guardrails/asca/delta.go +++ b/internal/commands/agenthooks/guardrails/asca/delta.go @@ -11,7 +11,7 @@ import ( ) // findingKey is the deduplication tuple used for delta detection. -// Mirrors the cx-security plugin's matching logic. +// Mirrors the cx-devassist plugin's matching logic. type findingKey struct { ruleID uint32 problematicLine string // TrimSpace applied @@ -115,7 +115,7 @@ func additionalContext(filePath, cxBinary string, findings []grpcs.ScanDetail, w "Do not bypass the scan by writing the same content through another tool or shell command. "+ "ANALYZE each finding to determine if it is a real vulnerability or a false positive "+ "caused by ASCA's single-file scope (it cannot see imported modules or helper files). "+ - "For each real finding, invoke the cx-security:cx-security-asca skill — "+ + "For each real finding, invoke the cx-devassist:cx-devassist-asca skill — "+ "the findings are already in context so it will skip the scan and go directly to "+ "MCP-driven remediation; the skill also handles MCP unavailability and self-recovery. "+ "If that skill is not available in this session, call mcp__Checkmarx__codeRemediation directly:\n"+ diff --git a/internal/services/realtimeengine/ossrealtime/oss-realtime.go b/internal/services/realtimeengine/ossrealtime/oss-realtime.go index 59dade31..bcf53af4 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/wrappers/client.go b/internal/wrappers/client.go index de73ba99..003b2132 100644 --- a/internal/wrappers/client.go +++ b/internal/wrappers/client.go @@ -72,6 +72,7 @@ type ClientCredentialsError struct { const FailedToAuth = "Failed to authenticate - please provide an %s" const BaseAuthURLSuffix = "protocol/openid-connect/token" const BaseAuthURLPrefix = "auth/realms/organization" + // BaseURLKey is the JWT claim that carries the AST base URL. It is present only // on the exchanged access token (see GetURL), not on the stored refresh token. const BaseURLKey = "ast-base-url" @@ -129,7 +130,13 @@ func retryHTTPForIAMRequest(requestFunc func() (*http.Response, error), retries } func setAgentNameAndOrigin(req *http.Request, isAuth bool) { - agentStr := viper.GetString(commonParams.AgentNameKey) + "/" + commonParams.Version + + var agentStr string + if strings.Contains(viper.GetString(commonParams.AgentNameKey), "_") { + agentStr = viper.GetString(commonParams.AgentNameKey) + "/ASTCLI_" + commonParams.Version + } else { + agentStr = viper.GetString(commonParams.AgentNameKey) + "/" + commonParams.Version + } req.Header.Set("User-Agent", agentStr) originStr := viper.GetString(commonParams.OriginKey) diff --git a/test/integration/scan_test.go b/test/integration/scan_test.go index dd07eba9..95f21a66 100644 --- a/test/integration/scan_test.go +++ b/test/integration/scan_test.go @@ -2746,3 +2746,80 @@ func TestCreateScan_AsMultipartUpload_Success(t *testing.T) { log.SetOutput(os.Stderr) assert.Assert(t, strings.Contains(buf.String(), "Uploading source code in multiple parts"), "Test for uploading file in multiple parts failed.") } + +// Scenario 1: Test scan creation with a project already associated with an application +// Pre-condition: Project and application exist and are already linked +// Verifies the CLI skips reassociation and logs the expected message during scan creation. +func TestScanCreate_ProjectAlreadyAssociatedWithApplication_SkipsAssociation(t *testing.T) { + // Capture debug output to verify log messages + var buf bytes.Buffer + log.SetOutput(&buf) + defer log.SetOutput(os.Stderr) + + args := []string{ + "scan", "create", + flag(params.ProjectName), "TestProjectAppAssign", // Pre-existing project + flag(params.ApplicationName), "TestApplicationProAssign", // Pre-existing, already associated application + flag(params.SourcesFlag), "data/sources-gitignore.zip", + flag(params.ScanTypes), params.IacType, + flag(params.BranchFlag), "main", + flag(params.DebugFlag), + } + + err, _ := executeCommand(t, args...) + assert.NilError(t, err, "Scan creation with already-associated project and application should succeed") + + logOutput := buf.String() + assert.Assert(t, strings.Contains(logOutput, "Project is already associated with the application. Skipping association"), + "Expected log message about skipping association not found. Log output: %s", logOutput) +} + +// Scenario 2: Create a scan with a new project and an existing application +// Pre-condition: Application already exists +// Verifies the project is created, linked to the application, and a success message is logged. +func TestScanCreate_NewProjectWithExistingApplication_SuccessfullyUpdatesApplication(t *testing.T) { + // Capture debug output to verify log messages + var buf bytes.Buffer + log.SetOutput(&buf) + defer log.SetOutput(os.Stderr) + + _, projectName := createNewProject(t, nil, nil, GenerateRandomProjectNameForScan()) + defer deleteProjectByName(t, projectName) + + args := []string{ + "scan", "create", + flag(params.ProjectName), projectName, // NEW project (created at runtime) + flag(params.ApplicationName), "TestApplicationForProject", // PRE-EXISTING application + flag(params.SourcesFlag), "data/sources-gitignore.zip", + flag(params.ScanTypes), params.IacType, + flag(params.BranchFlag), "main", + flag(params.DebugFlag), + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + + err, _ := executeCommand(t, args...) + assert.NilError(t, err, "Scan creation with new project and existing application should succeed") + + logOutput := buf.String() + assert.Assert(t, strings.Contains(logOutput, "Successfully updated the application"), + "Expected log message about successful application update not found. Log output: %s", logOutput) +} + +// Scenario 3: Test scan creation using manually provided existing project and application with --branch-primary and --project-tags. +// Verifies that project parameters are updated successfully (when application-level query override is performed) and the scan completes without errors. +// this test only validates successful execution, not specific log messages. +func TestScanCreate_ProjectAlreadyAssociatedWithApplication_BranchPrimaryQueryOverride(t *testing.T) { + args := []string{ + "scan", "create", + flag(params.ProjectName), "TestProjectQueryOverride", // Pre-existing, manually provided project + flag(params.ApplicationName), "TestApplicationQueryOverride", // Pre-existing, manually provided application + flag(params.SourcesFlag), "data/sources-gitignore.zip", + flag(params.ScanTypes), "sast,iac-security", // Scan types as comma-separated value + flag(params.BranchFlag), "main", + flag(params.BranchPrimaryFlag), // Branch primary flag + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + + err, _ := executeCommand(t, args...) + assert.NilError(t, err, "Scan creation with branch-primary and query override flags should succeed without error") +}