Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
5724432
is_private_ip: Use IPMatcher
bitterpanda63 Dec 8, 2025
6702883
use singleton structure
bitterpanda63 Dec 8, 2025
3901f87
add benchmarking script
bitterpanda63 Dec 8, 2025
1176b48
Install pytricia
bitterpanda63 Dec 8, 2025
a8e2713
Switch ip matcher up: use pytricia
bitterpanda63 Dec 8, 2025
9022d86
remove useless code in favor of pytricia
bitterpanda63 Dec 8, 2025
e74e49a
pytricia add support for ipv6
bitterpanda63 Dec 8, 2025
1070aa0
linting
bitterpanda63 Dec 8, 2025
e2b38c9
re-lock sample apps
bitterpanda63 Dec 9, 2025
c94a60c
Allow for a "freeze()" command to be run
bitterpanda63 Dec 9, 2025
42d84c2
update benchmark to freeze
bitterpanda63 Dec 9, 2025
5290817
update is_private_ip to test frozen
bitterpanda63 Dec 9, 2025
6f7ed36
freeze should return IPMatcher
bitterpanda63 Dec 9, 2025
52f9261
imds: Use constructor instead
bitterpanda63 Dec 9, 2025
857c89c
bypassed_ips: use constructor instead
bitterpanda63 Dec 9, 2025
7ff8990
is_private_ip use constructor
bitterpanda63 Dec 9, 2025
750fcb1
IPMatcher always freeze on init
bitterpanda63 Dec 9, 2025
92658ca
ip matcher benhcmakr update: is always frozen on init
bitterpanda63 Dec 9, 2025
a5b4216
remove .freeze() incompat
bitterpanda63 Dec 9, 2025
3469410
finally remove the freeze command from IPMatcher
bitterpanda63 Dec 9, 2025
d7f0c98
Add a preparse function to IPMatcher
bitterpanda63 Dec 9, 2025
0b7a825
Fix thread_cache_test cases because add is now not working anymore
bitterpanda63 Dec 9, 2025
ccdcd46
check ipv4 mapped ipv6
bitterpanda63 Dec 9, 2025
5944920
Fix one test case in ip_matcher
bitterpanda63 Dec 9, 2025
0474f04
change py version
bitterpanda63 Dec 9, 2025
18535ab
Revert "change py version"
bitterpanda63 Dec 9, 2025
dc9988f
add ip_matcher_fallback back in
bitterpanda63 Jan 14, 2026
c17f6ec
install pytricia only if the platform is not windows, if the platform…
bitterpanda63 Jan 14, 2026
245723c
re-lock
bitterpanda63 Jan 14, 2026
3fcd7b4
Update comment explaining why we have the fallback
bitterpanda63 Jan 14, 2026
a258f30
also put app start in the re-try github action
bitterpanda63 Jan 14, 2026
8d117fb
add the removed test cases back in
bitterpanda63 Jan 14, 2026
b2380ec
Don't run poetry install quietly
bitterpanda63 Jan 14, 2026
8be88b0
install: ignore failures
bitterpanda63 Jan 14, 2026
c524a59
Revert "also put app start in the re-try github action"
bitterpanda63 Jan 14, 2026
d3ec3b6
Update aikido_zen/helpers/ip_matcher/__init__.py
bitterpanda63 Jan 14, 2026
7ae85e9
add comment explaining SystemError
bitterpanda63 Jan 15, 2026
178be55
update fallback comment to be clearer
bitterpanda63 Jan 15, 2026
4910099
Test an edge case
bitterpanda63 Feb 5, 2026
21b30a7
test edge case again
bitterpanda63 Feb 5, 2026
a52255f
Update aikido_zen/helpers/net/is_private_ip.py
bitterpanda63 Feb 5, 2026
7a4e26d
Update aikido_zen/helpers/net/is_private_ip.py
bitterpanda63 Feb 5, 2026
2f83c3e
Apply suggestions from code review
bitterpanda63 Feb 5, 2026
42bed16
re-lock newer sample app django-asgi-uvicorn
bitterpanda63 Feb 5, 2026
1dd6792
Fix linting errors: remove unused imports
bitterpanda63 Feb 5, 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
4 changes: 1 addition & 3 deletions aikido_zen/background_process/service_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,7 @@ def get_endpoints(self, route_metadata):

def set_bypassed_ips(self, bypassed_ips):
"""Creates an IPMatcher from the given bypassed ip set"""
self.bypassed_ips = IPMatcher()
for ip in bypassed_ips:
self.bypassed_ips.add(ip)
self.bypassed_ips = IPMatcher(bypassed_ips)

def is_bypassed_ip(self, ip):
"""Checks if the IP is on the bypass list"""
Expand Down
109 changes: 62 additions & 47 deletions aikido_zen/helpers/ip_matcher/__init__.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,64 @@
"""
Based on https://github.com/demskie/netparser
MIT License - Copyright (c) 2019 alex
"""

from .shared import parse_base_network, sort_networks, summarize_sorted_networks
from .sort import binary_search_for_insertion_index


class IPMatcher:
def __init__(self, networks=None):
self.sorted = []
if networks is not None:
subnets = []
for s in networks:
net = parse_base_network(s, False)
if net and net.is_valid():
subnets.append(net)
sort_networks(subnets)
self.sorted = summarize_sorted_networks(subnets)

