Skip to content

Commit f2ff6fb

Browse files
authored
Merge pull request GreedyBear-Project#800 from intelowlproject/develop
3.1.0
2 parents a935808 + acf8fb8 commit f2ff6fb

61 files changed

Lines changed: 3635 additions & 1156 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env_template

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ COMPOSE_FILE=docker/default.yml:docker/local.override.yml
1313
#COMPOSE_FILE=docker/default.yml:docker/local.override.yml:docker/elasticsearch.yml
1414

1515
# If you want to run a specific version, populate this
16-
# REACT_APP_INTELOWL_VERSION="3.0.1"
16+
# REACT_APP_INTELOWL_VERSION="3.1.0"

.github/actions/python_requirements/restore_virtualenv/action.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ inputs:
1212
description: A git reference (name of the branch, reference to the PR) that will be used to build the cache key.
1313
required: false
1414
default: ${{ github.ref_name }}
15+
python_version:
16+
description: Python version string to include in the cache key, preventing cross-version cache collisions.
17+
required: true
1518

1619
outputs:
1720
cache-hit:
@@ -32,7 +35,7 @@ runs:
3235
uses: actions/cache/restore@v4
3336
with:
3437
path: ${{ inputs.virtual_environment_path }}
35-
key: ${{ inputs.git_reference }}-venv-${{ steps.compute_requirements_files_sha256_hash.outputs.computed_hash }}
38+
key: ${{ inputs.git_reference }}-py${{ inputs.python_version }}-venv-${{ steps.compute_requirements_files_sha256_hash.outputs.computed_hash }}
3639

3740
- name: Activate restored virtual environment
3841
if: >

.github/actions/python_requirements/save_virtualenv/action.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ inputs:
1212
description: A git reference (name of the branch, reference to the PR) that will be used to build the cache key.
1313
required: false
1414
default: ${{ github.ref_name }}
15+
python_version:
16+
description: Python version string to include in the cache key, preventing cross-version cache collisions.
17+
required: true
1518

1619
runs:
1720
using: "composite"
@@ -26,4 +29,4 @@ runs:
2629
uses: actions/cache/save@v4
2730
with:
2831
path: ${{ inputs.virtual_environment_path }}
29-
key: ${{ inputs.git_reference }}-venv-${{ steps.compute_requirements_files_sha256_hash.outputs.computed_hash }}
32+
key: ${{ inputs.git_reference }}-py${{ inputs.python_version }}-venv-${{ steps.compute_requirements_files_sha256_hash.outputs.computed_hash }}

.github/workflows/_python.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ jobs:
261261
uses: actions/checkout@v4
262262

263263
- name: Set up Python
264+
id: setup_python
264265
uses: actions/setup-python@v5
265266
with:
266267
python-version: ${{ matrix.python_version }}
@@ -343,6 +344,7 @@ jobs:
343344
uses: ./.github/actions/python_requirements/restore_virtualenv/
344345
with:
345346
requirements_paths: "${{ inputs.requirements_path }} requirements-linters.txt requirements-dev.txt requirements-docs.txt"
347+
python_version: ${{ steps.setup_python.outputs.python-version }}
346348

347349
- name: Restore Python virtual environment related to target branch
348350
id: restore_python_virtual_environment_target_branch
@@ -351,6 +353,7 @@ jobs:
351353
with:
352354
requirements_paths: ${{ inputs.requirements_path }}
353355
git_reference: ${{ github.base_ref }}
356+
python_version: ${{ steps.setup_python.outputs.python-version }}
354357

355358
- name: Create Python virtual environment
356359
if: >
@@ -423,6 +426,7 @@ jobs:
423426
uses: ./.github/actions/python_requirements/save_virtualenv
424427
with:
425428
requirements_paths: "${{ inputs.requirements_path }} requirements-linters.txt requirements-dev.txt requirements-docs.txt"
429+
python_version: ${{ steps.setup_python.outputs.python-version }}
426430

427431
- name: Save pip cache related to PR event
428432
if: >

.github/workflows/create_python_cache.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ jobs:
3333
# sudo apt-get update && sudo apt install <package1> <package2>...
3434

3535
- name: Set up Python
36+
id: setup_python
3637
uses: actions/setup-python@v5
3738
with:
38-
python-version: "3.12"
39+
python-version: "3.13"
3940

4041
- name: Set up Python virtual environment
4142
uses: ./.github/actions/python_requirements/create_virtualenv
@@ -52,4 +53,5 @@ jobs:
5253
uses: ./.github/actions/python_requirements/save_virtualenv
5354
with:
5455
requirements_paths: .github/test/python_test/requirements.txt
56+
python_version: ${{ steps.setup_python.outputs.python-version }}
5557

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@ venv/
22
docs/build
33
docker/env_file
44
docker/env_file_postgres
5+
docker/env_file.backup.*
6+
docker/env_file_postgres.backup.*
57
.env
8+
.env.backup.*
69
__pycache__/
710
mlmodels/
11+
# Backups created by gbctl
12+
backups/
813
# JetBrains IDEs (PyCharm, IntelliJ, etc.)
914
.idea/
1015
# Ruff cache
@@ -16,3 +21,5 @@ coverage.xml
1621
*.cover
1722
.coverage.*
1823

