flow.record 3.19.dev7__tar.gz → 3.19.dev8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/PKG-INFO +1 -1
  2. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/fieldtypes/net/__init__.py +8 -1
  3. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/fieldtypes/net/ip.py +58 -6
  4. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/jsonpacker.py +1 -1
  5. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/version.py +2 -2
  6. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/whitelist.py +1 -0
  7. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow.record.egg-info/PKG-INFO +1 -1
  8. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_fieldtype_ip.py +79 -0
  9. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/.git-blame-ignore-revs +0 -0
  10. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/COPYRIGHT +0 -0
  11. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/LICENSE +0 -0
  12. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/MANIFEST.in +0 -0
  13. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/README.md +0 -0
  14. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/examples/filesystem.py +0 -0
  15. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/examples/passivedns.py +0 -0
  16. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/examples/records.json +0 -0
  17. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/examples/tcpconn.py +0 -0
  18. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/__init__.py +0 -0
  19. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/adapter/__init__.py +0 -0
  20. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/adapter/archive.py +0 -0
  21. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/adapter/avro.py +0 -0
  22. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/adapter/broker.py +0 -0
  23. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/adapter/csvfile.py +0 -0
  24. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/adapter/duckdb.py +0 -0
  25. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/adapter/elastic.py +0 -0
  26. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/adapter/jsonfile.py +0 -0
  27. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/adapter/line.py +0 -0
  28. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/adapter/mongo.py +0 -0
  29. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/adapter/split.py +0 -0
  30. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/adapter/splunk.py +0 -0
  31. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/adapter/sqlite.py +0 -0
  32. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/adapter/stream.py +0 -0
  33. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/adapter/text.py +0 -0
  34. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/adapter/xlsx.py +0 -0
  35. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/base.py +0 -0
  36. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/exceptions.py +0 -0
  37. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/fieldtypes/__init__.py +0 -0
  38. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/fieldtypes/credential.py +0 -0
  39. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/fieldtypes/net/ipv4.py +0 -0
  40. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/fieldtypes/net/tcp.py +0 -0
  41. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/fieldtypes/net/udp.py +0 -0
  42. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/packer.py +0 -0
  43. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/selector.py +0 -0
  44. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/stream.py +0 -0
  45. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/tools/__init__.py +0 -0
  46. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/tools/geoip.py +0 -0
  47. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/tools/rdump.py +0 -0
  48. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow/record/utils.py +0 -0
  49. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow.record.egg-info/SOURCES.txt +0 -0
  50. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow.record.egg-info/dependency_links.txt +0 -0
  51. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow.record.egg-info/entry_points.txt +0 -0
  52. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow.record.egg-info/requires.txt +0 -0
  53. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/flow.record.egg-info/top_level.txt +0 -0
  54. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/pyproject.toml +0 -0
  55. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/setup.cfg +0 -0
  56. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/__init__.py +0 -0
  57. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/_utils.py +0 -0
  58. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/docs/Makefile +0 -0
  59. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/docs/conf.py +0 -0
  60. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/docs/index.rst +0 -0
  61. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/selector_explain_example.py +0 -0
  62. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/standalone_test.py +0 -0
  63. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_adapter_line.py +0 -0
  64. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_adapter_text.py +0 -0
  65. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_avro.py +0 -0
  66. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_avro_adapter.py +0 -0
  67. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_compiled_selector.py +0 -0
  68. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_csv_adapter.py +0 -0
  69. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_deprecations.py +0 -0
  70. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_elastic_adapter.py +0 -0
  71. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_fieldtypes.py +0 -0
  72. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_json_packer.py +0 -0
  73. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_json_record_adapter.py +0 -0
  74. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_multi_timestamp.py +0 -0
  75. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_packer.py +0 -0
  76. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_rdump.py +0 -0
  77. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_record.py +0 -0
  78. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_record_adapter.py +0 -0
  79. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_record_descriptor.py +0 -0
  80. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_regression.py +0 -0
  81. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_selector.py +0 -0
  82. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_splunk_adapter.py +0 -0
  83. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_sqlite_duckdb_adapter.py +0 -0
  84. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tests/test_xlsx_adapter.py +0 -0
  85. {flow_record-3.19.dev7 → flow_record-3.19.dev8}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: flow.record
3
- Version: 3.19.dev7
3
+ Version: 3.19.dev8
4
4
  Summary: A library for defining and creating structured data (called records) that can be streamed to disk or piped to other tools that use flow.record
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Affero General Public License v3
@@ -1,12 +1,19 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from flow.record.fieldtypes import string
4
- from flow.record.fieldtypes.net.ip import IPAddress, IPNetwork, ipaddress, ipnetwork
4
+ from flow.record.fieldtypes.net.ip import (
5
+ IPAddress,
6
+ IPNetwork,
7
+ ipaddress,
8
+ ipinterface,
9
+ ipnetwork,
10
+ )
5
11
 