def has(self, network):
"""
Checks if the given IP address is in the list of networks.
"""
net = parse_base_network(network, False)
if not net or not net.is_valid():
return False
idx = binary_search_for_insertion_index(net, self.sorted)
if idx < 0:
return False
if idx < len(self.sorted) and self.sorted[idx].contains(net):
return True
if idx - 1 >= 0 and self.sorted[idx - 1].contains(net):
return True
return False

def add(self, network):
net = parse_base_network(network, False)
if not net or not net.is_valid():
return self
idx = binary_search_for_insertion_index(net, self.sorted)
if idx < len(self.sorted) and self.sorted[idx].compare(net) == 0:
import ipaddress

try:
import pytricia

PYTRICIA_AVAILABLE = True
except ImportError:
PYTRICIA_AVAILABLE = False
from aikido_zen.helpers.logging import logger

logger.warning(
"pytricia is not available. This happens on windows devices where pytricia is not supported yet."
"Using fallback, this may result in slower performance."
"You can try to install pytricia for better performance: pip install pytricia"
)


def preparse(network: str) -> str:
# Remove the brackets around IPv6 addresses if they are there.
network = network.strip("[]")
try:
ip = ipaddress.IPv6Address(network)
if ip.ipv4_mapped:
return str(ip.ipv4_mapped)
except ValueError:
pass
return network


if PYTRICIA_AVAILABLE:

class IPMatcher:
def __init__(self, networks=None):
self.trie = pytricia.PyTricia(128)
if networks is not None:
for s in networks:
self._add(s)
# We freeze in constructor ensuring that after initialization the IPMatcher is always frozen.
self.trie.freeze()

def has(self, network):
try:
return self.trie.get(preparse(network)) is not None
except ValueError:
return False

def _add(self, network):
try:
self.trie[preparse(network)] = True
except ValueError:
pass
except SystemError:
# SystemError's have been known to occur in the PyTricia library (see issue #34 e.g.),
# best to play it safe and catch these errors.
pass
return self
self.sorted.insert(idx, net)
return self

def is_empty(self):
return len(self.sorted) == 0
def is_empty(self):
return len(self.trie) == 0

else:
# Fallback to pure Python implementation - this happens on windows machines since pytricia is not
# fully supported there.
from aikido_zen.helpers.ip_matcher_fallback import IPMatcher # noqa: F401
11 changes: 8 additions & 3 deletions aikido_zen/helpers/ip_matcher/init_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,7 @@ def test_add_ips_later():
matcher = IPMatcher()
assert matcher.has("2001:db8::0") == False
assert matcher.has("2002:db8::1") == False
for ip in input_list:
matcher.add(ip)
matcher = IPMatcher(input_list)
assert matcher.has("2001:db8::1") == False
assert matcher.has("2001:db8::0") == False
assert matcher.has("2002:db8::1") == True
Expand All @@ -145,7 +144,7 @@ def test_strange_ips():
matcher = IPMatcher(input_list)
assert matcher.has("::ffff:0.0.0.0") == True
assert matcher.has("::ffff:127.0.0.1") == True
assert matcher.has("::ffff:123") == False
assert matcher.has("::ffff:123") == True
assert matcher.has("2001:db8::1") == False
assert matcher.has("[::ffff:0.0.0.0]") == True
assert matcher.has("::ffff:0:0:0:0") == True
Expand Down Expand Up @@ -196,3 +195,9 @@ def test_allow_all_ips():
assert matcher.has("10.0.0.1") == True
assert matcher.has("10.0.0.255") == True
assert matcher.has("192.168.1.1") == True


def test_edge_cases():
matcher1 = IPMatcher(["224.0.0.0/4"])
assert matcher1.has("224.0.0.1") == True
assert matcher1.has("240.0.0.0") == False
49 changes: 49 additions & 0 deletions aikido_zen/helpers/ip_matcher_fallback/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
Based on https://github.com/demskie/netparser
MIT License - Copyright (c) 2019 alex
"""

from .shared import parse_base_network, sort_networks, summarize_sorted_networks
from .sort import binary_search_for_insertion_index


class IPMatcher:
def __init__(self, networks=None):
self.sorted = []
if networks is not None:
subnets = []
for s in networks:
net = parse_base_network(s, False)
if net and net.is_valid():
subnets.append(net)
sort_networks(subnets)
self.sorted = summarize_sorted_networks(subnets)

def has(self, network):
"""
Checks if the given IP address is in the list of networks.
"""
net = parse_base_network(network, False)
if not net or not net.is_valid():
return False
idx = binary_search_for_insertion_index(net, self.sorted)
if idx < 0:
return False
if idx < len(self.sorted) and self.sorted[idx].contains(net):
return True
if idx - 1 >= 0 and self.sorted[idx - 1].contains(net):
return True
return False

def add(self, network):
net = parse_base_network(network, False)
if not net or not net.is_valid():
return self
idx = binary_search_for_insertion_index(net, self.sorted)
if idx < len(self.sorted) and self.sorted[idx].compare(net) == 0:
return self
self.sorted.insert(idx, net)
return self

def is_empty(self):
return len(self.sorted) == 0
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
MIT License - Copyright (c) 2019 alex
"""

from aikido_zen.helpers.ip_matcher import parse
from aikido_zen.helpers.ip_matcher_fallback import parse

# Constants
BEFORE = -1
Expand Down
Loading
Loading