Skip to content

Commit f606f03

Browse files
feat: add confluence page id to secret extra details and to sarif properties (#357)
1 parent a649e3c commit f606f03

File tree

6 files changed

+190
-11
lines changed

6 files changed

+190
-11
lines changed

engine/engine.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ func buildSecret(
478478
pluginName string,
479479
) (*secrets.Secret, error) {
480480
gitInfo := item.GetGitInfo()
481-
itemId, err := getFindingId(item, &value)
481+
findingID, err := getFindingId(item, &value)
482482
if err != nil {
483483
return nil, fmt.Errorf("failed to get finding ID: %w", err)
484484
}
@@ -509,7 +509,7 @@ func buildSecret(
509509
}
510510

511511
secret := &secrets.Secret{
512-
ID: itemId,
512+
ID: findingID,
513513
Source: item.GetSource(),
514514
RuleID: value.RuleID,
515515
StartLine: startLine,
@@ -520,6 +520,15 @@ func buildSecret(
520520
LineContent: lineContent,
521521
RuleDescription: value.Description,
522522
}
523+
524+
if pluginName == "confluence" {
525+
if pageID, ok := plugins.ParseConfluenceItemID(item.GetID()); ok {
526+
if secret.ExtraDetails == nil {
527+
secret.ExtraDetails = make(map[string]interface{})
528+
}
529+
secret.ExtraDetails["confluence.pageId"] = pageID
530+
}
531+
}
523532
return secret, nil
524533
}
525534

engine/engine_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,3 +1223,36 @@ func TestProcessScoreWithoutValidation(t *testing.T) {
12231223
})
12241224
}
12251225
}
1226+
1227+
func TestBuildSecret(t *testing.T) {
1228+
t.Run("confluence plugin sets page id in extra details", func(t *testing.T) {
1229+
rawPlugin := plugins.NewConfluencePlugin()
1230+
confluencePlugin, ok := rawPlugin.(*plugins.ConfluencePlugin)
1231+
pageID := "6995346180"
1232+
version := 9
1233+
itemID := confluencePlugin.NewConfluenceItemID(pageID, version)
1234+
pluginName := confluencePlugin.GetName()
1235+
sourceURL := "https://example.atlassian.net/wiki/spaces/SCS/pages/" + pageID
1236+
content := "dummy"
1237+
it := &item{
1238+
id: itemID,
1239+
source: sourceURL,
1240+
content: &content,
1241+
}
1242+
finding := report.Finding{
1243+
RuleID: "github-pat",
1244+
StartLine: 1,
1245+
EndLine: 1,
1246+
Line: "token=SECRET",
1247+
Secret: "SECRET",
1248+
Description: "test finding",
1249+
}
1250+
secret, err := buildSecret(context.Background(), it, finding, pluginName)
1251+
require.NoError(t, err)
1252+
require.NotNil(t, secret)
1253+
require.NotNil(t, secret.ExtraDetails, "ExtraDetails should not be nil for confluence plugin")
1254+
value, ok := secret.ExtraDetails["confluence.pageId"]
1255+
assert.True(t, ok, "ExtraDetails should contain key %q", "confluence.pageId")
1256+
assert.Equal(t, pageID, value)
1257+
})
1258+
}

lib/reporting/report_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
var (
2121
ruleID1 = "ruleID1"
2222
ruleID2 = "ruleID2"
23+
ruleID4 = "ruleID4"
2324
result1 = &secrets.Secret{
2425
ID: "ID1",
2526
Source: "file1",
@@ -64,6 +65,24 @@ var (
6465
CvssScore: 0.0,
6566
RuleDescription: "Rule Description",
6667
}
68+
// result for confluence.pageId validation
69+
result4 = &secrets.Secret{
70+
ID: "ID4",
71+
Source: "file4",
72+
RuleID: "ruleID4",
73+
StartLine: 0,
74+
EndLine: 0,
75+
LineContent: "line content4",
76+
StartColumn: 11,
77+
EndColumn: 130,
78+
Value: "value 4",
79+
ValidationStatus: secrets.UnknownResult,
80+
CvssScore: 0.0,
81+
RuleDescription: "Rule Description",
82+
ExtraDetails: map[string]interface{}{
83+
"confluence.pageId": "1234567890",
84+
},
85+
}
6786
)
6887

6988
// test expected outputs
@@ -81,6 +100,12 @@ var (
81100
Text: result2.RuleDescription,
82101
},
83102
}
103+
rule4Sarif = &SarifRule{
104+
ID: ruleID4,
105+
FullDescription: &Message{
106+
Text: result4.RuleDescription,
107+
},
108+
}
84109
// sarif results
85110
result1Sarif = Results{
86111
Message: Message{
@@ -175,6 +200,38 @@ var (
175200
"cvssScore": result3.CvssScore,
176201
},
177202
}
203+
result4Sarif = Results{
204+
Message: Message{
205+
Text: createMessageText(result4.RuleID, result4.Source),
206+
},
207+
RuleId: ruleID4,
208+
Locations: []Locations{
209+
{
210+
PhysicalLocation: PhysicalLocation{
211+
ArtifactLocation: ArtifactLocation{
212+
URI: result4.Source,
213+
},
214+
Region: Region{
215+
StartLine: result4.StartLine,
216+
StartColumn: result4.StartColumn,
217+
EndLine: result4.EndLine,
218+
EndColumn: result4.EndColumn,
219+
Snippet: Snippet{
220+
Text: result4.Value,
221+
Properties: Properties{
222+
"lineContent": strings.TrimSpace(result4.LineContent),
223+
},
224+
},
225+
},
226+
},
227+
},
228+
},
229+
Properties: Properties{
230+
"validationStatus": string(result4.ValidationStatus),
231+
"cvssScore": result4.CvssScore,
232+
"confluence.pageId": result4.ExtraDetails["confluence.pageId"],
233+
},
234+
}
178235
)
179236

