diff --git a/pkg/github/__toolsnaps__/list_code_scanning_alerts.snap b/pkg/github/__toolsnaps__/list_code_scanning_alerts.snap index e33bcccb12..05075411e9 100644 --- a/pkg/github/__toolsnaps__/list_code_scanning_alerts.snap +++ b/pkg/github/__toolsnaps__/list_code_scanning_alerts.snap @@ -67,31 +67,19 @@ }, "name": "list_code_scanning_alerts", "outputSchema": { - "items": { - "additionalProperties": false, - "properties": { - "dismissed_comment": { - "type": [ - "null", - "string" - ] - }, - "dismissed_reason": { - "type": [ - "null", - "string" - ] - }, - "html_url": { - "type": [ - "null", - "string" - ] - }, - "most_recent_instance": { + "additionalProperties": false, + "properties": { + "alerts": { + "items": { "additionalProperties": false, "properties": { - "commit_sha": { + "dismissed_comment": { + "type": [ + "null", + "string" + ] + }, + "dismissed_reason": { "type": [ "null", "string" @@ -103,37 +91,76 @@ "string" ] }, - "location": { + "most_recent_instance": { "additionalProperties": false, "properties": { - "end_column": { + "commit_sha": { + "type": [ + "null", + "string" + ] + }, + "html_url": { "type": [ "null", - "integer" + "string" ] }, - "end_line": { + "location": { + "additionalProperties": false, + "properties": { + "end_column": { + "type": [ + "null", + "integer" + ] + }, + "end_line": { + "type": [ + "null", + "integer" + ] + }, + "path": { + "type": [ + "null", + "string" + ] + }, + "start_column": { + "type": [ + "null", + "integer" + ] + }, + "start_line": { + "type": [ + "null", + "integer" + ] + } + }, "type": [ "null", - "integer" + "object" ] }, - "path": { + "message": { "type": [ "null", "string" ] }, - "start_column": { + "ref": { "type": [ "null", - "integer" + "string" ] }, - "start_line": { + "state": { "type": [ "null", - "integer" + "string" ] } }, @@ -142,16 +169,56 @@ "object" ] }, - "message": { + "number": { "type": [ "null", - "string" + "integer" ] }, - "ref": { + "rule": { + "additionalProperties": false, + "properties": { + "description": { + "type": [ + "null", + "string" + ] + }, + "help": { + "type": [ + "null", + "string" + ] + }, + "id": { + "type": [ + "null", + "string" + ] + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "security_severity_level": { + "description": "Security impact level, distinct from rule diagnostic severity.", + "type": [ + "null", + "string" + ] + }, + "severity": { + "type": [ + "null", + "string" + ] + } + }, "type": [ "null", - "string" + "object" ] }, "state": { @@ -166,73 +233,38 @@ "object" ] }, - "number": { - "type": [ - "null", - "integer" - ] - }, - "rule": { - "additionalProperties": false, - "properties": { - "description": { - "type": [ - "null", - "string" - ] - }, - "help": { - "type": [ - "null", - "string" - ] - }, - "id": { - "type": [ - "null", - "string" - ] - }, - "name": { - "type": [ - "null", - "string" - ] - }, - "security_severity_level": { - "description": "Security impact level, distinct from rule diagnostic severity.", - "type": [ - "null", - "string" - ] - }, - "severity": { - "type": [ - "null", - "string" - ] - } + "type": [ + "null", + "array" + ] + }, + "pageInfo": { + "additionalProperties": false, + "properties": { + "hasNextPage": { + "type": "boolean" }, - "type": [ - "null", - "object" - ] + "hasPreviousPage": { + "type": "boolean" + }, + "nextCursor": { + "type": "string" + }, + "prevCursor": { + "type": "string" + } }, - "state": { - "type": [ - "null", - "string" - ] - } - }, - "type": [ - "null", - "object" - ] + "required": [ + "hasNextPage", + "hasPreviousPage" + ], + "type": "object" + } }, - "type": [ - "null", - "array" - ] + "required": [ + "alerts", + "pageInfo" + ], + "type": "object" } } \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/list_secret_scanning_alerts.snap b/pkg/github/__toolsnaps__/list_secret_scanning_alerts.snap index 2bceafa03d..e466f76d85 100644 --- a/pkg/github/__toolsnaps__/list_secret_scanning_alerts.snap +++ b/pkg/github/__toolsnaps__/list_secret_scanning_alerts.snap @@ -59,46 +59,131 @@ }, "name": "list_secret_scanning_alerts", "outputSchema": { - "items": { - "additionalProperties": false, - "properties": { - "first_location_detected": { + "additionalProperties": false, + "properties": { + "alerts": { + "items": { "additionalProperties": false, "properties": { - "commit_sha": { + "first_location_detected": { + "additionalProperties": false, + "properties": { + "commit_sha": { + "type": [ + "null", + "string" + ] + }, + "end_column": { + "type": [ + "null", + "integer" + ] + }, + "end_line": { + "type": [ + "null", + "integer" + ] + }, + "path": { + "type": [ + "null", + "string" + ] + }, + "start_column": { + "type": [ + "null", + "integer" + ] + }, + "start_line": { + "type": [ + "null", + "integer" + ] + } + }, + "type": [ + "null", + "object" + ] + }, + "has_more_locations": { + "description": "Whether additional secret locations exist beyond the first location.", + "type": [ + "null", + "boolean" + ] + }, + "html_url": { "type": [ "null", "string" ] }, - "end_column": { + "is_base64_encoded": { + "description": "Whether the detected secret is base64 encoded.", "type": [ "null", - "integer" + "boolean" ] }, - "end_line": { + "number": { "type": [ "null", "integer" ] }, - "path": { + "push_protection_bypassed": { + "description": "Whether push protection was bypassed for this secret.", + "type": [ + "null", + "boolean" + ] + }, + "resolution": { "type": [ "null", "string" ] }, - "start_column": { + "resolution_comment": { "type": [ "null", - "integer" + "string" ] }, - "start_line": { + "secret": { "type": [ "null", - "integer" + "string" + ] + }, + "secret_type": { + "type": [ + "null", + "string" + ] + }, + "secret_type_display_name": { + "type": [ + "null", + "string" + ] + }, + "state": { + "type": [ + "null", + "string" + ] + }, + "validity": { + "description": "Credential validity reported by GitHub: active, inactive, or unknown.", + "type": [ + "null", + "string" ] } }, @@ -107,91 +192,38 @@ "object" ] }, - "has_more_locations": { - "description": "Whether additional secret locations exist beyond the first location.", - "type": [ - "null", - "boolean" - ] - }, - "html_url": { - "type": [ - "null", - "string" - ] - }, - "is_base64_encoded": { - "description": "Whether the detected secret is base64 encoded.", - "type": [ - "null", - "boolean" - ] - }, - "number": { - "type": [ - "null", - "integer" - ] - }, - "push_protection_bypassed": { - "description": "Whether push protection was bypassed for this secret.", - "type": [ - "null", - "boolean" - ] - }, - "resolution": { - "type": [ - "null", - "string" - ] - }, - "resolution_comment": { - "type": [ - "null", - "string" - ] - }, - "secret": { - "type": [ - "null", - "string" - ] - }, - "secret_type": { - "type": [ - "null", - "string" - ] - }, - "secret_type_display_name": { - "type": [ - "null", - "string" - ] - }, - "state": { - "type": [ - "null", - "string" - ] - }, - "validity": { - "description": "Credential validity reported by GitHub: active, inactive, or unknown.", - "type": [ - "null", - "string" - ] - } + "type": [ + "null", + "array" + ] }, - "type": [ - "null", - "object" - ] + "pageInfo": { + "additionalProperties": false, + "properties": { + "hasNextPage": { + "type": "boolean" + }, + "hasPreviousPage": { + "type": "boolean" + }, + "nextCursor": { + "type": "string" + }, + "prevCursor": { + "type": "string" + } + }, + "required": [ + "hasNextPage", + "hasPreviousPage" + ], + "type": "object" + } }, - "type": [ - "null", - "array" - ] + "required": [ + "alerts", + "pageInfo" + ], + "type": "object" } } \ No newline at end of file diff --git a/pkg/github/code_scanning.go b/pkg/github/code_scanning.go index 6d0ef073b5..6f13268aa8 100644 --- a/pkg/github/code_scanning.go +++ b/pkg/github/code_scanning.go @@ -133,7 +133,7 @@ func ListCodeScanningAlerts(t translations.TranslationHelperFunc) inventory.Serv } WithPagination(schema) - return NewToolWithSchemaOptions[ListCodeScanningAlertsInput, []*CodeScanningAlertOutput]( + return NewToolWithSchemaOptions[ListCodeScanningAlertsInput, CodeScanningAlertsOutput]( ToolsetMetadataCodeSecurity, mcp.Tool{ Name: "list_code_scanning_alerts", @@ -148,18 +148,18 @@ func ListCodeScanningAlerts(t translations.TranslationHelperFunc) inventory.Serv inventory.TypedSchemaOptions{ ValidationInputSchema: inventory.CloneSchemaWithoutDefaults(schema), }, - func(ctx context.Context, deps ToolDependencies, _ *mcp.CallToolRequest, args ListCodeScanningAlertsInput) (*mcp.CallToolResult, []*CodeScanningAlertOutput, error) { + func(ctx context.Context, deps ToolDependencies, _ *mcp.CallToolRequest, args ListCodeScanningAlertsInput) (*mcp.CallToolResult, CodeScanningAlertsOutput, error) { if args.Owner == "" { - return utils.NewToolResultError("missing required parameter: owner"), nil, nil + return utils.NewToolResultError("missing required parameter: owner"), CodeScanningAlertsOutput{}, nil } if args.Repo == "" { - return utils.NewToolResultError("missing required parameter: repo"), nil, nil + return utils.NewToolResultError("missing required parameter: repo"), CodeScanningAlertsOutput{}, nil } pagination := securityPagination(args.Page, args.PerPage) client, err := deps.GetClient(ctx) if err != nil { - return utils.NewToolResultErrorFromErr("failed to get GitHub client", err), nil, nil + return utils.NewToolResultErrorFromErr("failed to get GitHub client", err), CodeScanningAlertsOutput{}, nil } alerts, resp, err := client.CodeScanning.ListAlertsForRepo(ctx, args.Owner, args.Repo, &github.AlertListOptions{ Ref: args.Ref, @@ -176,21 +176,34 @@ func ListCodeScanningAlerts(t translations.TranslationHelperFunc) inventory.Serv "failed to list alerts", resp, err, - ), nil, nil + ), CodeScanningAlertsOutput{}, nil } defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { body, err := io.ReadAll(resp.Body) if err != nil { - return utils.NewToolResultErrorFromErr("failed to read response body", err), nil, nil + return utils.NewToolResultErrorFromErr("failed to read response body", err), CodeScanningAlertsOutput{}, nil } - return ghErrors.NewGitHubAPIStatusErrorResponse(ctx, "failed to list alerts", resp, body), nil, nil + return ghErrors.NewGitHubAPIStatusErrorResponse(ctx, "failed to list alerts", resp, body), CodeScanningAlertsOutput{}, nil + } + + apiPageInfo := buildOffsetPageInfo(resp) + output := CodeScanningAlertsOutput{ + Alerts: mapSecurityOutputs(alerts, codeScanningAlertOutput), + PageInfo: SecurityPageInfo(apiPageInfo), } - r, err := json.Marshal(alerts) + response := struct { + Alerts []*github.Alert `json:"alerts"` + PageInfo pageInfo `json:"pageInfo"` + }{ + Alerts: alerts, + PageInfo: apiPageInfo, + } + r, err := json.Marshal(response) if err != nil { - return utils.NewToolResultErrorFromErr("failed to marshal alerts", err), nil, nil + return utils.NewToolResultErrorFromErr("failed to marshal alerts", err), CodeScanningAlertsOutput{}, nil } result := utils.NewToolResultText(string(r)) @@ -198,7 +211,7 @@ func ListCodeScanningAlerts(t translations.TranslationHelperFunc) inventory.Serv // visibility and embed attacker-influenceable code snippets, so the // label is always private-untrusted. result = attachStaticIFCLabel(ctx, deps, result, ifc.LabelSecurityAlert()) - return result, mapSecurityOutputs(alerts, codeScanningAlertOutput), nil + return result, output, nil }, normalizeSecurityIntegerArguments("page", "perPage"), normalizeTypedReadArguments(nil, false), diff --git a/pkg/github/code_scanning_test.go b/pkg/github/code_scanning_test.go index 4095a46f1a..691f0866f2 100644 --- a/pkg/github/code_scanning_test.go +++ b/pkg/github/code_scanning_test.go @@ -163,6 +163,7 @@ func Test_ListCodeScanningAlerts(t *testing.T) { expectError bool expectedAlerts []*github.Alert expectedErrMsg string + expectNextPage bool }{ { name: "successful alerts listing", @@ -208,6 +209,24 @@ func Test_ListCodeScanningAlerts(t *testing.T) { expectError: false, expectedAlerts: mockAlerts, }, + { + name: "successful alerts listing advertises next page", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposCodeScanningAlertsByOwnerByRepo: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Link", `; rel="next"`) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(mockAlerts) + }), + }), + requestArgs: map[string]any{ + "owner": "owner", + "repo": "repo", + }, + expectError: false, + expectedAlerts: mockAlerts, + expectNextPage: true, + }, { name: "alerts listing fails", mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ @@ -256,16 +275,24 @@ func Test_ListCodeScanningAlerts(t *testing.T) { textContent := getTextResult(t, result) // Unmarshal and verify the result - var returnedAlerts []*github.Alert - err = json.Unmarshal([]byte(textContent.Text), &returnedAlerts) + var returnedResult struct { + Alerts []*github.Alert `json:"alerts"` + PageInfo struct { + HasNextPage bool `json:"hasNextPage"` + HasPreviousPage bool `json:"hasPreviousPage"` + } `json:"pageInfo"` + } + err = json.Unmarshal([]byte(textContent.Text), &returnedResult) assert.NoError(t, err) - assert.Len(t, returnedAlerts, len(tc.expectedAlerts)) - for i, alert := range returnedAlerts { + assert.Len(t, returnedResult.Alerts, len(tc.expectedAlerts)) + for i, alert := range returnedResult.Alerts { assert.Equal(t, *tc.expectedAlerts[i].Number, *alert.Number) assert.Equal(t, *tc.expectedAlerts[i].State, *alert.State) assert.Equal(t, *tc.expectedAlerts[i].Rule.ID, *alert.Rule.ID) assert.Equal(t, *tc.expectedAlerts[i].HTMLURL, *alert.HTMLURL) } + assert.Equal(t, tc.expectNextPage, returnedResult.PageInfo.HasNextPage) + assert.False(t, returnedResult.PageInfo.HasPreviousPage) }) } } diff --git a/pkg/github/params.go b/pkg/github/params.go index 416020abb7..782e369ba4 100644 --- a/pkg/github/params.go +++ b/pkg/github/params.go @@ -471,6 +471,15 @@ func buildPageInfo(resp *github.Response) pageInfo { } } +// buildOffsetPageInfo builds pageInfo for REST list endpoints that paginate with +// page/per_page Link headers (NextPage/PrevPage) rather than cursor tokens. +func buildOffsetPageInfo(resp *github.Response) pageInfo { + return pageInfo{ + HasNextPage: resp.NextPage != 0, + HasPreviousPage: resp.PrevPage != 0, + } +} + // ToGraphQLParams converts cursor pagination parameters to GraphQL-specific parameters. func (p CursorPaginationParams) ToGraphQLParams() (*GraphQLPaginationParams, error) { if p.PerPage > 100 { diff --git a/pkg/github/secret_scanning.go b/pkg/github/secret_scanning.go index 43dd1fd005..1ec02939be 100644 --- a/pkg/github/secret_scanning.go +++ b/pkg/github/secret_scanning.go @@ -129,7 +129,7 @@ func ListSecretScanningAlerts(t translations.TranslationHelperFunc) inventory.Se } WithPagination(schema) - return NewTool[ListSecretScanningAlertsInput, []*SecretScanningAlertOutput]( + return NewTool[ListSecretScanningAlertsInput, SecretScanningAlertsOutput]( ToolsetMetadataSecretProtection, mcp.Tool{ Name: "list_secret_scanning_alerts", @@ -141,18 +141,18 @@ func ListSecretScanningAlerts(t translations.TranslationHelperFunc) inventory.Se InputSchema: schema, }, scopes.RequireAll(scopes.SecurityEvents), - func(ctx context.Context, deps ToolDependencies, _ *mcp.CallToolRequest, args ListSecretScanningAlertsInput) (*mcp.CallToolResult, []*SecretScanningAlertOutput, error) { + func(ctx context.Context, deps ToolDependencies, _ *mcp.CallToolRequest, args ListSecretScanningAlertsInput) (*mcp.CallToolResult, SecretScanningAlertsOutput, error) { if args.Owner == "" { - return utils.NewToolResultError("missing required parameter: owner"), nil, nil + return utils.NewToolResultError("missing required parameter: owner"), SecretScanningAlertsOutput{}, nil } if args.Repo == "" { - return utils.NewToolResultError("missing required parameter: repo"), nil, nil + return utils.NewToolResultError("missing required parameter: repo"), SecretScanningAlertsOutput{}, nil } pagination := securityPagination(args.Page, args.PerPage) client, err := deps.GetClient(ctx) if err != nil { - return nil, nil, fmt.Errorf("failed to get GitHub client: %w", err) + return nil, SecretScanningAlertsOutput{}, fmt.Errorf("failed to get GitHub client: %w", err) } alerts, resp, err := client.SecretScanning.ListAlertsForRepo(ctx, args.Owner, args.Repo, &github.SecretScanningAlertListOptions{ State: args.State, @@ -168,21 +168,34 @@ func ListSecretScanningAlerts(t translations.TranslationHelperFunc) inventory.Se fmt.Sprintf("failed to list alerts for repository '%s/%s'", args.Owner, args.Repo), resp, err, - ), nil, nil + ), SecretScanningAlertsOutput{}, nil } defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { body, err := io.ReadAll(resp.Body) if err != nil { - return nil, nil, fmt.Errorf("failed to read response body: %w", err) + return nil, SecretScanningAlertsOutput{}, fmt.Errorf("failed to read response body: %w", err) } - return ghErrors.NewGitHubAPIStatusErrorResponse(ctx, "failed to list alerts", resp, body), nil, nil + return ghErrors.NewGitHubAPIStatusErrorResponse(ctx, "failed to list alerts", resp, body), SecretScanningAlertsOutput{}, nil + } + + apiPageInfo := buildOffsetPageInfo(resp) + output := SecretScanningAlertsOutput{ + Alerts: mapSecurityOutputs(alerts, secretScanningAlertOutput), + PageInfo: SecurityPageInfo(apiPageInfo), } - r, err := json.Marshal(alerts) //nolint:gosec // G117: This security_events-scoped tool intentionally returns alert secrets; the result is labeled private-untrusted. + response := struct { + Alerts []*github.SecretScanningAlert `json:"alerts"` + PageInfo pageInfo `json:"pageInfo"` + }{ + Alerts: alerts, + PageInfo: apiPageInfo, + } + r, err := json.Marshal(response) //nolint:gosec // G117: This security_events-scoped tool intentionally returns alert secrets; the result is labeled private-untrusted. if err != nil { - return nil, nil, fmt.Errorf("failed to marshal alerts: %w", err) + return nil, SecretScanningAlertsOutput{}, fmt.Errorf("failed to marshal alerts: %w", err) } result := utils.NewToolResultText(string(r)) @@ -190,7 +203,7 @@ func ListSecretScanningAlerts(t translations.TranslationHelperFunc) inventory.Se // visibility and surface the matched secret material itself, so the // label is always private-untrusted. result = attachStaticIFCLabel(ctx, deps, result, ifc.LabelSecurityAlert()) - return result, mapSecurityOutputs(alerts, secretScanningAlertOutput), nil + return result, output, nil }, normalizeSecurityIntegerArguments("page", "perPage"), normalizeTypedReadArguments(nil, false), diff --git a/pkg/github/secret_scanning_test.go b/pkg/github/secret_scanning_test.go index 4891b142ec..a54481eba2 100644 --- a/pkg/github/secret_scanning_test.go +++ b/pkg/github/secret_scanning_test.go @@ -258,17 +258,25 @@ func Test_ListSecretScanningAlerts(t *testing.T) { textContent := getTextResult(t, result) // Unmarshal and verify the result - var returnedAlerts []*github.SecretScanningAlert - err = json.Unmarshal([]byte(textContent.Text), &returnedAlerts) + var returnedResult struct { + Alerts []*github.SecretScanningAlert `json:"alerts"` + PageInfo struct { + HasNextPage bool `json:"hasNextPage"` + HasPreviousPage bool `json:"hasPreviousPage"` + } `json:"pageInfo"` + } + err = json.Unmarshal([]byte(textContent.Text), &returnedResult) assert.NoError(t, err) - assert.Len(t, returnedAlerts, len(tc.expectedAlerts)) - for i, alert := range returnedAlerts { + assert.Len(t, returnedResult.Alerts, len(tc.expectedAlerts)) + for i, alert := range returnedResult.Alerts { assert.Equal(t, *tc.expectedAlerts[i].Number, *alert.Number) assert.Equal(t, *tc.expectedAlerts[i].HTMLURL, *alert.HTMLURL) assert.Equal(t, *tc.expectedAlerts[i].State, *alert.State) assert.Equal(t, *tc.expectedAlerts[i].Resolution, *alert.Resolution) assert.Equal(t, *tc.expectedAlerts[i].SecretType, *alert.SecretType) } + assert.False(t, returnedResult.PageInfo.HasNextPage) + assert.False(t, returnedResult.PageInfo.HasPreviousPage) }) } } diff --git a/pkg/github/security_outputs.go b/pkg/github/security_outputs.go index 88fb34f12b..1c9bd2ee26 100644 --- a/pkg/github/security_outputs.go +++ b/pkg/github/security_outputs.go @@ -254,6 +254,16 @@ type SecurityPageInfo struct { PrevCursor string `json:"prevCursor,omitempty"` } +type CodeScanningAlertsOutput struct { + Alerts []*CodeScanningAlertOutput `json:"alerts"` + PageInfo SecurityPageInfo `json:"pageInfo"` +} + +type SecretScanningAlertsOutput struct { + Alerts []*SecretScanningAlertOutput `json:"alerts"` + PageInfo SecurityPageInfo `json:"pageInfo"` +} + type DependabotAlertsOutput struct { Alerts []*DependabotAlertOutput `json:"alerts"` PageInfo SecurityPageInfo `json:"pageInfo"` diff --git a/pkg/github/typed_security_compatibility_test.go b/pkg/github/typed_security_compatibility_test.go index 994aacb69e..e4e093f052 100644 --- a/pkg/github/typed_security_compatibility_test.go +++ b/pkg/github/typed_security_compatibility_test.go @@ -148,7 +148,9 @@ func TestSecurityEmptyAndErrorOutputs(t *testing.T) { return } if protocol == "2025-11-25" { - if tt.tool.Tool.Name == "list_dependabot_alerts" { + if tt.tool.Tool.Name == "list_dependabot_alerts" || + tt.tool.Tool.Name == "list_code_scanning_alerts" || + tt.tool.Tool.Name == "list_secret_scanning_alerts" { assert.JSONEq(t, `{"alerts":`+body+`,"pageInfo":{"hasNextPage":false,"hasPreviousPage":false}}`, getTextResult(t, result).Text) } else { assert.Equal(t, body, getTextResult(t, result).Text) @@ -175,7 +177,9 @@ func TestSecurityEmptyAndErrorOutputs(t *testing.T) { } if tt.list { assert.JSONEq(t, mustMarshalJSON(t, result.StructuredContent), getTextResult(t, result).Text, "modern lists expose the same DTO through both channels") - if tt.tool.Tool.Name == "list_dependabot_alerts" { + if tt.tool.Tool.Name == "list_dependabot_alerts" || + tt.tool.Tool.Name == "list_code_scanning_alerts" || + tt.tool.Tool.Name == "list_secret_scanning_alerts" { assert.JSONEq(t, getTextResult(t, result).Text, mustMarshalJSON(t, result.StructuredContent)) } else { assert.Equal(t, body, mustMarshalJSON(t, result.StructuredContent)) diff --git a/pkg/github/typed_security_outputs_test.go b/pkg/github/typed_security_outputs_test.go index 414e8cb7a8..c00ce33eb4 100644 --- a/pkg/github/typed_security_outputs_test.go +++ b/pkg/github/typed_security_outputs_test.go @@ -27,11 +27,11 @@ func securityOutputSchema(t *testing.T, name string) *jsonschema.Schema { case "get_code_scanning_alert": schema, err = jsonschema.For[*CodeScanningAlertOutput](nil) case "list_code_scanning_alerts": - schema, err = jsonschema.For[[]*CodeScanningAlertOutput](nil) + schema, err = jsonschema.For[CodeScanningAlertsOutput](nil) case "get_secret_scanning_alert": schema, err = jsonschema.For[*SecretScanningAlertOutput](nil) case "list_secret_scanning_alerts": - schema, err = jsonschema.For[[]*SecretScanningAlertOutput](nil) + schema, err = jsonschema.For[SecretScanningAlertsOutput](nil) case "get_dependabot_alert": schema, err = jsonschema.For[*DependabotAlertOutput](nil) case "list_dependabot_alerts": @@ -138,10 +138,16 @@ func TestTypedSecurityToolOutputs(t *testing.T) { legacyOutputs := map[string]any{ "get_code_quality_finding": codeQualityFinding, "get_code_scanning_alert": codeScanningAlert, - "list_code_scanning_alerts": []*github.Alert{codeScanningAlert}, - "get_secret_scanning_alert": secretScanningAlert, - "list_secret_scanning_alerts": []*github.SecretScanningAlert{secretScanningAlert}, - "get_dependabot_alert": dependabotAlert, + "list_code_scanning_alerts": struct { + Alerts []*github.Alert `json:"alerts"` + PageInfo pageInfo `json:"pageInfo"` + }{Alerts: []*github.Alert{codeScanningAlert}}, + "get_secret_scanning_alert": secretScanningAlert, + "list_secret_scanning_alerts": struct { + Alerts []*github.SecretScanningAlert `json:"alerts"` + PageInfo pageInfo `json:"pageInfo"` + }{Alerts: []*github.SecretScanningAlert{secretScanningAlert}}, + "get_dependabot_alert": dependabotAlert, "list_dependabot_alerts": struct { Alerts []*github.DependabotAlert `json:"alerts"` PageInfo pageInfo `json:"pageInfo"` @@ -383,7 +389,9 @@ func TestSecurityOutputDTOs(t *testing.T) { for _, typ := range []reflect.Type{ reflect.TypeFor[CodeQualityFindingOutput](), reflect.TypeFor[CodeScanningAlertOutput](), + reflect.TypeFor[CodeScanningAlertsOutput](), reflect.TypeFor[SecretScanningAlertOutput](), + reflect.TypeFor[SecretScanningAlertsOutput](), reflect.TypeFor[DependabotAlertsOutput](), reflect.TypeFor[GlobalSecurityAdvisoryOutput](), } {