RSS Feed

RSS Feed reader can ingest new items as report indicators.

Data Enrichment & Threat Intelligence · RSS Feed · Feed

Details

IDRSS Feed
ProviderOpen Source
CategoryData Enrichment & Threat Intelligence
From Version6.2.0
Docker Imagedemisto/py3-tools:1.0.0.2072021
Supported ModulesAgentix XSIAM

README

RSS Feed can ingest new items as report indicators.
This integration was integrated and tested with version 2.0 of RSS.

Configure RSS Feed in Cortex

Parameter Description Required
Feed URL The RSS URL should be a URL with ‘feed’ as the suffix or prefix. True
Article content max size in KB Default is 45KB. If you increase the limit substantialy, it may slow performance. You need to specify only a number, e.g., 50. False
Fetch indicators   False
Indicator Reputation Indicators from this integration instance will be marked with this reputation. False
Source Reliability Reliability of the source providing the intelligence data. True
Traffic Light Protocol Color The Traffic Light Protocol (TLP) designation to apply to indicators fetched from the feed. False
Feed Expiration Method   False
feedExpirationInterval   False
Feed Fetch Interval   False
Tags Supports CSV values. False
Bypass exclusion list When selected, the exclusion list is ignored for indicators from this feed. This means that if an indicator from this feed is on the exclusion list, the indicator might still be added to the system. False
Trust any certificate (not secure)   False
Use system proxy settings   False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

rss-get-indicators


Gets the reports from the RSS feed.

Base Command

rss-get-indicators

Input

Argument Name Description Required
limit The maximum number of indicators to return. Default is 10. Optional

Context Output

There is no context output for this command.

Command Example

!rss-get-indicators

Human Readable Output

RSS Feed:

Title Link Type
Title of an article https://article-example.com Report

Configuration parameters

  • server_url — Feed URL (required)
  • max_size — Article content max size in KB
  • read_timeout — Read Timeout
  • default_headers — Request headers in JSON format.
  • feed — Fetch indicators
  • feedReputation — Indicator Reputation
  • feedReliability — Source Reliability (required)
  • tlp_color — Traffic Light Protocol Color
  • feedExpirationPolicy
  • feedExpirationInterval
  • feedFetchInterval — Feed Fetch Interval
  • feedTags — Tags
  • feedBypassExclusionList — Bypass exclusion list
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • enrichmentExcluded — Enrichment Excluded

Commands (1)

  • rss-get-indicators

    Gets the reports from the RSS feed.

import pytest
from CommonServerPython import DemistoException
from FeedRSS import *
from requests.models import Response
from test_data.test_variables import FEED_DATA, FEED_DATA_NO_PUBLISH_FIELD, HTML_CONTENT, TEST_DATA_MAX_SIZE


def side_effect_feed_url(mocker, client):
    feed_content_res = Response()
    type(feed_content_res).status_code = mocker.PropertyMock(return_value=200)
    type(feed_content_res).content = mocker.PropertyMock(return_value="text_to_parse")

    client.feed_response = feed_content_res


def mock_client(mocker, dict_to_parse: dict, content_max_size: int = 45, enrichment_excluded: bool = False) -> Client:
    """Create a mock client"""

    client = Client(
        server_url="test.com",
        use_ssl=False,
        proxy=False,
        reliability="F - Reliability cannot be judged",
        feed_tags=[],
        tlp_color=None,
        content_max_size=content_max_size,
        enrichment_excluded=enrichment_excluded,
    )

    mocker.patch.object(feedparser, "parse", return_value=feedparser.util.FeedParserDict(dict_to_parse))
    mocker.patch.object(Client, "request_feed_url", side_effect=side_effect_feed_url(mocker=mocker, client=client))

    return client


@pytest.mark.parametrize("parse_response,expected_output", FEED_DATA)
def test_parsed_indicators_from_response(mocker, parse_response, expected_output):
    """
    Given:
    - RSS feed url

    When:
    - After parsing the feed content, we hold a list of items and create a Report indicator from each one of them

    Then:
    - Ensure all indicator fields extracted properly
    """

    client = mock_client(mocker, parse_response)

    mocker.patch.object(Client, "get_url_content", return_value="test description")
    indicators = fetch_indicators(client)
    assert indicators == expected_output


