Skip to content

Commit 49549f8

Browse files
committed
IPQuery Analyzer, Closes #2707
1 parent af54a9e commit 49549f8

File tree

3 files changed

+233
-0
lines changed

3 files changed

+233
-0
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
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": "ipquery.IPQuery",
15+
"base_path": "api_app.analyzers_manager.observable_analyzers",
16+
},
17+
"name": "IPQuery",
18+
"description": "[IPQuery](https://ipquery.io/) aims to provide a straightforward and simplistic API for working with IP addresses.",
19+
"disabled": False,
20+
"soft_time_limit": 60,
21+
"routing_key": "default",
22+
"health_check_status": True,
23+
"type": "observable",
24+
"docker_based": False,
25+
"maximum_tlp": "AMBER",
26+
"observable_supported": ["ip"],
27+
"supported_filetypes": [],
28+
"run_hash": False,
29+
"run_hash_type": "",
30+
"not_supported_filetypes": [],
31+
"mapping_data_model": {},
32+
"model": "analyzers_manager.AnalyzerConfig",
33+
}
34+
35+
params = []
36+
37+
values = []
38+
39+
40+
def _get_real_obj(Model, field, value):
41+
def _get_obj(Model, other_model, value):
42+
if isinstance(value, dict):
43+
real_vals = {}
44+
for key, real_val in value.items():
45+
real_vals[key] = _get_real_obj(other_model, key, real_val)
46+
value = other_model.objects.get_or_create(**real_vals)[0]
47+
# it is just the primary key serialized
48+
else:
49+
if isinstance(value, int):
50+
if Model.__name__ == "PluginConfig":
51+
value = other_model.objects.get(name=plugin["name"])
52+
else:
53+
value = other_model.objects.get(pk=value)
54+
else:
55+
value = other_model.objects.get(name=value)
56+
return value
57+
58+
if (
59+
type(getattr(Model, field))
60+
in [
61+
ForwardManyToOneDescriptor,
62+
ReverseManyToOneDescriptor,
63+
ReverseOneToOneDescriptor,
64+
ForwardOneToOneDescriptor,
65+
]
66+
and value
67+
):
68+
other_model = getattr(Model, field).get_queryset().model
69+
value = _get_obj(Model, other_model, value)
70+
elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value:
71+
other_model = getattr(Model, field).rel.model
72+
value = [_get_obj(Model, other_model, val) for val in value]
73+
return value
74+
75+
76+
def _create_object(Model, data):
77+
mtm, no_mtm = {}, {}
78+
for field, value in data.items():
79+
value = _get_real_obj(Model, field, value)
80+
if type(getattr(Model, field)) is ManyToManyDescriptor:
81+
mtm[field] = value
82+
else:
83+
no_mtm[field] = value
84+
try:
85+
o = Model.objects.get(**no_mtm)
86+
except Model.DoesNotExist:
87+
o = Model(**no_mtm)
88+
o.full_clean()
89+
o.save()
90+
for field, value in mtm.items():
91+
attribute = getattr(o, field)
92+
if value is not None:
93+
attribute.set(value)
94+
return False
95+
return True
96+
97+
98+
def migrate(apps, schema_editor):
99+
Parameter = apps.get_model("api_app", "Parameter")
100+
PluginConfig = apps.get_model("api_app", "PluginConfig")
101+
python_path = plugin.pop("model")
102+
Model = apps.get_model(*python_path.split("."))
103+
if not Model.objects.filter(name=plugin["name"]).exists():
104+
exists = _create_object(Model, plugin)
105+
if not exists:
106+
for param in params:
107+
_create_object(Parameter, param)
108+
for value in values:
109+
_create_object(PluginConfig, value)
110+
111+
112+
def reverse_migrate(apps, schema_editor):
113+
python_path = plugin.pop("model")
114+
Model = apps.get_model(*python_path.split("."))
115+
Model.objects.get(name=plugin["name"]).delete()
116+
117+
118+
class Migration(migrations.Migration):
119+
atomic = False
120+
dependencies = [
121+
("api_app", "0065_job_mpnodesearch"),
122+
(
123+
"analyzers_manager",
124+
"0147_alter_analyzer_config_feodo_yaraify_urlhaus_yaraify_scan",
125+
),
126+
]
127+
128+
operations = [migrations.RunPython(migrate, reverse_migrate)]
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl
2+
# See the file 'LICENSE' for copying permission.
3+
4+
import logging
5+
6+
import requests
7+
8+
from api_app.analyzers_manager import classes
9+
from api_app.analyzers_manager.exceptions import AnalyzerRunException
10+
from tests.mock_utils import MockUpResponse, if_mock_connections, patch
11+
12+
logger = logging.getLogger(__name__)
13+
14+
15+
class IPQuery(classes.ObservableAnalyzer):
16+
url: str = "https://api.ipquery.io/"
17+
18+
@classmethod
19+
def update(cls) -> bool:
20+
pass
21+
22+
def run(self):
23+
logger.info(f"Running IPQuery Analyzer for {self.observable_name}")
24+
25+
try:
26+
response = requests.get(f"{self.url}{self.observable_name}?format=json")
27+
response.raise_for_status()
28+
except requests.RequestException as e:
29+
raise AnalyzerRunException(e)
30+
31+
results = response.json()
32+
return results
33+
34+
@classmethod
35+
def _monkeypatch(cls):
36+
response = {
37+
"ip": "1.1.1.1",
38+
"isp": {
39+
"asn": "AS13335",
40+
"isp": "Cloudflare, Inc.",
41+
"org": "Cloudflare, Inc.",
42+
},
43+
"risk": {
44+
"is_tor": "false",
45+
"is_vpn": "false",
46+
"is_proxy": "false",
47+
"is_mobile": "false",
48+
"risk_score": 0,
49+
"is_datacenter": "true",
50+
},
51+
"location": {
52+
"city": "Sydney",
53+
"state": "New South Wales",
54+
"country": "Australia",
55+
"zipcode": "1001",
56+
"latitude": -33.854548400186665,
57+
"timezone": "Australia/Sydney",
58+
"localtime": "2025-02-03T13:06:16",
59+
"longitude": 151.20016200912815,
60+
"country_code": "AU",
61+
},
62+
}
63+
patches = [
64+
if_mock_connections(
65+
patch(
66+
"requests.get",
67+
return_value=MockUpResponse(response, 200),
68+
)
69+
)
70+
]
71+
return super()._monkeypatch(patches=patches)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl
2+
# See the file 'LICENSE' for copying permission.
3+
4+
5+
from django.db import migrations
6+
7+
8+
def migrate(apps, schema_editor):
9+
playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig")
10+
AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig")
11+
pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS")
12+
pc.analyzers.add(AnalyzerConfig.objects.get(name="IPQuery").id)
13+
pc.full_clean()
14+
pc.save()
15+
16+
17+
def reverse_migrate(apps, schema_editor):
18+
playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig")
19+
AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig")
20+
pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS")
21+
pc.analyzers.remove(AnalyzerConfig.objects.get(name="IPQuery").id)
22+
pc.full_clean()
23+
pc.save()
24+
25+
26+
class Migration(migrations.Migration):
27+
dependencies = [
28+
("playbooks_manager", "0058_add_ultradns_to_free_to_use_and_dns"),
29+
("analyzers_manager", "0148_analyzer_config_ipquery"),
30+
]
31+
32+
operations = [
33+
migrations.RunPython(migrate, reverse_migrate),
34+
]

0 commit comments

Comments
 (0)