24+
# gbctl configuration file
25+
.gbctl.conf

api/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
feeds_asn,
1414
feeds_pagination,
1515
general_honeypot_list,
16+
news_view,
1617
)
1718

1819
# Routers provide an easy way of automatically determining the URL conf.
@@ -29,6 +30,7 @@
2930
path("cowrie_session", cowrie_session_view),
3031
path("command_sequence", command_sequence_view),
3132
path("general_honeypot", general_honeypot_list),
33+
path("news/", news_view),
3234
# router viewsets
3335
path("", include(router.urls)),
3436
# certego_saas:

api/views/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
from api.views.enrichment import *
44
from api.views.feeds import *
55
from api.views.general_honeypot import *
6+
from api.views.news import *
67
from api.views.statistics import *

api/views/news.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from rest_framework.decorators import api_view
2+
from rest_framework.response import Response
3+
4+
from api.views.utils import get_greedybear_news
5+
6+
7+
@api_view(["GET"])
8+
def news_view(request):
9+
"""
10+
Fetch GreedyBear blog posts from an RSS feed.
11+
12+
Filters for posts with "GreedyBear" in the title, truncates long summaries,
13+
sorts by newest first, and caches results to improve performance.
14+
15+
Returns:
16+
List[dict]: Each dict contains title, date, link, and subtext.
17+
Returns an empty list if no relevant posts are found or feed fails.
18+
"""
19+
news_list = get_greedybear_news()
20+
return Response(news_list)

api/views/utils.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@
66
from datetime import datetime, timedelta
77
from ipaddress import ip_address
88

9+
import feedparser
10+
import requests
911
from django.conf import settings
1012
from django.contrib.postgres.aggregates import ArrayAgg
13+
from django.core.cache import cache
1114
from django.db.models import Count, F, Max, Min, Sum
1215
from django.http import HttpResponse, HttpResponseBadRequest, StreamingHttpResponse
1316
from rest_framework import status
1417
from rest_framework.response import Response
1518

1619
from api.serializers import FeedsRequestSerializer
20+
from greedybear.consts import CACHE_KEY_GREEDYBEAR_NEWS, CACHE_TIMEOUT_SECONDS, RSS_FEED_URL
1721
from greedybear.models import IOC, GeneralHoneypot, Statistics
1822

1923
logger = logging.getLogger(__name__)
@@ -401,3 +405,57 @@ def asn_aggregated_queryset(iocs_qs, request, feed_params):
401405
result.append(row_dict)
402406

403407
return result
408+
409+
410+
def get_greedybear_news() -> list[dict]:
411+
"""
412+
Fetch GreedyBear-related blog posts from the IntelOwl RSS feed.
413+
414+
Returns:
415+
List of dicts with keys: title, date, link, subtext
416+
Sorted newest first, or empty list on failure.
417+
"""
418+
cached = cache.get(CACHE_KEY_GREEDYBEAR_NEWS)
419+
if cached is not None:
420+
return cached
421+
422+
try:
423+
response = requests.get(RSS_FEED_URL, timeout=5)
424+
response.raise_for_status()
425+
feed = feedparser.parse(response.content)
426+
427+
filtered_entries = sorted(
428+
[entry for entry in feed.entries if "greedybear" in entry.get("title", "").lower() and entry.get("published_parsed")],
429+
key=lambda e: e.published_parsed,
430+
reverse=True,
431+
)
432+
433+
news_items: list[dict] = []
434+
for entry in filtered_entries:
435+
summary = entry.get("summary", "").strip().replace("\n", " ")
436+
437+
subtext = summary[:180].rsplit(" ", 1)[0] + "..." if len(summary) > 180 else summary
438+
439+
news_items.append(
440+
{
441+
"title": entry.get("title"),
442+
"date": entry.get("published"),
443+
"link": entry.get("link"),
444+
"subtext": subtext,
445+
}
446+
)
447+
448+
cache.set(
449+
CACHE_KEY_GREEDYBEAR_NEWS,
450+
news_items,
451+
CACHE_TIMEOUT_SECONDS,
452+
)
453+
454+
return news_items
455+
456+
except Exception as exc:
457+
logger.error(
458+
"Failed to fetch GreedyBear news from RSS feed",
459+
exc_info=exc,
460+
)
461+
return []

0 commit comments

Comments
 (0)