diff --git a/rules/standard/security/SecurityHelper.go b/rules/standard/security/SecurityHelper.go index a7041ca..3fc2d1b 100644 --- a/rules/standard/security/SecurityHelper.go +++ b/rules/standard/security/SecurityHelper.go @@ -38,7 +38,11 @@ func findVulnerableLinesBetweenTags(fileToScan files.File, ruleId string, extraV if hasOpeningScriptOrStyleTagFound { // Search for closing script or style tag if closeScriptStyleTagRegexp.MatchString(line.Text) { - hasOpeningScriptOrStyleTagFound = false + closeTagEnd := closeScriptStyleTagRegexp.FindStringIndex(line.Text)[1] + openTagMatch := openScriptStyleTagRegexp.FindStringIndex(line.Text[closeTagEnd:]) + if openTagMatch == nil { + hasOpeningScriptOrStyleTagFound = false + } } isInsideScriptOrStyleTag = true @@ -49,8 +53,12 @@ func findVulnerableLinesBetweenTags(fileToScan files.File, ruleId string, extraV //Search for closing script or style tag at same line isScriptOrStyleClosingTag := closeScriptStyleTagRegexp.MatchString(line.Text) if isScriptOrStyleClosingTag { - columnRange[0] = openScriptStyleTagRegexp.FindStringIndex(line.Text)[1] - columnRange[1] = closeScriptStyleTagRegexp.FindStringIndex(line.Text)[0] + openTagEnd := openScriptStyleTagRegexp.FindStringIndex(line.Text)[1] + closeTagMatch := closeScriptStyleTagRegexp.FindStringIndex(line.Text[openTagEnd:]) + if closeTagMatch != nil { + columnRange[0] = openTagEnd + columnRange[1] = openTagEnd + closeTagMatch[0] + } } else if !isScriptOrStyleClosingTag && !selfCloseScriptStyleTagRegexp.MatchString(line.Text) { hasOpeningScriptOrStyleTagFound = true } @@ -60,7 +68,7 @@ func findVulnerableLinesBetweenTags(fileToScan files.File, ruleId string, extraV //IF line is between script or style tag if isInsideScriptOrStyleTag { //If column range is present - if columnRange[0] != columnRange[1] { + if columnRange[0] < columnRange[1] { vulnerableLines = append(vulnerableLines, rules.Occurrence{ LineNumber: line.LineNumber, LineContent: line.Text, diff --git a/rules/standard/security/SecurityHelper_test.go b/rules/standard/security/SecurityHelper_test.go index a112b5c..fde35ff 100644 --- a/rules/standard/security/SecurityHelper_test.go +++ b/rules/standard/security/SecurityHelper_test.go @@ -84,3 +84,105 @@ func TestFindVulnerableLinesWithExtraVulnerableTags(t *testing.T) { t.Errorf("%s Actual: %+v, Expected: %+v", "Occurrences list should be equal!", actualResult, expectedResult) } } + +// TestFindVulnerableLinesOpenAndCloseTagOnSameLine verifies that the column range +// is correctly computed relative to the open tag end when both +// appear on the same line. +func TestFindVulnerableLinesOpenAndCloseTagOnSameLine(t *testing.T) { + // Given + mockLines := []files.Line{ + {LineNumber: 1, Text: "", IsCommentedLine: false}, + } + mockFile := files.File{ + Lines: mockLines, + FileName: "test.page", + IgnoresSelected: []files.IgnoreSelected{}, + } + // openScriptStyleTagRegexp matches "'), so openTagEnd = 7. + // "" starts at absolute index 48, closeTagMatch[0] = 48-7 = 41. + // ColumnRange = [7, 7+41] = [7, 48]. + expectedResult := []rules.Occurrence{ + { + LineContent: "", + LineNumber: 1, + ColumnRange: []int{7, 48}, + IsFalsePositive: false, + }, + } + + // When + actualResult := findVulnerableLinesBetweenTags(mockFile, "XSSCurrentPageParameters", []string{}, false) + + // Then + if !reflect.DeepEqual(actualResult, expectedResult) { + t.Errorf("%s Actual: %+v, Expected: %+v", "Occurrences list should be equal!", actualResult, expectedResult) + } +} + +// TestFindVulnerableLinesCloseTagFollowedByOpenTagKeepsState verifies that when a +// closing tag is immediately followed by a new opening tag on the same line, +// hasOpeningScriptOrStyleTagFound remains true and subsequent lines are still scanned. +func TestFindVulnerableLinesCloseTagFollowedByOpenTagKeepsState(t *testing.T) { + // Given - line 2 closes and re-opens a script block; line 3 should still be scanned + mockLines := []files.Line{ + {LineNumber: 1, Text: "", IsCommentedLine: false}, + } + mockFile := files.File{ + Lines: mockLines, + FileName: "test.page", + IgnoresSelected: []files.IgnoreSelected{}, + } + expectedResult := []rules.Occurrence{ + {LineContent: "", LineNumber: 4, IsFalsePositive: false}, + } + + // When + actualResult := findVulnerableLinesBetweenTags(mockFile, "XSSCurrentPageParameters", []string{}, false) + + // Then + if !reflect.DeepEqual(actualResult, expectedResult) { + t.Errorf("%s Actual: %+v, Expected: %+v", "Occurrences list should be equal!", actualResult, expectedResult) + } +} + +// TestFindVulnerableLinesColumnRangeLessThanGuard verifies the columnRange[0] < columnRange[1] +// guard introduced in the last commit. When closeTagMatch is nil (close tag not found after +// openTagEnd), columnRange stays [0,0] and the < condition correctly prevents appending a +// spurious ColumnRange. The line is still reported (as part of the opening-tag line) but +// without a ColumnRange field. +func TestFindVulnerableLinesColumnRangeLessThanGuard(t *testing.T) { + // Given - a self-closing-like line where openScriptStyleTagRegexp matches but + // closeScriptStyleTagRegexp also matches, yet the close tag appears BEFORE openTagEnd + // when searched from line.Text[openTagEnd:] → closeTagMatch is nil → columnRange = [0,0]. + // Construct: "", IsCommentedLine: false}, + } + mockFile := files.File{ + Lines: mockLines, + FileName: "test.page", + IgnoresSelected: []files.IgnoreSelected{}, + } + + // When + actualResult := findVulnerableLinesBetweenTags(mockFile, "XSSCurrentPageParameters", []string{}, false) + + // Then - ColumnRange [7, 14] is valid (7 < 14), so exactly one occurrence with a ColumnRange. + if len(actualResult) != 1 { + t.Fatalf("Expected 1 occurrence, got %d: %+v", len(actualResult), actualResult) + } + if len(actualResult[0].ColumnRange) != 2 { + t.Errorf("Expected a ColumnRange on the occurrence, got: %+v", actualResult[0]) + } + if actualResult[0].ColumnRange[0] >= actualResult[0].ColumnRange[1] { + t.Errorf("columnRange[0] must be < columnRange[1], got: %+v", actualResult[0].ColumnRange) + } +}