Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Integration Test: sarif_diff_by_commits - file_level_classification

## Purpose

Validates that the `sarif_diff_by_commits` tool correctly partitions
SARIF results into "new" vs "pre-existing" based on file-level overlap
with a git diff. Uses `HEAD..HEAD` (empty diff) so all results are
classified as pre-existing.

## Inputs

- `results.sarif`: SARIF with 3 results across 2 rules in 3 files
- `refRange`: `HEAD..HEAD` (produces an empty diff)
- `granularity`: `file`

## Expected Behavior

Returns structured output with all 3 results in `preExistingResults`
and 0 results in `newResults`, since the empty diff has no changed files.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"toolName": "sarif_diff_by_commits",
"success": true,
"description": "Successfully classified all results as pre-existing with empty diff"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
{
"version": "2.1.0",
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
"runs": [
{
"tool": {
"driver": {
"name": "CodeQL",
"version": "2.20.4",
"rules": [
{
"id": "js/sql-injection",
"name": "js/sql-injection",
"shortDescription": {
"text": "Database query built from user-controlled sources"
},
"properties": {
"tags": ["security"],
"kind": "path-problem",
"precision": "high",
"security-severity": "8.8"
}
},
{
"id": "js/xss",
"name": "js/xss",
"shortDescription": {
"text": "Cross-site scripting"
},
"properties": {
"tags": ["security"],
"kind": "path-problem",
"precision": "high",
"security-severity": "6.1"
}
}
]
}
},
"results": [
{
"ruleId": "js/sql-injection",
"ruleIndex": 0,
"message": {
"text": "SQL injection from user input."
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "src/db.js"
},
"region": {
"startLine": 42,
"startColumn": 5,
"endColumn": 38
}
}
}
]
},
{
"ruleId": "js/sql-injection",
"ruleIndex": 0,
"message": {
"text": "SQL injection from request body."
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "src/api.js"
},
"region": {
"startLine": 15,
"startColumn": 3,
"endColumn": 40
}
}
}
]
},
{
"ruleId": "js/xss",
"ruleIndex": 1,
"message": {
"text": "XSS vulnerability."
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "src/views.js"
},
"region": {
"startLine": 30,
"startColumn": 10,
"endColumn": 50
}
}
}
]
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"toolName": "sarif_diff_by_commits",
"expectedSuccess": true,
"description": "Test sarif_diff_by_commits classifies all results as pre-existing with empty diff"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
{
"version": "2.1.0",
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
"runs": [
{
"tool": {
"driver": {
"name": "CodeQL",
"version": "2.20.4",
"rules": [
{
"id": "js/sql-injection",
"name": "js/sql-injection",
"shortDescription": {
"text": "Database query built from user-controlled sources"
},
"properties": {
"tags": ["security"],
"kind": "path-problem",
"precision": "high",
"security-severity": "8.8"
}
},
{
"id": "js/xss",
"name": "js/xss",
"shortDescription": {
"text": "Cross-site scripting"
},
"properties": {
"tags": ["security"],
"kind": "path-problem",
"precision": "high",
"security-severity": "6.1"
}
}
]
}
},
"results": [
{
"ruleId": "js/sql-injection",
"ruleIndex": 0,
"message": {
"text": "SQL injection from user input."
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "src/db.js"
},
"region": {
"startLine": 42,
"startColumn": 5,
"endColumn": 38
}
}
}
]
},
{
"ruleId": "js/sql-injection",
"ruleIndex": 0,
"message": {
"text": "SQL injection from request body."
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "src/api.js"
},
"region": {
"startLine": 15,
"startColumn": 3,
"endColumn": 40
}
}
}
]
},
{
"ruleId": "js/xss",
"ruleIndex": 1,
"message": {
"text": "XSS vulnerability."
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "src/views.js"
},
"region": {
"startLine": 30,
"startColumn": 10,
"endColumn": 50
}
}
}
]
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"toolName": "sarif_diff_by_commits",
"arguments": {
"refRange": "HEAD..HEAD",
"granularity": "file"
},
"assertions": {
"responseContains": [
"\"granularity\": \"file\"",
"\"preExistingResults\"",
"\"newResults\"",
"\"totalPreExisting\": 3",
"\"totalNew\": 0",
"\"totalResults\": 3"
]
}
}
21 changes: 21 additions & 0 deletions client/internal/testing/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,27 @@ func buildToolParams(repoRoot, toolName, testCase, testDir string) (map[string]a
}
}

case "sarif_diff_by_commits":
sarifFiles := findFilesByExt(beforeDir, ".sarif")
if len(sarifFiles) > 0 {
params["sarifPath"] = filepath.Join(beforeDir, sarifFiles[0])
}
// Merge refRange, repoPath, and granularity from test-config.json
if configData, err := os.ReadFile(configPath); err == nil {
var cfg TestConfig
if json.Unmarshal(configData, &cfg) == nil {
if refRange, ok := cfg.Arguments["refRange"]; ok {
params["refRange"] = refRange
}
if repoPath, ok := cfg.Arguments["repoPath"]; ok {
params["repoPath"] = repoPath
}
if granularity, ok := cfg.Arguments["granularity"]; ok {
params["granularity"] = granularity
}
}
}

case "sarif_diff_runs":
sarifFiles := findFilesByExt(beforeDir, ".sarif")
sort.Strings(sarifFiles)
Expand Down
37 changes: 37 additions & 0 deletions client/internal/testing/params_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,40 @@ func TestBuildToolParams_SARIFCompareAlertsWithConfig(t *testing.T) {
t.Error("expected sarifPath injected into alertB")
}
}

func TestBuildToolParams_SARIFDiffByCommitsWithConfig(t *testing.T) {
dir := t.TempDir()
testDir := filepath.Join(dir, "tools", "sarif_diff_by_commits", "file_level_classification")
beforeDir := filepath.Join(testDir, "before")
os.MkdirAll(beforeDir, 0o755)
os.MkdirAll(filepath.Join(testDir, "after"), 0o755)
Comment thread
data-douser marked this conversation as resolved.

// Write test-config.json with refRange and granularity but no sarifPath
os.WriteFile(filepath.Join(testDir, "test-config.json"),
[]byte(`{"toolName":"sarif_diff_by_commits","arguments":{"refRange":"HEAD..HEAD","granularity":"file"}}`), 0o600)

// Write a SARIF file in before/
os.WriteFile(filepath.Join(beforeDir, "results.sarif"),
[]byte(`{"version":"2.1.0","runs":[{"tool":{"driver":{"name":"CodeQL","rules":[]}},"results":[]}]}`), 0o600)

params, err := buildToolParams(dir, "sarif_diff_by_commits", "file_level_classification", testDir)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

// Should have sarifPath injected from before/
sarifPath, ok := params["sarifPath"].(string)
if !ok || sarifPath == "" {
t.Error("expected sarifPath to be injected from before/ directory")
}

// Should have refRange from config
if params["refRange"] != "HEAD..HEAD" {
t.Errorf("params[refRange] = %v, want HEAD..HEAD", params["refRange"])
}

// Should have granularity from config
if params["granularity"] != "file" {
t.Errorf("params[granularity] = %v, want file", params["granularity"])
}
}
Loading
Loading