Add RAG feature - #4
Conversation
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdded retrieval-backed Spring AI chat for codebases. The API authenticates requests, retrieves relevant code, enriches prompts, and streams messages, citations, completion, and error events over SSE. ChangesCodebase chat flow
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
server/src/main/java/com/meet/server/feature/codebase/CodebaseService.java (1)
106-112: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy liftSSRF (CWE-918): Server-Side Request Forgery (SSRF)
Reachability: External
Reachability path
● Entry server/src/main/java/com/meet/server/feature/codebase/CodebaseController.java:54 streamChat │ ▼ ● Sink server/src/main/java/com/meet/server/feature/codebase/CodebaseService.javaBlock non-public clone destinations before Git connections.
validateCloneUrlacceptshttps://127.0.0.1/...and HTTPS hostnames that resolve to loopback, link-local, private, or metadata addresses.startClonesaves the caller-controlled clone URL, andprocesspasses it directly toGitService.cloneRepository, creating an SSRF path. Reject non-public DNS/redirect hops or route clones through an egress proxy/allowlist that blocks loopback, link-local, private, and metadata ranges.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/src/main/java/com/meet/server/feature/codebase/CodebaseService.java` around lines 106 - 112, Update validateCloneUrl and the startClone/process flow to prevent SSRF: reject literal and DNS-resolved loopback, link-local, private, and metadata destinations, and ensure redirects cannot reach non-public addresses before GitService.cloneRepository connects. Prefer the existing egress proxy or allowlist mechanism if available; otherwise validate every resolved destination and redirect hop while preserving acceptance of public HTTPS clone URLs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@server/specs/codebase.md`:
- Line 1: Update the document’s opening heading from the bracketed form to a
Markdown top-level heading, using `# Codebase API Specification` so the required
document heading is recognized.
- Line 177: Update the SecurityConfig.java entry in the codebase documentation
by removing the malformed `]([]())` suffix, leaving only the intended file
reference, and ensure the file ends with exactly one newline.
In `@server/src/main/java/com/meet/server/feature/retriver/CodeRetriever.java`:
- Around line 31-48: Update the retrieval method around the citations stream and
context-building loop so citations are created only for results whose nonblank
content is actually appended to the prompt. Remove the eager citation
collection, build each CodeCitation after appending its snippet, and retain the
existing truncation and remaining-budget behavior.
---
Outside diff comments:
In `@server/src/main/java/com/meet/server/feature/codebase/CodebaseService.java`:
- Around line 106-112: Update validateCloneUrl and the startClone/process flow
to prevent SSRF: reject literal and DNS-resolved loopback, link-local, private,
and metadata destinations, and ensure redirects cannot reach non-public
addresses before GitService.cloneRepository connects. Prefer the existing egress
proxy or allowlist mechanism if available; otherwise validate every resolved
destination and redirect hop while preserving acceptance of public HTTPS clone
URLs.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 2cfaec77-c869-49be-aa0b-1ea06dd52b58
📒 Files selected for processing (10)
server/specs/codebase.mdserver/src/main/java/com/meet/server/feature/advisor/CodeAdvisor.javaserver/src/main/java/com/meet/server/feature/codebase/CodebaseController.javaserver/src/main/java/com/meet/server/feature/codebase/CodebaseService.javaserver/src/main/java/com/meet/server/feature/codebase/dto/CodeChatRequest.javaserver/src/main/java/com/meet/server/feature/codebase/dto/CodeCitation.javaserver/src/main/java/com/meet/server/feature/codechunk/CodeChunkRepositoryImpl.javaserver/src/main/java/com/meet/server/feature/embedding/SimilaritySearchRequest.javaserver/src/main/java/com/meet/server/feature/retriver/CodeRetriever.javaserver/src/main/resources/application.yaml
📜 Review details
🧰 Additional context used
🪛 markdownlint-cli2 (0.23.1)
server/specs/codebase.md
[warning] 1-1: First line in a file should be a top-level heading
(MD041, first-line-heading, first-line-h1)
[warning] 177-177: No empty links
(MD042, no-empty-links)
[warning] 177-177: Files should end with a single newline character
(MD047, single-trailing-newline)
🔇 Additional comments (10)
server/src/main/java/com/meet/server/feature/codebase/CodebaseController.java (1)
6-6: LGTM!Also applies to: 16-19, 48-55
server/src/main/resources/application.yaml (1)
7-10: LGTM!Also applies to: 41-41
server/src/main/java/com/meet/server/feature/codebase/CodebaseService.java (1)
68-70: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy liftDenial of Service (CWE-400): Uncontrolled Resource Consumption
Reachability: External
Reachability path
● Entry server/src/main/java/com/meet/server/feature/codebase/CodebaseController.java:54 streamChat │ ▼ ● Sink server/src/main/java/com/meet/server/feature/codebase/CodebaseService.javaBound the number and lifetime of chat conversations.
chatIdis caller-controlled, and each distinct value creates a newconversationId.maxMessages(20)limits messages per conversation, not the number of conversations.MessageWindowChatMemoryuses in-memory conversation storage by default, so the shown configuration has no expiry or total-conversation limit. An authenticated caller can create unbounded entries and exhaust heap memory. (docs.spring.io)Use a repository or cache with expiry and a per-user conversation limit. Delete conversation state when a chat is closed.
#!/bin/bash set -euo pipefail # Confirm the configured Spring AI version and inspect memory configuration. fd -HI -t f '^(pom\.xml|build\.gradle|build\.gradle\.kts|gradle\.properties)$' \ -x rg -n -C 3 'spring-ai|jackson|MessageWindowChatMemory|ChatMemoryRepository' {} # Find all conversation-key creation, storage, expiry, and cleanup paths. rg -n -C 5 'MessageWindowChatMemory|ChatMemoryRepository|CONVERSATION_ID|conversationId|chatId|\.clear\s*\(' server/srcAlso applies to: 164-165
server/src/main/java/com/meet/server/feature/codebase/dto/CodeCitation.java (1)
3-5: LGTM!server/src/main/java/com/meet/server/feature/embedding/SimilaritySearchRequest.java (1)
14-34: LGTM!server/src/main/java/com/meet/server/feature/codechunk/CodeChunkRepositoryImpl.java (1)
229-241: LGTM!server/src/main/java/com/meet/server/feature/advisor/CodeAdvisor.java (2)
33-34: 🩺 Stability & AvailabilityVerify that retrieval does not block a reactive request thread.
adviseStreamevaluatesenrich(request)before it returns theFlux.enrichcallsCodeRetriever.retrieve, which performs synchronousEmbeddingModel.embedandJdbcClient.query(...).list()work. If this endpoint runs on a WebFlux event loop, each chat request blocks that loop. Verify the execution scheduler and timeouts. If the path is reactive, defer and offload retrieval or use nonblocking clients.Also applies to: 42-42
25-29: LGTM!Also applies to: 48-57
server/src/main/java/com/meet/server/feature/codebase/dto/CodeChatRequest.java (1)
5-5: 🔒 Security & PrivacyIDOR (CWE-639): Authorization Bypass Through User-Controlled Key (IDOR)
Verify chat-memory key isolation.
chatIdacceptsnull, blank values, and caller-selected IDs. The suppliedCodebaseServiceconstructor createsMessageWindowChatMemoryand installsMessageChatMemoryAdvisor. Verify thatstreamChatderivesChatMemory.CONVERSATION_IDfrom the authenticated user andcodebaseId, or generates a server-side ID whenchatIdis absent. If it forwards the raw or default value, an authenticated caller can place another conversation's history into model context. This violates conversation confidentiality.server/src/main/java/com/meet/server/feature/retriver/CodeRetriever.java (1)
23-29: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winSensitive Data Exposure (CWE-532): Insertion of Sensitive Information into Log File
Reachability: External
Reachability path
● Entry server/src/main/java/com/meet/server/feature/codebase/CodebaseController.java:24 CodebaseController │ ▼ ● Hop server/src/main/java/com/meet/server/feature/advisor/CodeAdvisor.java:37 enrich │ ▼ ● Sink server/src/main/java/com/meet/server/feature/retriver/CodeRetriever.javaStop logging the raw chat prompt.
An authenticated caller can submit secrets or PII in the chat prompt.
CodeAdvisor.enrichforwards that text toretrieve, and Line 24 logs it unchanged. Authentication and@NotBlankdo not prevent log retention. If DEBUG output is stored, this violates chat-content confidentiality.Proposed fix
- log.debug("Retrieving code for {} using query {}", codebaseId, query); + log.debug("Retrieving code for {}", codebaseId);
| var citations = results.stream().map(result -> { | ||
| var chunk = result.chunk(); | ||
| return new CodeCitation(chunk.getId(), chunk.getPath(), chunk.getStartLine(), chunk.getEndLine(), chunk.getLanguage(), result.distance()); | ||
| }).toList(); | ||
|
|
||
| StringBuilder context = new StringBuilder("\n\nRelevant code snippets:\n"); | ||
| int remaining = 12000; | ||
| for (var result : results) { | ||
| String content = result.chunk().getContent(); | ||
| if (content == null || content.isBlank() || remaining <= 0) continue; | ||
| String snippet = content.substring(0, Math.min(content.length(), remaining)); | ||
| context.append("\n--- ").append(result.chunk().getPath()).append(":") | ||
| .append(result.chunk().getStartLine()).append("-").append(result.chunk().getEndLine()) | ||
| .append(" ---\n").append(snippet).append('\n'); | ||
| remaining -= snippet.length(); | ||
| } | ||
| log.debug("Total relevant code snippets: {}", citations.size()); | ||
| return new RetrievalContext(context.toString(), citations); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Build citations from snippets that enter the prompt.
When a result has null or blank content, the loop skips its text. The earlier stream still adds its citation. RetrievalContext can then cite code that the model did not receive. Add the citation after the snippet is appended.
Proposed fix
+import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
- var citations = results.stream().map(result -> {
- var chunk = result.chunk();
- return new CodeCitation(chunk.getId(), chunk.getPath(), chunk.getStartLine(), chunk.getEndLine(), chunk.getLanguage(), result.distance());
- }).toList();
+ var citations = new ArrayList<CodeCitation>();
StringBuilder context = new StringBuilder("\n\nRelevant code snippets:\n");
int remaining = 12000;
for (var result : results) {
- String content = result.chunk().getContent();
+ var chunk = result.chunk();
+ String content = chunk.getContent();
if (content == null || content.isBlank() || remaining <= 0) continue;
String snippet = content.substring(0, Math.min(content.length(), remaining));
- context.append("\n--- ").append(result.chunk().getPath()).append(":")
- .append(result.chunk().getStartLine()).append("-").append(result.chunk().getEndLine())
+ context.append("\n--- ").append(chunk.getPath()).append(":")
+ .append(chunk.getStartLine()).append("-").append(chunk.getEndLine())
.append(" ---\n").append(snippet).append('\n');
remaining -= snippet.length();
+ citations.add(new CodeCitation(chunk.getId(), chunk.getPath(), chunk.getStartLine(),
+ chunk.getEndLine(), chunk.getLanguage(), result.distance()));
}
- return new RetrievalContext(context.toString(), citations);
+ return new RetrievalContext(context.toString(), List.copyOf(citations));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| var citations = results.stream().map(result -> { | |
| var chunk = result.chunk(); | |
| return new CodeCitation(chunk.getId(), chunk.getPath(), chunk.getStartLine(), chunk.getEndLine(), chunk.getLanguage(), result.distance()); | |
| }).toList(); | |
| StringBuilder context = new StringBuilder("\n\nRelevant code snippets:\n"); | |
| int remaining = 12000; | |
| for (var result : results) { | |
| String content = result.chunk().getContent(); | |
| if (content == null || content.isBlank() || remaining <= 0) continue; | |
| String snippet = content.substring(0, Math.min(content.length(), remaining)); | |
| context.append("\n--- ").append(result.chunk().getPath()).append(":") | |
| .append(result.chunk().getStartLine()).append("-").append(result.chunk().getEndLine()) | |
| .append(" ---\n").append(snippet).append('\n'); | |
| remaining -= snippet.length(); | |
| } | |
| log.debug("Total relevant code snippets: {}", citations.size()); | |
| return new RetrievalContext(context.toString(), citations); | |
| var citations = new ArrayList<CodeCitation>(); | |
| StringBuilder context = new StringBuilder("\n\nRelevant code snippets:\n"); | |
| int remaining = 12000; | |
| for (var result : results) { | |
| var chunk = result.chunk(); | |
| String content = chunk.getContent(); | |
| if (content == null || content.isBlank() || remaining <= 0) continue; | |
| String snippet = content.substring(0, Math.min(content.length(), remaining)); | |
| context.append("\n--- ").append(chunk.getPath()).append(":") | |
| .append(chunk.getStartLine()).append("-").append(chunk.getEndLine()) | |
| .append(" ---\n").append(snippet).append('\n'); | |
| remaining -= snippet.length(); | |
| citations.add(new CodeCitation(chunk.getId(), chunk.getPath(), chunk.getStartLine(), | |
| chunk.getEndLine(), chunk.getLanguage(), result.distance())); | |
| } | |
| log.debug("Total relevant code snippets: {}", citations.size()); | |
| return new RetrievalContext(context.toString(), List.copyOf(citations)); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@server/src/main/java/com/meet/server/feature/retriver/CodeRetriever.java`
around lines 31 - 48, Update the retrieval method around the citations stream
and context-building loop so citations are created only for results whose
nonblank content is actually appended to the prompt. Remove the eager citation
collection, build each CodeCitation after appending its snippet, and retain the
existing truncation and remaining-budget behavior.
This pull request introduces the initial implementation of the codebase import and code-aware chat streaming features, including API documentation, controller endpoints, service logic, and advisor integration for code retrieval. The most important changes are grouped below:
API Documentation and Contracts
codebasefeature inserver/specs/codebase.md, detailing endpoints for codebase import and streaming chat, authentication, expected request/response schemas, error handling, and source references.Controller and Service Layer
POST /api/codebases/{codebaseId}/chat/streamendpoint inCodebaseController, enabling streaming chat over codebases using Server-Sent Events (SSE). [1] [2] [3]CodebaseServiceto support streaming chat, including dependency injection forChatClient, advisor setup, and SSE response handling. [1] [2]Retrieval-Augmented Generation (RAG) Integration
CodeAdvisor, aCallAdvisorandStreamAdvisorcomponent that enriches chat requests with code context and citations by invoking theCodeRetrieverduring chat interactions.CodeAdvisorand chat memory advisor in theCodebaseService'sChatClientpipeline for retrieval-augmented chat.Security and Validation
Code Simplification
validateCloneUrl, simplifying clone URL checks to focus on basic URI structure and public accessibility.