Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

karton-core

Package Overview
Dependencies
Maintainers
1
Versions
31
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

karton-core - npm Package Compare versions

Comparing version
5.4.0
to
5.5.0
+1
karton_core-5.5.0-nspkg.pth
import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('karton',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('karton', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('karton', [os.path.dirname(p)])));m = m or sys.modules.setdefault('karton', types.ModuleType('karton'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p)
import fnmatch
import re
from collections.abc import Mapping, Sequence
from typing import Dict, Type
# Source code adopted from https://github.com/kapouille/mongoquery
# Original licenced under "The Unlicense" license.
class QueryError(Exception):
"""Query error exception"""
pass
class _Undefined(object):
pass
def is_non_string_sequence(entry):
"""Returns True if entry is a Python sequence iterable, and not a string"""
return isinstance(entry, Sequence) and not isinstance(entry, str)
class Query(object):
"""The Query class is used to match an object against a MongoDB-like query"""
def __init__(self, definition):
self._definition = definition
def match(self, entry):
"""Matches the entry object against the query specified on instanciation"""
return self._match(self._definition, entry)
def _match(self, condition, entry):
if isinstance(condition, Mapping):
return all(
self._process_condition(sub_operator, sub_condition, entry)
for sub_operator, sub_condition in condition.items()
)
if is_non_string_sequence(entry):
return condition in entry
return condition == entry
def _extract(self, entry, path):
if not path:
return entry
if entry is None:
return entry
if is_non_string_sequence(entry):
try:
index = int(path[0])
return self._extract(entry[index], path[1:])
except ValueError:
return [self._extract(item, path) for item in entry]
elif isinstance(entry, Mapping) and path[0] in entry:
return self._extract(entry[path[0]], path[1:])
else:
return _Undefined()
def _path_exists(self, operator, condition, entry):
keys_list = list(operator.split("."))
for i, k in enumerate(keys_list):
if isinstance(entry, Sequence) and not k.isdigit():
for elem in entry:
operator = ".".join(keys_list[i:])
if self._path_exists(operator, condition, elem) == condition:
return condition
return not condition
elif isinstance(entry, Sequence):
k = int(k)
try:
entry = entry[k]
except (TypeError, IndexError, KeyError):
return not condition
return condition
def _process_condition(self, operator, condition, entry):
if isinstance(condition, Mapping) and "$exists" in condition:
if isinstance(operator, str) and operator.find(".") != -1:
return self._path_exists(operator, condition["$exists"], entry)
elif condition["$exists"] != (operator in entry):
return False
elif tuple(condition.keys()) == ("$exists",):
return True
if isinstance(operator, str):
if operator.startswith("$"):
try:
return getattr(self, "_" + operator[1:])(condition, entry)
except AttributeError:
raise QueryError(f"{operator} operator isn't supported")
else:
try:
extracted_data = self._extract(entry, operator.split("."))
except IndexError:
extracted_data = _Undefined()
else:
if operator not in entry:
return False
extracted_data = entry[operator]
return self._match(condition, extracted_data)
@staticmethod
def _not_implemented(*_):
raise NotImplementedError
@staticmethod
def _noop(*_):
return True
@staticmethod
def _eq(condition, entry):
try:
return entry == condition
except TypeError:
return False
@staticmethod
def _gt(condition, entry):
try:
return entry > condition
except TypeError:
return False
@staticmethod
def _gte(condition, entry):
try:
return entry >= condition
except TypeError:
return False
@staticmethod
def _in(condition, entry):
if is_non_string_sequence(condition):
for elem in condition:
if is_non_string_sequence(entry) and elem in entry:
return True
elif not is_non_string_sequence(entry) and elem == entry:
return True
return False
else:
raise TypeError("condition must be a list")
@staticmethod
def _lt(condition, entry):
try:
return entry < condition
except TypeError:
return False
@staticmethod
def _lte(condition, entry):
try:
return entry <= condition
except TypeError:
return False
@staticmethod
def _ne(condition, entry):
return entry != condition
def _nin(self, condition, entry):
return not self._in(condition, entry)
def _and(self, condition, entry):
if isinstance(condition, Sequence):
return all(self._match(sub_condition, entry) for sub_condition in condition)
raise QueryError(f"$and has been attributed incorrect argument {condition}")
def _nor(self, condition, entry):
if isinstance(condition, Sequence):
return all(
not self._match(sub_condition, entry) for sub_condition in condition
)
raise QueryError(f"$nor has been attributed incorrect argument {condition}")
def _not(self, condition, entry):
return not self._match(condition, entry)
def _or(self, condition, entry):
if isinstance(condition, Sequence):
return any(self._match(sub_condition, entry) for sub_condition in condition)
raise QueryError(f"$or has been attributed incorrect argument {condition}")
@staticmethod
def _type(condition, entry):
bson_type: Dict[int, Type] = {
1: float,
2: str,
3: Mapping,
4: Sequence,
5: bytearray,
7: str, # object id (uuid)
8: bool,
9: str, # date (UTC datetime)
10: type(None),
11: re.Pattern, # regex,
13: str, # Javascript
15: str, # JavaScript (with scope)
16: int, # 32-bit integer
17: int, # Timestamp
18: int, # 64-bit integer
}
bson_alias = {
"double": 1,
"string": 2,
"object": 3,
"array": 4,
"binData": 5,
"objectId": 7,
"bool": 8,
"date": 9,
"null": 10,
"regex": 11,
"javascript": 13,
"javascriptWithScope": 15,
"int": 16,
"timestamp": 17,
"long": 18,
}
if condition == "number":
return any(
[
isinstance(entry, bson_type[bson_alias[alias]])
for alias in ["double", "int", "long"]
]
)
# resolves bson alias, or keeps original condition value
condition = bson_alias.get(condition, condition)
if condition not in bson_type:
raise QueryError(f"$type has been used with unknown type {condition}")
return isinstance(entry, bson_type[condition])
_exists = _noop
@staticmethod
def _mod(condition, entry):
return entry % condition[0] == condition[1]
@staticmethod
def _regex(condition, entry):
if not isinstance(entry, str):
return False
# If the caller has supplied a compiled regex, assume options are already
# included.
if isinstance(condition, re.Pattern):
return bool(re.search(condition, entry))
try:
regex = re.match(r"\A/(.+)/([imsx]{,4})\Z", condition, flags=re.DOTALL)
except TypeError:
raise QueryError(
f"{condition} is not a regular expression and should be a string"
)
flags = 0
if regex:
options = regex.group(2)
for option in options:
flags |= getattr(re, option.upper())
exp = regex.group(1)
else:
exp = condition
try:
match = re.search(exp, entry, flags=flags)
except Exception as error:
raise QueryError(f"{condition} failed to execute with error {error!r}")
return bool(match)
_options = _text = _where = _not_implemented
def _all(self, condition, entry):
return all(self._match(item, entry) for item in condition)
def _elemMatch(self, condition, entry):
if not isinstance(entry, Sequence):
return False
return any(
all(
self._process_condition(sub_operator, sub_condition, element)
for sub_operator, sub_condition in condition.items()
)
for element in entry
)
@staticmethod
def _size(condition, entry):
if not isinstance(condition, int):
raise QueryError(
f"$size has been attributed incorrect argument {condition}"
)
if is_non_string_sequence(entry):
return len(entry) == condition
return False
def __repr__(self):
return f"<Query({self._definition})>"
def toregex(wildcard):
if not isinstance(wildcard, str):
raise QueryError(f"Unexpected value in the regex conversion: {wildcard}")
# If is not neessary, but we avoid unnecessary regular expressions.
if any(c in wildcard for c in "?*[]!"):
return {"$regex": fnmatch.translate(wildcard)}
return wildcard
def convert(filters):
"""Convert filters to the mongo query syntax.
A special care is taken to handle old-style negative filters correctly
"""
# Negative_filters are old-style negative assertions, and behave differently.
# See issue #246 for the original bug report.
#
# For a short example:
# [{"platform": "!win32"}, {"platform": "!linux"}]
# will match all non-linux non-windows samples, but:
# [{"platform": {"$not": "win32"}}, {"platform": {"$not": "linux"}}]
# means `platform != "win32" or "platform != "linux"` and will match everything.
# To get equivalent behaviour with mongo syntax, you should use:
# [{"platform": {"$not": {"$or": ["win32", "linux"]}}}]
regular_filter, negative_filter = [], []
for rule in filters:
positive_checks, negative_checks = [], []
for key, value in rule.items():
if isinstance(value, str):
if value and value[0] == "!": # negative check
negative_checks.append({key: toregex(value[1:])})
else:
positive_checks.append({key: toregex(value)})
else:
positive_checks.append({key: value})
regular_filter.append({"$and": positive_checks})
negative_filter.append({"$and": positive_checks + [{"$or": negative_checks}]})
return Query(
{
"$and": [
{"$not": {"$or": negative_filter}},
{"$or": regular_filter},
]
}
)
+1
-1

@@ -1,1 +0,1 @@

__version__ = "5.4.0"
__version__ = "5.5.0"

@@ -11,2 +11,3 @@ """

from . import query
from .__version__ import __version__

@@ -126,2 +127,5 @@ from .backend import KartonBackend, KartonBind, KartonMetrics

# Dummy conversion to make sure the filters are well-formed.
query.convert(self.filters)
self.persistent = (

@@ -128,0 +132,0 @@ self.config.getboolean("karton", "persistent", self.persistent)

import enum
import fnmatch
import json

@@ -19,2 +18,3 @@ import time

from . import query
from .resource import RemoteResource, ResourceBase

@@ -227,72 +227,5 @@ from .utils import recursive_iter, recursive_iter_with_keys, recursive_map

def matches_filters(self, filters: List[Dict[str, Any]]) -> bool:
"""
Checks whether provided task headers match filters
"""Check if a task matches the given filters"""
return query.convert(filters).match(self.headers)
:param filters: Task header filters
:return: True if task headers match specific filters
:meta private:
"""
def test_filter(headers: Dict[str, Any], filter: Dict[str, Any]) -> int:
"""
Filter match follows AND logic, but it's non-boolean because filters may be
negated (task:!platform).
Result values are as follows:
- 1 - positive match, no mismatched values in headers
(all matched)
- 0 - no match, found value that doesn't match to the filter
(some are not matched)
- -1 - negative match, found value that matches negated filter value
(all matched but found negative matches)
"""
matches = 1
for filter_key, filter_value in filter.items():
# Coerce filter value to string
filter_value_str = str(filter_value)
negated = False
if filter_value_str.startswith("!"):
negated = True
filter_value_str = filter_value_str[1:]
# If expected key doesn't exist in headers
if filter_key not in headers:
# Negated filter ignores non-existent values
if negated:
continue
# But positive filter doesn't
return 0
# Coerce header value to string
header_value_str = str(headers[filter_key])
# fnmatch is great for handling simple wildcard patterns (?, *, [abc])
match = fnmatch.fnmatchcase(header_value_str, filter_value_str)
# If matches, but it's negated: it's negative match
if match and negated:
matches = -1
# If doesn't match but filter is not negated: it's not a match
if not match and not negated:
return 0
# If there are no mismatched values: filter is matched
return matches
# List of filter matches follow OR logic, but -1 is special
# If there is any -1, result is False
# (any matched, but it's negative match)
# If there is any 1, but no -1's: result is True
# (any matched, no negative match)
# If there are only 0's: result is False
# (none matched)
matches = False
for task_filter in filters:
match_result = test_filter(self.headers, task_filter)
if match_result == -1:
# Any negative match results in False
return False
if match_result == 1:
# Any positive match but without negative matches results in True
matches = True
return matches
def set_task_parent(self, parent: "Task"):

@@ -299,0 +232,0 @@ """

@@ -6,2 +6,3 @@ import argparse

from karton.core import query
from karton.core.__version__ import __version__

@@ -179,3 +180,8 @@ from karton.core.backend import (

identity = bind.identity
if task.matches_filters(bind.filters):
try:
is_match = task.matches_filters(bind.filters)
except query.QueryError:
self.log.error("Task matching failed - invalid filters?")
continue
if is_match:
routed_task = task.fork_task()

@@ -182,0 +188,0 @@ routed_task.status = TaskState.SPAWNED

Metadata-Version: 2.1
Name: karton-core
Version: 5.4.0
Version: 5.5.0
Summary: Distributed malware analysis orchestration framework

@@ -5,0 +5,0 @@ Home-page: https://github.com/CERT-Polska/karton

+13
-12

@@ -1,4 +0,4 @@

karton_core-5.4.0-nspkg.pth,sha256=vHa-jm6pBTeInFrmnsHMg9AOeD88czzQy-6QCFbpRcM,539
karton_core-5.5.0-nspkg.pth,sha256=vHa-jm6pBTeInFrmnsHMg9AOeD88czzQy-6QCFbpRcM,539
karton/core/__init__.py,sha256=QuT0BWZyp799eY90tK3H1OD2hwuusqMJq8vQwpB3kG4,337
karton/core/__version__.py,sha256=xjYaBGUFGg0kGZj_WhuoFyPD8NILPsr79SaMwmYQGSg,22
karton/core/__version__.py,sha256=zFTHldBmR5ReiC3uSZ8VkZOEirtsq_l6QbUJYRBHlTs,22
karton/core/backend.py,sha256=-sQG7utnaWLJOEcafeSwEDLnkflPqtSCwg_mn_nnFhg,36727

@@ -9,8 +9,9 @@ karton/core/base.py,sha256=C6Lco3E0XCsxvEjeVOLR9fxh_IWJ1vjC9BqUYsQyewE,8083

karton/core/inspect.py,sha256=aIJQEOEkD5q2xLlV8nhxY5qL5zqcnprP-2DdP6ecKlE,6150
karton/core/karton.py,sha256=9SOAviG42kSsPqc3EuaHzWtA_KywMtc01hmU6FaJpHo,15007
karton/core/karton.py,sha256=cXLleTEPCVBIXkj09kKu2hjd1XNUSpTAk87-BES1WlA,15133
karton/core/logger.py,sha256=J3XAyG88U0cwYC9zR6E3QD1uJenrQh7zS9-HgxhqeAs,2040
karton/core/main.py,sha256=ir1-dhn3vbwfh2YHiM6ZYfRBbjwLvJSz0d8tuK1mb_4,8310
karton/core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
karton/core/query.py,sha256=Ay0VzfrBQwdJzcZ27JbOlUc1ZZdOl6A8sh4iIYTmLyE,11493
karton/core/resource.py,sha256=tA3y_38H9HVKIrCeAU70zHUkQUv0BuCQWMC470JLxxc,20321
karton/core/task.py,sha256=diwg8uUl57NEYNRjT1l5CPiNw3EQcU11BnrLul33fx0,21350
karton/core/task.py,sha256=1E_d60XbzqX0O9gFhYe_8aNGH7vuXDHe-bir5cRot_0,18515
karton/core/test.py,sha256=tms-YM7sUKQDHN0vm2_W7DIvHnO_ld_VPsWHnsbKSfk,9102

@@ -20,9 +21,9 @@ karton/core/utils.py,sha256=sEVqGdVPyYswWuVn8wYXBQmln8Az826N_2HgC__pmW8,4090

karton/system/__main__.py,sha256=QJkwIlSwaPRdzwKlNmCAL41HtDAa73db9MZKWmOfxGM,56
karton/system/system.py,sha256=yF_d71a8w7JYA7IXUt63d5_QBH6x1QplB-xcrzQTXL4,13792
karton_core-5.4.0.dist-info/LICENSE,sha256=o8h7hYhn7BJC_-DmrfqWwLjaR_Gbe0TZOOQJuN2ca3I,1519
karton_core-5.4.0.dist-info/METADATA,sha256=kopeYFCI9EoFQbc7J7woZWjI_5egy29-lYUW7UzEQ2I,6847
karton_core-5.4.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
karton_core-5.4.0.dist-info/entry_points.txt,sha256=FJj5EZuvFP0LkagjX_dLbRGBUnuLjgBiSyiFfq4c86U,99
karton_core-5.4.0.dist-info/namespace_packages.txt,sha256=X8SslCPsqXDCnGZqrYYolzT3xPzJMq1r-ZQSc0jfAEA,7
karton_core-5.4.0.dist-info/top_level.txt,sha256=X8SslCPsqXDCnGZqrYYolzT3xPzJMq1r-ZQSc0jfAEA,7
karton_core-5.4.0.dist-info/RECORD,,
karton/system/system.py,sha256=tptar24RuXUnlII1xKbuJtfNkQsSxTtS3g4O8S99tbg,14011
karton_core-5.5.0.dist-info/LICENSE,sha256=o8h7hYhn7BJC_-DmrfqWwLjaR_Gbe0TZOOQJuN2ca3I,1519
karton_core-5.5.0.dist-info/METADATA,sha256=h4-M_JnMm8z_An5IDFPHAkQ4YuR_-YpwekETiNMjIxQ,6847
karton_core-5.5.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
karton_core-5.5.0.dist-info/entry_points.txt,sha256=FJj5EZuvFP0LkagjX_dLbRGBUnuLjgBiSyiFfq4c86U,99
karton_core-5.5.0.dist-info/namespace_packages.txt,sha256=X8SslCPsqXDCnGZqrYYolzT3xPzJMq1r-ZQSc0jfAEA,7
karton_core-5.5.0.dist-info/top_level.txt,sha256=X8SslCPsqXDCnGZqrYYolzT3xPzJMq1r-ZQSc0jfAEA,7
karton_core-5.5.0.dist-info/RECORD,,
import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('karton',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('karton', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('karton', [os.path.dirname(p)])));m = m or sys.modules.setdefault('karton', types.ModuleType('karton'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p)