Skip to content

feat(security-agent): warn about disabled Dependabot alerts - #4904

Merged
jeanduplessis merged 3 commits into
mainfrom
whimsical-orca
Jul 31, 2026
Merged

feat(security-agent): warn about disabled Dependabot alerts#4904
jeanduplessis merged 3 commits into
mainfrom
whimsical-orca

Conversation

@jeanduplessis

@jeanduplessis jeanduplessis commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Warn users when selected repositories cannot supply Security Agent findings because Dependabot alerts are disabled.

Why this change is needed

Security Agent relies on GitHub Dependabot alerts. Repositories without alerts enabled can currently be selected without any indication that findings cannot be imported, which can give users a false sense of coverage.

How this is addressed

  • Check Dependabot alert availability for repositories shown in Security Agent settings and reuse recent results for 30 minutes.
  • Mark affected repositories with a "Dependabot alerts off" badge.
  • Show a warning when the current selection includes affected repositories.
  • Treat authentication, permission, and transient failures as unknown rather than incorrectly reporting alerts as disabled.
  • Keep repository selection available if GitHub availability checks fail.

Verification

CleanShot X 2026-07-30 21 45 17 CleanShot X 2026-07-30 21 45 18

Reviewer Notes

Human Reviewer Flags

  • Repository availability is checked with bounded concurrency and cached for 30 minutes per installation and repository. The cache sweeps expired entries and is capped at 10,000 entries; transient or unknown results are not cached.
  • Only definitive GitHub responses produce a disabled warning. Ambiguous failures remain unknown to avoid false warnings.

Code Reviewer Agent

Code Reviewer Notes
  • The repository response now carries an enabled, disabled, or unknown Dependabot status.
  • Checks use the existing GitHub App installation token and request one open alert, which proves API availability even when the result is empty. Server-side caching and in-flight deduplication limit repeated GitHub requests.
  • The shared repository picker gained an optional accessory renderer so Security Agent can add status UI without affecting its other consumers.
  • Failure handling preserves the existing repository list and does not block configuration.

Comment thread apps/web/src/lib/security-agent/router/shared-handlers.ts
Comment thread apps/web/src/lib/security-agent/github/dependabot-api.ts Outdated
Comment thread apps/web/src/lib/security-agent/github/dependabot-api.ts
Comment thread apps/web/src/lib/security-agent/github/dependabot-api.ts
Comment thread apps/web/src/components/security-agent/SecurityAgentContext.tsx Outdated
Comment thread apps/web/src/components/security-agent/SecurityConfigSections.tsx
@kilo-code-bot

kilo-code-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Executive Summary

All four previously reported findings are fixed in b7a2e9ab2; the remaining items are a redundant full-cache sweep after every Dependabot availability write and a new test that does not exercise the error-isolation path it targets.

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 2
Issue Details (click to expand)

SUGGESTION

File Line Issue
apps/web/src/lib/security-agent/github/dependabot-api.ts 299 Sweeping the whole cache after every individual write makes a cold check O(repositories x cache size) (up to 10,000 entries per scan); the request-start sweep at line 207 already handles TTL eviction, so only the size cap needs enforcing here
apps/web/src/lib/security-agent/github/dependabot-api.test.ts 129 The malformed-name case short-circuits at the cacheKey === null guard, so the new per-repository try/catch that maps unexpected rejections to unknown remains uncovered
Resolved since the previous review
  • Unbounded dependabotAvailabilityCache: now sweeps expired entries and enforces a 10,000-entry cap.
  • Cache-key computation throwing on a malformed fullName: the key builder accepts unknown and returns null, and each repository check is individually wrapped.
  • 30-minute client staleTime on getRepositories: removed, so the repository list uses the default query freshness while the server cache absorbs GitHub calls.
  • Non-actionable multi-repository warning: the alert now names up to five affected repositories and appends "and N more".
Files Reviewed (4 files)
  • apps/web/src/lib/security-agent/github/dependabot-api.ts - 1 issue
  • apps/web/src/lib/security-agent/github/dependabot-api.test.ts - 1 issue
  • apps/web/src/components/security-agent/SecurityAgentContext.tsx - 0 issues
  • apps/web/src/components/security-agent/SecurityConfigSections.tsx - 0 issues

Fix these issues in Kilo Cloud

Previous Review Summary (commit fcbf765)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit fcbf765)

Status: 4 Issues Found | Recommendation: Address before merge

Executive Summary

