Skip to content

Commit 2b32fdf

Browse files
fix(threat-intel): resolve RSS adapter URLs from the code catalog
After #287201, feed URLs live in catalog_source_urls rather than source config. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 9ccc62e commit 2b32fdf

3 files changed

Lines changed: 12 additions & 26 deletions

File tree

x-pack/solutions/security/plugins/security_solution/server/threat_intel/adapters/rss/rss_adapter.test.ts

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
*/
77

88
import { loggingSystemMock } from '@kbn/core/server/mocks';
9+
import { CATALOG_SOURCE_URLS } from '../../../../common/threat_intel';
910
import { extractIocs } from '../../services/extract_iocs';
1011
import { rssAdapter } from './rss_adapter';
1112
import type { AdapterRunContext, SourceHit } from '../types';
1213

13-
const FEED_URL = 'https://acme.example/feed.xml';
14+
const SOURCE_ID = 'vendor_api:elastic-security-labs';
15+
const FEED_URL = CATALOG_SOURCE_URLS[SOURCE_ID];
1416
const FEED_BODY = `<?xml version="1.0" encoding="UTF-8"?>
1517
<rss version="2.0"><channel>
1618
<title>Acme</title>
@@ -31,11 +33,10 @@ const FEED_BODY = `<?xml version="1.0" encoding="UTF-8"?>
3133
</channel></rss>`;
3234

3335
const buildSource = (overrides: Partial<SourceHit['_source']> = {}): SourceHit => ({
34-
_id: 'rss:acme',
36+
_id: SOURCE_ID,
3537
_source: {
3638
adapter_type: 'rss',
37-
name: 'Acme',
38-
config: { url: FEED_URL },
39+
name: 'Elastic Security Labs',
3940
...overrides,
4041
},
4142
});
@@ -73,9 +74,9 @@ describe('rssAdapter', () => {
7374
space_id: '*',
7475
source: {
7576
type: 'rss',
76-
name: 'Acme',
77+
name: 'Elastic Security Labs',
7778
url: 'https://acme.example/1',
78-
adapter_id: 'rss:rss:acme',
79+
adapter_id: 'rss:vendor_api:elastic-security-labs',
7980
},
8081
content: {
8182
title: 'Item one',
@@ -225,10 +226,10 @@ describe('rssAdapter', () => {
225226
expect(reports[0].space_id).toBe('team-a');
226227
});
227228

228-
it('returns [] when the source has no config.url', async () => {
229+
it('returns [] when the source id has no catalog URL', async () => {
229230
const fetchMock = jest.fn();
230231
const reports = await rssAdapter.run(
231-
buildSource({ config: {} as Record<string, unknown> }),
232+
{ _id: 'rss:unknown', _source: { adapter_type: 'rss', name: 'Unknown' } },
232233
buildContext(fetchMock)
233234
);
234235
expect(reports).toEqual([]);
@@ -250,18 +251,4 @@ describe('rssAdapter', () => {
250251
const reports = await rssAdapter.run(buildSource(), buildContext(fetchMock));
251252
expect(reports).toEqual([]);
252253
});
253-
254-
it('ingests percent-encoded data: fixture URLs without calling fetch', async () => {
255-
const dataUrl = `data:application/rss+xml;charset=utf-8,${encodeURIComponent(FEED_BODY)}`;
256-
const fetchMock = jest.fn();
257-
const reports = await rssAdapter.run(
258-
buildSource({ config: { url: dataUrl } }),
259-
buildContext(fetchMock)
260-
);
261-
262-
expect(fetchMock).not.toHaveBeenCalled();
263-
expect(reports).toHaveLength(2);
264-
expect(reports[0].content.title).toBe('Item one');
265-
expect(reports[0].lineage.extraction_method).toBe('pending');
266-
});
267254
});

x-pack/solutions/security/plugins/security_solution/server/threat_intel/adapters/rss/rss_adapter.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* 2.0.
66
*/
77

8-
import { GLOBAL_SPACE_ID } from '../../../../common/threat_intel';
8+
import { GLOBAL_SPACE_ID, resolveCatalogSourceUrl } from '../../../../common/threat_intel';
99
import { fetchUrlForContext, redactUrl } from '../http_client';
1010
import { buildFingerprint } from '../fingerprint';
1111
import { DEFAULT_SEVERITY_LEVEL, DEFAULT_SEVERITY_SCORE } from '../../services/severity';
@@ -21,7 +21,7 @@ const BODY_TEXT_MAX_LENGTH = 32_000;
2121
const SOURCE_DOC_REF_INDEX = 'rss:feed';
2222

2323
const readFeedUrl = (source: SourceHit): string | undefined => {
24-
const url = source._source.config.url;
24+
const url = resolveCatalogSourceUrl(source._id);
2525
return typeof url === 'string' && url.length > 0 ? url : undefined;
2626
};
2727

@@ -58,7 +58,7 @@ export const rssAdapter: FetchAdapter = {
5858
const log = context.logger.get('rss-adapter');
5959
const feedUrl = readFeedUrl(source);
6060
if (!feedUrl) {
61-
log.warn(`Source ${source._id} has no config.url — skipping`);
61+
log.warn(`Source ${source._id} has no catalog URL — skipping`);
6262
return [];
6363
}
6464

x-pack/solutions/security/plugins/security_solution/server/threat_intel/adapters/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ export interface SourceHit {
2626
adapter_type: FetchAdapterType;
2727
name: string;
2828
enabled?: boolean;
29-
config: Record<string, unknown>;
3029
tags?: string[];
3130
space_id?: string;
3231
};

0 commit comments

Comments
 (0)