Skip to content

Commit 951321f

Browse files
authored
fix: re-purpose the security-vulnerabilities-auto-suggest handler to call mystique (#1673)
This PR changes the behavior of the `security-vulnerabilities-auto-suggest` handler. New behavior: * recommended version is always added to the opportunity * the handler determines if we should generate code suggestions using mystique * removed `security-vulnerabilities-auto-fix`
1 parent eef8831 commit 951321f

File tree

4 files changed

+16
-81
lines changed

4 files changed

+16
-81
lines changed

src/vulnerabilities/handler.js

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -272,14 +272,6 @@ export const opportunityAndSuggestionsStep = async (context) => {
272272
createOpportunityProps(auditResult.vulnerabilityReport),
273273
);
274274

275-
const configuration = await Configuration.findLatest();
276-
const generateSuggestions = configuration.isHandlerEnabledForSite('security-vulnerabilities-auto-suggest', site);
277-
if (!generateSuggestions) {
278-
log.debug(
279-
`[${AUDIT_TYPE}] [Site: ${site.getId()}] security-vulnerabilities-auto-suggest not configured, skipping version recommendations`,
280-
);
281-
}
282-
283275
// As a buildKey we hash all the component details and add name and version for readability
284276
const buildKey = (item) => {
285277
const s = JSON.stringify(item);
@@ -294,12 +286,14 @@ export const opportunityAndSuggestionsStep = async (context) => {
294286
context,
295287
buildKey,
296288
mapNewSuggestion:
297-
(entry) => mapVulnerabilityToSuggestion(opportunity, entry, generateSuggestions),
289+
(entry) => mapVulnerabilityToSuggestion(opportunity, entry),
298290
log,
299291
});
300292

301-
const generateCodeFix = configuration.isHandlerEnabledForSite('security-vulnerabilities-auto-fix', site);
302-
if (generateSuggestions && generateCodeFix && dataContainsCode(data)) {
293+
const configuration = await Configuration.findLatest();
294+
const generateSuggestions = configuration.isHandlerEnabledForSite('security-vulnerabilities-auto-suggest', site);
295+
296+
if (generateSuggestions && dataContainsCode(data)) {
303297
// TODO => only add new suggestions to the payload
304298
// TODO => optimize payload to reduce size use s3 instead of data as transport medium
305299

@@ -319,9 +313,8 @@ export const opportunityAndSuggestionsStep = async (context) => {
319313
await sqs.sendMessage(env.QUEUE_SPACECAT_TO_MYSTIQUE, message);
320314
} else {
321315
log.debug(
322-
`[${AUDIT_TYPE}] [Site: ${site.getId()}] skipping code generation with mystique, because one of:
323-
security-vulnerabilities-auto-fix not configured, security-vulnerabilities-auto-suggest' not configured
324-
or import worker could not get code.`,
316+
`[${AUDIT_TYPE}] [Site: ${site.getId()}] skipping code generation with mystique, because
317+
'security-vulnerabilities-auto-suggest' not configured or import worker could not get code.`,
325318
);
326319
}
327320
return { status: 'complete' };

src/vulnerabilities/suggestion-data-mapper.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ function highestScore(vulnerabilities) {
4040
* @param {boolean} generateSuggestions - Whether to generate version recommendations
4141
* @return {Object} A suggestion object providing a structured representation of the vulnerability
4242
*/
43-
export function mapVulnerabilityToSuggestion(opportunity, vulnerability, generateSuggestions) {
43+
export function mapVulnerabilityToSuggestion(opportunity, vulnerability) {
4444
const {
4545
name, version, recommendedVersion, vulnerabilities,
4646
} = vulnerability;
@@ -55,7 +55,7 @@ export function mapVulnerabilityToSuggestion(opportunity, vulnerability, generat
5555
data: {
5656
library: name,
5757
current_version: version,
58-
recommended_version: generateSuggestions ? recommendedVersion : '',
58+
recommended_version: recommendedVersion,
5959
cves: safeVulnerabilities.sort((a, b) => b.score - a.score).map((vuln) => ({
6060
cve_id: vuln.id,
6161
score: vuln.score,

test/audits/vulnerabilities.test.js

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -522,53 +522,14 @@ describe('Vulnerabilities Handler Integration Tests', () => {
522522
expect(springSuggestion.data.cves[1].score).to.equal(5.5);
523523
});
524524

525-
it('should handle disabled auto-suggest configuration', async () => {
525+
it('should handle auto suggest to trigger mystique', async () => {
526526
const configuration = {
527527
isHandlerEnabledForSite: sandbox.stub(),
528528
};
529529
context.dataAccess.Configuration.findLatest.resolves(configuration);
530530

531-
// Enable main handler but disable auto-suggest
532-
configuration.isHandlerEnabledForSite.withArgs('security-vulnerabilities').returns(true);
533-
configuration.isHandlerEnabledForSite.withArgs('security-vulnerabilities-auto-suggest').returns(false);
534-
535-
context.audit = {
536-
getAuditResult: () => ({
537-
vulnerabilityReport: VULNERABILITY_REPORT_WITH_VULNERABILITIES,
538-
success: true,
539-
}),
540-
getId: () => 'test-audit-id',
541-
};
542-
543-
const result = await opportunityAndSuggestionsStep(context);
544-
545-
expect(result).to.deep.equal({ status: 'complete' });
546-
expect(context.dataAccess.Opportunity.create).to.have.been.calledOnce;
547-
expect(context.log.debug).to.have.been.calledWithMatch(
548-
/security-vulnerabilities-auto-suggest not configured, skipping version recommendations/,
549-
);
550-
551-
// Verify opportunity was created and addSuggestions was called
552-
const createdOpportunity = await context.dataAccess.Opportunity.create.getCall(0).returnValue;
553-
expect(createdOpportunity.addSuggestions).to.have.been.calledOnce;
554-
555-
// Verify suggestions were created with empty recommended_version (generateSuggestions=false)
556-
const suggestionsCall = createdOpportunity.addSuggestions.getCall(0);
557-
const suggestions = suggestionsCall.args[0];
558-
expect(suggestions).to.be.an('array');
559-
expect(suggestions[0].data.recommended_version).to.equal('');
560-
});
561-
562-
it('should handle code fixt to trigger mystique', async () => {
563-
const configuration = {
564-
isHandlerEnabledForSite: sandbox.stub(),
565-
};
566-
context.dataAccess.Configuration.findLatest.resolves(configuration);
567-
568-
// Enable main handler but disable auto-suggest
569531
configuration.isHandlerEnabledForSite.withArgs('security-vulnerabilities').returns(true);
570532
configuration.isHandlerEnabledForSite.withArgs('security-vulnerabilities-auto-suggest').returns(true);
571-
configuration.isHandlerEnabledForSite.withArgs('security-vulnerabilities-auto-fix').returns(true);
572533

573534
context.audit = {
574535
getAuditResult: () => ({

test/audits/vulnerabilities/suggestion-data-mapper.test.js

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ describe('Vulnerabilities Suggestion Data Mapper', () => {
6767
describe('mapVulnerabilityToSuggestion', () => {
6868
it('should map vulnerability to suggestion with single vulnerability', () => {
6969
const vulnerability = createVulnerability();
70-
const result = mapVulnerabilityToSuggestion(mockOpportunity, vulnerability, true);
70+
const result = mapVulnerabilityToSuggestion(mockOpportunity, vulnerability);
7171

7272
expect(result).to.deep.equal({
7373
opportunityId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
@@ -99,7 +99,7 @@ describe('Vulnerabilities Suggestion Data Mapper', () => {
9999
]),
100100
});
101101

102-
const result = mapVulnerabilityToSuggestion(mockOpportunity, vulnerability, true);
102+
const result = mapVulnerabilityToSuggestion(mockOpportunity, vulnerability);
103103

104104
expect(result.rank).to.equal(9.5);
105105
expect(result.data.cves).to.have.lengthOf(3);
@@ -120,7 +120,7 @@ describe('Vulnerabilities Suggestion Data Mapper', () => {
120120

121121
testCases.forEach(({ vulnerabilities }) => {
122122
const vulnerability = createVulnerability({ vulnerabilities });
123-
const result = mapVulnerabilityToSuggestion(mockOpportunity, vulnerability, true);
123+
const result = mapVulnerabilityToSuggestion(mockOpportunity, vulnerability);
124124

125125
expect(result.rank).to.equal(0);
126126
expect(result.data.cves).to.deep.equal([]);
@@ -140,7 +140,7 @@ describe('Vulnerabilities Suggestion Data Mapper', () => {
140140
]),
141141
});
142142

143-
const result = mapVulnerabilityToSuggestion(mockOpportunity, vulnerability, true);
143+
const result = mapVulnerabilityToSuggestion(mockOpportunity, vulnerability);
144144

145145
expect(result.data.library).to.equal('test-library');
146146
expect(result.data.current_version).to.equal('1.0.0');
@@ -162,7 +162,7 @@ describe('Vulnerabilities Suggestion Data Mapper', () => {
162162
]),
163163
});
164164

165-
const result = mapVulnerabilityToSuggestion(mockOpportunity, vulnerability, true);
165+
const result = mapVulnerabilityToSuggestion(mockOpportunity, vulnerability);
166166

167167
expect(result.rank).to.equal(7.5);
168168
expect(result.data.cves).to.have.lengthOf(3);
@@ -199,31 +199,12 @@ describe('Vulnerabilities Suggestion Data Mapper', () => {
199199
],
200200
});
201201

202-
const result = mapVulnerabilityToSuggestion(mockOpportunity, vulnerability, true);
202+
const result = mapVulnerabilityToSuggestion(mockOpportunity, vulnerability);
203203

204204
expect(result.data.cves).to.have.lengthOf(3);
205205
expect(result.data.cves[0].url).to.equal('https://example.com/cve');
206206
expect(result.data.cves[1].url).to.equal(''); // null URL should become empty string
207207
expect(result.data.cves[2].url).to.equal(''); // undefined URL should become empty string
208208
});
209-
210-
it('should handle generateSuggestions=false by setting empty recommended_version', () => {
211-
const vulnerability = createVulnerability({
212-
name: 'test-library',
213-
version: '1.0.0',
214-
recommendedVersion: '2.0.0',
215-
vulnerabilities: createVulnerabilities([
216-
{ score: 7.5, severity: 'High' },
217-
]),
218-
});
219-
220-
const result = mapVulnerabilityToSuggestion(mockOpportunity, vulnerability, false);
221-
222-
expect(result.data.library).to.equal('test-library');
223-
expect(result.data.current_version).to.equal('1.0.0');
224-
expect(result.data.recommended_version).to.equal(''); // Should be empty when generateSuggestions=false
225-
expect(result.rank).to.equal(7.5);
226-
expect(result.data.cves).to.have.lengthOf(1);
227-
});
228209
});
229210
});

0 commit comments

Comments
 (0)