6
12
  __all__ = [
7
13
  "IPAddress",
8
14
  "IPNetwork",
9
15
  "ipaddress",
16
+ "ipinterface",
10
17
  "ipnetwork",
11
18
  ]
12
19
 
@@ -2,10 +2,13 @@ from __future__ import annotations
2
2
 
3
3
  from ipaddress import (
4
4
  IPv4Address,
5
+ IPv4Interface,
5
6
  IPv4Network,
6
7
  IPv6Address,
8
+ IPv6Interface,
7
9
  IPv6Network,
8
10
  ip_address,
11
+ ip_interface,
9
12
  ip_network,
10
13
  )
11
14
  from typing import Union
@@ -15,16 +18,19 @@ from flow.record.fieldtypes import defang
15
18
 
16
19
  _IPNetwork = Union[IPv4Network, IPv6Network]
17
20
  _IPAddress = Union[IPv4Address, IPv6Address]
21
+ _IPInterface = Union[IPv4Interface, IPv6Interface]
22
+ _ConversionTypes = Union[str, int, bytes]
23
+ _IPTypes = Union[_IPNetwork, _IPAddress, _IPInterface]
18
24
 
19
25
 
20
26
  class ipaddress(FieldType):
21
- val = None
27
+ val: _IPAddress = None
22
28
  _type = "net.ipaddress"
23
29
 
24
- def __init__(self, addr: str | int | bytes):
30
+ def __init__(self, addr: _ConversionTypes | _IPAddress):
25
31
  self.val = ip_address(addr)
26
32
 
27
- def __eq__(self, b: str | int | bytes | _IPAddress) -> bool:
33
+ def __eq__(self, b: _ConversionTypes | _IPAddress) -> bool:
28
34
  try:
29
35
  return self.val == ip_address(b)
30
36
  except ValueError:
@@ -53,13 +59,13 @@ class ipaddress(FieldType):
53
59
 
54
60
 
55
61
  class ipnetwork(FieldType):
56
- val = None
62
+ val: _IPNetwork = None
57
63
  _type = "net.ipnetwork"
58
64
 
59
- def __init__(self, addr: str | int | bytes):
65
+ def __init__(self, addr: _ConversionTypes | _IPNetwork):
60
66
  self.val = ip_network(addr)
61
67
 
62
- def __eq__(self, b: str | int | bytes | _IPNetwork) -> bool:
68
+ def __eq__(self, b: _ConversionTypes | _IPNetwork) -> bool:
63
69
  try:
64
70
  return self.val == ip_network(b)
65
71
  except ValueError:
@@ -98,6 +104,52 @@ class ipnetwork(FieldType):
98
104
  def _unpack(data: str) -> ipnetwork:
99
105
  return ipnetwork(data)
100
106
 
107
+ @property
108
+ def netmask(self) -> ipaddress:
109
+ return ipaddress(self.val.netmask)
110
+
111
+
112
+ class ipinterface(FieldType):
113
+ val: _IPInterface = None
114
+ _type = "net.ipinterface"
115
+
116
+ def __init__(self, addr: _ConversionTypes | _IPTypes) -> None:
117
+ self.val = ip_interface(addr)
118
+
119
+ def __eq__(self, b: _ConversionTypes | _IPTypes) -> bool:
120
+ try:
121
+ return self.val == ip_interface(b)
122
+ except ValueError:
123
+ return False
124
+
125
+ def __hash__(self) -> int:
126
+ return hash(self.val)
127
+
128
+ def __str__(self) -> str:
129
+ return str(self.val)
130
+
131
+ def __repr__(self) -> str:
132
+ return f"{self._type}({str(self)!r})"
133
+
134
+ @property
135
+ def ip(self) -> ipaddress:
136
+ return ipaddress(self.val.ip)
137
+
138
+ @property
139
+ def network(self) -> ipnetwork:
140
+ return ipnetwork(self.val.network)
141
+
142
+ @property
143
+ def netmask(self) -> ipaddress:
144
+ return ipaddress(self.val.netmask)
145
+
146
+ def _pack(self) -> str:
147
+ return self.val.compressed
148
+
149
+ @staticmethod
150
+ def _unpack(data: str) -> ipinterface:
151
+ return ipinterface(data)
152
+
101
153
 
102
154
  # alias: net.IPAddress -> net.ipaddress
103
155
  # alias: net.IPNetwork -> net.ipnetwork
@@ -68,7 +68,7 @@ class JsonRecordPacker:
68
68
  "sha1": obj.sha1,
69
69
  "sha256": obj.sha256,
70
70
  }
71
- if isinstance(obj, (fieldtypes.net.ipaddress, fieldtypes.net.ipnetwork)):
71
+ if isinstance(obj, (fieldtypes.net.ipaddress, fieldtypes.net.ipnetwork, fieldtypes.net.ipinterface)):
72
72
  return str(obj)
73
73
  if isinstance(obj, bytes):
