Repository navigation
feat(security-agent): warn about disabled Dependabot alerts #4904
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,10 @@ | ||
| import { beforeEach, describe, expect, it, jest } from '@jest/globals'; | ||
|
|
||
| const mockPaginate = jest.fn() as jest.MockedFunction<(...args: unknown[]) => Promise<unknown[]>>; | ||
| const mockListAlertsForRepo = jest.fn() as jest.MockedFunction< | ||
| (...args: unknown[]) => Promise<unknown> | ||
| >; | ||
| const mockGenerateGitHubInstallationToken = jest.fn(async () => ({ token: 'token' })); | ||
| const mockWarnExceptInTest = jest.fn(); | ||
| const mockErrorExceptInTest = jest.fn(); | ||
| const mockSentryLog = jest.fn(); | ||
|
|
@@ -11,14 +15,14 @@ jest.mock('@octokit/rest', () => ({ | |
| paginate: mockPaginate, | ||
| rest: { | ||
| dependabot: { | ||
| listAlertsForRepo: jest.fn(), | ||
| listAlertsForRepo: mockListAlertsForRepo, | ||
| }, | ||
| }, | ||
| })), | ||
| })); | ||
|
|
||
| jest.mock('@/lib/integrations/platforms/github/adapter', () => ({ | ||
| generateGitHubInstallationToken: jest.fn(async () => ({ token: 'token' })), | ||
| generateGitHubInstallationToken: mockGenerateGitHubInstallationToken, | ||
| })); | ||
|
|
||
| jest.mock('@/lib/utils.server', () => ({ | ||
|
|
@@ -63,4 +67,77 @@ describe('dependabot-api', () => { | |
| }); | ||
| expect(mockSentryLog).toHaveBeenCalledTimes(1); | ||
| }); | ||
|
|
||
| it('reports enabled only when the Dependabot alerts endpoint succeeds', async () => { | ||
| const { checkDependabotAlertsAvailability } = await import('./dependabot-api'); | ||
| mockListAlertsForRepo.mockResolvedValueOnce({ data: [] }); | ||
| mockListAlertsForRepo.mockRejectedValueOnce({ | ||
| status: 422, | ||
| message: 'Dependabot alerts are disabled for this repository', | ||
| }); | ||
| mockListAlertsForRepo.mockRejectedValueOnce({ status: 403, message: 'Forbidden' }); | ||
|
|
||
| await expect( | ||
| checkDependabotAlertsAvailability('inst-1', 'standard', [ | ||
| { id: 1, fullName: 'acme/enabled' }, | ||
| { id: 2, fullName: 'acme/disabled' }, | ||
| { id: 3, fullName: 'acme/unknown' }, | ||
| ]) | ||
| ).resolves.toEqual([ | ||
| { id: 1, status: 'enabled' }, | ||
| { id: 2, status: 'disabled' }, | ||
| { id: 3, status: 'unknown' }, | ||
| ]); | ||
|
|
||
| expect(mockListAlertsForRepo).toHaveBeenCalledWith({ | ||
| owner: 'acme', | ||
| repo: 'enabled', | ||
| state: 'open', | ||
| per_page: 1, | ||
| }); | ||
| }); | ||
|
|
||
| it('reuses a recent repository result without another GitHub request', async () => { | ||
| const { checkDependabotAlertsAvailability } = await import('./dependabot-api'); | ||
| mockListAlertsForRepo.mockResolvedValue({ data: [] }); | ||
| const repositories = [{ id: 1, fullName: 'cache-test/widgets' }]; | ||
|
|
||
| await expect( | ||
| checkDependabotAlertsAvailability('cache-installation', 'standard', repositories) | ||
| ).resolves.toEqual([{ id: 1, status: 'enabled' }]); | ||
| await expect( | ||
| checkDependabotAlertsAvailability('cache-installation', 'standard', repositories) | ||
| ).resolves.toEqual([{ id: 1, status: 'enabled' }]); | ||
|
|
||
| expect(mockGenerateGitHubInstallationToken).toHaveBeenCalledTimes(1); | ||
| expect(mockListAlertsForRepo).toHaveBeenCalledTimes(1); | ||
| }); | ||
|
|
||
| it('keeps every repository unknown when GitHub authentication fails', async () => { | ||
| const { checkDependabotAlertsAvailability } = await import('./dependabot-api'); | ||
| mockGenerateGitHubInstallationToken.mockRejectedValueOnce(new Error('auth unavailable')); | ||
|
|
||
| await expect( | ||
| checkDependabotAlertsAvailability('auth-failure-installation', 'standard', [ | ||
| { id: 1, fullName: 'auth-failure/widgets' }, | ||
| ]) | ||
| ).resolves.toEqual([{ id: 1, status: 'unknown' }]); | ||
|
|
||
| expect(mockListAlertsForRepo).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it('isolates malformed repository names instead of failing the whole check', async () => { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SUGGESTION: This test does not cover the new With Adding a case where the per-repository check rejects unexpectedly (for example Reply with |
||
| const { checkDependabotAlertsAvailability } = await import('./dependabot-api'); | ||
| mockListAlertsForRepo.mockResolvedValueOnce({ data: [] }); | ||
|
|
||
| await expect( | ||
| checkDependabotAlertsAvailability('malformed-installation', 'standard', [ | ||
| { id: 1, fullName: undefined as never }, | ||
| { id: 2, fullName: 'acme/valid' }, | ||
| ]) | ||
| ).resolves.toEqual([ | ||
| { id: 1, status: 'unknown' }, | ||
| { id: 2, status: 'enabled' }, | ||
| ]); | ||
| }); | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.