Skip to content

Commit 97c00fc

Browse files
committed
fix: avoid unsafe multiline commit suggestions
1 parent 09e82f8 commit 97c00fc

4 files changed

Lines changed: 44 additions & 8 deletions

File tree

__tests__/unit/analysis/synthesis.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,31 @@ describe('SynthesisEngine', () => {
4747
expect(review.inlineComments[0].body).toContain('<summary>🤖 Prompt for AI Agents</summary>');
4848
});
4949

50+
it('does not render committable suggestions for multi-line suggestions without a range', () => {
51+
const finding: Finding = {
52+
file: 'src/users.js',
53+
line: 10,
54+
severity: 'major',
55+
title: 'Email lookup bypass',
56+
message: 'The query no longer filters by email.',
57+
suggestion: [
58+
'const normalized = normalizeEmail(email);',
59+
"const rows = await db.query('SELECT * FROM users WHERE email = ? LIMIT 1', [normalized]);",
60+
].join('\n'),
61+
};
62+
63+
const review = new SynthesisEngine({
64+
...DEFAULT_CONFIG,
65+
inlineMinSeverity: 'minor',
66+
inlineMaxComments: 5,
67+
}).synthesize([finding], pr);
68+
69+
expect(review.inlineComments[0].body).toContain('<summary>Suggested fix</summary>');
70+
expect(review.inlineComments[0].body).toContain('<summary>🤖 Prompt for AI Agents</summary>');
71+
expect(review.inlineComments[0].body).not.toContain('<summary>📝 Committable suggestion</summary>');
72+
expect(review.inlineComments[0].body).not.toContain('```suggestion');
73+
});
74+
5075
it('sorts inline comments by severity before applying the inline limit', () => {
5176
const findings: Finding[] = [
5277
{

dist/index.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16457,9 +16457,11 @@ var SynthesisEngine = class {
1645716457
];
1645816458
if (finding.suggestion) {
1645916459
parts.push("", this.suggestedFixDetails(finding.suggestion));
16460-
parts.push("", "<!-- suggestion_start -->");
16461-
parts.push("", this.committableSuggestionDetails(finding.suggestion));
16462-
parts.push("", "<!-- suggestion_end -->");
16460+
if (isSingleLineSuggestion(finding.suggestion)) {
16461+
parts.push("", "<!-- suggestion_start -->");
16462+
parts.push("", this.committableSuggestionDetails(finding.suggestion));
16463+
parts.push("", "<!-- suggestion_end -->");
16464+
}
1646316465
}
1646416466
parts.push("", this.agentPromptDetails(finding));
1646516467
if (finding.providers && finding.providers.length > 1) {
@@ -16532,6 +16534,9 @@ ${fence}`;
1653216534
function suggestionToDiff(suggestion) {
1653316535
return suggestion.trimEnd().split("\n").map((line) => `+${line}`).join("\n");
1653416536
}
16537+
function isSingleLineSuggestion(suggestion) {
16538+
return suggestion.trimEnd().split("\n").length === 1;
16539+
}
1653516540

1653616541
// src/analysis/test-coverage.ts
1653716542
var fs8 = __toESM(require("fs"));

dist/index.js.map

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/analysis/synthesis.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,11 @@ export class SynthesisEngine {
157157
];
158158
if (finding.suggestion) {
159159
parts.push('', this.suggestedFixDetails(finding.suggestion));
160-
parts.push('', '<!-- suggestion_start -->');
161-
parts.push('', this.committableSuggestionDetails(finding.suggestion));
162-
parts.push('', '<!-- suggestion_end -->');
160+
if (isSingleLineSuggestion(finding.suggestion)) {
161+
parts.push('', '<!-- suggestion_start -->');
162+
parts.push('', this.committableSuggestionDetails(finding.suggestion));
163+
parts.push('', '<!-- suggestion_end -->');
164+
}
163165
}
164166
parts.push('', this.agentPromptDetails(finding));
165167
if (finding.providers && finding.providers.length > 1) {
@@ -245,3 +247,7 @@ function suggestionToDiff(suggestion: string): string {
245247
.map((line) => `+${line}`)
246248
.join('\n');
247249
}
250+
251+
function isSingleLineSuggestion(suggestion: string): boolean {
252+
return suggestion.trimEnd().split('\n').length === 1;
253+
}

0 commit comments

Comments
 (0)