Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ed67741
feat: Add comprehensive start script to simplify setup and update pro…
opbot-xd Feb 4, 2026
298ee0d
Remove unused Log4pot from GeneralHoneypot. Closes #773 (#780)
opbot-xd Feb 5, 2026
92a1289
Make pending migration
regulartim Feb 5, 2026
887febc
Tooltip to show feed scores on frontend (#776)
armoredvortex Feb 10, 2026
9450626
Reorganize Celery beat schedule for better maintainability (#765)
SupRaKoshti Feb 10, 2026
ee0d79c
Track IoC-Sensor relationship. Closes #779 (#784)
opbot-xd Feb 10, 2026
b7ce44a
build(deps): bump django-ses from 4.6.0 to 4.7.1 in /requirements (#792)
dependabot[bot] Feb 11, 2026
b234695
build(deps): bump library/nginx in /docker (#791)
dependabot[bot] Feb 11, 2026
7476a4c
build(deps): bump elasticsearch from 9.2.1 to 9.3.0 in /requirements …
dependabot[bot] Feb 11, 2026
a4a1edb
feat(frontend): Dynamic GreedyBear news widget with filtering, sortin…
drona-gyawali Feb 11, 2026
3b24661
Bump axios form 1.6.0 to 1.13.5 to mitigate CVE-2026-25639
regulartim Feb 12, 2026
7917a1e
Fix frontend update process. Closes #795 (#799)
regulartim Feb 12, 2026
6898f14
Improve build performance. Closes #796 (#798)
regulartim Feb 12, 2026
54def5b
Several improvements to gbctl. Follow up on #752 (#793)
regulartim Feb 12, 2026
bbc92f1
Include Python version in virtualenv cache key in CI. Closes #806 (#805)
regulartim Feb 12, 2026
7b69dd8
Bump 3.1.0
regulartim Feb 14, 2026
4fac309
Add libexpat1 as a runtime dependency
regulartim Feb 14, 2026
f2d6ba4
Spliting test_view file for better test structure. Closes #794 (#803)
drona-gyawali Feb 16, 2026
acf8fb8
Add missing closing quote in nginx 408 response
regulartim Feb 16, 2026
f2ff6fb
Merge pull request #800 from intelowlproject/develop
regulartim Feb 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env_template
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ COMPOSE_FILE=docker/default.yml:docker/local.override.yml
#COMPOSE_FILE=docker/default.yml:docker/local.override.yml:docker/elasticsearch.yml

# If you want to run a specific version, populate this
# REACT_APP_INTELOWL_VERSION="3.0.1"
# REACT_APP_INTELOWL_VERSION="3.1.0"
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ inputs:
description: A git reference (name of the branch, reference to the PR) that will be used to build the cache key.
required: false
default: ${{ github.ref_name }}
python_version:
description: Python version string to include in the cache key, preventing cross-version cache collisions.
required: true

outputs:
cache-hit:
Expand All @@ -32,7 +35,7 @@ runs:
uses: actions/cache/restore@v4
with:
path: ${{ inputs.virtual_environment_path }}
key: ${{ inputs.git_reference }}-venv-${{ steps.compute_requirements_files_sha256_hash.outputs.computed_hash }}
key: ${{ inputs.git_reference }}-py${{ inputs.python_version }}-venv-${{ steps.compute_requirements_files_sha256_hash.outputs.computed_hash }}

- name: Activate restored virtual environment
if: >
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ inputs:
description: A git reference (name of the branch, reference to the PR) that will be used to build the cache key.
required: false
default: ${{ github.ref_name }}
python_version:
description: Python version string to include in the cache key, preventing cross-version cache collisions.
required: true

runs:
using: "composite"
Expand All @@ -26,4 +29,4 @@ runs:
uses: actions/cache/save@v4
with:
path: ${{ inputs.virtual_environment_path }}
key: ${{ inputs.git_reference }}-venv-${{ steps.compute_requirements_files_sha256_hash.outputs.computed_hash }}
key: ${{ inputs.git_reference }}-py${{ inputs.python_version }}-venv-${{ steps.compute_requirements_files_sha256_hash.outputs.computed_hash }}
4 changes: 4 additions & 0 deletions .github/workflows/_python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ jobs:
uses: actions/checkout@v4

- name: Set up Python
id: setup_python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python_version }}
Expand Down Expand Up @@ -343,6 +344,7 @@ jobs:
uses: ./.github/actions/python_requirements/restore_virtualenv/
with:
requirements_paths: "${{ inputs.requirements_path }} requirements-linters.txt requirements-dev.txt requirements-docs.txt"
python_version: ${{ steps.setup_python.outputs.python-version }}

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

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

- name: Save pip cache related to PR event
if: >
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/create_python_cache.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ jobs:
# sudo apt-get update && sudo apt install <package1> <package2>...

- name: Set up Python
id: setup_python
uses: actions/setup-python@v5
with:
python-version: "3.12"
python-version: "3.13"

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

7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ venv/
docs/build
docker/env_file
docker/env_file_postgres
docker/env_file.backup.*
docker/env_file_postgres.backup.*
.env
.env.backup.*
__pycache__/
mlmodels/
# Backups created by gbctl
backups/
# JetBrains IDEs (PyCharm, IntelliJ, etc.)
.idea/
# Ruff cache
Expand All @@ -16,3 +21,5 @@ coverage.xml
*.cover
.coverage.*

# gbctl configuration file
.gbctl.conf
2 changes: 2 additions & 0 deletions api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
feeds_asn,
feeds_pagination,
general_honeypot_list,
news_view,
)

# Routers provide an easy way of automatically determining the URL conf.
Expand All @@ -29,6 +30,7 @@
path("cowrie_session", cowrie_session_view),
path("command_sequence", command_sequence_view),
path("general_honeypot", general_honeypot_list),
path("news/", news_view),
# router viewsets
path("", include(router.urls)),
# certego_saas:
Expand Down
1 change: 1 addition & 0 deletions api/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
from api.views.enrichment import *
from api.views.feeds import *
from api.views.general_honeypot import *
from api.views.news import *
from api.views.statistics import *
20 changes: 20 additions & 0 deletions api/views/news.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from rest_framework.decorators import api_view
from rest_framework.response import Response

from api.views.utils import get_greedybear_news


@api_view(["GET"])
def news_view(request):
"""
Fetch GreedyBear blog posts from an RSS feed.

Filters for posts with "GreedyBear" in the title, truncates long summaries,
sorts by newest first, and caches results to improve performance.

Returns:
List[dict]: Each dict contains title, date, link, and subtext.
Returns an empty list if no relevant posts are found or feed fails.
"""
news_list = get_greedybear_news()
return Response(news_list)
58 changes: 58 additions & 0 deletions api/views/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@
from datetime import datetime, timedelta
from ipaddress import ip_address

import feedparser
import requests
from django.conf import settings
from django.contrib.postgres.aggregates import ArrayAgg
from django.core.cache import cache
from django.db.models import Count, F, Max, Min, Sum
from django.http import HttpResponse, HttpResponseBadRequest, StreamingHttpResponse
from rest_framework import status
from rest_framework.response import Response

from api.serializers import FeedsRequestSerializer
from greedybear.consts import CACHE_KEY_GREEDYBEAR_NEWS, CACHE_TIMEOUT_SECONDS, RSS_FEED_URL
from greedybear.models import IOC, GeneralHoneypot, Statistics

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

return result


def get_greedybear_news() -> list[dict]:
"""
Fetch GreedyBear-related blog posts from the IntelOwl RSS feed.

Returns:
List of dicts with keys: title, date, link, subtext
Sorted newest first, or empty list on failure.
"""
cached = cache.get(CACHE_KEY_GREEDYBEAR_NEWS)
if cached is not None:
return cached

try:
response = requests.get(RSS_FEED_URL, timeout=5)
response.raise_for_status()
feed = feedparser.parse(response.content)

filtered_entries = sorted(
[entry for entry in feed.entries if "greedybear" in entry.get("title", "").lower() and entry.get("published_parsed")],
key=lambda e: e.published_parsed,
reverse=True,
)

news_items: list[dict] = []
for entry in filtered_entries:
summary = entry.get("summary", "").strip().replace("\n", " ")

subtext = summary[:180].rsplit(" ", 1)[0] + "..." if len(summary) > 180 else summary

news_items.append(
{
"title": entry.get("title"),
"date": entry.get("published"),
"link": entry.get("link"),
"subtext": subtext,
}
)

cache.set(
CACHE_KEY_GREEDYBEAR_NEWS,
news_items,
CACHE_TIMEOUT_SECONDS,
)

return news_items

except Exception as exc:
logger.error(
"Failed to fetch GreedyBear news from RSS feed",
exc_info=exc,
)
return []
2 changes: 1 addition & 1 deletion configuration/nginx/errors.conf
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ location /404.json {

error_page 408 /408.json;
location /408.json {
return 408 '{"code":408,"message":"Request Timeout}';
return 408 '{"code":408,"message":"Request Timeout"}';
}

error_page 413 /413.json;
Expand Down
2 changes: 1 addition & 1 deletion docker/.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
REACT_APP_GREEDYBEAR_VERSION="3.0.1"
REACT_APP_GREEDYBEAR_VERSION="3.1.0"
34 changes: 19 additions & 15 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@
FROM node:lts-alpine3.22 AS frontend-build

WORKDIR /
# copy react source code
COPY frontend/ .
# copy version file as an env file
COPY docker/.version .env.local
# install and build
RUN npm install npm@latest --location=global
# install dependencies first for layer caching
COPY frontend/package.json frontend/package-lock.json ./
RUN npm install

# copy source code and build
COPY frontend/ .
COPY docker/.version .env.local
RUN PUBLIC_URL=/static/reactapp/ npm run build

# Stage 2: Backend
FROM python:3.13-alpine3.22
FROM python:3.13-slim-trixie

ENV PYTHONUNBUFFERED=1
ENV DJANGO_SETTINGS_MODULE=greedybear.settings
Expand All @@ -22,12 +21,15 @@ ENV LOG_PATH=/var/log/greedybear

ARG WATCHMAN=false

RUN mkdir -p ${LOG_PATH} \
${LOG_PATH}/django \
${LOG_PATH}/uwsgi \
# py3-psycopg2 is required to use PostgresSQL with Django \
# libgomp is required to train the model
&& apk --no-cache -U add bash py3-psycopg2 gcc python3-dev alpine-sdk linux-headers libgomp \
RUN mkdir -p ${LOG_PATH}/django ${LOG_PATH}/uwsgi \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
# Build dependencies (removed after pip install)
gcc python3-dev \
# Runtime dependencies:
# libexpat1 is required by uWSGI/Python,
# libgomp1 is required for model training, netcat-openbsd for healthcheck
libexpat1 libgomp1 netcat-openbsd \
&& pip3 install --no-cache-dir --upgrade pip

COPY requirements/project-requirements.txt $PYTHONPATH/project-requirements.txt
Expand All @@ -51,11 +53,13 @@ RUN touch ${LOG_PATH}/django/api.log ${LOG_PATH}/django/api_errors.log \
&& touch ${LOG_PATH}/django/django_errors.log ${LOG_PATH}/django/elasticsearch.log\
&& touch ${LOG_PATH}/django/authentication.log ${LOG_PATH}/django/authentication_errors.log \
&& mkdir -p ${PYTHONPATH}/mlmodels \
&& adduser -S -H -u 2000 -D -g www-data www-data \
&& usermod -u 2000 www-data \
&& chown -R www-data:www-data ${LOG_PATH} /opt/deploy/ ${PYTHONPATH}/mlmodels/ \
&& rm -rf docs/ frontend/ tests/ .github/ docker/hooks/ \
&& /bin/bash ./docker/watchman_install.sh \
&& apk del gcc python3-dev alpine-sdk linux-headers
&& apt-get purge -y gcc python3-dev \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/* # remove the apt package index cache

# start period is high to allow data migration for 1.4.0
HEALTHCHECK --interval=10s --timeout=2s --start-period=500s --retries=3 CMD nc -z localhost 8001 || exit 1
Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile_nginx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM library/nginx:1.29.4-alpine
FROM library/nginx:1.29.5-alpine
RUN mkdir -p /var/cache/nginx /var/cache/nginx/feeds
RUN apk update && apk upgrade && apk add bash
ENV NGINX_LOG_DIR=/var/log/nginx
Expand Down
9 changes: 7 additions & 2 deletions docker/entrypoint_uwsgi.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@ echo "Waiting for db to be ready..."
python manage.py makemigrations durin
python manage.py migrate

# Collect static files
python manage.py collectstatic --noinput
# Collect static files, overwriting existing ones
python manage.py collectstatic --noinput --clear --verbosity 0

# Obtain the current GreedyBear version number
. /opt/deploy/greedybear/docker/.version
export REACT_APP_GREEDYBEAR_VERSION

echo "------------------------------"
echo "GreedyBear $REACT_APP_GREEDYBEAR_VERSION"
echo "DEBUG: " $DEBUG
echo "DJANGO_TEST_SERVER: " $DJANGO_TEST_SERVER
echo "------------------------------"
Expand Down
2 changes: 1 addition & 1 deletion docker/watchman_install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# This script can be disabled during development using REPO_DOWNLOADER_ENABLED=true env variable
if [ "$WATCHMAN" = "false" ]; then echo "Skipping WATCHMAN installation because we are not in test mode"; exit 0; fi

apk add gcc linux-headers build-base
apt-get update && apt-get install -y --no-install-recommends gcc build-essential
pip3 install --compile -r requirements/django-server-requirements.txt

# install Watchman to enhance performance on the Django development Server
Expand Down
Loading
Loading