Skip to content

Commit 33d5af4

Browse files
committed
add world-map example
1 parent d443950 commit 33d5af4

6 files changed

+138
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
visualization/*.json

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ By flagging clients originating from these sources you can achieve a nice securi
1010

1111
The databases created from the gathered data will be and stay open-source!
1212

13+
<a href="https://github.com/O-X-L/risk-db/blob/latest/visualization">
14+
<img src="https://raw.githubusercontent.com/O-X-L/risk-db/refs/heads/latest/visualization/world_map_example.webp" alt="World Map Example" width="800"/>
15+
</a>
16+
1317
----
1418

1519
## Contribute

visualization/world_map.html

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<link rel="icon" type="image/svg" href="https://files.oxl.at/img/oxl3_sm.png">
7+
8+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/svg-pan-zoom.min.js"></script>
9+
<script src="https://cdn.jsdelivr.net/gh/StephanWagner/[email protected]/dist/svgMap.min.js"></script>
10+
<link href="https://cdn.jsdelivr.net/gh/StephanWagner/[email protected]/dist/svgMap.min.css" rel="stylesheet">
11+
</head>
12+
<body>
13+
<!-- see: https://stephanwagner.me/create-world-map-charts-with-svgmap#svgMapDemoGDP -->
14+
<div id="svgMap"></div>
15+
<script>
16+
// start test webserver: 'python3 world_map_test.py'
17+
const RISK_DB_MAP_DATA = 'http://localhost:8000/world_map.json';
18+
19+
function createMap(resp) {
20+
console.log(resp);
21+
new svgMap(resp);
22+
}
23+
24+
function fetchMapData() {
25+
var http = new XMLHttpRequest();
26+
http.onreadystatechange = function() {
27+
if (this.readyState == 4 && this.status == 200) {
28+
createMap(JSON.parse(this.responseText));
29+
}
30+
};
31+
http.open("GET", RISK_DB_MAP_DATA, true);
32+
http.send();
33+
}
34+
fetchMapData();
35+
</script>
36+
</body>
37+
</html>

visualization/world_map_data.py

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/usr/bin/env python3
2+
3+
# data to visualize with svgmap: https://stephanwagner.me/create-world-map-charts-with-svgmap#svgMapDemoGDP
4+
5+
from argparse import ArgumentParser
6+
from json import loads as json_loads
7+
from json import dumps as json_dumps
8+
from pathlib import Path
9+
10+
from maxminddb import open_database as mmdb_database
11+
12+
SRC_PATH = Path(__file__).resolve().parent
13+
HIGHLIGHT_TOP_N = 20
14+
CATEGORIES = ['all', 'bot', 'probe', 'rate', 'attack', 'crawler']
15+
16+
# todo: add change to last month
17+
DATA = {
18+
'targetElementID': 'svgMap',
19+
'data': {
20+
'applyData': 'all',
21+
'data': {
22+
'all': {
23+
'name': 'Reported abuse originating from this country',
24+
'format': '{0}',
25+
'thousandSeparator': '.',
26+
'thresholdMax': 2_000,
27+
'thresholdMin': 500
28+
},
29+
'bot': {
30+
'name': 'Reported bots',
31+
'format': '{0}',
32+
},
33+
'probe': {
34+
'name': 'Reported probes/scanners',
35+
'format': '{0}',
36+
},
37+
'rate': {
38+
'name': 'Reported rate-limit hits',
39+
'format': '{0}',
40+
},
41+
'attack': {
42+
'name': 'Reported attacks',
43+
'format': '{0}',
44+
},
45+
'crawler': {
46+
'name': 'Reported crawlers',
47+
'format': '{0}',
48+
},
49+
},
50+
'values': {},
51+
}
52+
}
53+
54+
55+
def main():
56+
with open(args.file, 'r', encoding='utf-8') as f:
57+
raw = json_loads(f.read())
58+
59+
with mmdb_database(args.country_db) as m:
60+
for asn, ips in raw.items():
61+
for ip, reports in ips.items():
62+
ip_md = m.get(ip)
63+
if ip_md['country'] not in DATA['data']['values']:
64+
DATA['data']['values'][ip_md['country']] = {c: 0 for c in CATEGORIES}
65+
66+
for c in CATEGORIES:
67+
if c in reports:
68+
DATA['data']['values'][ip_md['country']][c] += reports[c]
69+
70+
DATA['data']['values'] = dict(sorted(DATA['data']['values'].items(), key=lambda item: item[1]['all'], reverse=True))
71+
with open(SRC_PATH / 'world_map.json', 'w', encoding='utf-8') as f:
72+
top_country_values = list(DATA['data']['values'].values())
73+
DATA['data']['data']['all']['thresholdMax'] = top_country_values[0]['all']
74+
DATA['data']['data']['all']['thresholdMin'] = top_country_values[HIGHLIGHT_TOP_N]['all']
75+
f.write(json_dumps(DATA, indent=4))
76+
77+
78+
if __name__ == '__main__':
79+
parser = ArgumentParser()
80+
parser.add_argument('-f', '--file', help='JSON file to parse', default='risk_ip4_med.json')
81+
parser.add_argument('-c', '--country-db', help='MMDB country data to use (IPInfo)', default='country_asn.mmdb')
82+
args = parser.parse_args()
83+
main()

visualization/world_map_example.webp

33.8 KB
Binary file not shown.

visualization/world_map_test.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env python3
2+
3+
from http.server import HTTPServer, SimpleHTTPRequestHandler, test
4+
5+
6+
class CORSRequestHandler (SimpleHTTPRequestHandler):
7+
def end_headers (self):
8+
self.send_header('Access-Control-Allow-Origin', '*')
9+
SimpleHTTPRequestHandler.end_headers(self)
10+
11+
12+
if __name__ == '__main__':
13+
test(CORSRequestHandler, HTTPServer, port=8000)

0 commit comments

Comments
 (0)