clue-api 1.6.0.dev335__tar.gz → 1.6.0.dev337__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.
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/PKG-INFO +1 -1
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/api/v1/lookup.py +8 -2
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/services/lookup_service.py +68 -6
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/pyproject.toml +1 -1
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/LICENSE +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/README.md +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/.gitignore +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/api/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/api/base.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/api/v1/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/api/v1/actions.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/api/v1/auth.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/api/v1/configs.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/api/v1/fetchers.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/api/v1/registration.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/api/v1/static.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/api/v1/sync.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/app.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/cache/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/common/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/common/bytes_utils.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/common/classification.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/common/classification.yml +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/common/dict_utils.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/common/exceptions.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/common/forge.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/common/json_utils.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/common/list_utils.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/common/logging/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/common/logging/audit.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/common/logging/format.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/common/regex.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/common/str_utils.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/common/swagger.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/common/uid.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/config.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/constants/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/constants/env.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/constants/supported_types.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/cronjobs/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/cronjobs/plugins.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/error.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/extensions/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/extensions/config.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/gunicorn_config.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/healthz.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/helper/discover.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/helper/headers.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/helper/oauth.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/actions.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/config.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/fetchers.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/graph.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/model_list.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/network.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/results/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/results/base.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/results/file.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/results/graph.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/results/image.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/results/status.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/results/validation.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/schema.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/selector.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/sync.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/models/validators.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/patched.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/plugin/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/plugin/celery_app.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/plugin/helpers/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/plugin/helpers/central_server.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/plugin/helpers/email_render.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/plugin/helpers/token.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/plugin/helpers/trino.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/plugin/models.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/plugin/utils.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/py.typed +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/remote/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/remote/datatypes/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/remote/datatypes/cache.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/remote/datatypes/events.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/remote/datatypes/hash.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/remote/datatypes/queues/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/remote/datatypes/queues/comms.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/remote/datatypes/set.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/remote/datatypes/user_quota_tracker.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/security/__init__.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/security/obo.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/security/utils.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/services/action_service.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/services/auth_service.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/services/config_service.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/services/fetcher_service.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/services/jwt_service.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/services/mongo_service.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/services/type_service.py +0 -0
- {clue_api-1.6.0.dev335 → clue_api-1.6.0.dev337}/clue/services/user_service.py +0 -0
|
@@ -90,7 +90,10 @@ def bulk_enrich(**kwargs) -> dict[str, dict[str, dict[str, QueryResult]]]:
|
|
|
90
90
|
|
|
91
91
|
Optional Arguments:
|
|
92
92
|
classification: string => Classification of the type [Default: minimum configured classification]
|
|
93
|
-
sources: string => | separated list of data sources.
|
|
93
|
+
sources: string => | separated list of data sources.
|
|
94
|
+
A source prefixed with '-' will be excluded. Exclusion takes precedence over inclusion.
|
|
95
|
+
If sources is empty or only exclusions, all default configured sources are used (with exclusions applied).
|
|
96
|
+
Note, a source list that includes and excludes the same sources (e.g. sources=vt|-vt) is not treated as empty.
|
|
94
97
|
max_timeout: number => Maximum execution time for the call in seconds
|
|
95
98
|
limit: number => limit the amount of returned results counted per source
|
|
96
99
|
no_annotation: boolean => Do not return any anotations
|
|
@@ -171,7 +174,10 @@ def enrich(type_name: str, value: str, **kwargs) -> dict[str, QueryResult]:
|
|
|
171
174
|
|
|
172
175
|
Optional Arguments:
|
|
173
176
|
classification: string => Classification of the type [Default: minimum configured classification]
|
|
174
|
-
sources: string => | separated list of data sources.
|
|
177
|
+
sources: string => | separated list of data sources.
|
|
178
|
+
A source prefixed with '-' will be excluded. Exclusion takes precedence over inclusion.
|
|
179
|
+
If sources is empty or only exclusions, all default configured sources are used (with exclusions applied).
|
|
180
|
+
Note, a source list that includes and excludes the same sources (e.g. sources=vt|-vt) is not treated as empty.
|
|
175
181
|
max_timeout: number => Maximum execution time for the call in seconds
|
|
176
182
|
limit: number => limit the amount of returned results counted per source
|
|
177
183
|
no_annotation: boolean => Do not return any anotations
|
|
@@ -107,6 +107,7 @@ class ParsedParams(BaseModel):
|
|
|
107
107
|
"Validation of parameters parsed from request"
|
|
108
108
|
|
|
109
109
|
query_sources: list[str]
|
|
110
|
+
excluded_sources: list[str]
|
|
110
111
|
max_timeout: float
|
|
111
112
|
limit: int
|
|
112
113
|
type_classification: str
|
|
@@ -153,8 +154,11 @@ def parse_query_params(request: Request, limit: int = 10, timeout: float = 5.0):
|
|
|
153
154
|
else:
|
|
154
155
|
query_sources = []
|
|
155
156
|
|
|
157
|
+
include_sources, exclude_sources = _parse_source_list(query_sources)
|
|
158
|
+
|
|
156
159
|
return ParsedParams(
|
|
157
|
-
query_sources=
|
|
160
|
+
query_sources=include_sources,
|
|
161
|
+
excluded_sources=exclude_sources,
|
|
158
162
|
max_timeout=parse_timeout(timeout),
|
|
159
163
|
limit=limit,
|
|
160
164
|
type_classification=type_classification,
|
|
@@ -456,6 +460,7 @@ def enrich(type_name: str, value: str, user: dict[str, Any]): # noqa: C901
|
|
|
456
460
|
"""
|
|
457
461
|
query_params = parse_query_params(request=request)
|
|
458
462
|
query_sources = query_params.query_sources
|
|
463
|
+
excluded_sources = query_params.excluded_sources
|
|
459
464
|
available_sources = get_sources(user)
|
|
460
465
|
|
|
461
466
|
access_token = request.headers.get("Authorization", type=str)
|
|
@@ -465,7 +470,8 @@ def enrich(type_name: str, value: str, user: dict[str, Any]): # noqa: C901
|
|
|
465
470
|
|
|
466
471
|
logger.debug(
|
|
467
472
|
f"Beginning enrichment for single selector on sources "
|
|
468
|
-
f"[{','.join(query_sources or [source.name for source in available_sources])}]"
|
|
473
|
+
f"[{','.join(query_sources or [source.name for source in available_sources if source.include_default])}] "
|
|
474
|
+
f"excluding sources [{','.join(excluded_sources)}]"
|
|
469
475
|
)
|
|
470
476
|
|
|
471
477
|
results: dict[str, QueryResult] = {}
|
|
@@ -478,7 +484,9 @@ def enrich(type_name: str, value: str, user: dict[str, Any]): # noqa: C901
|
|
|
478
484
|
for source in available_sources:
|
|
479
485
|
if query_sources and source.name not in query_sources:
|
|
480
486
|
continue
|
|
481
|
-
|
|
487
|
+
if excluded_sources and source.name in excluded_sources:
|
|
488
|
+
continue
|
|
489
|
+
if not query_sources and not source.include_default:
|
|
482
490
|
continue
|
|
483
491
|
|
|
484
492
|
finish_result = functools.partial(build_result, type_name, value, source)
|
|
@@ -668,11 +676,13 @@ def bulk_enrich(data: list[Selector], user: dict[str, Any]): # noqa: C901
|
|
|
668
676
|
"""create searches for external sources"""
|
|
669
677
|
query_params = parse_query_params(request=request)
|
|
670
678
|
query_sources = query_params.query_sources
|
|
679
|
+
excluded_sources = query_params.excluded_sources
|
|
671
680
|
available_sources = get_sources(user)
|
|
672
681
|
|
|
673
682
|
logger.debug(
|
|
674
683
|
f"Beginning enrichment for {len(data)} selectors on sources "
|
|
675
|
-
f"[{','.join(query_sources or [source.name for source in available_sources])}]"
|
|
684
|
+
f"[{','.join(query_sources or [source.name for source in available_sources if source.include_default])}] "
|
|
685
|
+
f"excluding sources [{','.join(excluded_sources)}]"
|
|
676
686
|
)
|
|
677
687
|
|
|
678
688
|
access_token = request.headers.get("Authorization", type=str)
|
|
@@ -695,11 +705,30 @@ def bulk_enrich(data: list[Selector], user: dict[str, Any]): # noqa: C901
|
|
|
695
705
|
if config.ui.replication:
|
|
696
706
|
existing_results = mongo_service.existing_results(user["uname"], "selectors", data, available_sources)
|
|
697
707
|
|
|
708
|
+
sources_per_entry: dict[tuple[str, str], tuple[list[str], list[str]]] = {}
|
|
709
|
+
for entry in data:
|
|
710
|
+
if entry.sources is not None:
|
|
711
|
+
entry_key = (entry.type, entry.value)
|
|
712
|
+
if entry_key not in sources_per_entry:
|
|
713
|
+
sources_per_entry[entry_key] = _parse_source_list(entry.sources)
|
|
714
|
+
|
|
715
|
+
else:
|
|
716
|
+
logger.warning("Duplicate sources found for entry %s:%s. Merging sources.", entry.type, entry.value)
|
|
717
|
+
|
|
718
|
+
existing_include_list, existing_exclude_list = sources_per_entry[entry_key]
|
|
719
|
+
include_sources, exclude_sources = _parse_source_list(entry.sources)
|
|
720
|
+
sources_per_entry[entry_key] = (
|
|
721
|
+
list(set(existing_include_list + include_sources)),
|
|
722
|
+
list(set(existing_exclude_list + exclude_sources)),
|
|
723
|
+
)
|
|
724
|
+
|
|
698
725
|
greenlets: list[tuple[list[Selector], ExternalSource, Greenlet[Any, dict[str, dict[str, QueryResult]]]]] = []
|
|
699
726
|
for source in available_sources:
|
|
700
727
|
if query_sources and source.name not in query_sources:
|
|
701
728
|
continue
|
|
702
|
-
|
|
729
|
+
if excluded_sources and source.name in excluded_sources:
|
|
730
|
+
continue
|
|
731
|
+
if not query_sources and not source.include_default:
|
|
703
732
|
continue
|
|
704
733
|
|
|
705
734
|
obo_access_token, error = auth_service.check_obo(source, access_token, user["uname"])
|
|
@@ -720,9 +749,15 @@ def bulk_enrich(data: list[Selector], user: dict[str, Any]): # noqa: C901
|
|
|
720
749
|
# check query against the max supported classification of the external system
|
|
721
750
|
# if this is not supported, we should let the user know.
|
|
722
751
|
for entry in data:
|
|
723
|
-
if entry.sources
|
|
752
|
+
if entry.sources == []: # support old behaviour of empty entry sources -> use no sources
|
|
724
753
|
continue
|
|
725
754
|
|
|
755
|
+
parsed_sources = sources_per_entry.get((entry.type, entry.value))
|
|
756
|
+
if parsed_sources is not None:
|
|
757
|
+
include_sources, exclude_sources = parsed_sources
|
|
758
|
+
if (include_sources and source.name not in include_sources) or source.name in exclude_sources:
|
|
759
|
+
continue
|
|
760
|
+
|
|
726
761
|
if (
|
|
727
762
|
source.name in existing_results
|
|
728
763
|
and {"type": entry.type, "value": entry.value} in existing_results[source.name]
|
|
@@ -802,3 +837,30 @@ def bulk_enrich(data: list[Selector], user: dict[str, Any]): # noqa: C901
|
|
|
802
837
|
thread_pool.kill(block=False)
|
|
803
838
|
|
|
804
839
|
return bulk_result
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
def _parse_source_list(source_list: list[str]) -> tuple[list[str], list[str]]:
|
|
843
|
+
"""Splits the provided source list into include and exclude lists.
|
|
844
|
+
|
|
845
|
+
Args:
|
|
846
|
+
source_list (list[str]): The list of sources to parse.
|
|
847
|
+
|
|
848
|
+
Returns:
|
|
849
|
+
tuple[list[str], list[str]]: (include_sources, exclude_sources)
|
|
850
|
+
The list of sources to include and the list of sources to exclude.
|
|
851
|
+
"""
|
|
852
|
+
include_sources = []
|
|
853
|
+
exclude_sources = []
|
|
854
|
+
|
|
855
|
+
for source in source_list:
|
|
856
|
+
source = source.strip()
|
|
857
|
+
if not source:
|
|
858
|
+
continue
|
|
859
|
+
if source.startswith("-"):
|
|
860
|
+
excluded_source = source[1:].strip()
|
|
861
|
+
if excluded_source:
|
|
862
|
+
exclude_sources.append(excluded_source)
|
|
863
|
+
else:
|
|
864
|
+
include_sources.append(source)
|
|
865
|
+
|
|
866
|
+
return include_sources, exclude_sources
|
|
@@ -141,7 +141,7 @@ log_cli_level = "WARN"
|
|
|
141
141
|
[tool.poetry]
|
|
142
142
|
package-mode = true
|
|
143
143
|
name = "clue-api"
|
|
144
|
-
version = "1.6.0.
|
|
144
|
+
version = "1.6.0.dev337"
|
|
145
145
|
description = "Clue distributed enrichment service"
|
|
146
146
|
authors = ["Canadian Centre for Cyber Security <contact@cyber.gc.ca>"]
|
|
147
147
|
license = "MIT"
|
|
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
|
|
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
|