Skip to content

Commit a2d1b51

Browse files
authored
Merge pull request #3840 from intelowlproject/develop
v6.7.0
2 parents 38ebe10 + a03b82f commit a2d1b51

165 files changed

Lines changed: 12458 additions & 1059 deletions

File tree

Some content is hidden

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

.github/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
[**Upgrade Guide**](https://intelowlproject.github.io/docs/IntelOwl/installation/#update-to-the-most-recent-version)
44

5+
## [v6.7.0](https://github.com/intelowlproject/IntelOwl/releases/tag/v6.7.0)
6+
We welcome the first working AI-based integration in IntelOwl! :tada:
7+
8+
We invite all the users to test our new awesome Chatbot! :sunglasses: Check the [official doc](https://intelowlproject.github.io/docs/IntelOwl/chatbot/) for more info.
9+
10+
Mainly this release merges all the developments performed by our Google Summer of Code contributors during the last month:
11+
* [Francesco Berardi](https://github.com/berardifra): "Integrating a Self-Deployed LLM Chatbot for Threat Intelligence"
12+
* [Sanjib Behera](https://github.com/sanjib2006): "Integration Ecosystem & Connector Optimization"
13+
514
## [v6.6.1](https://github.com/intelowlproject/IntelOwl/releases/tag/v6.6.1)
615
A lot of minor contributions to fix bugs and improve maintenance.
716

.github/workflows/codeql-analysis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ jobs:
4444
fetch-depth: 2
4545

4646
- name: Set up Python
47-
uses: actions/setup-python@v6.2.0
47+
uses: actions/setup-python@v6.3.0
4848
with:
4949
python-version: '3.11'
5050

.github/workflows/dependency_review.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,11 @@ jobs:
1313
- name: 'Checkout Repository'
1414
uses: actions/checkout@v6.0.2
1515
- name: 'Dependency Review'
16-
uses: actions/dependency-review-action@v4
16+
uses: actions/dependency-review-action@v4
17+
with:
18+
# GHSA-gr75-jv2w-4656: LangChain path traversal in file-search middleware/loaders.
19+
# Patched only in langchain 1.3.9 (breaking major upgrade). The chatbot uses no
20+
# document loaders, file-search middleware, or hub prompt pulls (only langchain.agents,
21+
# langchain_core.*, langchain_ollama), so the vulnerable code paths are unreachable.
22+
# Accepted until the planned langchain 1.x migration. Approved by @mlodic.
23+
allow-ghsas: GHSA-gr75-jv2w-4656

.github/workflows/pull_request_automation.yml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
uses: actions/checkout@v6.0.2
3838

3939
- name: Set up Python
40-
uses: actions/setup-python@v6.2.0
40+
uses: actions/setup-python@v6.3.0
4141
with:
4242
python-version: 3.11
4343

@@ -87,18 +87,23 @@ jobs:
8787
password: ${{ secrets.GITHUB_TOKEN }}
8888

8989
- name: Startup script launch (Slow)
90-
if: contains(github.base_ref, 'master')
90+
# Only the develop -> master release PR runs the full build: extra analyzers plus the
91+
# chatbot stack (--ollama), which smoke-tests the ollama + celery_worker_chatbot topology
92+
# on the :ci image. Kept off every other PR because the model pull is heavy.
93+
if: github.base_ref == 'master' && github.head_ref == 'develop'
9194
run: |
9295
cp docker/env_file_integrations_template docker/env_file_integrations
93-
./start ci up --malware_tools_analyzers --phishing_analyzers -- --build -d
96+
./start ci up --malware_tools_analyzers --phishing_analyzers --ollama -- --build -d
9497
env:
9598
DOCKER_BUILDKIT: 1
9699
BUILDKIT_PROGRESS: "plain"
97100
STAGE: "ci"
98101
REPO_DOWNLOADER_ENABLED: false
99102

100103
- name: Startup script launch (Fast)
101-
if: "!contains(github.base_ref, 'master')"
104+
# Exact complement of the Slow step so every other PR (incl. a non-develop head into
105+
# master) still gets a container up for the test steps below.
106+
if: ${{ !(github.base_ref == 'master' && github.head_ref == 'develop') }}
102107
run: |
103108
./start ci up -- --build -d
104109
env:
@@ -137,7 +142,7 @@ jobs:
137142
with:
138143
node-version: 18
139144
- name: Cache node modules
140-
uses: actions/cache@v5
145+
uses: actions/cache@v6
141146
with:
142147
path: ~/.npm
143148
key: npm-build-${{ hashFiles('frontend/package-lock.json') }}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl
2+
# See the file 'LICENSE' for copying permission.
3+
4+
"""IPQualityScore file analyzer.
5+
6+
Provides `IPQSFileScan`, a file-scanning analyzer that uploads files to
7+
IPQualityScore for malware detection and polls for results.
8+
"""
9+
10+
import logging
11+
12+
from api_app.analyzers_manager.classes import FileAnalyzer
13+
from api_app.mixins import IPQualityScoreMixin
14+
15+
logger = logging.getLogger(__name__)
16+
17+
18+
class IPQSFileScan(FileAnalyzer, IPQualityScoreMixin):
19+
"""
20+
Scan a binary file using IPQualityScore malware detection service.
21+
"""
22+
23+
@classmethod
24+
def update(cls):
25+
pass
26+
27+
def run(self):
28+
binary = self.read_file_bytes()
29+
files = {"files": (self.filename, binary)}
30+
# lookup endpoint check for cached result
31+
lookup_result = self._make_request(
32+
self.lookup_endpoint,
33+
method="POST",
34+
_api_key=self._ipqs_api_key,
35+
files=files,
36+
)
37+
if lookup_result.get("status", False) == "cached":
38+
lookup_result.pop("update_url", None)
39+
return lookup_result
40+
# sending file to ipqs for scan
41+
scan_result = self._make_request(
42+
self.scan_endpoint,
43+
method="POST",
44+
_api_key=self._ipqs_api_key,
45+
files=files,
46+
)
47+
# waiting for scan result with help of request id
48+
result = self._poll_for_report(
49+
endpoint=self.postback_endpoint,
50+
_api_key=self._ipqs_api_key,
51+
request_id=scan_result.get("request_id"),
52+
)
53+
result.pop("update_url", None)
54+
return result
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
from django.db import migrations
2+
from django.db.models.fields.related_descriptors import (
3+
ForwardManyToOneDescriptor,
4+
ForwardOneToOneDescriptor,
5+
ManyToManyDescriptor,
6+
ReverseManyToOneDescriptor,
7+
ReverseOneToOneDescriptor,
8+
)
9+
10+
plugin = {
11+
"python_module": {
12+
"health_check_schedule": None,
13+
"update_schedule": None,
14+
"module": "ipqsfile.IPQSFileScan",
15+
"base_path": "api_app.analyzers_manager.file_analyzers",
16+
},
17+
"name": "IPQS_Malware_File_Scanner",
18+
"description": "Scan files for malware, viruses, and malicious payloads in real-time using [IPQualityScore](https://www.ipqualityscore.com/)'s advanced file scanning engine.",
19+
"disabled": False,
20+
"soft_time_limit": 140,
21+
"routing_key": "default",
22+
"health_check_status": True,
23+
"type": "file",
24+
"docker_based": False,
25+
"maximum_tlp": "AMBER",
26+
"observable_supported": [],
27+
"supported_filetypes": [
28+
"application/w-script-file",
29+
"application/javascript",
30+
"application/x-javascript",
31+
"text/javascript",
32+
"application/x-vbscript",
33+
"text/x-ms-iqy",
34+
"application/vnd.android.package-archive",
35+
"application/x-dex",
36+
"application/onenote",
37+
"application/zip",
38+
"multipart/x-zip",
39+
"application/java-archive",
40+
"text/rtf",
41+
"application/rtf",
42+
"application/x-sharedlib",
43+
"application/vnd.microsoft.portable-executable",
44+
"application/x-elf",
45+
"application/octet-stream",
46+
"application/vnd.tcpdump.pcap",
47+
"application/pdf",
48+
"text/html",
49+
"application/x-mspublisher",
50+
"application/vnd.ms-excel.addin.macroEnabled",
51+
"application/vnd.ms-excel.sheet.macroEnabled.12",
52+
"application/vnd.ms-excel",
53+
"application/excel",
54+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
55+
"application/xml",
56+
"text/xml",
57+
"application/encrypted",
58+
"text/plain",
59+
"text/csv",
60+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
61+
"application/msword",
62+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
63+
"application/vnd.ms-powerpoint",
64+
"application/vnd.ms-office",
65+
"application/x-binary",
66+
"application/x-macbinary",
67+
"application/mac-binary",
68+
"application/x-mach-binary",
69+
"application/x-zip-compressed",
70+
"application/x-compressed",
71+
"application/vnd.ms-outlook",
72+
"message/rfc822",
73+
"application/pkcs7-signature",
74+
"application/x-pkcs7-signature",
75+
"multipart/mixed",
76+
"text/x-shellscript",
77+
"application/x-chrome-extension",
78+
"application/json",
79+
"application/x-executable",
80+
"text/x-java",
81+
"text/x-kotlin",
82+
"text/x-swift",
83+
"text/x-objective-c",
84+
"application/x-ms-shortcut",
85+
"application/gzip",
86+
],
87+
"run_hash": False,
88+
"run_hash_type": "",
89+
"not_supported_filetypes": [],
90+
"mapping_data_model": {},
91+
"model": "analyzers_manager.AnalyzerConfig",
92+
}
93+
94+
params = [
95+
{
96+
"python_module": {
97+
"module": "ipqsfile.IPQSFileScan",
98+
"base_path": "api_app.analyzers_manager.file_analyzers",
99+
},
100+
"name": "ipqs_api_key",
101+
"type": "str",
102+
"description": "Please provide the IPQS API key.",
103+
"is_secret": True,
104+
"required": True,
105+
},
106+
{
107+
"python_module": {
108+
"module": "ipqsfile.IPQSFileScan",
109+
"base_path": "api_app.analyzers_manager.file_analyzers",
110+
},
111+
"name": "polling_interval",
112+
"type": "int",
113+
"description": "Recommended polling interval: 10 seconds.",
114+
"is_secret": False,
115+
"required": True,
116+
},
117+
{
118+
"python_module": {
119+
"module": "ipqsfile.IPQSFileScan",
120+
"base_path": "api_app.analyzers_manager.file_analyzers",
121+
},
122+
"name": "max_retries",
123+
"type": "int",
124+
"description": "Recommended max retries: 8.",
125+
"is_secret": False,
126+
"required": True,
127+
},
128+
]
129+
130+
values = []
131+
132+
133+
def _get_real_obj(Model, field, value):
134+
def _get_obj(Model, other_model, value):
135+
if isinstance(value, dict):
136+
real_vals = {}
137+
for key, real_val in value.items():
138+
real_vals[key] = _get_real_obj(other_model, key, real_val)
139+
value = other_model.objects.get_or_create(**real_vals)[0]
140+
# it is just the primary key serialized
141+
else:
142+
if isinstance(value, int):
143+
if Model.__name__ == "PluginConfig":
144+
value = other_model.objects.get(name=plugin["name"])
145+
else:
146+
value = other_model.objects.get(pk=value)
147+
else:
148+
value = other_model.objects.get(name=value)
149+
return value
150+
151+
if (
152+
type(getattr(Model, field))
153+
in [
154+
ForwardManyToOneDescriptor,
155+
ReverseManyToOneDescriptor,
156+
ReverseOneToOneDescriptor,
157+
ForwardOneToOneDescriptor,
158+
]
159+
and value
160+
):
161+
other_model = getattr(Model, field).get_queryset().model
162+
value = _get_obj(Model, other_model, value)
163+
elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value:
164+
other_model = getattr(Model, field).rel.model
165+
value = [_get_obj(Model, other_model, val) for val in value]
166+
return value
167+
168+
169+
def _create_object(Model, data):
170+
mtm, no_mtm = {}, {}
171+
for field, value in data.items():
172+
value = _get_real_obj(Model, field, value)
173+
if type(getattr(Model, field)) is ManyToManyDescriptor:
174+
mtm[field] = value
175+
else:
176+
no_mtm[field] = value
177+
try:
178+
o = Model.objects.get(**no_mtm)
179+
except Model.DoesNotExist:
180+
o = Model(**no_mtm)
181+
o.full_clean()
182+
o.save()
183+
for field, value in mtm.items():
184+
attribute = getattr(o, field)
185+
if value is not None:
186+
attribute.set(value)
187+
return False
188+
return True
189+
190+
191+
def migrate(apps, schema_editor):
192+
Parameter = apps.get_model("api_app", "Parameter")
193+
PluginConfig = apps.get_model("api_app", "PluginConfig")
194+
python_path = plugin.pop("model")
195+
Model = apps.get_model(*python_path.split("."))
196+
if not Model.objects.filter(name=plugin["name"]).exists():
197+
exists = _create_object(Model, plugin)
198+
if not exists:
199+
for param in params:
200+
_create_object(Parameter, param)
201+
for value in values:
202+
_create_object(PluginConfig, value)
203+
204+
205+
def reverse_migrate(apps, schema_editor):
206+
python_path = plugin.pop("model")
207+
Model = apps.get_model(*python_path.split("."))
208+
Model.objects.get(name=plugin["name"]).delete()
209+
210+
211+
class Migration(migrations.Migration):
212+
atomic = False
213+
dependencies = [
214+
("api_app", "0073_alter_updatecheckstatus_last_checked_at_and_more"),
215+
("analyzers_manager", "0190_remove_greynoise_labs_analyzer"),
216+
]
217+
218+
operations = [migrations.RunPython(migrate, reverse_migrate)]

0 commit comments

Comments
 (0)