74
74
  return base64.b64encode(obj).decode()
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '3.19.dev7'
16
- __version_tuple__ = version_tuple = (3, 19, 'dev7')
15
+ __version__ = version = '3.19.dev8'
16
+ __version_tuple__ = version_tuple = (3, 19, 'dev8')
@@ -24,6 +24,7 @@ WHITELIST = [
24
24
  "bytes",
25
25
  "record",
26
26
  "net.ipaddress",
27
+ "net.ipinterface",
27
28
  "net.ipnetwork",
28
29
  "net.IPAddress",
29
30
  "net.IPNetwork",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: flow.record
3
- Version: 3.19.dev7
3
+ Version: 3.19.dev8
4
4
  Summary: A library for defining and creating structured data (called records) that can be streamed to disk or piped to other tools that use flow.record
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Affero General Public License v3
@@ -128,6 +128,85 @@ def test_record_ipnetwork() -> None:
128
128
  assert "::1" not in data
129
129
 
130
130
 
131
+ def test_record_ipinterface() -> None:
132
+ TestRecord = RecordDescriptor(
133
+ "test/ipinterface",
134
+ [
135
+ ("net.ipinterface", "interface"),
136
+ ],
137
+ )
138
+
139
+ # ipv4
140
+ r = TestRecord("192.168.0.0/24")
141
+ assert r.interface == "192.168.0.0/24"
142
+ assert "bad.ip" not in r.interface.network
143
+ assert "192.168.0.1" in r.interface.network
144
+ assert isinstance(r.interface, net.ipinterface)
145
+ assert repr(r.interface) == "net.ipinterface('192.168.0.0/24')"
146
+ assert hash(r.interface) == hash(net.ipinterface("192.168.0.0/24"))
147
+
148
+ r = TestRecord("192.168.1.1")
149
+ assert r.interface.ip == "192.168.1.1"
150
+ assert r.interface.network == "192.168.1.1/32"
151
+ assert r.interface == "192.168.1.1/32"
152
+ assert r.interface.netmask == "255.255.255.255"
153
+
154
+ r = TestRecord("192.168.1.24/255.255.255.0")
155
+ assert r.interface == "192.168.1.24/24"
156
+ assert r.interface.ip == "192.168.1.24"
157
+ assert r.interface.network == "192.168.1.0/24"
158
+ assert r.interface.netmask == "255.255.255.0"
159
+
160
+ # ipv6 - https://en.wikipedia.org/wiki/IPv6_address
161
+ r = TestRecord("::1")
162
+ assert r.interface == "::1"
163
+ assert r.interface == "::1/128"
164
+
165
+ r = TestRecord("64:ff9b::2/96")
166
+ assert r.interface == "64:ff9b::2/96"
167
+ assert r.interface.ip == "64:ff9b::2"
168
+ assert r.interface.network == "64:ff9b::/96"
169
+ assert r.interface.netmask == "ffff:ffff:ffff:ffff:ffff:ffff::"
170
+
171
+ # instantiate from different types
172
+ assert TestRecord(1).interface == "0.0.0.1/32"
173
+ assert TestRecord(0x7F0000FF).interface == "127.0.0.255/32"
174
+ assert TestRecord(b"\x7f\xff\xff\xff").interface == "127.255.255.255/32"
175
+
176
+ # Test whether it functions in a set
177
+ data = {TestRecord(x).interface for x in ["192.168.0.0/24", "192.168.0.0/24", "::1", "::1"]}
178
+ assert len(data) == 2
179
+ assert net.ipinterface("::1") in data
180
+ assert net.ipinterface("192.168.0.0/24") in data
181
+ assert "::1" not in data
182
+
183
+
184
+ def test_record_ipinterface_types() -> None:
185
+ TestRecord = RecordDescriptor(
186
+ "test/ipinterface",
187
+ [
188
+ (
189
+ "net.ipinterface",
190
+ "interface",
191
+ )
192
+ ],
193
+ )
194
+
195
+ r = TestRecord("192.168.0.255/24")
196
+ _if = r.interface
197
+ assert isinstance(_if, net.ipinterface)
198
+ assert isinstance(_if.ip, net.ipaddress)
199
+ assert isinstance(_if.network, net.ipnetwork)
200
+ assert isinstance(_if.netmask, net.ipaddress)
201
+
202
+ r = TestRecord("64:ff9b::/96")
203
+ _if = r.interface
204
+ assert isinstance(_if, net.ipinterface)
205
+ assert isinstance(_if.ip, net.ipaddress)
206
+ assert isinstance(_if.network, net.ipnetwork)
207
+ assert isinstance(_if.netmask, net.ipaddress)
208
+
209
+
131
210
  @pytest.mark.parametrize("PSelector", [Selector, CompiledSelector])
132
211
  def test_selector_ipaddress(PSelector: type[Selector]) -> None:
133
212
  TestRecord = RecordDescriptor(
File without changes
File without changes