flow.record 3.15.dev7__tar.gz → 3.15.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.
- {flow.record-3.15.dev7/flow.record.egg-info → flow.record-3.15.dev8}/PKG-INFO +1 -1
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/__init__.py +4 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/base.py +34 -3
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/version.py +2 -2
- {flow.record-3.15.dev7 → flow.record-3.15.dev8/flow.record.egg-info}/PKG-INFO +1 -1
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_record.py +65 -1
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/COPYRIGHT +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/LICENSE +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/MANIFEST.in +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/README.md +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/examples/filesystem.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/examples/passivedns.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/examples/records.json +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/examples/tcpconn.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/__init__.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/archive.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/avro.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/broker.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/csvfile.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/duckdb.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/elastic.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/jsonfile.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/line.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/mongo.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/split.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/splunk.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/sqlite.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/stream.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/text.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/xlsx.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/exceptions.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/fieldtypes/__init__.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/fieldtypes/credential.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/fieldtypes/net/__init__.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/fieldtypes/net/ip.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/fieldtypes/net/ipv4.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/fieldtypes/net/tcp.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/fieldtypes/net/udp.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/jsonpacker.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/packer.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/selector.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/stream.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/tools/__init__.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/tools/geoip.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/tools/rdump.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/utils.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/whitelist.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow.record.egg-info/SOURCES.txt +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow.record.egg-info/dependency_links.txt +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow.record.egg-info/entry_points.txt +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow.record.egg-info/requires.txt +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow.record.egg-info/top_level.txt +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/pyproject.toml +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/setup.cfg +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/__init__.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/_utils.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/docs/Makefile +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/docs/conf.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/docs/index.rst +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/selector_explain_example.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/standalone_test.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_avro.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_avro_adapter.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_compiled_selector.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_csv_adapter.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_deprecations.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_fieldtype_ip.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_fieldtypes.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_json_packer.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_json_record_adapter.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_multi_timestamp.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_packer.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_rdump.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_record_adapter.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_record_descriptor.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_regression.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_selector.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_splunk_adapter.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_sqlite_duckdb_adapter.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/utils_inspect.py +0 -0
- {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flow.record
|
|
3
|
-
Version: 3.15.
|
|
3
|
+
Version: 3.15.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
|
|
@@ -2,6 +2,7 @@ import gzip
|
|
|
2
2
|
import os
|
|
3
3
|
|
|
4
4
|
from flow.record.base import (
|
|
5
|
+
IGNORE_FIELDS_FOR_COMPARISON,
|
|
5
6
|
RECORD_VERSION,
|
|
6
7
|
RECORDSTREAM_MAGIC,
|
|
7
8
|
DynamicDescriptor,
|
|
@@ -20,6 +21,7 @@ from flow.record.base import (
|
|
|
20
21
|
open_path,
|
|
21
22
|
open_path_or_stream,
|
|
22
23
|
open_stream,
|
|
24
|
+
set_ignored_fields_for_comparison,
|
|
23
25
|
stream,
|
|
24
26
|
)
|
|
25
27
|
from flow.record.jsonpacker import JsonRecordPacker
|
|
@@ -35,6 +37,7 @@ from flow.record.stream import (
|
|
|
35
37
|
)
|
|
36
38
|
|
|
37
39
|
__all__ = [
|
|
40
|
+
"IGNORE_FIELDS_FOR_COMPARISON",
|
|
38
41
|
"RECORD_VERSION",
|
|
39
42
|
"RECORDSTREAM_MAGIC",
|
|
40
43
|
"FieldType",
|
|
@@ -54,6 +57,7 @@ __all__ = [
|
|
|
54
57
|
"open_path_or_stream",
|
|
55
58
|
"open_path",
|
|
56
59
|
"open_stream",
|
|
60
|
+
"set_ignored_fields_for_comparison",
|
|
57
61
|
"stream",
|
|
58
62
|
"dynamic_fieldtype",
|
|
59
63
|
"DynamicDescriptor",
|
|
@@ -15,7 +15,17 @@ import warnings
|
|
|
15
15
|
from datetime import datetime, timezone
|
|
16
16
|
from itertools import zip_longest
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
from typing import
|
|
18
|
+
from typing import (
|
|
19
|
+
IO,
|
|
20
|
+
Any,
|
|
21
|
+
BinaryIO,
|
|
22
|
+
Iterable,
|
|
23
|
+
Iterator,
|
|
24
|
+
Mapping,
|
|
25
|
+
Optional,
|
|
26
|
+
Sequence,
|
|
27
|
+
Union,
|
|
28
|
+
)
|
|
19
29
|
from urllib.parse import parse_qsl, urlparse
|
|
20
30
|
|
|
21
31
|
from flow.record.adapter import AbstractReader, AbstractWriter
|
|
@@ -96,6 +106,18 @@ class {name}(Record):
|
|
|
96
106
|
"""
|
|
97
107
|
|
|
98
108
|
|
|
109
|
+
if env_excluded_fields := os.environ.get("FLOW_RECORD_IGNORE"):
|
|
110
|
+
IGNORE_FIELDS_FOR_COMPARISON = set(env_excluded_fields.split(","))
|
|
111
|
+
else:
|
|
112
|
+
IGNORE_FIELDS_FOR_COMPARISON = set()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def set_ignored_fields_for_comparison(ignored_fields: Iterable[str]) -> None:
|
|
116
|
+
"""Can be used to update the IGNORE_FIELDS_FOR_COMPARISON from outside the flow.record package scope"""
|
|
117
|
+
global IGNORE_FIELDS_FOR_COMPARISON
|
|
118
|
+
IGNORE_FIELDS_FOR_COMPARISON = set(ignored_fields)
|
|
119
|
+
|
|
120
|
+
|
|
99
121
|
class FieldType:
|
|
100
122
|
def _typename(self):
|
|
101
123
|
t = type(self)
|
|
@@ -117,14 +139,20 @@ class Record:
|
|
|
117
139
|
def __eq__(self, other):
|
|
118
140
|
if not isinstance(other, Record):
|
|
119
141
|
return False
|
|
120
|
-
return self._pack() == other._pack()
|
|
121
142
|
|
|
122
|
-
|
|
143
|
+
return self._pack(excluded_fields=IGNORE_FIELDS_FOR_COMPARISON) == other._pack(
|
|
144
|
+
excluded_fields=IGNORE_FIELDS_FOR_COMPARISON
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
def _pack(self, unversioned=False, excluded_fields: list = None):
|
|
123
148
|
values = []
|
|
124
149
|
for k in self.__slots__:
|
|
125
150
|
v = getattr(self, k)
|
|
126
151
|
v = v._pack() if isinstance(v, FieldType) else v
|
|
127
152
|
|
|
153
|
+
if excluded_fields and k in excluded_fields:
|
|
154
|
+
continue
|
|
155
|
+
|
|
128
156
|
# Skip version field if requested (only for compatibility reasons)
|
|
129
157
|
if unversioned and k == "_version" and v == 1:
|
|
130
158
|
continue
|
|
@@ -160,6 +188,9 @@ class Record:
|
|
|
160
188
|
raise ValueError("Got unexpected field names: {kwds!r}".format(kwds=list(kwds)))
|
|
161
189
|
return result
|
|
162
190
|
|
|
191
|
+
def __hash__(self) -> int:
|
|
192
|
+
return hash(self._pack(excluded_fields=IGNORE_FIELDS_FOR_COMPARISON))
|
|
193
|
+
|
|
163
194
|
def __repr__(self):
|
|
164
195
|
return "<{} {}>".format(
|
|
165
196
|
self._desc.name, " ".join("{}={!r}".format(k, getattr(self, k)) for k in self._desc.fields)
|
|
@@ -12,5 +12,5 @@ __version__: str
|
|
|
12
12
|
__version_tuple__: VERSION_TUPLE
|
|
13
13
|
version_tuple: VERSION_TUPLE
|
|
14
14
|
|
|
15
|
-
__version__ = version = '3.15.
|
|
16
|
-
__version_tuple__ = version_tuple = (3, 15, '
|
|
15
|
+
__version__ = version = '3.15.dev8'
|
|
16
|
+
__version_tuple__ = version_tuple = (3, 15, 'dev8')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flow.record
|
|
3
|
-
Version: 3.15.
|
|
3
|
+
Version: 3.15.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,4 +1,7 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import os
|
|
1
3
|
import sys
|
|
4
|
+
from unittest.mock import patch
|
|
2
5
|
|
|
3
6
|
import pytest
|
|
4
7
|
|
|
@@ -15,7 +18,11 @@ from flow.record import (
|
|
|
15
18
|
fieldtypes,
|
|
16
19
|
record_stream,
|
|
17
20
|
)
|
|
18
|
-
from flow.record.base import
|
|
21
|
+
from flow.record.base import (
|
|
22
|
+
merge_record_descriptors,
|
|
23
|
+
normalize_fieldname,
|
|
24
|
+
set_ignored_fields_for_comparison,
|
|
25
|
+
)
|
|
19
26
|
from flow.record.exceptions import RecordDescriptorError
|
|
20
27
|
from flow.record.stream import RecordFieldRewriter
|
|
21
28
|
|
|
@@ -792,3 +799,60 @@ def test_normalize_fieldname():
|
|
|
792
799
|
assert normalize_fieldname("my name (with) parentheses") == "my_name__with__parentheses"
|
|
793
800
|
assert normalize_fieldname("_generated") == "_generated"
|
|
794
801
|
assert normalize_fieldname("_source") == "_source"
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
def test_compare_global_variable():
|
|
805
|
+
TestRecord = RecordDescriptor(
|
|
806
|
+
"test/record",
|
|
807
|
+
[
|
|
808
|
+
("string", "firstname"),
|
|
809
|
+
("string", "lastname"),
|
|
810
|
+
],
|
|
811
|
+
)
|
|
812
|
+
|
|
813
|
+
same_same = TestRecord(firstname="James", lastname="Bond")
|
|
814
|
+
but_different = TestRecord(firstname="Ethan", lastname="Hunt")
|
|
815
|
+
but_still_same = TestRecord(firstname="Andrew", lastname="Bond")
|
|
816
|
+
|
|
817
|
+
records = [same_same, but_different, but_still_same]
|
|
818
|
+
|
|
819
|
+
assert same_same != but_still_same
|
|
820
|
+
|
|
821
|
+
set_ignored_fields_for_comparison({"_generated", "firstname"})
|
|
822
|
+
assert same_same == but_still_same
|
|
823
|
+
assert same_same != but_different
|
|
824
|
+
assert len(set(records)) == 2
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
def test_compare_environment_variable():
|
|
828
|
+
with patch.dict(os.environ), patch.dict(sys.modules):
|
|
829
|
+
os.environ["FLOW_RECORD_IGNORE"] = "_generated,lastname"
|
|
830
|
+
|
|
831
|
+
# Force a re-import of flow.record so the global variable gets re-initialized based on the environment variable
|
|
832
|
+
keys = [key for key in sys.modules if key == "flow" or "flow." in key]
|
|
833
|
+
for key in keys:
|
|
834
|
+
del sys.modules[key]
|
|
835
|
+
|
|
836
|
+
importlib.import_module("flow.record")
|
|
837
|
+
|
|
838
|
+
from flow.record import IGNORE_FIELDS_FOR_COMPARISON, RecordDescriptor
|
|
839
|
+
|
|
840
|
+
assert IGNORE_FIELDS_FOR_COMPARISON == {"_generated", "lastname"}
|
|
841
|
+
|
|
842
|
+
TestRecord = RecordDescriptor(
|
|
843
|
+
"test/record",
|
|
844
|
+
[
|
|
845
|
+
("string", "firstname"),
|
|
846
|
+
("string", "lastname"),
|
|
847
|
+
],
|
|
848
|
+
)
|
|
849
|
+
|
|
850
|
+
same_same = TestRecord(firstname="John", lastname="Rambo")
|
|
851
|
+
but_different = TestRecord(firstname="Johnny", lastname="English")
|
|
852
|
+
but_still_same = TestRecord(firstname="John", lastname="McClane")
|
|
853
|
+
|
|
854
|
+
records = [same_same, but_different, but_still_same]
|
|
855
|
+
|
|
856
|
+
assert same_same == but_still_same
|
|
857
|
+
assert same_same != but_different
|
|
858
|
+
assert len(set(records)) == 2
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|