180237
func TestAddSecretToFile(t *testing.T) {
@@ -293,6 +350,33 @@ func TestGetOutputSarif(t *testing.T) {
293350
},
294351
},
295352
},
353+
{
354+
name: "includes confluence.pageId in sarif result properties",
355+
arg: &Report{
356+
TotalItemsScanned: 1,
357+
TotalSecretsFound: 1,
358+
Results: map[string][]*secrets.Secret{
359+
"secret1": {result4},
360+
},
361+
},
362+
wantErr: false,
363+
want: []Runs{
364+
{
365+
Tool: Tool{
366+
Driver: Driver{
367+
Name: "report",
368+
SemanticVersion: "1",
369+
Rules: []*SarifRule{
370+
rule4Sarif,
371+
},
372+
},
373+
},
374+
Results: []Results{
375+
result4Sarif,
376+
},
377+
},
378+
},
379+
},
296380
}
297381

298382
for _, tt := range tests {

lib/reporting/sarif.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,18 +89,26 @@ func getResults(report *Report) []Results {
8989
return results
9090
}
9191

92-
for _, secrets := range report.Results {
93-
for _, secret := range secrets {
92+
for _, secretsSlice := range report.Results {
93+
for _, secret := range secretsSlice {
94+
props := Properties{
95+
"validationStatus": secret.ValidationStatus,
96+
"cvssScore": secret.CvssScore,
97+
}
98+
99+
if secret.ExtraDetails != nil {
100+
if pageID, ok := secret.ExtraDetails["confluence.pageId"]; ok {
101+
props["confluence.pageId"] = pageID
102+
}
103+
}
104+
94105
r := Results{
95106
Message: Message{
96107
Text: createMessageText(secret.RuleID, secret.Source),
97108
},
98-
RuleId: secret.RuleID,
99-
Locations: getLocation(secret),
100-
Properties: Properties{
101-
"validationStatus": secret.ValidationStatus,
102-
"cvssScore": secret.CvssScore,
103-
},
109+
RuleId: secret.RuleID,
110+
Locations: getLocation(secret),
111+
Properties: props,
104112
}
105113
results = append(results, r)
106114
}

plugins/confluence.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ func chunkStrings(input []string, chunkSize int) [][]string {
469469

470470
// convertPageToItem converts a Confluence Page into an ISourceItem.
471471
func (p *ConfluencePlugin) convertPageToItem(page *Page) ISourceItem {
472-
itemID := fmt.Sprintf("%s-%s-%s", p.GetName(), page.ID, strconv.Itoa(page.Version.Number))
472+
itemID := p.NewConfluenceItemID(page.ID, page.Version.Number)
473473

474474
sourceURL := ""
475475
if resolvedURL, ok := p.resolveConfluenceSourceURL(page, page.Version.Number); ok {
@@ -668,3 +668,31 @@ func isValidNumericID(s string) bool {
668668
}
669669
return true
670670
}
671+
672+
// NewConfluenceItemID builds the item ID for a Confluence page.
673+
func (p *ConfluencePlugin) NewConfluenceItemID(pageID string, version int) string {
674+
return fmt.Sprintf("%s-%s-%d", p.GetName(), pageID, version)
675+
}
676+
677+
// ParseConfluenceItemID extracts the Confluence page ID from an item ID
678+
// produced by NewConfluenceItemID. It returns ("", false) if the ID does not
679+
// conform to the expected pattern.
680+
func ParseConfluenceItemID(id string) (string, bool) {
681+
parts := strings.Split(id, "-")
682+
if len(parts) != 3 {
683+
return "", false
684+
}
685+
686+
// Last segment must be an integer version.
687+
if _, err := strconv.Atoi(parts[len(parts)-1]); err != nil {
688+
return "", false
689+
}
690+
691+
// Second-to-last segment must be a valid numeric pageId.
692+
pageID := parts[len(parts)-2]
693+
if !isValidNumericID(pageID) {
694+
return "", false
695+
}
696+
697+
return pageID, true
698+
}

plugins/confluence_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,6 +1699,23 @@ func TestTrimNonEmpty(t *testing.T) {
16991699
}
17001700
}
17011701

1702+
func TestNewConfluenceItemID(t *testing.T) {
1703+
p := &ConfluencePlugin{}
1704+
pageID := "6995346180"
1705+
version := 9
1706+
expectedID := "confluence-6995346180-9"
1707+
actual := p.NewConfluenceItemID(pageID, version)
1708+
assert.Equal(t, expectedID, actual)
1709+
}
1710+
1711+
func TestParseConfluenceItemID(t *testing.T) {
1712+
p := &ConfluencePlugin{}
1713+
id := p.NewConfluenceItemID("123456", 3)
1714+
actualPageID, ok := ParseConfluenceItemID(id)
1715+
assert.True(t, ok)
1716+
assert.Equal(t, "123456", actualPageID)
1717+
}
1718+
17021719
func newPluginWithMock(t *testing.T) (*ConfluencePlugin, *gomock.Controller, *MockConfluenceClient, *chunk.MockIChunk) {
17031720
t.Helper()
17041721
ctrl := gomock.NewController(t)

0 commit comments

Comments
 (0)