Skip to content

Commit afd6721

Browse files
committed
Fix vuln logging in code blocks
1 parent 9b22c85 commit afd6721

File tree

4 files changed

+54
-15
lines changed

4 files changed

+54
-15
lines changed

src/vs/workbench/contrib/chat/browser/codeBlockPart.ts

+1
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ export class CodeBlockPart extends Disposable {
320320
}
321321

322322
async render(data: ICodeBlockData, width: number, editable: boolean | undefined) {
323+
this.currentCodeBlockData = data;
323324
if (data.parentContextKeyService) {
324325
this.contextKeyService.updateParent(data.parentContextKeyService);
325326
}

src/vs/workbench/contrib/chat/common/annotations.ts

+29-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { basename } from 'vs/base/common/resources';
77
import { URI } from 'vs/base/common/uri';
88
import { IRange } from 'vs/editor/common/core/range';
99
import { IChatProgressRenderableResponseContent, IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel';
10-
import { IChatAgentMarkdownContentWithVulnerability, IChatAgentVulnerabilityDetails, IChatContentInlineReference } from 'vs/workbench/contrib/chat/common/chatService';
10+
import { IChatAgentMarkdownContentWithVulnerability, IChatAgentVulnerabilityDetails, IChatContentInlineReference, IChatMarkdownContent } from 'vs/workbench/contrib/chat/common/chatService';
1111

1212
export const contentRefUrl = 'http://_vscodecontentref_'; // must be lowercase for URI
1313

@@ -43,16 +43,40 @@ export function annotateSpecialMarkdownContent(response: ReadonlyArray<IChatProg
4343
}
4444

4545
export interface IMarkdownVulnerability {
46-
title: string;
47-
description: string;
48-
range: IRange;
46+
readonly title: string;
47+
readonly description: string;
48+
readonly range: IRange;
49+
}
50+
51+
export function annotateVulnerabilitiesInText(response: ReadonlyArray<IChatProgressResponseContent>): readonly IChatMarkdownContent[] {
52+
const result: IChatMarkdownContent[] = [];
53+
for (const item of response) {
54+
const previousItem = result[result.length - 1];
55+
if (item.kind === 'markdownContent') {
56+
if (previousItem?.kind === 'markdownContent') {
57+
result[result.length - 1] = { content: new MarkdownString(previousItem.content.value + item.content.value, { isTrusted: previousItem.content.isTrusted }), kind: 'markdownContent' };
58+
} else {
59+
result.push(item);
60+
}
61+
} else if (item.kind === 'markdownVuln') {
62+
const vulnText = encodeURIComponent(JSON.stringify(item.vulnerabilities));
63+
const markdownText = `<vscode_annotation details='${vulnText}'>${item.content.value}</vscode_annotation>`;
64+
if (previousItem?.kind === 'markdownContent') {
65+
result[result.length - 1] = { content: new MarkdownString(previousItem.content.value + markdownText, { isTrusted: previousItem.content.isTrusted }), kind: 'markdownContent' };
66+
} else {
67+
result.push({ content: new MarkdownString(markdownText), kind: 'markdownContent' });
68+
}
69+
}
70+
}
71+
72+
return result;
4973
}
5074

5175
export function extractVulnerabilitiesFromText(text: string): { newText: string; vulnerabilities: IMarkdownVulnerability[] } {
5276
const vulnerabilities: IMarkdownVulnerability[] = [];
5377
let newText = text;
5478
let match: RegExpExecArray | null;
55-
while ((match = /<vscode_annotation details="(.*?)">(.*?)<\/vscode_annotation>/ms.exec(newText)) !== null) {
79+
while ((match = /<vscode_annotation details=["'](.*?)["']>(.*?)<\/vscode_annotation>/ms.exec(newText)) !== null) {
5680
const [full, details, content] = match;
5781
const start = match.index;
5882
const textBefore = newText.substring(0, start);

src/vs/workbench/contrib/chat/common/chatViewModel.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,20 @@
55

66
import { Emitter, Event } from 'vs/base/common/event';
77
import { Disposable } from 'vs/base/common/lifecycle';
8+
import { ResourceMap } from 'vs/base/common/map';
89
import { marked } from 'vs/base/common/marked/marked';
910
import { ThemeIcon } from 'vs/base/common/themables';
1011
import { URI } from 'vs/base/common/uri';
12+
import { TextEdit } from 'vs/editor/common/languages';
1113
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
1214
import { ILogService } from 'vs/platform/log/common/log';
15+
import { annotateVulnerabilitiesInText } from 'vs/workbench/contrib/chat/common/annotations';
1316
import { IChatAgentCommand, IChatAgentData, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents';
1417
import { ChatModelInitState, IChatModel, IChatRequestModel, IChatResponseModel, IChatWelcomeMessageContent, IResponse } from 'vs/workbench/contrib/chat/common/chatModel';
1518
import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes';
1619
import { IChatCommandButton, IChatContentReference, IChatFollowup, IChatProgressMessage, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IChatUsedContext, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
1720
import { countWords } from 'vs/workbench/contrib/chat/common/chatWordCounter';
1821
import { CodeBlockModelCollection } from './codeBlockModelCollection';
19-
import { TextEdit } from 'vs/editor/common/languages';
20-
import { ResourceMap } from 'vs/base/common/map';
2122

2223
export function isRequestVM(item: unknown): item is IChatRequestViewModel {
2324
return !!item && typeof item === 'object' && 'message' in item;
@@ -259,10 +260,15 @@ export class ChatViewModel extends Disposable implements IChatViewModel {
259260
}
260261

261262
updateCodeBlockTextModels(model: IChatRequestViewModel | IChatResponseViewModel) {
262-
const content = isRequestVM(model) ? model.messageText : model.response.asString();
263-
const renderer = new marked.Renderer();
263+
let content: string;
264+
if (isRequestVM(model)) {
265+
content = model.messageText;
266+
} else {
267+
content = annotateVulnerabilitiesInText(model.response.value).map(x => x.content.value).join('');
268+
}
264269

265270
let codeBlockIndex = 0;
271+
const renderer = new marked.Renderer();
266272
renderer.code = (value, languageId) => {
267273
languageId ??= '';
268274
const newText = this.fixCodeText(value, languageId);

src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,16 @@ export class CodeBlockModelCollection extends Disposable {
3434
this.clear();
3535
}
3636

37-
get(sessionId: string, chat: IChatRequestViewModel | IChatResponseViewModel, codeBlockIndex: number): { model: Promise<IResolvedTextEditorModel>; vulns: readonly IMarkdownVulnerability[] } | undefined {
37+
get(sessionId: string, chat: IChatRequestViewModel | IChatResponseViewModel, codeBlockIndex: number): { model: Promise<IResolvedTextEditorModel>; readonly vulns: readonly IMarkdownVulnerability[] } | undefined {
3838
const uri = this.getUri(sessionId, chat, codeBlockIndex);
39-
const entries = this._models.get(uri);
40-
if (!entries) {
39+
const entry = this._models.get(uri);
40+
if (!entry) {
4141
return;
4242
}
43-
return { model: entries.model.then(ref => ref.object), vulns: entries.vulns };
43+
return { model: entry.model.then(ref => ref.object), vulns: entry.vulns };
4444
}
4545

46-
getOrCreate(sessionId: string, chat: IChatRequestViewModel | IChatResponseViewModel, codeBlockIndex: number): { model: Promise<IResolvedTextEditorModel>; vulns: readonly IMarkdownVulnerability[] } {
46+
getOrCreate(sessionId: string, chat: IChatRequestViewModel | IChatResponseViewModel, codeBlockIndex: number): { model: Promise<IResolvedTextEditorModel>; readonly vulns: readonly IMarkdownVulnerability[] } {
4747
const existing = this.get(sessionId, chat, codeBlockIndex);
4848
if (existing) {
4949
return existing;
@@ -65,7 +65,7 @@ export class CodeBlockModelCollection extends Disposable {
6565

6666
const extractedVulns = extractVulnerabilitiesFromText(content.text);
6767
const newText = extractedVulns.newText;
68-
entry.vulns = extractedVulns.vulnerabilities;
68+
this.setVulns(sessionId, chat, codeBlockIndex, extractedVulns.vulnerabilities);
6969

7070
const textModel = (await entry.model).textEditorModel;
7171
if (content.languageId) {
@@ -91,6 +91,14 @@ export class CodeBlockModelCollection extends Disposable {
9191
}
9292
}
9393

94+
private setVulns(sessionId: string, chat: IChatRequestViewModel | IChatResponseViewModel, codeBlockIndex: number, vulnerabilities: IMarkdownVulnerability[]) {
95+
const uri = this.getUri(sessionId, chat, codeBlockIndex);
96+
const entry = this._models.get(uri);
97+
if (entry) {
98+
entry.vulns = vulnerabilities;
99+
}
100+
}
101+
94102
private getUri(sessionId: string, chat: IChatRequestViewModel | IChatResponseViewModel, index: number): URI {
95103
const metadata = this.getUriMetaData(chat);
96104
return URI.from({

0 commit comments

Comments
 (0)