From 3d7580a7776cd039f53e1a6f9c625562f3c54640 Mon Sep 17 00:00:00 2001 From: Sumit Morchhale Date: Mon, 28 Sep 2026 18:49:13 +0530 Subject: [PATCH 1/4] ai classification --- internal/commands/result.go | 5 ++ internal/commands/result_test.go | 72 ++++++++++++++++++++++++++ internal/params/flags.go | 1 + internal/wrappers/mock/results-mock.go | 6 ++- internal/wrappers/results-json.go | 2 + internal/wrappers/results-sarif.go | 9 ++-- 6 files changed, 89 insertions(+), 6 deletions(-) diff --git a/internal/commands/result.go b/internal/commands/result.go index 7714d90e1..a02d0519a 100644 --- a/internal/commands/result.go +++ b/internal/commands/result.go @@ -176,6 +176,7 @@ var ( commonParams.StatusQueryParam, commonParams.SeverityQueryParam, commonParams.StateQueryParam, + commonParams.AIClassificationQueryParam, }, ",", ), ) @@ -2961,6 +2962,10 @@ func parseSarifResultSast(result *wrappers.ScanResult, scanResults []wrappers.Sa }, } + if result.ScanResultData.AIClassification != nil { + scanResult.Properties = &wrappers.SarifResultProperties{AIClassification: result.ScanResultData.AIClassification} + } + scanResults = append(scanResults, scanResult) return scanResults } diff --git a/internal/commands/result_test.go b/internal/commands/result_test.go index 660074995..67388c1ab 100644 --- a/internal/commands/result_test.go +++ b/internal/commands/result_test.go @@ -228,6 +228,46 @@ func TestRunWithoutScsResults_Other_AgentsShouldNotShowScsResults(t *testing.T) mock.SetScsMockVarsToDefault() } +func TestRunGetResultsByScanIdJSONFormat_AIClassificationPresentWhenSetByServer(t *testing.T) { + clearFlags() + + execCmdNilAssertion(t, "results", "show", "--scan-id", "SAST_ONLY", "--report-format", "json") + + reportBytes, err := os.ReadFile(fileName + "." + printer.FormatJSON) + assert.NilError(t, err, "Error reading file") + + var scanResultsCollection *wrappers.ScanResultsCollection + err = json.Unmarshal(reportBytes, &scanResultsCollection) + assert.NilError(t, err, "Error unmarshalling JSON data") + assert.Assert(t, len(scanResultsCollection.Results) == 1) + assert.Assert(t, scanResultsCollection.Results[0].ScanResultData.AIClassification != nil) + assert.Equal(t, "TP", *scanResultsCollection.Results[0].ScanResultData.AIClassification) + + // The raw JSON must carry the key, not just the unmarshalled struct + assert.Assert(t, strings.Contains(string(reportBytes), `"aiClassification":"TP"`)) + + removeFileBySuffix(t, printer.FormatJSON) +} + +func TestRunGetResultsByScanIdJSONFormat_AIClassificationOmittedWhenAbsent(t *testing.T) { + clearFlags() + + execCmdNilAssertion(t, "results", "show", "--scan-id", "MOCK", "--report-format", "json") + + reportBytes, err := os.ReadFile(fileName + "." + printer.FormatJSON) + assert.NilError(t, err, "Error reading file") + + var scanResultsCollection *wrappers.ScanResultsCollection + err = json.Unmarshal(reportBytes, &scanResultsCollection) + assert.NilError(t, err, "Error unmarshalling JSON data") + for i := range scanResultsCollection.Results { + assert.Assert(t, scanResultsCollection.Results[i].ScanResultData.AIClassification == nil) + } + assert.Assert(t, !strings.Contains(string(reportBytes), "aiClassification")) + + removeFileBySuffix(t, printer.FormatJSON) +} + func TestRunNilResults_Other_AgentsShouldNotShowAnyResults(t *testing.T) { clearFlags() @@ -395,6 +435,38 @@ func TestParseSarifResultSastClampsZeroColumns(t *testing.T) { assert.Equal(t, uint(6), threadLocations[1].Location.PhysicalLocation.Region.EndColumn) } +func TestParseSarifResultSastSetsAIClassificationWhenPresent(t *testing.T) { + aiClassification := "TP" + result := &wrappers.ScanResult{ + ScanResultData: wrappers.ScanResultData{ + AIClassification: &aiClassification, + Nodes: []*wrappers.ScanResultNode{ + {FileName: "/src/app.go", Line: 10, Column: 1, Length: 5}, + }, + }, + } + + sarifResults := parseSarifResultSast(result, nil) + assert.Assert(t, len(sarifResults) == 1) + assert.Assert(t, sarifResults[0].Properties != nil) + assert.Assert(t, sarifResults[0].Properties.AIClassification != nil) + assert.Equal(t, "TP", *sarifResults[0].Properties.AIClassification) +} + +func TestParseSarifResultSastOmitsPropertiesWhenAIClassificationAbsent(t *testing.T) { + result := &wrappers.ScanResult{ + ScanResultData: wrappers.ScanResultData{ + Nodes: []*wrappers.ScanResultNode{ + {FileName: "/src/app.go", Line: 10, Column: 1, Length: 5}, + }, + }, + } + + sarifResults := parseSarifResultSast(result, nil) + assert.Assert(t, len(sarifResults) == 1) + assert.Assert(t, sarifResults[0].Properties == nil) +} + func TestParseSarifResultKicsClampsZeroStartLine(t *testing.T) { result := &wrappers.ScanResult{ ScanResultData: wrappers.ScanResultData{ diff --git a/internal/params/flags.go b/internal/params/flags.go index eae820f30..096660832 100644 --- a/internal/params/flags.go +++ b/internal/params/flags.go @@ -303,6 +303,7 @@ const ( IncludeNodesQueryParam = "include-nodes" SortQueryParam = "sort" SearchQueryParam = "search" + AIClassificationQueryParam = "ai-classification" Profile = "default" BaseURI = "" BaseIAMURI = "" diff --git a/internal/wrappers/mock/results-mock.go b/internal/wrappers/mock/results-mock.go index 5378e8ec9..b81600441 100644 --- a/internal/wrappers/mock/results-mock.go +++ b/internal/wrappers/mock/results-mock.go @@ -105,6 +105,7 @@ func (r ResultsMockWrapper) GetAllResultsByScanID(params map[string]string) ( }, nil, nil } if params["scan-id"] == "SAST_ONLY" { + aiClassification := "TP" return &wrappers.ScanResultsCollection{ TotalCount: 1, Results: []*wrappers.ScanResult{ @@ -113,8 +114,9 @@ func (r ResultsMockWrapper) GetAllResultsByScanID(params map[string]string) ( ID: "1", Severity: "high", ScanResultData: wrappers.ScanResultData{ - LanguageName: "JavaScript", - QueryName: "mock-query-name-1", + LanguageName: "JavaScript", + QueryName: "mock-query-name-1", + AIClassification: &aiClassification, Nodes: []*wrappers.ScanResultNode{ { FileName: "dummy-file-name-1", diff --git a/internal/wrappers/results-json.go b/internal/wrappers/results-json.go index a2a5b3f2c..37a0699f6 100644 --- a/internal/wrappers/results-json.go +++ b/internal/wrappers/results-json.go @@ -129,4 +129,6 @@ type ScanResultData struct { Validity string `json:"validity,omitempty"` IsInSource bool `json:"isInSource,omitempty"` CommitURL string `json:"commitUrl,omitempty"` + // Added to support FAE Phase 2 AI classification + AIClassification *string `json:"aiClassification,omitempty"` } diff --git a/internal/wrappers/results-sarif.go b/internal/wrappers/results-sarif.go index 60c6ec63a..967cd365c 100644 --- a/internal/wrappers/results-sarif.go +++ b/internal/wrappers/results-sarif.go @@ -112,8 +112,9 @@ type SarifResultFingerprint struct { } type SarifResultProperties struct { - Severity string `json:"severity,omitempty"` - Validity string `json:"validity,omitempty"` - IsInSource bool `json:"isInSource,omitempty"` - CommitURL string `json:"commitUrl,omitempty"` + Severity string `json:"severity,omitempty"` + Validity string `json:"validity,omitempty"` + IsInSource bool `json:"isInSource,omitempty"` + CommitURL string `json:"commitUrl,omitempty"` + AIClassification *string `json:"aiClassification,omitempty"` } From 1b5f209e76e1dca92464452332a0d43c83acbb1a Mon Sep 17 00:00:00 2001 From: Sumit Morchhale Date: Tue, 29 Sep 2026 14:21:21 +0530 Subject: [PATCH 2/4] fix integration and trivy vul --- go.mod | 2 +- go.sum | 4 ++-- test/integration/result_test.go | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 9eec06f4f..20a14f20c 100644 --- a/go.mod +++ b/go.mod @@ -121,7 +121,7 @@ require ( github.com/charmbracelet/x/term v0.2.2 // indirect github.com/cloudflare/circl v1.6.5 // indirect github.com/containerd/cgroups/v3 v3.1.3 // indirect - github.com/containerd/containerd v1.7.35 // indirect + github.com/containerd/containerd v1.7.36 // indirect github.com/containerd/containerd/api v1.10.0 // indirect github.com/containerd/continuity v0.5.0 // indirect github.com/containerd/errdefs v1.0.0 // indirect diff --git a/go.sum b/go.sum index 07a1cdf4a..8253948ba 100644 --- a/go.sum +++ b/go.sum @@ -255,8 +255,8 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/cgroups/v3 v3.1.3 h1:eUNflyMddm18+yrDmZPn3jI7C5hJ9ahABE5q6dyLYXQ= github.com/containerd/cgroups/v3 v3.1.3/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw= -github.com/containerd/containerd v1.7.35 h1:7AU2T1qI2OdNBmKkreWZ7kASHUNFItN5Hgejd9sY938= -github.com/containerd/containerd v1.7.35/go.mod h1:ozI//0TomTCLPhQREnx0IXDIQMg+Fk7yTtg9fNvU8EQ= +github.com/containerd/containerd v1.7.36 h1:HyMsOG5kmp1LQsGzqwI6Ts06T4VpYZbszfDZYB0bW5w= +github.com/containerd/containerd v1.7.36/go.mod h1:ozI//0TomTCLPhQREnx0IXDIQMg+Fk7yTtg9fNvU8EQ= github.com/containerd/containerd/api v1.10.0 h1:5n0oHYVBwN4VhoX9fFykCV9dF1/BvAXeg2F8W6UYq1o= github.com/containerd/containerd/api v1.10.0/go.mod h1:NBm1OAk8ZL+LG8R0ceObGxT5hbUYj7CzTmR3xh0DlMM= github.com/containerd/continuity v0.5.0 h1:7a85HZpCSs+1Zps0Ee3DPSuAWY+0SJM1JNM51nlEVDg= diff --git a/test/integration/result_test.go b/test/integration/result_test.go index 4938f7177..d851a5414 100644 --- a/test/integration/result_test.go +++ b/test/integration/result_test.go @@ -204,7 +204,7 @@ func TestCodeBashingList(t *testing.T) { "results", "codebashing", flag(params.LanguageFlag), "PHP", - flag(params.VulnerabilityTypeFlag), "Reflected XSS All Clients", + flag(params.VulnerabilityTypeFlag), "Reflected_XSS", flag(params.CweIDFlag), "79") codebashing := []wrappers.CodeBashingCollection{} @@ -221,7 +221,7 @@ func TestCodeBashingListJson(t *testing.T) { "results", "codebashing", flag(params.LanguageFlag), "PHP", - flag(params.VulnerabilityTypeFlag), "Reflected XSS All Clients", + flag(params.VulnerabilityTypeFlag), "Reflected_XSS", flag(params.CweIDFlag), "79", flag(params.FormatFlag), "json") @@ -239,7 +239,7 @@ func TestCodeBashingListTable(t *testing.T) { "results", "codebashing", flag(params.LanguageFlag), "PHP", - flag(params.VulnerabilityTypeFlag), "Reflected XSS All Clients", + flag(params.VulnerabilityTypeFlag), "Reflected_XSS", flag(params.CweIDFlag), "79", flag(params.FormatFlag), "table") From 62145156e090dd59fa6413c8123d20592d4c6218 Mon Sep 17 00:00:00 2001 From: Sumit Morchhale Date: Tue, 29 Sep 2026 20:39:42 +0530 Subject: [PATCH 3/4] empty commit From 3dbb62cf13766f13f3aab173c91acb5e4ad28d72 Mon Sep 17 00:00:00 2001 From: Sumit Morchhale Date: Mon, 5 Oct 2026 10:27:04 +0530 Subject: [PATCH 4/4] added format for ai classification filed --- internal/commands/result.go | 1 + internal/wrappers/results-sonar.go | 1 + 2 files changed, 2 insertions(+) diff --git a/internal/commands/result.go b/internal/commands/result.go index a02d0519a..d2b098e54 100644 --- a/internal/commands/result.go +++ b/internal/commands/result.go @@ -2421,6 +2421,7 @@ func initSonarIssue(result *wrappers.ScanResult) wrappers.SonarIssues { engineType := strings.TrimSpace(result.Type) if engineType == commonParams.SastType { sonarIssue.RuleID = result.ScanResultData.LanguageName + " - " + result.ScanResultData.QueryName + sonarIssue.AiClassification = result.ScanResultData.AIClassification } else if engineType == commonParams.KicsType { sonarIssue.RuleID = result.ScanResultData.QueryName } else if engineType == commonParams.ScaType { diff --git a/internal/wrappers/results-sonar.go b/internal/wrappers/results-sonar.go index a8f18392b..9632ee85c 100644 --- a/internal/wrappers/results-sonar.go +++ b/internal/wrappers/results-sonar.go @@ -24,6 +24,7 @@ type SonarIssues struct { PrimaryLocation SonarLocation `json:"primaryLocation"` EffortMinutes uint `json:"effortMinutes,omitempty"` SecondaryLocations []SonarLocation `json:"secondaryLocations"` + AiClassification *string `json:"aiClassification,omitempty"` // SAST-only } type SonarLocation struct {