def test_get_url_content(mocker):
    """
    Given:
    - Content of article in html format

    When:
    - when creating Report indicators from each item on the rss feed, we want to extract the article content.

    Then:
    - Ensure only the relevant html tags are extracted
    """

    client = mock_client(mocker=mocker, dict_to_parse={})
    article_content_res = Response()
    type(article_content_res).content = HTML_CONTENT
    mocker.patch.object(Client, "_http_request", return_value=article_content_res)
    assert (
        client.get_url_content("test-link.com")
        == "This is a dumped content of the article. Use the link under Publications field to read the full article. "
        "\n\n p in div p li inside ul li inside ul Coffee Tea Milk Month Savings January $100 This is h1"
    )


def test_get_url_content_invalid_link(mocker):
    """
    Given:
    - Invalid URL link to get the content from

    When:
    - when creating Report indicators from each item on the rss feed, we want to extract the article content.

    Then:
    - Ensure it returns an empty value
    """
    client = mock_client(mocker=mocker, dict_to_parse={})
    article_content_res = Response()
    article_content_res.status_code = 403
    mocker.patch.object(Client, "_http_request", side_effect=DemistoException(article_content_res))
    assert client.get_url_content("test-link.com") == ""


@pytest.mark.parametrize("article_content, expected_output", TEST_DATA_MAX_SIZE)
def test_content_max_size(mocker, article_content, expected_output):
    """Check if the content of an article exceed "content_max_size", if so we cut him as expected"""

    client = mock_client(mocker=mocker, dict_to_parse={}, content_max_size=1)
    article_content_res = Response()
    type(article_content_res).content = article_content
    mocker.patch.object(Client, "_http_request", return_value=article_content_res)
    assert client.get_url_content("test-link.com") == expected_output


@pytest.mark.parametrize("parse_response,expected_output", FEED_DATA_NO_PUBLISH_FIELD)
def test_parsed_indicators_from_response_no_publish_field(mocker, parse_response, expected_output):
    """
    Given:
    - RSS feed url with indicators without publish filed

    When:
    - After parsing the feed content, we hold a list of items and create a Report indicator from each one of them

    Then:
    - Ensure all indicator fields extracted properly
    """

    client = mock_client(mocker, parse_response)

    mocker.patch.object(Client, "get_url_content", return_value="test description")
    indicators = fetch_indicators(client)
    assert indicators == expected_output


def test_parsed_indicators_from_response_with_links_field(mocker):
    """
    Given:
    - RSS feed url

    When:
    - After parsing the feed content, we hold a list of items and create a Report indicator from each one of them

    Then:
    - Ensure all the links extracted properly to the publications field
    """

    from unittest.mock import MagicMock

    example_link = MagicMock()
    example_link.href = "https://example.com"

    FEED_DATA[0][0]["entries"][0]["links"] = [example_link]

    client = mock_client(mocker, FEED_DATA[0][0])

    mocker.patch.object(Client, "get_url_content", return_value="test description")
    indicators = fetch_indicators(client)

    expected_output = [
        {
            "timestamp": "2021-06-18T15:35:41",
            "link": "https://test-article.com/",
            "source": "test.com",
            "title": "Test Article, with comma",
        },
        {
            "timestamp": "2021-06-18T15:35:41",
            "link": "https://example.com",
            "source": "test.com",
            "title": "Test Article, with comma",
        },
    ]

    assert indicators[0]["fields"]["publications"] == expected_output


def test_feed_main_enrichment_excluded(mocker):
    """
    Given: params with tlp_color set to RED and enrichmentExcluded set to False
    When: Calling feed_main
    Then: validate enrichment_excluded is set to True
    """
    from FeedRSS import main

    params = {"tlp_color": "RED", "enrichmentExcluded": False, "server_url": "test.test.com"}

    client_mocker = mocker.patch("FeedRSS.Client")

    mocker.patch("FeedRSS.is_xsiam_or_xsoar_saas", return_value=True)
    mocker.patch("FeedRSS.fetch_indicators", return_value=[])
    mocker.patch.object(demisto, "params", return_value=params)
    mocker.patch.object(demisto, "command", return_value="fetch-indicators")

    # Call the function under test
    main()

    # Assertion - verify that enrichment_excluded is set to True
    assert client_mocker.call_args.kwargs.get("enrichment_excluded") is True