The new module-level Dependabot availability cache in dependabot-api.ts grows without bound in a long-lived server process, and the 30-minute client staleTime degrades freshness for the entire repository list rather than only the new status field.

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 2
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/lib/security-agent/github/dependabot-api.ts 146 dependabotAvailabilityCache is an unbounded module-level Map; expired entries are only evicted when the same key is looked up again, so it grows monotonically with (installations x repositories)
apps/web/src/components/security-agent/SecurityAgentContext.tsx 636 30-minute staleTime applies to the whole getRepositories payload (repository list, dashboard and findings filters), raising staleness from the 1-minute global default and leaving no way to refresh Dependabot status after enabling alerts in GitHub

SUGGESTION

File Line Issue
apps/web/src/lib/security-agent/github/dependabot-api.ts 208 Cache-key computation dereferences fullName before the malformed-name guard; a single missing full_name throws and degrades every repository to unknown, and Promise.all discards already-cached partial results
apps/web/src/components/security-agent/SecurityConfigSections.tsx 401 In "All repositories" mode with more than one affected repository the warning names none of them and the per-row badge is not rendered, so the warning is not actionable
Files Reviewed (9 files)
  • apps/web/src/lib/security-agent/github/dependabot-api.ts - 2 issues
  • apps/web/src/components/security-agent/SecurityAgentContext.tsx - 1 issue
  • apps/web/src/components/security-agent/SecurityConfigSections.tsx - 1 issue
  • apps/web/src/lib/security-agent/router/shared-handlers.ts - 0 new issues
  • apps/web/src/lib/security-agent/router/shared-handlers.test.ts - 0 issues
  • apps/web/src/lib/security-agent/github/dependabot-api.test.ts - 0 issues
  • apps/web/src/lib/security-agent/core/types.ts - 0 issues
  • apps/web/src/components/security-agent/security-config-types.ts - 0 issues
  • apps/web/src/components/code-reviews/RepositoryMultiSelect.tsx - 0 issues
Verified as correct (no finding)
  • renderRepositoryAccessory is optional and does not affect the four existing RepositoryMultiSelect consumers, including the string-id Bitbucket form.
  • toRepositoryOptions preserves the numeric repository id, so the accessory lookup, htmlFor, and selection state all key on the same value.
  • security-config-types.ts uses import type, so core/types.ts (zod, @kilocode/db/schema-types) cannot reach the client bundle.
  • Alert variant="warning" and the status-warning token classes exist in this repository.
  • No path reports enabled without a 2xx from listAlertsForRepo; 401, 451, and access-blocked responses all fall through to unknown, and the cache key includes installationId so no cross-tenant result reuse is possible.
  • No token, header, or repository credential is logged on the new failure paths.

Fix these issues in Kilo Cloud


Reviewed by claude-opus-5 · Input: 38 · Output: 10.5K · Cached: 1.1M

Review guidance: REVIEW.md from base branch main

status,
expiresAtMs: Date.now() + DEPENDABOT_AVAILABILITY_CACHE_TTL_MS,
});
sweepDependabotAvailabilityCache(Date.now());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Per-write sweep makes a cold check O(repositories x cache size)

sweepDependabotAvailabilityCache iterates the entire Map (up to DEPENDABOT_AVAILABILITY_CACHE_MAX_ENTRIES = 10,000 entries). Calling it here means one full scan per repository that resolves to enabled/disabled, on top of the scan already performed at the start of checkDependabotAlertsAvailability (line 207). For an installation with several hundred repositories on a cold cache this is hundreds of full-map scans per request, and the client now refetches getRepositories on the default 1-minute staleTime.

The request-start sweep already handles TTL eviction, so this call only needs to enforce the size cap; an unconditional while (size > MAX) trim (or sweeping once after Promise.all resolves) gets the same bound without the repeated O(n) expiry scan.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

expect(mockListAlertsForRepo).not.toHaveBeenCalled();
});

it('isolates malformed repository names instead of failing the whole check', async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: This test does not cover the new try/catch it appears to target

With fullName: undefined, dependabotAvailabilityCacheKey returns null, so checkRepositoryDependabotAlertsAvailability returns 'unknown' at the guard (dependabot-api.ts:282) and never reaches fetchRepositoryDependabotAlertsAvailability. Nothing throws, so the new catch block that maps an unexpected rejection to 'unknown' (dependabot-api.ts:249) stays uncovered.

Adding a case where the per-repository check rejects unexpectedly (for example mockListAlertsForRepo.mockImplementationOnce(() => { throw new Error('boom'); }) with a well-formed name, or an Octokit constructor that throws) would exercise the isolation path this commit adds.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@jeanduplessis
jeanduplessis merged commit b6da3ca into main Jul 31, 2026
16 checks passed
@jeanduplessis
jeanduplessis deleted the whimsical-orca branch July 31, 2026 12:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants