diff --git a/bbot_server/applets/base.py b/bbot_server/applets/base.py index c214f358..1e9ad901 100644 --- a/bbot_server/applets/base.py +++ b/bbot_server/applets/base.py @@ -372,6 +372,11 @@ def make_activity(self, *args, **kwargs): return Activity(*args, **kwargs) async def emit_activity(self, *args, **kwargs): + """ + Emits an activity to the message queue. + + Accepts either an Activity object, or arguments to create a new Activity object. + """ if not kwargs and len(args) == 1 and isinstance(args[0], Activity): activity = args[0] else: diff --git a/bbot_server/models/asset_models.py b/bbot_server/models/asset_models.py index 6216cdab..c83397ba 100644 --- a/bbot_server/models/asset_models.py +++ b/bbot_server/models/asset_models.py @@ -10,23 +10,14 @@ host_split_regex = re.compile(r"[^a-z0-9]") -class BaseAssetFacet(BaseBBOTServerModel): +class BaseHostModel(BaseBBOTServerModel): """ - An "asset facet" is a database object that contains data about an asset. + A base model for all BBOT Server models that have a host, port, netloc, and url - Unlike the main asset model which contains a summary of all the data, - a facet contains a certain detail which is too big to be stored in the main asset model. - - For example, the main asset might contain a summary of all the technologies found on the asset, - but a facet might contain the specific technologies and details about their discovery. - - A facet typically corresponds to an applet. + Inherited by Asset and Activity models. """ - # unless overridden, all asset facets are stored in the asset store - __store_type__ = "asset" - __table_name__ = "assets" - + # TODO: why is id commented out? # id: Annotated[str, "indexed", "unique"] = Field(default_factory=lambda: str(uuid.uuid4())) type: Annotated[Optional[str], "indexed"] = None host: Annotated[str, "indexed"] @@ -37,10 +28,8 @@ class BaseAssetFacet(BaseBBOTServerModel): modified: Annotated[float, "indexed"] = Field(default_factory=utc_now) ignored: bool = False archived: bool = False - scope: Annotated[list[UUID], "indexed"] = [] def __init__(self, *args, **kwargs): - kwargs["type"] = self.__class__.__name__ event = kwargs.pop("event", None) super().__init__(*args, **kwargs) if self.host and self.port: @@ -68,28 +57,38 @@ def set_event(self, event): @computed_field @property def reverse_host(self) -> Annotated[str, "indexed"]: + if not self.host: + return "" return self.host[::-1] @computed_field @property def host_parts(self) -> Annotated[list[str], "indexed"]: + if not self.host: + return [] return host_split_regex.split(self.host) - # def _ingest_event(self, event) -> list[Activity]: - # self_before = self.__class__.model_validate(self) - # self.ingest_event(event) - # return self.diff(self_before) - # def ingest_event(self, event): - # """ - # Given a BBOT event, update the asset facet. +class BaseAssetFacet(BaseHostModel): + """ + An "asset facet" is a database object that contains data about an asset. - # E.g., given an OPEN_TCP_PORT event, update the open_ports field to include the new port. - # """ - # raise NotImplementedError(f"Must define ingest_event() in {self.__class__.__name__}") + Unlike the main asset model which contains a summary of all the data, + a facet contains a certain detail which is too big to be stored in the main asset model. + + For example, the main asset might contain a summary of all the technologies found on the asset, + but a facet might contain the specific technologies and details about their discovery. + + A facet typically corresponds to an applet. + """ + + # scope is an array of target IDs, which are dynamically maintained as new scan data arrives, or as targets are created/updated. + scope: Annotated[list[UUID], "indexed"] = [] + + # unless overridden, all asset facets are stored in the asset store + __store_type__ = "asset" + __table_name__ = "assets" - # def diff(self, other) -> list[Activity]: - # """ - # Given another facet (typically an older version of the same host), return a list of AssetActivities which describe the new changes. - # """ - # raise NotImplementedError(f"Must define diff() in {self.__class__.__name__}") + def __init__(self, *args, **kwargs): + kwargs["type"] = self.__class__.__name__ + super().__init__(*args, **kwargs) diff --git a/bbot_server/modules/activity/activity_models.py b/bbot_server/modules/activity/activity_models.py index 97ee977a..3d86b770 100644 --- a/bbot_server/modules/activity/activity_models.py +++ b/bbot_server/modules/activity/activity_models.py @@ -9,14 +9,14 @@ from bbot_server.utils.misc import utc_now from bbot_server.cli.themes import COLOR, DARK_COLOR -from bbot_server.models.base import BaseBBOTServerModel +from bbot_server.models.asset_models import BaseHostModel remove_rich_color_pattern = re.compile(r"\[([\w ]+)\](.*?)\[/\1\]") log = logging.getLogger(__name__) -class Activity(BaseBBOTServerModel): +class Activity(BaseHostModel): """ An Activity is BBOT server's equivalent of an event. @@ -25,23 +25,19 @@ class Activity(BaseBBOTServerModel): They are usually associated with an asset, and can be traced back to a specific BBOT event. """ - __table_name__ = "history" __store_type__ = "asset" + __table_name__ = "history" # id is a UUID id: Annotated[str, "indexed", "unique"] = Field(default_factory=lambda: str(uuid.uuid4())) - type: Annotated[str, "indexed"] timestamp: Annotated[float, "indexed"] created: Annotated[float, "indexed"] = Field(default_factory=utc_now) archived: Annotated[bool, "indexed"] = False description: Annotated[str, "indexed"] description_colored: str = Field(default="") detail: dict[str, Any] = {} - host: Annotated[Optional[str], "indexed"] = None - port: Annotated[Optional[int], "indexed"] = None - netloc: Annotated[Optional[str], "indexed"] = None - url: Annotated[Optional[str], "indexed"] = None module: Annotated[Optional[str], "indexed"] = None scan: Annotated[Optional[str], "indexed"] = None + host: Annotated[Optional[str], "indexed"] = None parent_event_uuid: Annotated[Optional[str], "indexed"] = None parent_event_id: Annotated[Optional[str], "indexed"] = None parent_scan_run_id: Annotated[Optional[str], "indexed"] = None diff --git a/bbot_server/modules/assets/assets_api.py b/bbot_server/modules/assets/assets_api.py index 5da1a709..6682862d 100644 --- a/bbot_server/modules/assets/assets_api.py +++ b/bbot_server/modules/assets/assets_api.py @@ -163,7 +163,7 @@ async def refresh_assets(self): for child_applet in self.all_child_applets(include_self=True): activities = await child_applet.refresh(asset, events_by_type) for activity in activities: - await self._emit_activity(activity) + await self.emit_activity(activity) # update the asset with any changes made by the child applets await self.update_asset(asset) diff --git a/bbot_server/modules/findings/findings_api.py b/bbot_server/modules/findings/findings_api.py index d2730977..b40c2535 100644 --- a/bbot_server/modules/findings/findings_api.py +++ b/bbot_server/modules/findings/findings_api.py @@ -306,8 +306,10 @@ async def _insert_or_update_finding(self, finding: Finding, asset, event=None): { "$set": { "modified": self.helpers.utc_now(), - "confidence": finding.confidence_score, + "severity": finding.severity, "severity_score": finding.severity_score, + "confidence": finding.confidence, + "confidence_score": finding.confidence_score, } }, ) diff --git a/bbot_server/modules/presets/presets_models.py b/bbot_server/modules/presets/presets_models.py index 0bd43e3b..ed58a359 100644 --- a/bbot_server/modules/presets/presets_models.py +++ b/bbot_server/modules/presets/presets_models.py @@ -18,7 +18,7 @@ class Preset(BaseBBOTServerModel): @classmethod def sanitize_preset(cls, v: dict[str, Any]) -> dict[str, Any]: # remote target information - for value in ("target", "targets", "whitelist", "blacklist"): + for value in ("target", "targets", "seeds", "blacklist"): v.pop(value, None) # remove strict scope setting (this is stored in the target) config = v.pop("config", {}) diff --git a/bbot_server/modules/scans/scans_api.py b/bbot_server/modules/scans/scans_api.py index e69d06ec..678f9d4b 100644 --- a/bbot_server/modules/scans/scans_api.py +++ b/bbot_server/modules/scans/scans_api.py @@ -215,8 +215,8 @@ async def start_scans_loop(self): # merge target and preset scan_preset = dict(scan.preset.preset) scan_preset["scan_name"] = scan.name - scan_preset["target"] = scan.target.seeds - scan_preset["whitelist"] = scan.target.whitelist + scan_preset["target"] = scan.target.target + scan_preset["seeds"] = scan.target.seeds scan_preset["blacklist"] = scan.target.blacklist config = scan_preset.get("config", {}) scope_config = config.get("scope", {}) diff --git a/bbot_server/modules/scans/scans_cli.py b/bbot_server/modules/scans/scans_cli.py index 6d3de1f4..30b276c6 100644 --- a/bbot_server/modules/scans/scans_cli.py +++ b/bbot_server/modules/scans/scans_cli.py @@ -76,8 +76,8 @@ def iter_scans(): finished = "" if scan.finished_at is None else self.timestamp_to_human(scan.finished_at) target_name = f"[{self.COLOR}]{scan.target.name}[/{self.COLOR}]" for attr, friendly_attr in ( + ("target_size", "Target"), ("seed_size", "Seeds"), - ("whitelist_size", "Whitelist"), ("blacklist_size", "Blacklist"), ): if getattr(scan.target, attr): diff --git a/bbot_server/modules/targets/targets_api.py b/bbot_server/modules/targets/targets_api.py index 2ff57273..418cd9d2 100644 --- a/bbot_server/modules/targets/targets_api.py +++ b/bbot_server/modules/targets/targets_api.py @@ -7,8 +7,8 @@ from bbot_server.utils.misc import utc_now from bbot_server.assets import Asset from bbot_server.applets.base import BaseApplet, api_endpoint -from bbot_server.modules.targets.targets_models import Target from bbot_server.modules.activity.activity_models import Activity +from bbot_server.modules.targets.targets_models import Target, CreateTarget class BlacklistedError(Exception): @@ -27,7 +27,7 @@ class TargetsApplet(BaseApplet): name = "Targets" description = "scan targets" watched_events = ["*"] - watched_activities = ["*"] + watched_activities = ["TARGET_CREATED", "TARGET_UPDATED"] attach_to = "scans" model = Target @@ -50,15 +50,12 @@ async def handle_event(self, event, asset): if asset is None or event.host is None: return - resolved_hosts = {"SELF": [event.host]} dns_children = getattr(event, "dns_children", {}) - for rdtype in ("A", "AAAA"): - resolved_hosts[rdtype] = dns_children.get(rdtype, []) # check event against each of our targets for target_id in await self.get_target_ids(): bbot_target = await self._get_bbot_target(target_id) - scope_result = self._check_scope(event.host, resolved_hosts, bbot_target, target_id, asset.scope) + scope_result = await self._check_scope(event.host, dns_children, bbot_target, target_id, asset.scope) if scope_result is not None: scope_result.set_event(event) if scope_result.type == "NEW_IN_SCOPE_ASSET": @@ -78,18 +75,18 @@ async def handle_activity(self, activity, asset: Asset = None): """ # when a target is created or modified, we run a scope refresh on all the assets # debounce is set to 0.0 here because it's critical we're using the latest version of the target - if activity.type in ("TARGET_CREATED", "TARGET_UPDATED"): - self.log.debug(f"Target created or updated. Refreshing asset scope") - target_ids = await self.get_target_ids(debounce=0.0) - for target_id in target_ids: - target = await self._get_bbot_target(target_id, debounce=0.0) - for host in await self.root.get_hosts(): - await self.refresh_asset_scope(host, target, target_id, emit_activity=True) + # if activity.type in ("TARGET_CREATED", "TARGET_UPDATED"): + self.log.debug(f"Target created or updated. Refreshing asset scope") + target_ids = await self.get_target_ids(debounce=0.0) + for target_id in target_ids: + target = await self._get_bbot_target(target_id, debounce=0.0) + for host in await self.root.get_hosts(): + await self.refresh_asset_scope(host, target, target_id, emit_activity=True) # otherwise, for individual assets, we just refresh the scope for the given host - elif activity.host: - asset_scope = await self.get_asset_scope(activity.host) - await self.root._update_asset(activity.host, {"scope": [str(target_id) for target_id in asset_scope]}) + # elif activity.host: + # asset_scope = await self.get_asset_scope(activity.host) + # await self.root._update_asset(activity.host, {"scope": [str(target_id) for target_id in asset_scope]}) return [] @@ -109,30 +106,32 @@ async def refresh_asset_scope(self, host: str, target: BBOTTarget, target_id: UU raise self.BBOTServerNotFoundError(f"Asset not found for host {host}") asset_scope = [UUID(_target_id) for _target_id in asset.get("scope", [])] asset_dns_links = asset.get("dns_links", {}) - scope_result = self._check_scope(host, asset_dns_links, target, target_id, asset_scope) + scope_result = await self._check_scope(host, asset_dns_links, target, target_id, asset_scope) if scope_result is not None: if scope_result.type == "NEW_IN_SCOPE_ASSET": asset_scope = sorted(set(asset_scope) | set([target_id])) else: asset_scope = sorted(set(asset_scope) - set([target_id])) - results = await self.root.assets.collection.update_many( + asset_results = await self.root.assets.collection.update_many( {"host": host}, {"$set": {"scope": [str(_target_id) for _target_id in asset_scope]}}, ) - self.log.debug(f"Updated {results.modified_count} assets for host {host}") + self.log.debug(f"Updated {asset_results.modified_count} assets for host {host}") if emit_activity: await self.emit_activity(scope_result) async def get_asset_scope(self, host: str): """ Given a host, get all the targets it's a part of + + This works by getting the asset and all its DNS links, then checking each one against all the targets """ asset = await self.root.assets.collection.find_one({"host": host}, {"dns_links": 1}) or {} asset_dns_links = asset.get("dns_links", {}) asset_scope = [] for target_id in await self.get_target_ids(): target = await self._get_bbot_target(target_id) - in_scope = self._check_scope(host, asset_dns_links, target, target_id) + in_scope = await self._check_scope(host, asset_dns_links, target, target_id) if in_scope: asset_scope.append(target_id) return sorted(asset_scope) @@ -164,29 +163,27 @@ async def set_default_target(self, id: str): @api_endpoint("/create", methods=["POST"], summary="Create a new scan target") async def create_target( self, - name: str = "", - description: str = "", - seeds: list[str] = [], - whitelist: list[str] = None, - blacklist: list[str] = [], - strict_dns_scope: bool = False, + target: CreateTarget, ) -> Target: - if not whitelist and not seeds: - raise self.BBOTServerValueError("Must provide at least one seed or whitelist entry") - if not name: - name = await self.get_available_target_name() + if not target.target and not target.seeds: + raise self.BBOTServerValueError("Must provide at least one seed or target entry") + if not target.name: + target.name = await self.get_available_target_name() target = Target( - name=name, - description=description, - seeds=seeds, - whitelist=whitelist, - blacklist=blacklist, - strict_dns_scope=strict_dns_scope, + name=target.name, + description=target.description, + seeds=target.seeds, + target=target.target, + blacklist=target.blacklist, + strict_dns_scope=target.strict_dns_scope, ) if await self.target_count() == 0: target.default = True with self._handle_duplicate_target(target): await self.collection.insert_one(target.model_dump()) + # if target is the default target, set all others to not be default + if target.default: + await self.collection.update_many({"id": {"$ne": str(target.id)}}, {"$set": {"default": False}}) # emit an activity to show the target was created await self.emit_activity( type="TARGET_CREATED", @@ -261,10 +258,10 @@ async def in_scope(self, host: str, target_id: UUID = None) -> bool: bbot_target = await self._get_bbot_target(target_id) return bbot_target.in_scope(host) - @api_endpoint("/whitelisted", methods=["GET"], summary="Check if a host or URL is whitelisted") - async def is_whitelisted(self, host: str, target_id: UUID = None) -> bool: + @api_endpoint("/in-target", methods=["GET"], summary="Check if a host or URL is in the target") + async def is_in_target(self, host: str, target_id: UUID = None) -> bool: bbot_target = await self._get_bbot_target(target_id) - return bbot_target.whitelisted(host) + return bbot_target.in_target(host) @api_endpoint("/blacklisted", methods=["GET"], summary="Check if a host or URL is blacklisted") async def is_blacklisted(self, host: str, target_id: UUID = None) -> bool: @@ -297,12 +294,14 @@ async def get_available_target_name(self) -> str: counter += 1 return f"Target {counter}" - def _check_scope(self, host, resolved_hosts, target: BBOTTarget, target_id, asset_scope=None) -> Activity: + async def _check_scope(self, host, resolved_hosts, target: BBOTTarget, target_id, asset_scope=None) -> Activity: """ Given a host and its DNS records, check whether it's in scope for a given target If the scope status changes, return an activity + TODO: we may be able to speed this up by using a single RadixTarget cache for all the targets. Then we'd be able to give it a host, and in a single go, have it return all matching targets. + Args: host: the host to check resolved_hosts: a dict of DNS records for the host @@ -314,7 +313,7 @@ def _check_scope(self, host, resolved_hosts, target: BBOTTarget, target_id, asse Returns: Activity: an activity that occurred as a result of the scope check """ - whitelisted_reason = "" + in_target_reason = "" blacklisted_reason = "" resolved_hosts = {k: v for k, v in resolved_hosts.items() if k in ("A", "AAAA")} resolved_hosts["SELF"] = [host] @@ -325,13 +324,13 @@ def _check_scope(self, host, resolved_hosts, target: BBOTTarget, target_id, asse # if any of the hosts are blacklisted, abort immediately if target.blacklisted(host): blacklisted_reason = f"{rdtype}->{host}" - whitelisted_reason = "" + in_target_reason = "" # break out of the loop raise BlacklistedError # check against whitelist - if not whitelisted_reason: - if target.whitelisted(host): - whitelisted_reason = f"{rdtype}->{host}" + if not in_target_reason: + if target.in_target(host): + in_target_reason = f"{rdtype}->{host}" except BlacklistedError: pass @@ -339,17 +338,21 @@ def _check_scope(self, host, resolved_hosts, target: BBOTTarget, target_id, asse if asset_scope is None: if blacklisted_reason: return False - elif whitelisted_reason: + elif in_target_reason: return True return False + target_name = (await self._get_target(id=target_id, fields=["name"])).get("name", "") + if blacklisted_reason: scope_after = sorted(set(asset_scope) - set([target_id])) # it used to be in-scope, but not anymore if scope_after != asset_scope: - self.log.debug(f"Host {host} used to be in scope for target {target_id}, but is now blacklisted") + self.log.debug( + f"Host {host} used to be in scope for target {target_name} ({target_id}), but is now blacklisted" + ) reason = f"blacklisted host {blacklisted_reason}" - description = f"Host [COLOR]{host}[/COLOR] became out-of-scope due to {reason}" + description = f"Host [COLOR]{host}[/COLOR] became out-of-scope for target [COLOR]{target_name}[/COLOR] due to {reason}" return self.make_activity( type="ASSET_SCOPE_CHANGED", detail={ @@ -363,13 +366,15 @@ def _check_scope(self, host, resolved_hosts, target: BBOTTarget, target_id, asse description=description, ) # event is in-scope for this target - elif whitelisted_reason: + elif in_target_reason: scope_after = sorted(set(asset_scope) | set([target_id])) # it wasn't in-scope, but now it is if scope_after != asset_scope: - self.log.debug(f"Host {host} used to be out-of-scope for target {target_id}, but is now whitelisted") - reason = f"whitelisted host {whitelisted_reason}" - description = f"Host [COLOR]{host}[/COLOR] became in-scope due to {reason}" + self.log.debug( + f"Host {host} used to be out-of-scope for target {target_name} ({target_id}), but is now in-scope" + ) + reason = f"in-scope host {in_target_reason}" + description = f"Host [COLOR]{host}[/COLOR] became in-scope for target [COLOR]{target_name}[/COLOR] due to {reason}" return self.make_activity( type="NEW_IN_SCOPE_ASSET", detail={ @@ -435,8 +440,8 @@ def _bbot_target(self, target: Target) -> BBOTTarget: Given a target pydantic instance, return a BBOTTarget instance capable of fast host lookups """ return BBOTTarget( - *target.seeds, - whitelist=target.whitelist, + target=target.target, + seeds=target.seeds, blacklist=target.blacklist, strict_dns_scope=target.strict_dns_scope, ) diff --git a/bbot_server/modules/targets/targets_cli.py b/bbot_server/modules/targets/targets_cli.py index 2af8dd5a..a9fb1c16 100644 --- a/bbot_server/modules/targets/targets_cli.py +++ b/bbot_server/modules/targets/targets_cli.py @@ -2,6 +2,7 @@ from typer import Argument from bbot_server.cli import common +from bbot_server.modules.targets.targets_models import CreateTarget from bbot_server.cli.base import BaseBBCTL, subcommand, Option, Annotated @@ -14,15 +15,17 @@ class TargetCTL(BaseBBCTL): @subcommand(help="Create a new target") def create( self, - seeds: Annotated[Path, Option("--seeds", "-s", help="File containing seeds")], - whitelist: Annotated[ + target: Annotated[ Path, Option( - "--whitelist", - "-w", - help="File containing whitelist. If not provided, the seeds will be used as the whitelist.", + "--target", + "-t", + help="File containing target. This determines what's in-scope.", ), ] = None, + seeds: Annotated[ + Path, Option("--seeds", "-s", help="File containing seeds. If not specified, will be copied from target.") + ] = None, blacklist: Annotated[Path, Option("--blacklist", "-b", help="File containing blacklist")] = None, name: Annotated[str, Option("--name", "-n", help="Target name")] = "", description: Annotated[str, Option("--description", "-d", help="Target description")] = "", @@ -35,17 +38,18 @@ def create( ), ] = False, ): - seeds = self._read_file(seeds, "seeds") - whitelist = None if not whitelist else self._read_file(whitelist, "whitelist") + seeds = None if not seeds else self._read_file(seeds, "seeds") + target = [] if not target else self._read_file(target, "target") blacklist = None if not blacklist else self._read_file(blacklist, "blacklist") - target = self.bbot_server.create_target( + target = CreateTarget( name=name, description=description, + target=target, seeds=seeds, - whitelist=whitelist, blacklist=blacklist, strict_dns_scope=strict_dns_scope, ) + target = self.bbot_server.create_target(target) self.log.info(f"Target created successfully:") self.print_json(target.model_dump(), colorize=True) @@ -75,8 +79,8 @@ def list( { "name": target.name, "description": target.description, + "target": target.target_size, "seeds": target.seed_size, - "whitelist": target.whitelist_size, "blacklist": target.blacklist_size, "strict_scope": "Yes" if target.strict_dns_scope else "No", "created": self.timestamp_to_human(target.created), @@ -89,8 +93,8 @@ def list( fieldnames=[ "name", "description", + "target", "seeds", - "whitelist", "blacklist", "strict_scope", "created", @@ -103,8 +107,8 @@ def list( table = self.Table() table.add_column("Name", style=self.COLOR) table.add_column("Description") + table.add_column("Target") table.add_column("Seeds") - table.add_column("Whitelist") table.add_column("Blacklist") table.add_column("Strict Scope") table.add_column("Created", style=self.DARK_COLOR) @@ -113,8 +117,8 @@ def list( table.add_row( target.name, target.description, + f"{target.target_size:,}", f"{target.seed_size:,}", - f"{target.whitelist_size:,}", f"{target.blacklist_size:,}", "Yes" if target.strict_dns_scope else "No", self.timestamp_to_human(target.created), diff --git a/bbot_server/modules/targets/targets_models.py b/bbot_server/modules/targets/targets_models.py index f514ca0d..a48ff1ac 100644 --- a/bbot_server/modules/targets/targets_models.py +++ b/bbot_server/modules/targets/targets_models.py @@ -1,7 +1,7 @@ import uuid -from pydantic import Field -from typing import Optional -from typing import Annotated +from functools import cached_property +from typing import Optional, Annotated +from pydantic import Field, computed_field from bbot.scanner.target import BBOTTarget from bbot_server.utils.misc import utc_now @@ -9,44 +9,102 @@ class BaseTarget(BaseBBOTServerModel): - description: str = "" - seeds: list[str] = [] - whitelist: Optional[list[str]] = None - blacklist: list[str] = [] - strict_dns_scope: bool = False - hash: Annotated[str, "indexed", "unique"] = "" - scope_hash: Annotated[str, "indexed"] = "" - seed_hash: Annotated[str, "indexed"] = "" - whitelist_hash: Annotated[str, "indexed"] = "" - blacklist_hash: Annotated[str, "indexed"] = "" - seed_size: int = 0 - whitelist_size: int = 0 - blacklist_size: int = 0 + """Base class for all target models.""" + + description: str = Field("", description="Target description") + target: Optional[list[str]] = Field( + default_factory=list, + description="List of BBOT targets, e.g. domains, IPs, CIDRs, URLs, etc. These determine the scope of the scan. They are also used as seeds if no seeds are provided.", + ) + seeds: Optional[list[str]] = Field( + None, + description="Domains, IPs, CIDRs, URLs, etc. to seed the scan. If not provided, the target list will be used as seeds.", + ) + blacklist: Optional[list[str]] = Field( + default_factory=list, + description="Domains, IPs, CIDRs, URLs, etc. to blacklist from the scan. If a host is blacklisted, it will not be scanned.", + ) + strict_dns_scope: bool = Field( + False, + description="If True, only the exact hosts themselves should be considered in-scope, not their subdomains", + ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._bbot_target = BBOTTarget( - *self.seeds, whitelist=self.whitelist, blacklist=self.blacklist, strict_dns_scope=self.strict_dns_scope + target=self.target, seeds=self.seeds, blacklist=self.blacklist, strict_dns_scope=self.strict_dns_scope ) - self.hash = self.bbot_target.hash.hex() - self.scope_hash = self.bbot_target.scope_hash.hex() - self.seed_hash = self.bbot_target.seeds.hash.hex() - self.whitelist_hash = self.bbot_target.whitelist.hash.hex() - self.blacklist_hash = self.bbot_target.blacklist.hash.hex() - self.seed_size = len(self.bbot_target.seeds) - self.whitelist_size = 0 if not self.bbot_target._orig_whitelist else len(self.bbot_target.whitelist) - self.blacklist_size = len(self.bbot_target.blacklist) + # self.target = sorted(self.target.inputs) @property def bbot_target(self): return self._bbot_target + @computed_field( + description="Hash of the target. This is combined from the target, seeds, and blacklist hashes. Strict scope is also taken into account." + ) + @cached_property + def hash(self) -> Annotated[str, "indexed", "unique"]: + return self.bbot_target.hash.hex() + + @computed_field(description="Hash of the target list.") + @cached_property + def target_hash(self) -> Annotated[str, "indexed"]: + return self._bbot_target.target.hash.hex() + + @computed_field(description="Hash of the blacklist.") + @cached_property + def blacklist_hash(self) -> Annotated[str, "indexed"]: + return self._bbot_target.blacklist.hash.hex() + + @computed_field(description="Hash of the seeds.") + @cached_property + def seed_hash(self) -> Annotated[str, "indexed"]: + return self._bbot_target.seeds.hash.hex() + + @computed_field(description="Hash of the scope (target + blacklist + strict scope setting).") + @cached_property + def scope_hash(self) -> Annotated[str, "indexed"]: + return self._bbot_target.scope_hash.hex() + + @computed_field(description="Number of entries in the target list.") + @cached_property + def target_size(self) -> int: + return len(self.bbot_target.target) + + @computed_field(description="Number of entries in the blacklist.") + @cached_property + def blacklist_size(self) -> int: + return len(self.bbot_target.blacklist) + + @computed_field(description="Number of entries in the seeds list.") + @cached_property + def seed_size(self) -> int: + return 0 if not self.bbot_target._orig_seeds else len(self.bbot_target.seeds) + + +class CreateTarget(BaseTarget): + """Used for creating a new target.""" + + name: Annotated[str, "indexed", "unique", Field(description="Target name", default="")] + default: Annotated[ + bool, + "indexed", + Field(description="If True, this is the default target. There can only be one default target."), + ] = False + + +class Target(CreateTarget): + """Used for storing a target in the database.""" -class Target(BaseTarget): __table_name__ = "targets" __store_type__ = "user" - id: Annotated[uuid.UUID, "indexed", "unique"] = Field(default_factory=uuid.uuid4) - name: Annotated[str, "indexed", "unique"] - default: Annotated[bool, "indexed"] = False - created: Annotated[float, "indexed"] = Field(default_factory=utc_now) - modified: Annotated[float, "indexed"] = Field(default_factory=utc_now) + id: Annotated[uuid.UUID, "indexed", "unique"] = Field( + default_factory=uuid.uuid4, description="Universally Unique Target ID" + ) + created: Annotated[float, "indexed"] = Field( + default_factory=utc_now, description="Timestamp of when the target was created" + ) + modified: Annotated[float, "indexed"] = Field( + default_factory=utc_now, description="Timestamp of when the target was last modified" + ) diff --git a/poetry.lock b/poetry.lock index 899d034c..d8f22c9a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -62,24 +62,23 @@ files = [ [[package]] name = "anyio" -version = "4.11.0" +version = "4.12.0" description = "High-level concurrency and networking framework on top of asyncio or Trio" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"}, - {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"}, + {file = "anyio-4.12.0-py3-none-any.whl", hash = "sha256:dad2376a628f98eeca4881fc56cd06affd18f659b17a747d3ff0307ced94b1bb"}, + {file = "anyio-4.12.0.tar.gz", hash = "sha256:73c693b567b0c55130c104d0b43a9baf3aa6a31fc6110116509f27bf75e21ec0"}, ] [package.dependencies] exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" -sniffio = ">=1.1" typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -trio = ["trio (>=0.31.0)"] +trio = ["trio (>=0.31.0) ; python_version < \"3.10\"", "trio (>=0.32.0) ; python_version >= \"3.10\""] [[package]] name = "async-timeout" @@ -142,22 +141,22 @@ yara-python = "^4.5.1" type = "git" url = "https://github.com/blacklanternsecurity/bbot" reference = "3.0" -resolved_reference = "b3041f65a0796176bb49186df58ea6ebd3193050" +resolved_reference = "95b5dd45c879a555017a64ce5529719086f33c8c" [[package]] name = "beautifulsoup4" -version = "4.14.2" +version = "4.14.3" description = "Screen-scraping library" optional = false python-versions = ">=3.7.0" groups = ["main"] files = [ - {file = "beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515"}, - {file = "beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e"}, + {file = "beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb"}, + {file = "beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86"}, ] [package.dependencies] -soupsieve = ">1.2" +soupsieve = ">=1.6.1" typing-extensions = ">=4.0.0" [package.extras] @@ -460,104 +459,104 @@ markers = {main = "platform_system == \"Windows\" or sys_platform == \"win32\"", [[package]] name = "coverage" -version = "7.12.0" +version = "7.13.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "coverage-7.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:32b75c2ba3f324ee37af3ccee5b30458038c50b349ad9b88cee85096132a575b"}, - {file = "coverage-7.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cb2a1b6ab9fe833714a483a915de350abc624a37149649297624c8d57add089c"}, - {file = "coverage-7.12.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5734b5d913c3755e72f70bf6cc37a0518d4f4745cde760c5d8e12005e62f9832"}, - {file = "coverage-7.12.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b527a08cdf15753279b7afb2339a12073620b761d79b81cbe2cdebdb43d90daa"}, - {file = "coverage-7.12.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9bb44c889fb68004e94cab71f6a021ec83eac9aeabdbb5a5a88821ec46e1da73"}, - {file = "coverage-7.12.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4b59b501455535e2e5dde5881739897967b272ba25988c89145c12d772810ccb"}, - {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8842f17095b9868a05837b7b1b73495293091bed870e099521ada176aa3e00e"}, - {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5a6f20bf48b8866095c6820641e7ffbe23f2ac84a2efc218d91235e404c7777"}, - {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:5f3738279524e988d9da2893f307c2093815c623f8d05a8f79e3eff3a7a9e553"}, - {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0d68c1f7eabbc8abe582d11fa393ea483caf4f44b0af86881174769f185c94d"}, - {file = "coverage-7.12.0-cp310-cp310-win32.whl", hash = "sha256:7670d860e18b1e3ee5930b17a7d55ae6287ec6e55d9799982aa103a2cc1fa2ef"}, - {file = "coverage-7.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:f999813dddeb2a56aab5841e687b68169da0d3f6fc78ccf50952fa2463746022"}, - {file = "coverage-7.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa124a3683d2af98bd9d9c2bfa7a5076ca7e5ab09fdb96b81fa7d89376ae928f"}, - {file = "coverage-7.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d93fbf446c31c0140208dcd07c5d882029832e8ed7891a39d6d44bd65f2316c3"}, - {file = "coverage-7.12.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:52ca620260bd8cd6027317bdd8b8ba929be1d741764ee765b42c4d79a408601e"}, - {file = "coverage-7.12.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f3433ffd541380f3a0e423cff0f4926d55b0cc8c1d160fdc3be24a4c03aa65f7"}, - {file = "coverage-7.12.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7bbb321d4adc9f65e402c677cd1c8e4c2d0105d3ce285b51b4d87f1d5db5245"}, - {file = "coverage-7.12.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22a7aade354a72dff3b59c577bfd18d6945c61f97393bc5fb7bd293a4237024b"}, - {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3ff651dcd36d2fea66877cd4a82de478004c59b849945446acb5baf9379a1b64"}, - {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:31b8b2e38391a56e3cea39d22a23faaa7c3fc911751756ef6d2621d2a9daf742"}, - {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:297bc2da28440f5ae51c845a47c8175a4db0553a53827886e4fb25c66633000c"}, - {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6ff7651cc01a246908eac162a6a86fc0dbab6de1ad165dfb9a1e2ec660b44984"}, - {file = "coverage-7.12.0-cp311-cp311-win32.whl", hash = "sha256:313672140638b6ddb2c6455ddeda41c6a0b208298034544cfca138978c6baed6"}, - {file = "coverage-7.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a1783ed5bd0d5938d4435014626568dc7f93e3cb99bc59188cc18857c47aa3c4"}, - {file = "coverage-7.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:4648158fd8dd9381b5847622df1c90ff314efbfc1df4550092ab6013c238a5fc"}, - {file = "coverage-7.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:29644c928772c78512b48e14156b81255000dcfd4817574ff69def189bcb3647"}, - {file = "coverage-7.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8638cbb002eaa5d7c8d04da667813ce1067080b9a91099801a0053086e52b736"}, - {file = "coverage-7.12.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:083631eeff5eb9992c923e14b810a179798bb598e6a0dd60586819fc23be6e60"}, - {file = "coverage-7.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:99d5415c73ca12d558e07776bd957c4222c687b9f1d26fa0e1b57e3598bdcde8"}, - {file = "coverage-7.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e949ebf60c717c3df63adb4a1a366c096c8d7fd8472608cd09359e1bd48ef59f"}, - {file = "coverage-7.12.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d907ddccbca819afa2cd014bc69983b146cca2735a0b1e6259b2a6c10be1e70"}, - {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b1518ecbad4e6173f4c6e6c4a46e49555ea5679bf3feda5edb1b935c7c44e8a0"}, - {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51777647a749abdf6f6fd8c7cffab12de68ab93aab15efc72fbbb83036c2a068"}, - {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:42435d46d6461a3b305cdfcad7cdd3248787771f53fe18305548cba474e6523b"}, - {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5bcead88c8423e1855e64b8057d0544e33e4080b95b240c2a355334bb7ced937"}, - {file = "coverage-7.12.0-cp312-cp312-win32.whl", hash = "sha256:dcbb630ab034e86d2a0f79aefd2be07e583202f41e037602d438c80044957baa"}, - {file = "coverage-7.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:2fd8354ed5d69775ac42986a691fbf68b4084278710cee9d7c3eaa0c28fa982a"}, - {file = "coverage-7.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:737c3814903be30695b2de20d22bcc5428fdae305c61ba44cdc8b3252984c49c"}, - {file = "coverage-7.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47324fffca8d8eae7e185b5bb20c14645f23350f870c1649003618ea91a78941"}, - {file = "coverage-7.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ccf3b2ede91decd2fb53ec73c1f949c3e034129d1e0b07798ff1d02ea0c8fa4a"}, - {file = "coverage-7.12.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b365adc70a6936c6b0582dc38746b33b2454148c02349345412c6e743efb646d"}, - {file = "coverage-7.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bc13baf85cd8a4cfcf4a35c7bc9d795837ad809775f782f697bf630b7e200211"}, - {file = "coverage-7.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:099d11698385d572ceafb3288a5b80fe1fc58bf665b3f9d362389de488361d3d"}, - {file = "coverage-7.12.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:473dc45d69694069adb7680c405fb1e81f60b2aff42c81e2f2c3feaf544d878c"}, - {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:583f9adbefd278e9de33c33d6846aa8f5d164fa49b47144180a0e037f0688bb9"}, - {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2089cc445f2dc0af6f801f0d1355c025b76c24481935303cf1af28f636688f0"}, - {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:950411f1eb5d579999c5f66c62a40961f126fc71e5e14419f004471957b51508"}, - {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b1aab7302a87bafebfe76b12af681b56ff446dc6f32ed178ff9c092ca776e6bc"}, - {file = "coverage-7.12.0-cp313-cp313-win32.whl", hash = "sha256:d7e0d0303c13b54db495eb636bc2465b2fb8475d4c8bcec8fe4b5ca454dfbae8"}, - {file = "coverage-7.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:ce61969812d6a98a981d147d9ac583a36ac7db7766f2e64a9d4d059c2fe29d07"}, - {file = "coverage-7.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bcec6f47e4cb8a4c2dc91ce507f6eefc6a1b10f58df32cdc61dff65455031dfc"}, - {file = "coverage-7.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:459443346509476170d553035e4a3eed7b860f4fe5242f02de1010501956ce87"}, - {file = "coverage-7.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:04a79245ab2b7a61688958f7a855275997134bc84f4a03bc240cf64ff132abf6"}, - {file = "coverage-7.12.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:09a86acaaa8455f13d6a99221d9654df249b33937b4e212b4e5a822065f12aa7"}, - {file = "coverage-7.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:907e0df1b71ba77463687a74149c6122c3f6aac56c2510a5d906b2f368208560"}, - {file = "coverage-7.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b57e2d0ddd5f0582bae5437c04ee71c46cd908e7bc5d4d0391f9a41e812dd12"}, - {file = "coverage-7.12.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:58c1c6aa677f3a1411fe6fb28ec3a942e4f665df036a3608816e0847fad23296"}, - {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4c589361263ab2953e3c4cd2a94db94c4ad4a8e572776ecfbad2389c626e4507"}, - {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:91b810a163ccad2e43b1faa11d70d3cf4b6f3d83f9fd5f2df82a32d47b648e0d"}, - {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:40c867af715f22592e0d0fb533a33a71ec9e0f73a6945f722a0c85c8c1cbe3a2"}, - {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:68b0d0a2d84f333de875666259dadf28cc67858bc8fd8b3f1eae84d3c2bec455"}, - {file = "coverage-7.12.0-cp313-cp313t-win32.whl", hash = "sha256:73f9e7fbd51a221818fd11b7090eaa835a353ddd59c236c57b2199486b116c6d"}, - {file = "coverage-7.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:24cff9d1f5743f67db7ba46ff284018a6e9aeb649b67aa1e70c396aa1b7cb23c"}, - {file = "coverage-7.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c87395744f5c77c866d0f5a43d97cc39e17c7f1cb0115e54a2fe67ca75c5d14d"}, - {file = "coverage-7.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a1c59b7dc169809a88b21a936eccf71c3895a78f5592051b1af8f4d59c2b4f92"}, - {file = "coverage-7.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8787b0f982e020adb732b9f051f3e49dd5054cebbc3f3432061278512a2b1360"}, - {file = "coverage-7.12.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5ea5a9f7dc8877455b13dd1effd3202e0bca72f6f3ab09f9036b1bcf728f69ac"}, - {file = "coverage-7.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fdba9f15849534594f60b47c9a30bc70409b54947319a7c4fd0e8e3d8d2f355d"}, - {file = "coverage-7.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a00594770eb715854fb1c57e0dea08cce6720cfbc531accdb9850d7c7770396c"}, - {file = "coverage-7.12.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5560c7e0d82b42eb1951e4f68f071f8017c824ebfd5a6ebe42c60ac16c6c2434"}, - {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2e26b481c9159c2773a37947a9718cfdc58893029cdfb177531793e375cfc"}, - {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6e1a8c066dabcde56d5d9fed6a66bc19a2883a3fe051f0c397a41fc42aedd4cc"}, - {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f7ba9da4726e446d8dd8aae5a6cd872511184a5d861de80a86ef970b5dacce3e"}, - {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e0f483ab4f749039894abaf80c2f9e7ed77bbf3c737517fb88c8e8e305896a17"}, - {file = "coverage-7.12.0-cp314-cp314-win32.whl", hash = "sha256:76336c19a9ef4a94b2f8dc79f8ac2da3f193f625bb5d6f51a328cd19bfc19933"}, - {file = "coverage-7.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7c1059b600aec6ef090721f8f633f60ed70afaffe8ecab85b59df748f24b31fe"}, - {file = "coverage-7.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:172cf3a34bfef42611963e2b661302a8931f44df31629e5b1050567d6b90287d"}, - {file = "coverage-7.12.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:aa7d48520a32cb21c7a9b31f81799e8eaec7239db36c3b670be0fa2403828d1d"}, - {file = "coverage-7.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:90d58ac63bc85e0fb919f14d09d6caa63f35a5512a2205284b7816cafd21bb03"}, - {file = "coverage-7.12.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ca8ecfa283764fdda3eae1bdb6afe58bf78c2c3ec2b2edcb05a671f0bba7b3f9"}, - {file = "coverage-7.12.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:874fe69a0785d96bd066059cd4368022cebbec1a8958f224f0016979183916e6"}, - {file = "coverage-7.12.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3c889c0b8b283a24d721a9eabc8ccafcfc3aebf167e4cd0d0e23bf8ec4e339"}, - {file = "coverage-7.12.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bb5b894b3ec09dcd6d3743229dc7f2c42ef7787dc40596ae04c0edda487371e"}, - {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:79a44421cd5fba96aa57b5e3b5a4d3274c449d4c622e8f76882d76635501fd13"}, - {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:33baadc0efd5c7294f436a632566ccc1f72c867f82833eb59820ee37dc811c6f"}, - {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c406a71f544800ef7e9e0000af706b88465f3573ae8b8de37e5f96c59f689ad1"}, - {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e71bba6a40883b00c6d571599b4627f50c360b3d0d02bfc658168936be74027b"}, - {file = "coverage-7.12.0-cp314-cp314t-win32.whl", hash = "sha256:9157a5e233c40ce6613dead4c131a006adfda70e557b6856b97aceed01b0e27a"}, - {file = "coverage-7.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e84da3a0fd233aeec797b981c51af1cabac74f9bd67be42458365b30d11b5291"}, - {file = "coverage-7.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:01d24af36fedda51c2b1aca56e4330a3710f83b02a5ff3743a6b015ffa7c9384"}, - {file = "coverage-7.12.0-py3-none-any.whl", hash = "sha256:159d50c0b12e060b15ed3d39f87ed43d4f7f7ad40b8a534f4dd331adbb51104a"}, - {file = "coverage-7.12.0.tar.gz", hash = "sha256:fc11e0a4e372cb5f282f16ef90d4a585034050ccda536451901abfb19a57f40c"}, + {file = "coverage-7.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:02d9fb9eccd48f6843c98a37bd6817462f130b86da8660461e8f5e54d4c06070"}, + {file = "coverage-7.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:367449cf07d33dc216c083f2036bb7d976c6e4903ab31be400ad74ad9f85ce98"}, + {file = "coverage-7.13.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cdb3c9f8fef0a954c632f64328a3935988d33a6604ce4bf67ec3e39670f12ae5"}, + {file = "coverage-7.13.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d10fd186aac2316f9bbb46ef91977f9d394ded67050ad6d84d94ed6ea2e8e54e"}, + {file = "coverage-7.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f88ae3e69df2ab62fb0bc5219a597cb890ba5c438190ffa87490b315190bb33"}, + {file = "coverage-7.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4be718e51e86f553bcf515305a158a1cd180d23b72f07ae76d6017c3cc5d791"}, + {file = "coverage-7.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a00d3a393207ae12f7c49bb1c113190883b500f48979abb118d8b72b8c95c032"}, + {file = "coverage-7.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a7b1cd820e1b6116f92c6128f1188e7afe421c7e1b35fa9836b11444e53ebd9"}, + {file = "coverage-7.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:37eee4e552a65866f15dedd917d5e5f3d59805994260720821e2c1b51ac3248f"}, + {file = "coverage-7.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:62d7c4f13102148c78d7353c6052af6d899a7f6df66a32bddcc0c0eb7c5326f8"}, + {file = "coverage-7.13.0-cp310-cp310-win32.whl", hash = "sha256:24e4e56304fdb56f96f80eabf840eab043b3afea9348b88be680ec5986780a0f"}, + {file = "coverage-7.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:74c136e4093627cf04b26a35dab8cbfc9b37c647f0502fc313376e11726ba303"}, + {file = "coverage-7.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0dfa3855031070058add1a59fdfda0192fd3e8f97e7c81de0596c145dea51820"}, + {file = "coverage-7.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fdb6f54f38e334db97f72fa0c701e66d8479af0bc3f9bfb5b90f1c30f54500f"}, + {file = "coverage-7.13.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7e442c013447d1d8d195be62852270b78b6e255b79b8675bad8479641e21fd96"}, + {file = "coverage-7.13.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ed5630d946859de835a85e9a43b721123a8a44ec26e2830b296d478c7fd4259"}, + {file = "coverage-7.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f15a931a668e58087bc39d05d2b4bf4b14ff2875b49c994bbdb1c2217a8daeb"}, + {file = "coverage-7.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30a3a201a127ea57f7e14ba43c93c9c4be8b7d17a26e03bb49e6966d019eede9"}, + {file = "coverage-7.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a485ff48fbd231efa32d58f479befce52dcb6bfb2a88bb7bf9a0b89b1bc8030"}, + {file = "coverage-7.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:22486cdafba4f9e471c816a2a5745337742a617fef68e890d8baf9f3036d7833"}, + {file = "coverage-7.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:263c3dbccc78e2e331e59e90115941b5f53e85cfcc6b3b2fbff1fd4e3d2c6ea8"}, + {file = "coverage-7.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e5330fa0cc1f5c3c4c3bb8e101b742025933e7848989370a1d4c8c5e401ea753"}, + {file = "coverage-7.13.0-cp311-cp311-win32.whl", hash = "sha256:0f4872f5d6c54419c94c25dd6ae1d015deeb337d06e448cd890a1e89a8ee7f3b"}, + {file = "coverage-7.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51a202e0f80f241ccb68e3e26e19ab5b3bf0f813314f2c967642f13ebcf1ddfe"}, + {file = "coverage-7.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:d2a9d7f1c11487b1c69367ab3ac2d81b9b3721f097aa409a3191c3e90f8f3dd7"}, + {file = "coverage-7.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0b3d67d31383c4c68e19a88e28fc4c2e29517580f1b0ebec4a069d502ce1e0bf"}, + {file = "coverage-7.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:581f086833d24a22c89ae0fe2142cfaa1c92c930adf637ddf122d55083fb5a0f"}, + {file = "coverage-7.13.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0a3a30f0e257df382f5f9534d4ce3d4cf06eafaf5192beb1a7bd066cb10e78fb"}, + {file = "coverage-7.13.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:583221913fbc8f53b88c42e8dbb8fca1d0f2e597cb190ce45916662b8b9d9621"}, + {file = "coverage-7.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f5d9bd30756fff3e7216491a0d6d520c448d5124d3d8e8f56446d6412499e74"}, + {file = "coverage-7.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a23e5a1f8b982d56fa64f8e442e037f6ce29322f1f9e6c2344cd9e9f4407ee57"}, + {file = "coverage-7.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b01c22bc74a7fb44066aaf765224c0d933ddf1f5047d6cdfe4795504a4493f8"}, + {file = "coverage-7.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:898cce66d0836973f48dda4e3514d863d70142bdf6dfab932b9b6a90ea5b222d"}, + {file = "coverage-7.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:3ab483ea0e251b5790c2aac03acde31bff0c736bf8a86829b89382b407cd1c3b"}, + {file = "coverage-7.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d84e91521c5e4cb6602fe11ece3e1de03b2760e14ae4fcf1a4b56fa3c801fcd"}, + {file = "coverage-7.13.0-cp312-cp312-win32.whl", hash = "sha256:193c3887285eec1dbdb3f2bd7fbc351d570ca9c02ca756c3afbc71b3c98af6ef"}, + {file = "coverage-7.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:4f3e223b2b2db5e0db0c2b97286aba0036ca000f06aca9b12112eaa9af3d92ae"}, + {file = "coverage-7.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:086cede306d96202e15a4b77ace8472e39d9f4e5f9fd92dd4fecdfb2313b2080"}, + {file = "coverage-7.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:28ee1c96109974af104028a8ef57cec21447d42d0e937c0275329272e370ebcf"}, + {file = "coverage-7.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e97353dcc5587b85986cda4ff3ec98081d7e84dd95e8b2a6d59820f0545f8a"}, + {file = "coverage-7.13.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:99acd4dfdfeb58e1937629eb1ab6ab0899b131f183ee5f23e0b5da5cba2fec74"}, + {file = "coverage-7.13.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff45e0cd8451e293b63ced93161e189780baf444119391b3e7d25315060368a6"}, + {file = "coverage-7.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f4f72a85316d8e13234cafe0a9f81b40418ad7a082792fa4165bd7d45d96066b"}, + {file = "coverage-7.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:11c21557d0e0a5a38632cbbaca5f008723b26a89d70db6315523df6df77d6232"}, + {file = "coverage-7.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76541dc8d53715fb4f7a3a06b34b0dc6846e3c69bc6204c55653a85dd6220971"}, + {file = "coverage-7.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6e9e451dee940a86789134b6b0ffbe31c454ade3b849bb8a9d2cca2541a8e91d"}, + {file = "coverage-7.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5c67dace46f361125e6b9cace8fe0b729ed8479f47e70c89b838d319375c8137"}, + {file = "coverage-7.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f59883c643cb19630500f57016f76cfdcd6845ca8c5b5ea1f6e17f74c8e5f511"}, + {file = "coverage-7.13.0-cp313-cp313-win32.whl", hash = "sha256:58632b187be6f0be500f553be41e277712baa278147ecb7559983c6d9faf7ae1"}, + {file = "coverage-7.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:73419b89f812f498aca53f757dd834919b48ce4799f9d5cad33ca0ae442bdb1a"}, + {file = "coverage-7.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:eb76670874fdd6091eedcc856128ee48c41a9bbbb9c3f1c7c3cf169290e3ffd6"}, + {file = "coverage-7.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6e63ccc6e0ad8986386461c3c4b737540f20426e7ec932f42e030320896c311a"}, + {file = "coverage-7.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:494f5459ffa1bd45e18558cd98710c36c0b8fbfa82a5eabcbe671d80ecffbfe8"}, + {file = "coverage-7.13.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:06cac81bf10f74034e055e903f5f946e3e26fc51c09fc9f584e4a1605d977053"}, + {file = "coverage-7.13.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f2ffc92b46ed6e6760f1d47a71e56b5664781bc68986dbd1836b2b70c0ce2071"}, + {file = "coverage-7.13.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0602f701057c6823e5db1b74530ce85f17c3c5be5c85fc042ac939cbd909426e"}, + {file = "coverage-7.13.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:25dc33618d45456ccb1d37bce44bc78cf269909aa14c4db2e03d63146a8a1493"}, + {file = "coverage-7.13.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:71936a8b3b977ddd0b694c28c6a34f4fff2e9dd201969a4ff5d5fc7742d614b0"}, + {file = "coverage-7.13.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:936bc20503ce24770c71938d1369461f0c5320830800933bc3956e2a4ded930e"}, + {file = "coverage-7.13.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:af0a583efaacc52ae2521f8d7910aff65cdb093091d76291ac5820d5e947fc1c"}, + {file = "coverage-7.13.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f1c23e24a7000da892a312fb17e33c5f94f8b001de44b7cf8ba2e36fbd15859e"}, + {file = "coverage-7.13.0-cp313-cp313t-win32.whl", hash = "sha256:5f8a0297355e652001015e93be345ee54393e45dc3050af4a0475c5a2b767d46"}, + {file = "coverage-7.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6abb3a4c52f05e08460bd9acf04fec027f8718ecaa0d09c40ffbc3fbd70ecc39"}, + {file = "coverage-7.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:3ad968d1e3aa6ce5be295ab5fe3ae1bf5bb4769d0f98a80a0252d543a2ef2e9e"}, + {file = "coverage-7.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:453b7ec753cf5e4356e14fe858064e5520c460d3bbbcb9c35e55c0d21155c256"}, + {file = "coverage-7.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:af827b7cbb303e1befa6c4f94fd2bf72f108089cfa0f8abab8f4ca553cf5ca5a"}, + {file = "coverage-7.13.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9987a9e4f8197a1000280f7cc089e3ea2c8b3c0a64d750537809879a7b4ceaf9"}, + {file = "coverage-7.13.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3188936845cd0cb114fa6a51842a304cdbac2958145d03be2377ec41eb285d19"}, + {file = "coverage-7.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2bdb3babb74079f021696cb46b8bb5f5661165c385d3a238712b031a12355be"}, + {file = "coverage-7.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7464663eaca6adba4175f6c19354feea61ebbdd735563a03d1e472c7072d27bb"}, + {file = "coverage-7.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8069e831f205d2ff1f3d355e82f511eb7c5522d7d413f5db5756b772ec8697f8"}, + {file = "coverage-7.13.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6fb2d5d272341565f08e962cce14cdf843a08ac43bd621783527adb06b089c4b"}, + {file = "coverage-7.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5e70f92ef89bac1ac8a99b3324923b4749f008fdbd7aa9cb35e01d7a284a04f9"}, + {file = "coverage-7.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4b5de7d4583e60d5fd246dd57fcd3a8aa23c6e118a8c72b38adf666ba8e7e927"}, + {file = "coverage-7.13.0-cp314-cp314-win32.whl", hash = "sha256:a6c6e16b663be828a8f0b6c5027d36471d4a9f90d28444aa4ced4d48d7d6ae8f"}, + {file = "coverage-7.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:0900872f2fdb3ee5646b557918d02279dc3af3dfb39029ac4e945458b13f73bc"}, + {file = "coverage-7.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:3a10260e6a152e5f03f26db4a407c4c62d3830b9af9b7c0450b183615f05d43b"}, + {file = "coverage-7.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9097818b6cc1cfb5f174e3263eba4a62a17683bcfe5c4b5d07f4c97fa51fbf28"}, + {file = "coverage-7.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0018f73dfb4301a89292c73be6ba5f58722ff79f51593352759c1790ded1cabe"}, + {file = "coverage-7.13.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:166ad2a22ee770f5656e1257703139d3533b4a0b6909af67c6b4a3adc1c98657"}, + {file = "coverage-7.13.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f6aaef16d65d1787280943f1c8718dc32e9cf141014e4634d64446702d26e0ff"}, + {file = "coverage-7.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e999e2dcc094002d6e2c7bbc1fb85b58ba4f465a760a8014d97619330cdbbbf3"}, + {file = "coverage-7.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:00c3d22cf6fb1cf3bf662aaaa4e563be8243a5ed2630339069799835a9cc7f9b"}, + {file = "coverage-7.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22ccfe8d9bb0d6134892cbe1262493a8c70d736b9df930f3f3afae0fe3ac924d"}, + {file = "coverage-7.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9372dff5ea15930fea0445eaf37bbbafbc771a49e70c0aeed8b4e2c2614cc00e"}, + {file = "coverage-7.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:69ac2c492918c2461bc6ace42d0479638e60719f2a4ef3f0815fa2df88e9f940"}, + {file = "coverage-7.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:739c6c051a7540608d097b8e13c76cfa85263ced467168dc6b477bae3df7d0e2"}, + {file = "coverage-7.13.0-cp314-cp314t-win32.whl", hash = "sha256:fe81055d8c6c9de76d60c94ddea73c290b416e061d40d542b24a5871bad498b7"}, + {file = "coverage-7.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:445badb539005283825959ac9fa4a28f712c214b65af3a2c464f1adc90f5fcbc"}, + {file = "coverage-7.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:de7f6748b890708578fc4b7bb967d810aeb6fcc9bff4bb77dbca77dab2f9df6a"}, + {file = "coverage-7.13.0-py3-none-any.whl", hash = "sha256:850d2998f380b1e266459ca5b47bc9e7daf9af1d070f66317972f382d46f1904"}, + {file = "coverage-7.13.0.tar.gz", hash = "sha256:a394aa27f2d7ff9bc04cf703817773a59ad6dfbd577032e690f961d2460ee936"}, ] [package.dependencies] @@ -707,15 +706,15 @@ idna = ">=2.0.0" [[package]] name = "exceptiongroup" -version = "1.3.0" +version = "1.3.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["main", "dev"] markers = "python_version == \"3.10\"" files = [ - {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, - {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, ] [package.dependencies] @@ -1550,99 +1549,99 @@ test = ["pytest (>=8.3.0,<8.4.0)", "pytest-benchmark (>=5.1.0,<5.2.0)", "pytest- [[package]] name = "orjson" -version = "3.11.4" +version = "3.11.5" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "orjson-3.11.4-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e3aa2118a3ece0d25489cbe48498de8a5d580e42e8d9979f65bf47900a15aba1"}, - {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a69ab657a4e6733133a3dca82768f2f8b884043714e8d2b9ba9f52b6efef5c44"}, - {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3740bffd9816fc0326ddc406098a3a8f387e42223f5f455f2a02a9f834ead80c"}, - {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65fd2f5730b1bf7f350c6dc896173d3460d235c4be007af73986d7cd9a2acd23"}, - {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fdc3ae730541086158d549c97852e2eea6820665d4faf0f41bf99df41bc11ea"}, - {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e10b4d65901da88845516ce9f7f9736f9638d19a1d483b3883dc0182e6e5edba"}, - {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb6a03a678085f64b97f9d4a9ae69376ce91a3a9e9b56a82b1580d8e1d501aff"}, - {file = "orjson-3.11.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c82e4f0b1c712477317434761fbc28b044c838b6b1240d895607441412371ac"}, - {file = "orjson-3.11.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d58c166a18f44cc9e2bad03a327dc2d1a3d2e85b847133cfbafd6bfc6719bd79"}, - {file = "orjson-3.11.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94f206766bf1ea30e1382e4890f763bd1eefddc580e08fec1ccdc20ddd95c827"}, - {file = "orjson-3.11.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:41bf25fb39a34cf8edb4398818523277ee7096689db352036a9e8437f2f3ee6b"}, - {file = "orjson-3.11.4-cp310-cp310-win32.whl", hash = "sha256:fa9627eba4e82f99ca6d29bc967f09aba446ee2b5a1ea728949ede73d313f5d3"}, - {file = "orjson-3.11.4-cp310-cp310-win_amd64.whl", hash = "sha256:23ef7abc7fca96632d8174ac115e668c1e931b8fe4dde586e92a500bf1914dcc"}, - {file = "orjson-3.11.4-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:5e59d23cd93ada23ec59a96f215139753fbfe3a4d989549bcb390f8c00370b39"}, - {file = "orjson-3.11.4-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5c3aedecfc1beb988c27c79d52ebefab93b6c3921dbec361167e6559aba2d36d"}, - {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da9e5301f1c2caa2a9a4a303480d79c9ad73560b2e7761de742ab39fe59d9175"}, - {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8873812c164a90a79f65368f8f96817e59e35d0cc02786a5356f0e2abed78040"}, - {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d7feb0741ebb15204e748f26c9638e6665a5fa93c37a2c73d64f1669b0ddc63"}, - {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ee5487fefee21e6910da4c2ee9eef005bee568a0879834df86f888d2ffbdd9"}, - {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d40d46f348c0321df01507f92b95a377240c4ec31985225a6668f10e2676f9a"}, - {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95713e5fc8af84d8edc75b785d2386f653b63d62b16d681687746734b4dfc0be"}, - {file = "orjson-3.11.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad73ede24f9083614d6c4ca9a85fe70e33be7bf047ec586ee2363bc7418fe4d7"}, - {file = "orjson-3.11.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:842289889de515421f3f224ef9c1f1efb199a32d76d8d2ca2706fa8afe749549"}, - {file = "orjson-3.11.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3b2427ed5791619851c52a1261b45c233930977e7de8cf36de05636c708fa905"}, - {file = "orjson-3.11.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c36e524af1d29982e9b190573677ea02781456b2e537d5840e4538a5ec41907"}, - {file = "orjson-3.11.4-cp311-cp311-win32.whl", hash = "sha256:87255b88756eab4a68ec61837ca754e5d10fa8bc47dc57f75cedfeaec358d54c"}, - {file = "orjson-3.11.4-cp311-cp311-win_amd64.whl", hash = "sha256:e2d5d5d798aba9a0e1fede8d853fa899ce2cb930ec0857365f700dffc2c7af6a"}, - {file = "orjson-3.11.4-cp311-cp311-win_arm64.whl", hash = "sha256:6bb6bb41b14c95d4f2702bce9975fda4516f1db48e500102fc4d8119032ff045"}, - {file = "orjson-3.11.4-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d4371de39319d05d3f482f372720b841c841b52f5385bd99c61ed69d55d9ab50"}, - {file = "orjson-3.11.4-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:e41fd3b3cac850eaae78232f37325ed7d7436e11c471246b87b2cd294ec94853"}, - {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:600e0e9ca042878c7fdf189cf1b028fe2c1418cc9195f6cb9824eb6ed99cb938"}, - {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7bbf9b333f1568ef5da42bc96e18bf30fd7f8d54e9ae066d711056add508e415"}, - {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4806363144bb6e7297b8e95870e78d30a649fdc4e23fc84daa80c8ebd366ce44"}, - {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad355e8308493f527d41154e9053b86a5be892b3b359a5c6d5d95cda23601cb2"}, - {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a7517482667fb9f0ff1b2f16fe5829296ed7a655d04d68cd9711a4d8a4e708"}, - {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97eb5942c7395a171cbfecc4ef6701fc3c403e762194683772df4c54cfbb2210"}, - {file = "orjson-3.11.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:149d95d5e018bdd822e3f38c103b1a7c91f88d38a88aada5c4e9b3a73a244241"}, - {file = "orjson-3.11.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:624f3951181eb46fc47dea3d221554e98784c823e7069edb5dbd0dc826ac909b"}, - {file = "orjson-3.11.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:03bfa548cf35e3f8b3a96c4e8e41f753c686ff3d8e182ce275b1751deddab58c"}, - {file = "orjson-3.11.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:525021896afef44a68148f6ed8a8bf8375553d6066c7f48537657f64823565b9"}, - {file = "orjson-3.11.4-cp312-cp312-win32.whl", hash = "sha256:b58430396687ce0f7d9eeb3dd47761ca7d8fda8e9eb92b3077a7a353a75efefa"}, - {file = "orjson-3.11.4-cp312-cp312-win_amd64.whl", hash = "sha256:c6dbf422894e1e3c80a177133c0dda260f81428f9de16d61041949f6a2e5c140"}, - {file = "orjson-3.11.4-cp312-cp312-win_arm64.whl", hash = "sha256:d38d2bc06d6415852224fcc9c0bfa834c25431e466dc319f0edd56cca81aa96e"}, - {file = "orjson-3.11.4-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2d6737d0e616a6e053c8b4acc9eccea6b6cce078533666f32d140e4f85002534"}, - {file = "orjson-3.11.4-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:afb14052690aa328cc118a8e09f07c651d301a72e44920b887c519b313d892ff"}, - {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38aa9e65c591febb1b0aed8da4d469eba239d434c218562df179885c94e1a3ad"}, - {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f2cf4dfaf9163b0728d061bebc1e08631875c51cd30bf47cb9e3293bfbd7dcd5"}, - {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89216ff3dfdde0e4070932e126320a1752c9d9a758d6a32ec54b3b9334991a6a"}, - {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9daa26ca8e97fae0ce8aa5d80606ef8f7914e9b129b6b5df9104266f764ce436"}, - {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c8b2769dc31883c44a9cd126560327767f848eb95f99c36c9932f51090bfce9"}, - {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1469d254b9884f984026bd9b0fa5bbab477a4bfe558bba6848086f6d43eb5e73"}, - {file = "orjson-3.11.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:68e44722541983614e37117209a194e8c3ad07838ccb3127d96863c95ec7f1e0"}, - {file = "orjson-3.11.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8e7805fda9672c12be2f22ae124dcd7b03928d6c197544fe12174b86553f3196"}, - {file = "orjson-3.11.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:04b69c14615fb4434ab867bf6f38b2d649f6f300af30a6705397e895f7aec67a"}, - {file = "orjson-3.11.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:639c3735b8ae7f970066930e58cf0ed39a852d417c24acd4a25fc0b3da3c39a6"}, - {file = "orjson-3.11.4-cp313-cp313-win32.whl", hash = "sha256:6c13879c0d2964335491463302a6ca5ad98105fc5db3565499dcb80b1b4bd839"}, - {file = "orjson-3.11.4-cp313-cp313-win_amd64.whl", hash = "sha256:09bf242a4af98732db9f9a1ec57ca2604848e16f132e3f72edfd3c5c96de009a"}, - {file = "orjson-3.11.4-cp313-cp313-win_arm64.whl", hash = "sha256:a85f0adf63319d6c1ba06fb0dbf997fced64a01179cf17939a6caca662bf92de"}, - {file = "orjson-3.11.4-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:42d43a1f552be1a112af0b21c10a5f553983c2a0938d2bbb8ecd8bc9fb572803"}, - {file = "orjson-3.11.4-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:26a20f3fbc6c7ff2cb8e89c4c5897762c9d88cf37330c6a117312365d6781d54"}, - {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e3f20be9048941c7ffa8fc523ccbd17f82e24df1549d1d1fe9317712d19938e"}, - {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aac364c758dc87a52e68e349924d7e4ded348dedff553889e4d9f22f74785316"}, - {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5c54a6d76e3d741dcc3f2707f8eeb9ba2a791d3adbf18f900219b62942803b1"}, - {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f28485bdca8617b79d44627f5fb04336897041dfd9fa66d383a49d09d86798bc"}, - {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bfc2a484cad3585e4ba61985a6062a4c2ed5c7925db6d39f1fa267c9d166487f"}, - {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e34dbd508cb91c54f9c9788923daca129fe5b55c5b4eebe713bf5ed3791280cf"}, - {file = "orjson-3.11.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b13c478fa413d4b4ee606ec8e11c3b2e52683a640b006bb586b3041c2ca5f606"}, - {file = "orjson-3.11.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:724ca721ecc8a831b319dcd72cfa370cc380db0bf94537f08f7edd0a7d4e1780"}, - {file = "orjson-3.11.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:977c393f2e44845ce1b540e19a786e9643221b3323dae190668a98672d43fb23"}, - {file = "orjson-3.11.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1e539e382cf46edec157ad66b0b0872a90d829a6b71f17cb633d6c160a223155"}, - {file = "orjson-3.11.4-cp314-cp314-win32.whl", hash = "sha256:d63076d625babab9db5e7836118bdfa086e60f37d8a174194ae720161eb12394"}, - {file = "orjson-3.11.4-cp314-cp314-win_amd64.whl", hash = "sha256:0a54d6635fa3aaa438ae32e8570b9f0de36f3f6562c308d2a2a452e8b0592db1"}, - {file = "orjson-3.11.4-cp314-cp314-win_arm64.whl", hash = "sha256:78b999999039db3cf58f6d230f524f04f75f129ba3d1ca2ed121f8657e575d3d"}, - {file = "orjson-3.11.4-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:405261b0a8c62bcbd8e2931c26fdc08714faf7025f45531541e2b29e544b545b"}, - {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af02ff34059ee9199a3546f123a6ab4c86caf1708c79042caf0820dc290a6d4f"}, - {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b2eba969ea4203c177c7b38b36c69519e6067ee68c34dc37081fac74c796e10"}, - {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0baa0ea43cfa5b008a28d3c07705cf3ada40e5d347f0f44994a64b1b7b4b5350"}, - {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80fd082f5dcc0e94657c144f1b2a3a6479c44ad50be216cf0c244e567f5eae19"}, - {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e3704d35e47d5bee811fb1cbd8599f0b4009b14d451c4c57be5a7e25eb89a13"}, - {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:caa447f2b5356779d914658519c874cf3b7629e99e63391ed519c28c8aea4919"}, - {file = "orjson-3.11.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bba5118143373a86f91dadb8df41d9457498226698ebdf8e11cbb54d5b0e802d"}, - {file = "orjson-3.11.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:622463ab81d19ef3e06868b576551587de8e4d518892d1afab71e0fbc1f9cffc"}, - {file = "orjson-3.11.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3e0a700c4b82144b72946b6629968df9762552ee1344bfdb767fecdd634fbd5a"}, - {file = "orjson-3.11.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6e18a5c15e764e5f3fc569b47872450b4bcea24f2a6354c0a0e95ad21045d5a9"}, - {file = "orjson-3.11.4-cp39-cp39-win32.whl", hash = "sha256:fb1c37c71cad991ef4d89c7a634b5ffb4447dbd7ae3ae13e8f5ee7f1775e7ab1"}, - {file = "orjson-3.11.4-cp39-cp39-win_amd64.whl", hash = "sha256:e2985ce8b8c42d00492d0ed79f2bd2b6460d00f2fa671dfde4bf2e02f49bf5c6"}, - {file = "orjson-3.11.4.tar.gz", hash = "sha256:39485f4ab4c9b30a3943cfe99e1a213c4776fb69e8abd68f66b83d5a0b0fdc6d"}, + {file = "orjson-3.11.5-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:df9eadb2a6386d5ea2bfd81309c505e125cfc9ba2b1b99a97e60985b0b3665d1"}, + {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc70da619744467d8f1f49a8cadae5ec7bbe054e5232d95f92ed8737f8c5870"}, + {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:073aab025294c2f6fc0807201c76fdaed86f8fc4be52c440fb78fbb759a1ac09"}, + {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:835f26fa24ba0bb8c53ae2a9328d1706135b74ec653ed933869b74b6909e63fd"}, + {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667c132f1f3651c14522a119e4dd631fad98761fa960c55e8e7430bb2a1ba4ac"}, + {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42e8961196af655bb5e63ce6c60d25e8798cd4dfbc04f4203457fa3869322c2e"}, + {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75412ca06e20904c19170f8a24486c4e6c7887dea591ba18a1ab572f1300ee9f"}, + {file = "orjson-3.11.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6af8680328c69e15324b5af3ae38abbfcf9cbec37b5346ebfd52339c3d7e8a18"}, + {file = "orjson-3.11.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a86fe4ff4ea523eac8f4b57fdac319faf037d3c1be12405e6a7e86b3fbc4756a"}, + {file = "orjson-3.11.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e607b49b1a106ee2086633167033afbd63f76f2999e9236f638b06b112b24ea7"}, + {file = "orjson-3.11.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7339f41c244d0eea251637727f016b3d20050636695bc78345cce9029b189401"}, + {file = "orjson-3.11.5-cp310-cp310-win32.whl", hash = "sha256:8be318da8413cdbbce77b8c5fac8d13f6eb0f0db41b30bb598631412619572e8"}, + {file = "orjson-3.11.5-cp310-cp310-win_amd64.whl", hash = "sha256:b9f86d69ae822cabc2a0f6c099b43e8733dda788405cba2665595b7e8dd8d167"}, + {file = "orjson-3.11.5-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9c8494625ad60a923af6b2b0bd74107146efe9b55099e20d7740d995f338fcd8"}, + {file = "orjson-3.11.5-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:7bb2ce0b82bc9fd1168a513ddae7a857994b780b2945a8c51db4ab1c4b751ebc"}, + {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67394d3becd50b954c4ecd24ac90b5051ee7c903d167459f93e77fc6f5b4c968"}, + {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:298d2451f375e5f17b897794bcc3e7b821c0f32b4788b9bcae47ada24d7f3cf7"}, + {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa5e4244063db8e1d87e0f54c3f7522f14b2dc937e65d5241ef0076a096409fd"}, + {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1db2088b490761976c1b2e956d5d4e6409f3732e9d79cfa69f876c5248d1baf9"}, + {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2ed66358f32c24e10ceea518e16eb3549e34f33a9d51f99ce23b0251776a1ef"}, + {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2021afda46c1ed64d74b555065dbd4c2558d510d8cec5ea6a53001b3e5e82a9"}, + {file = "orjson-3.11.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b42ffbed9128e547a1647a3e50bc88ab28ae9daa61713962e0d3dd35e820c125"}, + {file = "orjson-3.11.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8d5f16195bb671a5dd3d1dbea758918bada8f6cc27de72bd64adfbd748770814"}, + {file = "orjson-3.11.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c0e5d9f7a0227df2927d343a6e3859bebf9208b427c79bd31949abcc2fa32fa5"}, + {file = "orjson-3.11.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:23d04c4543e78f724c4dfe656b3791b5f98e4c9253e13b2636f1af5d90e4a880"}, + {file = "orjson-3.11.5-cp311-cp311-win32.whl", hash = "sha256:c404603df4865f8e0afe981aa3c4b62b406e6d06049564d58934860b62b7f91d"}, + {file = "orjson-3.11.5-cp311-cp311-win_amd64.whl", hash = "sha256:9645ef655735a74da4990c24ffbd6894828fbfa117bc97c1edd98c282ecb52e1"}, + {file = "orjson-3.11.5-cp311-cp311-win_arm64.whl", hash = "sha256:1cbf2735722623fcdee8e712cbaaab9e372bbcb0c7924ad711b261c2eccf4a5c"}, + {file = "orjson-3.11.5-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:334e5b4bff9ad101237c2d799d9fd45737752929753bf4faf4b207335a416b7d"}, + {file = "orjson-3.11.5-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:ff770589960a86eae279f5d8aa536196ebda8273a2a07db2a54e82b93bc86626"}, + {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed24250e55efbcb0b35bed7caaec8cedf858ab2f9f2201f17b8938c618c8ca6f"}, + {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a66d7769e98a08a12a139049aac2f0ca3adae989817f8c43337455fbc7669b85"}, + {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86cfc555bfd5794d24c6a1903e558b50644e5e68e6471d66502ce5cb5fdef3f9"}, + {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a230065027bc2a025e944f9d4714976a81e7ecfa940923283bca7bbc1f10f626"}, + {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b29d36b60e606df01959c4b982729c8845c69d1963f88686608be9ced96dbfaa"}, + {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c74099c6b230d4261fdc3169d50efc09abf38ace1a42ea2f9994b1d79153d477"}, + {file = "orjson-3.11.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e697d06ad57dd0c7a737771d470eedc18e68dfdefcdd3b7de7f33dfda5b6212e"}, + {file = "orjson-3.11.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e08ca8a6c851e95aaecc32bc44a5aa75d0ad26af8cdac7c77e4ed93acf3d5b69"}, + {file = "orjson-3.11.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e8b5f96c05fce7d0218df3fdfeb962d6b8cfff7e3e20264306b46dd8b217c0f3"}, + {file = "orjson-3.11.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ddbfdb5099b3e6ba6d6ea818f61997bb66de14b411357d24c4612cf1ebad08ca"}, + {file = "orjson-3.11.5-cp312-cp312-win32.whl", hash = "sha256:9172578c4eb09dbfcf1657d43198de59b6cef4054de385365060ed50c458ac98"}, + {file = "orjson-3.11.5-cp312-cp312-win_amd64.whl", hash = "sha256:2b91126e7b470ff2e75746f6f6ee32b9ab67b7a93c8ba1d15d3a0caaf16ec875"}, + {file = "orjson-3.11.5-cp312-cp312-win_arm64.whl", hash = "sha256:acbc5fac7e06777555b0722b8ad5f574739e99ffe99467ed63da98f97f9ca0fe"}, + {file = "orjson-3.11.5-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:3b01799262081a4c47c035dd77c1301d40f568f77cc7ec1bb7db5d63b0a01629"}, + {file = "orjson-3.11.5-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:61de247948108484779f57a9f406e4c84d636fa5a59e411e6352484985e8a7c3"}, + {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:894aea2e63d4f24a7f04a1908307c738d0dce992e9249e744b8f4e8dd9197f39"}, + {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ddc21521598dbe369d83d4d40338e23d4101dad21dae0e79fa20465dbace019f"}, + {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7cce16ae2f5fb2c53c3eafdd1706cb7b6530a67cc1c17abe8ec747f5cd7c0c51"}, + {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e46c762d9f0e1cfb4ccc8515de7f349abbc95b59cb5a2bd68df5973fdef913f8"}, + {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d7345c759276b798ccd6d77a87136029e71e66a8bbf2d2755cbdde1d82e78706"}, + {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75bc2e59e6a2ac1dd28901d07115abdebc4563b5b07dd612bf64260a201b1c7f"}, + {file = "orjson-3.11.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:54aae9b654554c3b4edd61896b978568c6daa16af96fa4681c9b5babd469f863"}, + {file = "orjson-3.11.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4bdd8d164a871c4ec773f9de0f6fe8769c2d6727879c37a9666ba4183b7f8228"}, + {file = "orjson-3.11.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a261fef929bcf98a60713bf5e95ad067cea16ae345d9a35034e73c3990e927d2"}, + {file = "orjson-3.11.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c028a394c766693c5c9909dec76b24f37e6a1b91999e8d0c0d5feecbe93c3e05"}, + {file = "orjson-3.11.5-cp313-cp313-win32.whl", hash = "sha256:2cc79aaad1dfabe1bd2d50ee09814a1253164b3da4c00a78c458d82d04b3bdef"}, + {file = "orjson-3.11.5-cp313-cp313-win_amd64.whl", hash = "sha256:ff7877d376add4e16b274e35a3f58b7f37b362abf4aa31863dadacdd20e3a583"}, + {file = "orjson-3.11.5-cp313-cp313-win_arm64.whl", hash = "sha256:59ac72ea775c88b163ba8d21b0177628bd015c5dd060647bbab6e22da3aad287"}, + {file = "orjson-3.11.5-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e446a8ea0a4c366ceafc7d97067bfd55292969143b57e3c846d87fc701e797a0"}, + {file = "orjson-3.11.5-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:53deb5addae9c22bbe3739298f5f2196afa881ea75944e7720681c7080909a81"}, + {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82cd00d49d6063d2b8791da5d4f9d20539c5951f965e45ccf4e96d33505ce68f"}, + {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3fd15f9fc8c203aeceff4fda211157fad114dde66e92e24097b3647a08f4ee9e"}, + {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9df95000fbe6777bf9820ae82ab7578e8662051bb5f83d71a28992f539d2cda7"}, + {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92a8d676748fca47ade5bc3da7430ed7767afe51b2f8100e3cd65e151c0eaceb"}, + {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa0f513be38b40234c77975e68805506cad5d57b3dfd8fe3baa7f4f4051e15b4"}, + {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa1863e75b92891f553b7922ce4ee10ed06db061e104f2b7815de80cdcb135ad"}, + {file = "orjson-3.11.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4be86b58e9ea262617b8ca6251a2f0d63cc132a6da4b5fcc8e0a4128782c829"}, + {file = "orjson-3.11.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:b923c1c13fa02084eb38c9c065afd860a5cff58026813319a06949c3af5732ac"}, + {file = "orjson-3.11.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:1b6bd351202b2cd987f35a13b5e16471cf4d952b42a73c391cc537974c43ef6d"}, + {file = "orjson-3.11.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bb150d529637d541e6af06bbe3d02f5498d628b7f98267ff87647584293ab439"}, + {file = "orjson-3.11.5-cp314-cp314-win32.whl", hash = "sha256:9cc1e55c884921434a84a0c3dd2699eb9f92e7b441d7f53f3941079ec6ce7499"}, + {file = "orjson-3.11.5-cp314-cp314-win_amd64.whl", hash = "sha256:a4f3cb2d874e03bc7767c8f88adaa1a9a05cecea3712649c3b58589ec7317310"}, + {file = "orjson-3.11.5-cp314-cp314-win_arm64.whl", hash = "sha256:38b22f476c351f9a1c43e5b07d8b5a02eb24a6ab8e75f700f7d479d4568346a5"}, + {file = "orjson-3.11.5-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1b280e2d2d284a6713b0cfec7b08918ebe57df23e3f76b27586197afca3cb1e9"}, + {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c8d8a112b274fae8c5f0f01954cb0480137072c271f3f4958127b010dfefaec"}, + {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0a2ae6f09ac7bd47d2d5a5305c1d9ed08ac057cda55bb0a49fa506f0d2da00"}, + {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c0d87bd1896faac0d10b4f849016db81a63e4ec5df38757ffae84d45ab38aa71"}, + {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:801a821e8e6099b8c459ac7540b3c32dba6013437c57fdcaec205b169754f38c"}, + {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69a0f6ac618c98c74b7fbc8c0172ba86f9e01dbf9f62aa0b1776c2231a7bffe5"}, + {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea7339bdd22e6f1060c55ac31b6a755d86a5b2ad3657f2669ec243f8e3b2bdb"}, + {file = "orjson-3.11.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4dad582bc93cef8f26513e12771e76385a7e6187fd713157e971c784112aad56"}, + {file = "orjson-3.11.5-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:0522003e9f7fba91982e83a97fec0708f5a714c96c4209db7104e6b9d132f111"}, + {file = "orjson-3.11.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:7403851e430a478440ecc1258bcbacbfbd8175f9ac1e39031a7121dd0de05ff8"}, + {file = "orjson-3.11.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5f691263425d3177977c8d1dd896cde7b98d93cbf390b2544a090675e83a6a0a"}, + {file = "orjson-3.11.5-cp39-cp39-win32.whl", hash = "sha256:61026196a1c4b968e1b1e540563e277843082e9e97d78afa03eb89315af531f1"}, + {file = "orjson-3.11.5-cp39-cp39-win_amd64.whl", hash = "sha256:09b94b947ac08586af635ef922d69dc9bc63321527a3a04647f4986a73f4bd30"}, + {file = "orjson-3.11.5.tar.gz", hash = "sha256:82393ab47b4fe44ffd0a7659fa9cfaacc717eb617c93cde83795f14af5c2e9d5"}, ] [[package]] @@ -1823,14 +1822,14 @@ files = [ [[package]] name = "pydantic" -version = "2.12.4" +version = "2.12.5" description = "Data validation using Python type hints" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e"}, - {file = "pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac"}, + {file = "pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d"}, + {file = "pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49"}, ] [package.dependencies] @@ -2036,83 +2035,83 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] [[package]] name = "pymongo" -version = "4.15.4" +version = "4.15.5" description = "PyMongo - the Official MongoDB Python driver" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pymongo-4.15.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:84c7c7624a1298295487d0dfd8dbec75d14db44c017b5087c7fe7d6996a96e3d"}, - {file = "pymongo-4.15.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:71a5ab372ebe4e05453bae86a008f6db98b5702df551219fb2f137c394d71c3a"}, - {file = "pymongo-4.15.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:eee407bf1058a8f0d5b203028997b42ea6fc80a996537cc2886f89573bc0770f"}, - {file = "pymongo-4.15.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e286f5b9c13963bcaf9b9241846d388ac5022225a9e11c5364393a8cc3eb49"}, - {file = "pymongo-4.15.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:67c3b84a2a0e1794b2fbfe22dc36711a03c6bc147d9d2e0f8072fabed7a65092"}, - {file = "pymongo-4.15.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:94e50149fb9d982c234d0efa9c0eec4a04db7e82a412d3dae2c4f03a9926360e"}, - {file = "pymongo-4.15.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1903c0966969cf3e7b30922956bd82eb09e6a3f3d7431a727d12f20104f66d3"}, - {file = "pymongo-4.15.4-cp310-cp310-win32.whl", hash = "sha256:20ffcd883b6e187ef878558d0ebf9f09cc46807b6520022592522d3cdd21022d"}, - {file = "pymongo-4.15.4-cp310-cp310-win_amd64.whl", hash = "sha256:68ea93e7d19d3aa3182a6e41ba68288b9b234a3b0a70b368feb95fff3f94413f"}, - {file = "pymongo-4.15.4-cp310-cp310-win_arm64.whl", hash = "sha256:abfe72630190c0dc8f2222b02af7c4e5f72809d06b2ccb3f3ca83f6a7b60e302"}, - {file = "pymongo-4.15.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b2967bda6ccac75aefad26c4ef295f5054181d69928bb9d1159227d6771e8887"}, - {file = "pymongo-4.15.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7df1fad859c61bdbe0e2a0dec8f5893729d99b4407b88568e0e542d25f383f57"}, - {file = "pymongo-4.15.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:990c4898787e706d0ab59141cf5085c981d89c3f86443cd6597939d9f25dd71d"}, - {file = "pymongo-4.15.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad7ff0347e8306fc62f146bdad0635d9eec1d26e246c97c14dd1a189d3480e3f"}, - {file = "pymongo-4.15.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dd8c78c59fd7308239ef9bcafb7cd82f08cbc9466d1cfda22f9025c83468bf6d"}, - {file = "pymongo-4.15.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:44d95677aa23fe479bb531b393a4fad0210f808af52e4ab2b79c0b540c828957"}, - {file = "pymongo-4.15.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4ab985e61376ae5a04f162fb6bdddaffc7beec883ffbd9d84ea86a71be794d74"}, - {file = "pymongo-4.15.4-cp311-cp311-win32.whl", hash = "sha256:2f811e93dbcba0c488518ceae7873a40a64b6ad273622a18923ef2442eaab55c"}, - {file = "pymongo-4.15.4-cp311-cp311-win_amd64.whl", hash = "sha256:53bfcd8c11086a2457777cb4b1a6588d9dd6af77aeab47e04f2af02e3a077e59"}, - {file = "pymongo-4.15.4-cp311-cp311-win_arm64.whl", hash = "sha256:2096964b2b93607ed80a62ac6664396a826b7fe34e2b1eed3f20784681a17827"}, - {file = "pymongo-4.15.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4ab4eef031e722a8027c338c3d71704a8c85c17c64625d61c6effdf8a893b971"}, - {file = "pymongo-4.15.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0e12551e28007a341d15ebca5a024ef487edf304d612fba5efa1fd6b4d9a95a9"}, - {file = "pymongo-4.15.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1d21998fb9ccb3ea6d59a9f9971591b9efbcfbbe46350f7f8badef9b107707f3"}, - {file = "pymongo-4.15.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f83e8895d42eb51d259694affa9607c4d56e1c784928ccbbac568dc20df86a8"}, - {file = "pymongo-4.15.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0bd8126a507afa8ce4b96976c8e28402d091c40b7d98e3b5987a371af059d9e7"}, - {file = "pymongo-4.15.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e799e2cba7fcad5ab29f678784f90b1792fcb6393d571ecbe4c47d2888af30f3"}, - {file = "pymongo-4.15.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:563e793ad87633e50ad43a8cd2c740fbb17fca4a4637185996575ddbe99960b8"}, - {file = "pymongo-4.15.4-cp312-cp312-win32.whl", hash = "sha256:39bb3c12c772241778f4d7bf74885782c8d68b309d3c69891fe39c729334adbd"}, - {file = "pymongo-4.15.4-cp312-cp312-win_amd64.whl", hash = "sha256:6f43326f36bc540b04f5a7f1aa8be40b112d7fc9f6e785ae3797cd72a804ffdd"}, - {file = "pymongo-4.15.4-cp312-cp312-win_arm64.whl", hash = "sha256:263cfa2731a4bbafdce2cf06cd511eba8957bd601b3cad9b4723f2543d42c730"}, - {file = "pymongo-4.15.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ff080f23a12c943346e2bba76cf19c3d14fb3625956792aa22b69767bfb36de"}, - {file = "pymongo-4.15.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c4690e01d03773f7af21b1a8428029bd534c9fe467c6b594c591d8b992c0a975"}, - {file = "pymongo-4.15.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:78bfe3917d0606b30a91b02ad954c588007f82e2abb2575ac2665259b051a753"}, - {file = "pymongo-4.15.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f53c83c3fd80fdb412ce4177d4f59b70b9bb1add6106877da044cf21e996316b"}, - {file = "pymongo-4.15.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e41d6650c1cd77a8e7556ad65133455f819f8c8cdce3e9cf4bbf14252b7d805"}, - {file = "pymongo-4.15.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b60fd8125f52efffd697490b6ccebc6e09d44069ad9c8795df0a684a9a8f4b3c"}, - {file = "pymongo-4.15.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d1a1a0406acd000377f34ae91cdb501fa73601a2d071e4a661e0c862e1b166e"}, - {file = "pymongo-4.15.4-cp313-cp313-win32.whl", hash = "sha256:9c5710ed5f2af95315db0ee8ae02e9ff1e85e7b068c507d980bc24fe9d025257"}, - {file = "pymongo-4.15.4-cp313-cp313-win_amd64.whl", hash = "sha256:61b0863c7f9b460314db79b7f8541d3b490b453ece49afd56b611b214fc4b3b1"}, - {file = "pymongo-4.15.4-cp313-cp313-win_arm64.whl", hash = "sha256:0255af7d5c23c5e8cb4d9bb12906b142acebab0472117e1d5e3a8e6e689781cb"}, - {file = "pymongo-4.15.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:539f9fa5bb04a09fc2965cdcae3fc91d1c6a1f4f1965b34df377bc7119e3d7cd"}, - {file = "pymongo-4.15.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:68354a77cf78424d27216b1cb7c9b0f67da16aae855045279ba8d73bb61f5ad0"}, - {file = "pymongo-4.15.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a9a90d556c2ef1572d2aef525ef19477a82d659d117eb3a51fa99e617d07dc44"}, - {file = "pymongo-4.15.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1aac57614fb86a3fa707af3537c30eda5e7fd1be712c1f723296292ac057afe"}, - {file = "pymongo-4.15.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6c21b49c5e021d9ce02cac33525c722d4c6887f7cde19a5a9154f66cb845e84"}, - {file = "pymongo-4.15.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e93828768470026099119295c68ed0dbc0a50022558be5e334f6dbda054f1d32"}, - {file = "pymongo-4.15.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11840e9eb5a650ac190f2a3473631073daddbabdbb2779b6709dfddd3ba3b872"}, - {file = "pymongo-4.15.4-cp314-cp314-win32.whl", hash = "sha256:f0907b46df97b01911bf2e10ddbb23c2303629e482d81372031fd7f4313b9013"}, - {file = "pymongo-4.15.4-cp314-cp314-win_amd64.whl", hash = "sha256:111d7f65ccbde908546cb36d14e22f12a73a4de236fd056f41ed515d1365f134"}, - {file = "pymongo-4.15.4-cp314-cp314-win_arm64.whl", hash = "sha256:c689a5d057ef013612b5aa58e6bf52f7fdb186e22039f1a3719985b5d0399932"}, - {file = "pymongo-4.15.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:cdfa57760745387cde93615a48f622bf1eeae8ae28103a8a5100b9389eec22f9"}, - {file = "pymongo-4.15.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4fd6ba610e5a54090c4055a15f38d19ad8bf11e6bbc5a173e945c755a16db455"}, - {file = "pymongo-4.15.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bd3c7945b8a5563aa3951db26ba534372fba4c781473f5d55ce6340b7523cb0f"}, - {file = "pymongo-4.15.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41e98a31e79d74e9d78bc1638b71c3a10a910eae7d3318e2ae8587c760931451"}, - {file = "pymongo-4.15.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d18d89073b5e752391c237d2ee86ceec1e02a4ad764b3029f24419eedd12723e"}, - {file = "pymongo-4.15.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edbff27a56a80b8fe5c0319200c44e63b1349bf20db27d9734ddcf23c0d72b35"}, - {file = "pymongo-4.15.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1d75f5b51304176631c12e5bf47eed021446669e5f99379b76fd2bd3929c1b4"}, - {file = "pymongo-4.15.4-cp314-cp314t-win32.whl", hash = "sha256:e1bf4e0689cc48e0cfa6aef17f107c298d8898de0c6e782ea5c98450ae93a62f"}, - {file = "pymongo-4.15.4-cp314-cp314t-win_amd64.whl", hash = "sha256:3fc347ea5eda6c3a7177c3a9e4e9b4e570a444a351effda4a898c2d352a1ccd1"}, - {file = "pymongo-4.15.4-cp314-cp314t-win_arm64.whl", hash = "sha256:2d921b84c681c5385a6f7ba2b5740cb583544205a00877aad04b5b12ab86ad26"}, - {file = "pymongo-4.15.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e4c44bb6e781373915c56bc88f3b4849137869284e79c08a5b18f4c0d6adfd26"}, - {file = "pymongo-4.15.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:05e0f2ac285e24de802912908d64dfafca8bea5ab1718a88aab0f197b003dc28"}, - {file = "pymongo-4.15.4-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:94f8e9ab6954899d60babe48418e41217dc510d6fa4305af7aabee244b2a9882"}, - {file = "pymongo-4.15.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9e2f96e7e7a769a7804121c02f3290a39ca4d78a398bc56c6e024728d350897"}, - {file = "pymongo-4.15.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e84c95b185dce012575adc74a18342a2581dc9bb939712125317e03d92148167"}, - {file = "pymongo-4.15.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:649d24ce86f90a8c74897dba35e34c6b86e0d7d7381d7dc18cafbd06dc78fbc3"}, - {file = "pymongo-4.15.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cdab8a71f673b597dbe5cd610913525b59cba34835fc8c6ffb2b62f28028959"}, - {file = "pymongo-4.15.4-cp39-cp39-win32.whl", hash = "sha256:4eb3c2fea850104c41ce3f1f52f6a70f3d1a6998e9c63c197fcaab08c7c89e22"}, - {file = "pymongo-4.15.4-cp39-cp39-win_amd64.whl", hash = "sha256:4ea5dc8a0268ea2e12a6fcfc43bb8f3da969deed46734167238884cbb29c2598"}, - {file = "pymongo-4.15.4-cp39-cp39-win_arm64.whl", hash = "sha256:e6216290982178208b962edc9ba7ebc41b11f276a148ac3b496fc41f86963707"}, - {file = "pymongo-4.15.4.tar.gz", hash = "sha256:6ba7cdf46f03f406f77969a8081cfb659af16c0eee26b79a0a14e25f6c00827b"}, + {file = "pymongo-4.15.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a01a2054d50b50c121c720739a2216d855c48726b0002894de9b991cdd68a2a5"}, + {file = "pymongo-4.15.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e57968139d81367117ed7b75d921445a575d4d7e61536f5e860475df92ac0a9"}, + {file = "pymongo-4.15.5-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:266aa37e3673e5dcfdd359a81d27131fc133e49cf8e5d9f9f27a5845fac2cd1f"}, + {file = "pymongo-4.15.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2883da6bd0545cc2f12672f6a609b33d48e099a220872ca2bf9bf29fe96a32c3"}, + {file = "pymongo-4.15.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2fc32b354a608ec748d89bbe236b74b967890667eea1af54e92dfd8fbf26df52"}, + {file = "pymongo-4.15.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3c006cbaa4b40d296dd2bb8828976866c876ead4c39032b761dcf26f1ba56fde"}, + {file = "pymongo-4.15.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce21e3dc5939b83d03f871090d83ac29fef055bd057f8d3074b6cad10f86b04c"}, + {file = "pymongo-4.15.5-cp310-cp310-win32.whl", hash = "sha256:1b545dcf66a9f06e9b501bfb0438e1eb9af67336e8a5cf36c4bc0a5d3fbe7a37"}, + {file = "pymongo-4.15.5-cp310-cp310-win_amd64.whl", hash = "sha256:1ecc544f515f828f05d3c56cd98063ba3ef8b75f534c63de43306d59f1e93fcd"}, + {file = "pymongo-4.15.5-cp310-cp310-win_arm64.whl", hash = "sha256:1151968ab90db146f0591b6c7db27ce4f73c7ffa0bbddc1d7fb7cb14c9f0b967"}, + {file = "pymongo-4.15.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:57157a4b936e28e2fbe7017b2f6a751da5e284675cab371f2c596d4e0e4f58f3"}, + {file = "pymongo-4.15.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2a34a7391f4cc54fc584e49db6f7c3929221a9da08b3af2d2689884a5943843"}, + {file = "pymongo-4.15.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:be040c8cdaf9c2d5ae9ab60a67ecab453ec19d9ccd457a678053fdceab5ee4c8"}, + {file = "pymongo-4.15.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:defe93944526b1774265c16acf014689cb1b0b18eb84a7b370083b214f9e18cd"}, + {file = "pymongo-4.15.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:816e66116f0ef868eff0463a8b28774af8b547466dbad30c8e82bf0325041848"}, + {file = "pymongo-4.15.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66c7b332532e0f021d784d04488dbf7ed39b7e7d6d5505e282ec8e9cf1025791"}, + {file = "pymongo-4.15.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:acc46a9e47efad8c5229e644a3774169013a46ee28ac72d1fa4edd67c0b7ee9b"}, + {file = "pymongo-4.15.5-cp311-cp311-win32.whl", hash = "sha256:b9836c28ba350d8182a51f32ef9bb29f0c40e82ba1dfb9e4371cd4d94338a55d"}, + {file = "pymongo-4.15.5-cp311-cp311-win_amd64.whl", hash = "sha256:3a45876c5c2ab44e2a249fb542eba2a026f60d6ab04c7ef3924eae338d9de790"}, + {file = "pymongo-4.15.5-cp311-cp311-win_arm64.whl", hash = "sha256:e4a48fc5c712b3db85c9987cfa7fde0366b7930018de262919afd9e52cfbc375"}, + {file = "pymongo-4.15.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c33477af1a50d1b4d86555e098fc2cf5992d839ad538dea0c00a8682162b7a75"}, + {file = "pymongo-4.15.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e6b30defa4a52d3698cd84d608963a8932f7e9b6ec5130087e7082552ac685e5"}, + {file = "pymongo-4.15.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:45fec063f5672e6173bcb09b492431e3641cc74399c2b996fcb995881c2cac61"}, + {file = "pymongo-4.15.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8c6813110c0d9fde18674b7262f47a2270ae46c0ddd05711e6770caa3c9a3fb"}, + {file = "pymongo-4.15.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e8ec48d1db9f44c737b13be4299a1782d5fde3e75423acbbbe927cb37ebbe87d"}, + {file = "pymongo-4.15.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1f410694fdd76631ead7df6544cdeadaf2407179196c3642fced8e48bb21d0a6"}, + {file = "pymongo-4.15.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8c46765d6ac5727a899190aacdeec7a57f8c93346124ddd7e12633b573e2e65"}, + {file = "pymongo-4.15.5-cp312-cp312-win32.whl", hash = "sha256:647118a58dca7d3547714fc0b383aebf81f5852f4173dfd77dd34e80eea9d29b"}, + {file = "pymongo-4.15.5-cp312-cp312-win_amd64.whl", hash = "sha256:099d3e2dddfc75760c6a8fadfb99c1e88824a99c2c204a829601241dff9da049"}, + {file = "pymongo-4.15.5-cp312-cp312-win_arm64.whl", hash = "sha256:649cb906882c4058f467f334fb277083998ba5672ffec6a95d6700db577fd31a"}, + {file = "pymongo-4.15.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b736226f9001bbbd02f822acb9b9b6d28319f362f057672dfae2851f7da6125"}, + {file = "pymongo-4.15.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:60ea9f07fbbcc7c88f922082eb27436dce6756730fdef76a3a9b4c972d0a57a3"}, + {file = "pymongo-4.15.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:20af63218ae42870eaee31fb8cc4ce9e3af7f04ea02fc98ad751fb7a9c8d7be3"}, + {file = "pymongo-4.15.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20d9c11625392f1f8dec7688de5ce344e110ca695344efa313ae4839f13bd017"}, + {file = "pymongo-4.15.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1202b3e5357b161acb7b7cc98e730288a5c15544e5ef7254b33931cb9a27c36e"}, + {file = "pymongo-4.15.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:63af710e9700dbf91abccf119c5f5533b9830286d29edb073803d3b252862c0d"}, + {file = "pymongo-4.15.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f22eeb86861cf7b8ee6886361d52abb88e3cd96c6f6d102e45e2604fc6e9e316"}, + {file = "pymongo-4.15.5-cp313-cp313-win32.whl", hash = "sha256:aad6efe82b085bf77cec2a047ded2c810e93eced3ccf1a8e3faec3317df3cd52"}, + {file = "pymongo-4.15.5-cp313-cp313-win_amd64.whl", hash = "sha256:ccc801f6d71ebee2ec2fb3acc64b218fa7cdb7f57933b2f8eee15396b662a0a0"}, + {file = "pymongo-4.15.5-cp313-cp313-win_arm64.whl", hash = "sha256:f043abdf20845bf29a554e95e4fe18d7d7a463095d6a1547699a12f80da91e02"}, + {file = "pymongo-4.15.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ba0e75a390334221744e2666fd2d4c82419b580c9bc8d6e0d2d61459d263f3af"}, + {file = "pymongo-4.15.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:853ec7da97642eabaf94d3de4453a86365729327d920af167bf14b2e87b24dce"}, + {file = "pymongo-4.15.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7631304106487480ebbd8acbe44ff1e69d1fdc27e83d9753dc1fd227cea10761"}, + {file = "pymongo-4.15.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50505181365eba5d4d35c462870b3614c8eddd0b2407c89377c1a59380640dd9"}, + {file = "pymongo-4.15.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b75ec7006471299a571d6db1c5609ea4aa9c847a701e9b2953a8ede705d82db"}, + {file = "pymongo-4.15.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c3fc24cb1f4ec60ed83162d4bba0c26abc6c9ae78c928805583673f3b3ea6984"}, + {file = "pymongo-4.15.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:21d17bb2934b0640863361c08dd06991f128a97f9bee19425a499227be9ae6b4"}, + {file = "pymongo-4.15.5-cp314-cp314-win32.whl", hash = "sha256:5a3974236cb842b4ef50a5a6bfad9c7d83a713af68ea3592ba240bbcb863305a"}, + {file = "pymongo-4.15.5-cp314-cp314-win_amd64.whl", hash = "sha256:73fa8a7eee44fd95ba7d5cf537340ff3ff34efeb1f7d6790532d0a6ed4dee575"}, + {file = "pymongo-4.15.5-cp314-cp314-win_arm64.whl", hash = "sha256:d41288ca2a3eb9ac7c8cad4ea86ef8d63b69dc46c9b65c2bbd35331ec2a0fc57"}, + {file = "pymongo-4.15.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:552670f0c8bff103656d4e4b1f2c018f789c9de03f7615ed5e547d5b1b83cda0"}, + {file = "pymongo-4.15.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:41891b45f6ff1e23cfd1b7fbe40286664ad4507e2d2aa61c6d8c40eb6e11dded"}, + {file = "pymongo-4.15.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:524a8a593ae2eb1ec6db761daf0c03f98824e9882ab7df3d458d0c76c7ade255"}, + {file = "pymongo-4.15.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e7ceb35c41b86711a1b284c604e2b944a2d46cb1b8dd3f8b430a9155491378f2"}, + {file = "pymongo-4.15.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3be2336715924be3a861b5e40c634376fd6bfe6dd1892d391566aa5a88a31307"}, + {file = "pymongo-4.15.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d65df9c015e33f74ea9d1abf474971abca21e347a660384f8227dbdab75a33ca"}, + {file = "pymongo-4.15.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83c05bea05e151754357f8e6bbb80d5accead5110dc58f64e283173c71ec9de2"}, + {file = "pymongo-4.15.5-cp314-cp314t-win32.whl", hash = "sha256:7c285614a3e8570b03174a25db642e449b0e7f77a6c9e487b73b05c9bf228ee6"}, + {file = "pymongo-4.15.5-cp314-cp314t-win_amd64.whl", hash = "sha256:aae7d96f7b2b1a2753349130797543e61e93ee2ace8faa7fbe0565e2eb5d815f"}, + {file = "pymongo-4.15.5-cp314-cp314t-win_arm64.whl", hash = "sha256:576a7d4b99465d38112c72f7f3d345f9d16aeeff0f923a3b298c13e15ab4f0ad"}, + {file = "pymongo-4.15.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:092ed5f3a53b546f8350a77976dabb0a11105d6b7c0f86a39934464168c97cff"}, + {file = "pymongo-4.15.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5762f6445a611b34eb500260303483520bd73e6816a39503378444d551e92f7c"}, + {file = "pymongo-4.15.5-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:397fa40b6d331949debd3e0892c420a81a44e7e0f5a570661910b0c57a7e7431"}, + {file = "pymongo-4.15.5-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d5710c0e04c37932984241282d3011304c35eb798a0026d84e1bd3525266d026"}, + {file = "pymongo-4.15.5-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7a476310f3c9bdba08ab4b1d4309ee308a1b9e22823210fd7b48c83709e95ac4"}, + {file = "pymongo-4.15.5-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db5689bd2c1cf1dc4f4e94ec8a012ea521f9892a85b3c694fa9ace7cdc2d0416"}, + {file = "pymongo-4.15.5-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e2623deb5be1b5bc23319ba5ab435b5a526a1e92739ff0e0e9048823f295460"}, + {file = "pymongo-4.15.5-cp39-cp39-win32.whl", hash = "sha256:addaaa62c357e8de3d0fca2fce1acf5b72f4bbf4e7bb35ce1dd68e40e73880f9"}, + {file = "pymongo-4.15.5-cp39-cp39-win_amd64.whl", hash = "sha256:1e4070593ea98bc6def3c84cfc6de28da289e4ed944bb20845f9de9beefb0921"}, + {file = "pymongo-4.15.5-cp39-cp39-win_arm64.whl", hash = "sha256:01227e6bc75a949f7d3303005e27707a0e14a941dc63a183cd449c80e7853fe3"}, + {file = "pymongo-4.15.5.tar.gz", hash = "sha256:3a8d6bf2610abe0c97c567cf98bf5bba3e90ccc93cc03c9dde75fa11e4267b42"}, ] [package.dependencies] @@ -2680,14 +2679,14 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "rich-toolkit" -version = "0.16.0" +version = "0.17.0" description = "Rich toolkit for building command-line applications" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "rich_toolkit-0.16.0-py3-none-any.whl", hash = "sha256:3f4307f678c5c1e22c36d89ac05f1cd145ed7174f19c1ce5a4d3664ba77c0f9e"}, - {file = "rich_toolkit-0.16.0.tar.gz", hash = "sha256:2f554b00b194776639f4d80f2706980756b659ceed9f345ebbd9de77d1bdd0f4"}, + {file = "rich_toolkit-0.17.0-py3-none-any.whl", hash = "sha256:06fb47a5c5259d6b480287cd38aff5f551b6e1a307f90ed592453dd360e4e71e"}, + {file = "rich_toolkit-0.17.0.tar.gz", hash = "sha256:17ca7a32e613001aa0945ddea27a246f6de01dfc4c12403254c057a8ee542977"}, ] [package.dependencies] @@ -2845,18 +2844,6 @@ files = [ {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, ] -[[package]] -name = "sniffio" -version = "1.3.1" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - [[package]] name = "socksio" version = "1.0.0" @@ -3130,21 +3117,21 @@ files = [ [[package]] name = "urllib3" -version = "2.5.0" +version = "2.6.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, - {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, + {file = "urllib3-2.6.1-py3-none-any.whl", hash = "sha256:e67d06fe947c36a7ca39f4994b08d73922d40e6cca949907be05efa6fd75110b"}, + {file = "urllib3-2.6.1.tar.gz", hash = "sha256:5379eb6e1aba4088bae84f8242960017ec8d8e3decf30480b3a1abdaa9671a3f"}, ] [package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] +zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [[package]] name = "uvicorn" diff --git a/tests/test_applets/test_applet_activity.py b/tests/test_applets/test_applet_activity.py index b11c43ff..fe026423 100644 --- a/tests/test_applets/test_applet_activity.py +++ b/tests/test_applets/test_applet_activity.py @@ -1,4 +1,5 @@ from tests.test_applets.base import BaseAppletTest +from bbot_server.modules.targets.targets_models import CreateTarget class TestAppletActivity(BaseAppletTest): @@ -9,8 +10,10 @@ async def setup(self): assert [a async for a in self.bbot_server.list_activities()] == [] # create some targets - await self.bbot_server.create_target(name="evilcorp1", seeds=["www2.evilcorp.com"]) - await self.bbot_server.create_target(name="evilcorp2", seeds=["www.evilcorp.com", "api.evilcorp.com"]) + target1 = CreateTarget(name="evilcorp1", target=["tech.evilcorp.com"]) + target2 = CreateTarget(name="evilcorp2", target=["evilcorp.com"], blacklist=["api.evilcorp.com"]) + self.target1 = await self.bbot_server.create_target(target1) + self.target2 = await self.bbot_server.create_target(target2) async def after_scan_1(self): activities = [a async for a in self.bbot_server.list_activities()] @@ -31,6 +34,25 @@ async def after_scan_2(self): assert activities assert all(a.get("host", "").endswith("evilcorp.amazonaws.com") for a in activities) + activities = [a async for a in self.bbot_server.query_activities(domain="tech.evilcorp.com")] + sorted_descriptions = sorted(a["description"] for a in activities) + assert sorted_descriptions == [ + "Host t1.tech.evilcorp.com became in-scope for target evilcorp1 due to in-scope host SELF->t1.tech.evilcorp.com", + "Host t1.tech.evilcorp.com became in-scope for target evilcorp2 due to in-scope host SELF->t1.tech.evilcorp.com", + "Host t2.tech.evilcorp.com became in-scope for target evilcorp1 due to in-scope host SELF->t2.tech.evilcorp.com", + "Host t2.tech.evilcorp.com became in-scope for target evilcorp2 due to in-scope host SELF->t2.tech.evilcorp.com", + "New DNS link: t1.tech.evilcorp.com -(A)-> [192.168.1.1]", + "New DNS link: t2.tech.evilcorp.com -(A)-> [192.168.1.2]", + "New asset: [t1.tech.evilcorp.com]", + "New asset: [t2.tech.evilcorp.com]", + "New open port: [t1.tech.evilcorp.com:443]", + "New open port: [t1.tech.evilcorp.com:80]", + "New open port: [t2.tech.evilcorp.com:443]", + "New technology discovered on t1.tech.evilcorp.com: [cpe:/a:apache:http_server:2.4.12]", + "New technology discovered on t2.tech.evilcorp.com: [cpe:/a:apache:http_server:2.4.12]", + "New technology discovered on t2.tech.evilcorp.com: [cpe:/a:microsoft:internet_information_services]", + ] + # activities aggregation aggregate_result = [ a @@ -40,10 +62,17 @@ async def after_scan_2(self): ) ] assert aggregate_result == [ - {"_id": "t1.tech.evilcorp.com", "count": 5}, - {"_id": "t2.tech.evilcorp.com", "count": 5}, + {"_id": "t1.tech.evilcorp.com", "count": 7}, + {"_id": "t2.tech.evilcorp.com", "count": 7}, ] # test count count = await self.bbot_server.count_activities(domain="tech.evilcorp.com") - assert count == 10 + assert count == 14 + + # NOTE: we do not allow filtering activities by target ID. + # Why? Because activities can grow to a much larger size than assets, and maintaining up-to-date target IDs on them can become too expensive. + # If you want to filter activities by target ID, get the asset hosts you need, then query activities for those hosts. + # activities = [a async for a in self.bbot_server.query_activities(target_id=self.target1.id)] + # assert activities + # assert all(self.target1.id in a.scope for a in activities) diff --git a/tests/test_applets/test_applet_assets.py b/tests/test_applets/test_applet_assets.py index 2efd5bde..9b44fd2a 100644 --- a/tests/test_applets/test_applet_assets.py +++ b/tests/test_applets/test_applet_assets.py @@ -3,6 +3,7 @@ from bbot_server.assets import Asset from bbot_server.errors import BBOTServerValueError +from bbot_server.modules.targets.targets_models import CreateTarget from tests.test_applets.base import BaseAppletTest from ..conftest import INGEST_PROCESSING_DELAY @@ -269,10 +270,11 @@ async def after_archive(self): async def test_applet_target_filter(bbot_server, bbot_events): bbot_server = await bbot_server(needs_watchdog=True) - target1 = await bbot_server.create_target( - whitelist=["evilcorp.com", "127.0.0.0/30"], + target1 = CreateTarget( + target=["evilcorp.com", "127.0.0.0/30"], blacklist=["localhost.evilcorp.com"], ) + target1 = await bbot_server.create_target(target1) # ingest BBOT events scan1_events, scan2_events = bbot_events @@ -338,10 +340,11 @@ async def test_applet_target_filter(bbot_server, bbot_events): assert set(hosts) == all_hosts_target1 # new target - target = await bbot_server.create_target( - whitelist=["1.2.3.0/24"], + target = CreateTarget( + target=["1.2.3.0/24"], blacklist=["www2.evilcorp.com"], ) + target = await bbot_server.create_target(target) # wait for events to be tagged with new target await asyncio.sleep(1) diff --git a/tests/test_applets/test_applet_events.py b/tests/test_applets/test_applet_events.py index 87f2bab3..5300dece 100644 --- a/tests/test_applets/test_applet_events.py +++ b/tests/test_applets/test_applet_events.py @@ -85,7 +85,7 @@ async def after_scan_2(self): modules = {} for event in host_events: modules[event.module] = modules.get(event.module, 0) + 1 - assert modules == {"TARGET": 2, "speculate": 2} + assert modules == {"SEED": 2, "speculate": 2} host_events = [e async for e in self.bbot_server.list_events(host="com")] assert host_events == [] diff --git a/tests/test_applets/test_applet_findings.py b/tests/test_applets/test_applet_findings.py index 88b480db..d667510a 100644 --- a/tests/test_applets/test_applet_findings.py +++ b/tests/test_applets/test_applet_findings.py @@ -1,6 +1,7 @@ import asyncio from hashlib import sha1 from tests.test_applets.base import BaseAppletTest +from bbot_server.modules.targets.targets_models import CreateTarget class TestAppletFindings(BaseAppletTest): @@ -11,8 +12,10 @@ async def setup(self): assert [f async for f in self.bbot_server.list_findings()] == [] # create some targets - await self.bbot_server.create_target(name="evilcorp1", seeds=["www2.evilcorp.com"]) - await self.bbot_server.create_target(name="evilcorp2", seeds=["www.evilcorp.com", "api.evilcorp.com"]) + target1 = CreateTarget(name="evilcorp1", target=["www2.evilcorp.com"]) + target2 = CreateTarget(name="evilcorp2", target=["www.evilcorp.com", "api.evilcorp.com"]) + self.target1 = await self.bbot_server.create_target(target1) + self.target2 = await self.bbot_server.create_target(target2) async def after_scan_1(self): # we should have 2 findings @@ -98,7 +101,8 @@ async def after_scan_2(self): assert {f.host for f in findings2} == {"www.evilcorp.com", "api.evilcorp.com"} # create a new target that matches one finding - await self.bbot_server.create_target(name="evilcorp3", seeds=["www.evilcorp.com"]) + target = CreateTarget(name="evilcorp3", target=["www.evilcorp.com"]) + target = await self.bbot_server.create_target(target) # the finding should be automatically associated with the target for _ in range(60): findings = [f async for f in self.bbot_server.list_findings(target_id="evilcorp3")] diff --git a/tests/test_applets/test_applet_scans.py b/tests/test_applets/test_applet_scans.py index 371d01d5..4686b651 100644 --- a/tests/test_applets/test_applet_scans.py +++ b/tests/test_applets/test_applet_scans.py @@ -6,6 +6,7 @@ from bbot_server import BBOTServer from bbot_server.errors import BBOTServerValueError +from bbot_server.modules.targets.targets_models import CreateTarget from ..conftest import INGEST_PROCESSING_DELAY, log @@ -81,7 +82,8 @@ async def test_scan_with_invalid_preset(bbot_server, bbot_agent): preset = await bbot_server.create_preset( preset={"name": "preset1", "description": "preset1 description", "modules": ["invalid"]} ) - target = await bbot_server.create_target(name="target1", seeds=["127.0.0.1"]) + target = CreateTarget(name="target1", target=["127.0.0.1"]) + target = await bbot_server.create_target(target) await bbot_server.start_scan(name="scan1", preset_id=preset.id, target_id=target.id) for _ in range(30): @@ -117,9 +119,10 @@ async def watch_events(): else: assert False, "Agent did not become ready" - target = await bbot_server.create_target( - name="target1", seeds=["127.0.0.1"], whitelist=["127.0.0.2"], blacklist=["127.0.0.3"], strict_dns_scope=True + target = CreateTarget( + name="target1", target=["127.0.0.2"], seeds=["127.0.0.1"], blacklist=["127.0.0.3"], strict_dns_scope=True ) + target = await bbot_server.create_target(target) preset = await bbot_server.create_preset( preset={"name": "preset1", "description": "preset1 description", "scan_name": "teh_scan"} ) @@ -154,8 +157,8 @@ async def watch_events(): assert len(scan_events) == 2 for scan_event in scan_events: assert scan_event.data_json["name"] == "teh_scan" + assert scan_event.data_json["target"]["target"] == ["127.0.0.2"] assert scan_event.data_json["target"]["seeds"] == ["127.0.0.1"] - assert scan_event.data_json["target"]["whitelist"] == ["127.0.0.2"] assert scan_event.data_json["target"]["blacklist"] == ["127.0.0.3"] assert scan_event.data_json["target"]["strict_dns_scope"] == True @@ -201,7 +204,8 @@ async def test_queued_scan_cancellation(bbot_server): """ bbot_server = await bbot_server() - target = await bbot_server.create_target(name="target1", seeds=["evilcorp.com"]) + target = CreateTarget(name="target1", target=["evilcorp.com"]) + target = await bbot_server.create_target(target) preset = await bbot_server.create_preset(preset={"name": "preset1", "description": "preset1 description"}) scan = await bbot_server.start_scan(name="scan1", target_id=target.id, preset_id=preset.id) @@ -229,7 +233,8 @@ async def test_running_scan_cancellation(bbot_agent, bbot_watchdog): infinite_module_dir = Path(__file__).parent.parent / "bbot_modules" # start scan - target = await bbot_server.create_target(name="target1", seeds=["evilcorp.com"]) + target = CreateTarget(name="target1", target=["evilcorp.com"]) + target = await bbot_server.create_target(target) preset = await bbot_server.create_preset( preset={ "name": "preset1", diff --git a/tests/test_applets/test_applet_stats.py b/tests/test_applets/test_applet_stats.py index 69559caa..9ecdcea2 100644 --- a/tests/test_applets/test_applet_stats.py +++ b/tests/test_applets/test_applet_stats.py @@ -1,13 +1,15 @@ import asyncio +from bbot_server.modules.targets.targets_models import CreateTarget async def test_applet_stats(bbot_server, bbot_events): bbot_server = await bbot_server(needs_watchdog=True) - target1 = await bbot_server.create_target( - whitelist=["evilcorp.com"], + target1 = CreateTarget( + target=["evilcorp.com"], blacklist=["www.evilcorp.com"], ) + target1 = await bbot_server.create_target(target1) # ingest BBOT events for scan_events in bbot_events: diff --git a/tests/test_applets/test_applet_targets.py b/tests/test_applets/test_applet_targets.py index 27c58928..7f740583 100644 --- a/tests/test_applets/test_applet_targets.py +++ b/tests/test_applets/test_applet_targets.py @@ -3,6 +3,7 @@ from contextlib import suppress from tests.test_applets.base import BaseAppletTest +from bbot_server.modules.targets.targets_models import CreateTarget from bbot_server.errors import BBOTServerNotFoundError, BBOTServerValueError @@ -27,13 +28,14 @@ async def handle_activity(): assert num_targets == 0 # create a target - target1 = await bbot_server.create_target( + target1 = CreateTarget( name="target1", description="target1 description", + target=["127.0.0.1", "evilcorp.com"], seeds=["localhost"], - whitelist=["127.0.0.1", "evilcorp.com"], blacklist=["127.0.0.2"], ) + target1 = await bbot_server.create_target(target1) assert target1.created is not None assert target1.modified is not None @@ -51,7 +53,7 @@ async def handle_activity(): assert target.id == target1.id assert target.description == "target1 description" assert target.seeds == ["localhost"] - assert target.whitelist == ["127.0.0.1", "evilcorp.com"] + assert target.target == ["127.0.0.1", "evilcorp.com"] assert target.blacklist == ["127.0.0.2"] assert target.default is True @@ -62,7 +64,11 @@ async def handle_activity(): # creating a target with the same name should raise an error with pytest.raises(BBOTServerValueError, match='Target with name "target1" already exists'): try: - await bbot_server.create_target(name="target1", seeds=["localhost"]) + target = CreateTarget( + name="target1", + target=["localhost"], + ) + target = await bbot_server.create_target(target) except BBOTServerValueError as e: assert e.detail["name"] == "target1" raise @@ -70,26 +76,28 @@ async def handle_activity(): # creating a target with the same hash should raise an error with pytest.raises(BBOTServerValueError, match="Identical target already exists"): try: - await bbot_server.create_target( + target2 = CreateTarget( name="asdgasdgasdf", seeds=["localhost"], - whitelist=["127.0.0.1", "evilcorp.com"], + target=["127.0.0.1", "evilcorp.com"], blacklist=["127.0.0.2"], ) + target2 = await bbot_server.create_target(target2) except BBOTServerValueError as e: assert e.detail["hash"] == target1.hash raise # create a second target - target2 = await bbot_server.create_target( + target2 = CreateTarget( name="target2", description="target2 description", seeds=["localhost"], - whitelist=["127.0.0.1", "evilcorp.com", "localhost2"], + target=["127.0.0.1", "evilcorp.com", "localhost2"], blacklist=["127.0.0.2"], ) + target2 = await bbot_server.create_target(target2) - assert target2.whitelist_hash != target1.whitelist_hash + assert target2.target_hash != target1.target_hash assert target2.blacklist_hash == target1.blacklist_hash assert target2.seed_hash == target1.seed_hash assert target2.hash != target1.hash @@ -102,7 +110,7 @@ async def handle_activity(): assert target.id == target2.id assert target.description == "target2 description" assert target.seeds == ["localhost"] - assert target.whitelist == ["127.0.0.1", "evilcorp.com", "localhost2"] + assert target.target == ["127.0.0.1", "evilcorp.com", "localhost2"] assert target.blacklist == ["127.0.0.2"] assert target.default is False @@ -129,7 +137,7 @@ async def handle_activity(): # edit target2 target2.name = "target2_edited" target2.seeds = [] - target2.whitelist = [] + target2.target = [] target2.blacklist = [] await asyncio.sleep(0.1) await bbot_server.update_target(target2.id, target2) @@ -138,18 +146,19 @@ async def handle_activity(): target = targets[0] assert target.name == "target2_edited" assert target.seeds == [] - assert target.whitelist == [] + assert target.target == [] assert target.blacklist == [] assert abs(target.created - target.modified) >= 0.1, "Modified timestamp wasn't updated" # add target3 - target3 = await bbot_server.create_target( + target3 = CreateTarget( name="target3", description="target3 description", seeds=["localhost", "localhost3"], - whitelist=["127.0.0.1", "evilcorp.com", "localhost3"], + target=["127.0.0.1", "evilcorp.com", "localhost3"], blacklist=["127.0.0.2"], ) + target3 = await bbot_server.create_target(target3) # set target3 as the default target await bbot_server.set_default_target(target3.id) @@ -163,13 +172,14 @@ async def handle_activity(): assert target.default is False # create target4 - await bbot_server.create_target( + target4 = CreateTarget( name="target4", description="target4 description", seeds=["localhost"], - whitelist=["127.0.0.1", "evilcorp.com", "localhost4"], + target=["127.0.0.1", "evilcorp.com", "localhost4"], blacklist=["127.0.0.2"], ) + target4 = await bbot_server.create_target(target4) # deleting the default target without specifying a new default target should raise an error with pytest.raises( @@ -204,27 +214,32 @@ async def handle_activity(): async def test_target_default_names(bbot_server): bbot_server = await bbot_server() - with pytest.raises(BBOTServerValueError, match="Must provide at least one seed"): - await bbot_server.create_target() + target1 = CreateTarget() + with pytest.raises(BBOTServerValueError, match="Must provide at least one seed or target entry"): + await bbot_server.create_target(target1) - target1 = await bbot_server.create_target(seeds=["evilcorp.com"]) + target1 = CreateTarget(target=["evilcorp.com"]) + target1 = await bbot_server.create_target(target1) assert target1.name == "Target 1" - target2 = await bbot_server.create_target(seeds=["evilcorp.org"]) + target2 = CreateTarget(target=["evilcorp.org"]) + target2 = await bbot_server.create_target(target2) assert target2.name == "Target 2" - target3 = await bbot_server.create_target(seeds=["evilcorp.net"]) + target3 = CreateTarget(target=["evilcorp.net"]) + target3 = await bbot_server.create_target(target3) assert target3.name == "Target 3" async def test_target_size(bbot_server): bbot_server = await bbot_server() - target = await bbot_server.create_target( + target = CreateTarget( seeds=["evilcorp.com", "1.2.3.4/30"], - whitelist=["evilcorp.com", "1.2.3.4/29"], + target=["evilcorp.com", "1.2.3.4/29"], blacklist=["www.evilcorp.com", "test.evilcorp.com", "1.2.3.5/28"], ) + target = await bbot_server.create_target(target) assert target.seed_size == 5 # /30 (4 hosts) + 1 domain - assert target.whitelist_size == 9 # /29 (8 hosts) + 1 domain + assert target.target_size == 9 # /29 (8 hosts) + 1 domain assert target.blacklist_size == 18 # /28 (16 hosts) + 2 domains @@ -232,18 +247,19 @@ async def test_scope_checks(bbot_server): bbot_server = await bbot_server() # simple target - await bbot_server.create_target( + target1 = CreateTarget( name="target1", description="target1 description", - seeds=["evilcorp.com"], + target=["evilcorp.com"], ) + await bbot_server.create_target(target1) targets = await bbot_server.get_targets() assert len(targets) == 1 target = targets[0] assert target.name == "target1" - assert target.seeds == ["evilcorp.com"] - assert target.whitelist == None + assert target.target == ["evilcorp.com"] + assert target.seeds == None assert target.blacklist == [] assert await bbot_server.in_scope("evilcorp.com") == True @@ -254,13 +270,14 @@ async def test_scope_checks(bbot_server): assert await bbot_server.in_scope("http://test.evilcorp.net") == False # complex target - target2 = await bbot_server.create_target( + target2 = CreateTarget( name="target2", description="target2 description", seeds=["evilcorp.org"], - whitelist=["127.0.0.1/24", "external.evilcorp.org"], + target=["127.0.0.1/24", "external.evilcorp.org"], blacklist=["127.0.0.2", "test.external.evilcorp.org", "RE:plumbus"], ) + target2 = await bbot_server.create_target(target2) # default target is still target1 assert await bbot_server.in_scope("evilcorp.org") == False @@ -296,22 +313,23 @@ async def setup(self): assert await self.bbot_server.get_targets() == [] # target with domain blacklist - self.target1 = await self.bbot_server.create_target( + target1 = CreateTarget( name="evilcorp", description="evilcorp target", seeds=["evilcorp.com"], - whitelist=["evilcorp.com"], + target=["evilcorp.com"], blacklist=["www.evilcorp.com"], ) - + self.target1 = await self.bbot_server.create_target(target1) # target with IP blacklist - self.target2 = await self.bbot_server.create_target( + target2 = CreateTarget( name="www evilcorp", description="www evilcorp target", seeds=["evilcorp.com"], - whitelist=["www.evilcorp.com", "localhost.evilcorp.com", "127.0.0.1"], + target=["www.evilcorp.com", "localhost.evilcorp.com", "127.0.0.1"], blacklist=["127.0.0.2"], ) + self.target2 = await self.bbot_server.create_target(target2) async def after_scan_1(self): assets = [a async for a in self.bbot_server.list_assets()] @@ -353,8 +371,15 @@ async def after_scan_2(self): "127.0.0.1", } + target_1_assets_filtered = {a.host async for a in self.bbot_server.list_assets(target_id="evilcorp")} + assert target_1_assets_filtered == target_1_assets + target_2_assets_filtered = {a.host async for a in self.bbot_server.list_assets(target_id="www evilcorp")} + assert target_2_assets_filtered == target_2_assets + target_assets_default = {a.host async for a in self.bbot_server.list_assets(target_id="DEFAULT")} + assert target_assets_default == target_1_assets + # add evilcorp.azure.com to target2 - self.target2.whitelist = ["127.0.0.0/24"] + self.target2.target = ["127.0.0.0/24"] await self.bbot_server.update_target(self.target2.id, self.target2) await asyncio.sleep(1.0) diff --git a/tests/test_applets/test_applet_technologies.py b/tests/test_applets/test_applet_technologies.py index ddc6048a..0358a935 100644 --- a/tests/test_applets/test_applet_technologies.py +++ b/tests/test_applets/test_applet_technologies.py @@ -1,5 +1,6 @@ import asyncio from tests.test_applets.base import BaseAppletTest +from bbot_server.modules.targets.targets_models import CreateTarget class TestAppletTechnologies(BaseAppletTest): @@ -110,7 +111,8 @@ async def after_scan_2(self): } # create a new target that matches two technologies - await self.bbot_server.create_target(seeds=["t1.tech.evilcorp.com"], name="target1") + target = CreateTarget(name="target1", target=["t1.tech.evilcorp.com"]) + target = await self.bbot_server.create_target(target) # the technologies should be automatically associated with the target for _ in range(60): techs = [t async for t in self.bbot_server.list_technologies(target_id="target1")] diff --git a/tests/test_cli/test_cli_assetctl.py b/tests/test_cli/test_cli_assetctl.py index 12747b6b..8239350e 100644 --- a/tests/test_cli/test_cli_assetctl.py +++ b/tests/test_cli/test_cli_assetctl.py @@ -109,7 +109,7 @@ def test_cli_assetctl(bbot_server_http, bbot_watchdog, bbot_out_file, bbot_event "create", "--name", "test-target", - "--seeds", + "--target", str(target_file), "--blacklist", str(blacklist_file), diff --git a/tests/test_cli/test_cli_findingctl.py b/tests/test_cli/test_cli_findingctl.py index 91ba736b..9bdcc832 100644 --- a/tests/test_cli/test_cli_findingctl.py +++ b/tests/test_cli/test_cli_findingctl.py @@ -106,7 +106,7 @@ def test_cli_findingctl(bbot_server_http, bbot_watchdog, bbot_out_file): # create target target_file = BBOT_SERVER_TEST_DIR / "targets" target_file.write_text("www2.evilcorp.com") - command = BBCTL_COMMAND + ["scan", "target", "create", "--seeds", target_file, "--name", "evilcorp1"] + command = BBCTL_COMMAND + ["scan", "target", "create", "--target", target_file, "--name", "evilcorp1"] process = subprocess.run(command, capture_output=True, text=True) assert process.returncode == 0 diff --git a/tests/test_cli/test_cli_scanctl.py b/tests/test_cli/test_cli_scanctl.py index 8598e204..91c0e857 100644 --- a/tests/test_cli/test_cli_scanctl.py +++ b/tests/test_cli/test_cli_scanctl.py @@ -35,7 +35,7 @@ def test_cli_scan_start(bbot_server_http, bbot_watchdog, bbot_agent): "scan", "target", "create", - "--seeds", + "--target", str(target_file), "--name", "thetarget", @@ -90,11 +90,10 @@ def test_cli_scan_start(bbot_server_http, bbot_watchdog, bbot_agent): ) for _ in range(120): - process = subprocess.run(BBCTL_COMMAND + ["scan", "list", "--json"], capture_output=True, text=True) + process = subprocess.run(BBCTL_COMMAND + ["scan", "list", "--json"], capture_output=True, text=True, timeout=5) scans = [Scan(**orjson.loads(line)) for line in process.stdout.splitlines()] if scans[0].status == "FINISHED": break - sleep(0.5) else: assert False, f"Scan did not finish in time, scans: {scans}" @@ -156,12 +155,12 @@ def test_cli_scan_ingest(bbot_server_http, bbot_watchdog, bbot_out_file, bbot_ev assert {s.name for s in out_scan_runs} == {scan1_name, scan2_name} # test text version - process = subprocess.run(BBCTL_COMMAND + ["scan", "list"], capture_output=True, text=True) + process = subprocess.run(BBCTL_COMMAND + ["scan", "list"], capture_output=True, text=True, timeout=5) assert scan1_name in process.stdout assert scan2_name in process.stdout # test csv version - process = subprocess.run(BBCTL_COMMAND + ["scan", "list", "--csv"], capture_output=True, text=True) + process = subprocess.run(BBCTL_COMMAND + ["scan", "list", "--csv"], capture_output=True, text=True, timeout=5) assert len([l for l in process.stdout.splitlines() if l.strip()]) == 3 assert scan1_name in process.stdout assert scan2_name in process.stdout diff --git a/tests/test_cli/test_cli_targetctl.py b/tests/test_cli/test_cli_targetctl.py index c9c6d265..6572bef5 100644 --- a/tests/test_cli/test_cli_targetctl.py +++ b/tests/test_cli/test_cli_targetctl.py @@ -13,20 +13,20 @@ def test_cli_targetctl(bbot_server_http): assert process.stdout == "" # create a target (nonexistent file should fail) - seeds_file = BBOT_SERVER_TEST_DIR / "seeds.txt" - seeds_file.unlink(missing_ok=True) + target_file = BBOT_SERVER_TEST_DIR / "target.txt" + target_file.unlink(missing_ok=True) process = subprocess.run( - BBCTL_COMMAND + ["--no-color", "scan", "target", "create", "--seeds", str(seeds_file)], + BBCTL_COMMAND + ["--no-color", "scan", "target", "create", "--target", str(target_file)], capture_output=True, text=True, ) assert process.returncode == 1 - assert f"Unable to find seeds at {seeds_file}" in process.stderr + assert f"Unable to find target at {target_file}" in process.stderr # create a target - seeds_file.write_text("evilcorp.com\nevilcorp.net") + target_file.write_text("evilcorp.com\nevilcorp.net") process = subprocess.run( - BBCTL_COMMAND + ["--no-color", "scan", "target", "create", "--seeds", str(seeds_file)], + BBCTL_COMMAND + ["--no-color", "scan", "target", "create", "--target", str(target_file)], capture_output=True, text=True, ) @@ -34,11 +34,11 @@ def test_cli_targetctl(bbot_server_http): assert "Target created successfully" in process.stderr target = orjson.loads(process.stdout) assert target["name"] == "Target 1" - assert set(target["seeds"]) == {"evilcorp.com", "evilcorp.net"} + assert set(target["target"]) == {"evilcorp.com", "evilcorp.net"} # creating the same target again should fail process = subprocess.run( - BBCTL_COMMAND + ["--no-color", "scan", "target", "create", "--seeds", str(seeds_file)], + BBCTL_COMMAND + ["--no-color", "scan", "target", "create", "--target", str(target_file)], capture_output=True, text=True, ) @@ -46,9 +46,9 @@ def test_cli_targetctl(bbot_server_http): assert "Identical target already exists" in process.stderr # create a second target - seeds_file.write_text("evilcorp.org") + target_file.write_text("evilcorp.org") process = subprocess.run( - BBCTL_COMMAND + ["--no-color", "scan", "target", "create", "--seeds", str(seeds_file), "--strict-scope"], + BBCTL_COMMAND + ["--no-color", "scan", "target", "create", "--target", str(target_file), "--strict-scope"], capture_output=True, text=True, ) @@ -57,9 +57,9 @@ def test_cli_targetctl(bbot_server_http): assert process.returncode == 0 target2 = orjson.loads(process.stdout) assert target2["name"] == "Target 2" - assert set(target2["seeds"]) == {"evilcorp.org"} + assert set(target2["target"]) == {"evilcorp.org"} - seeds_file.unlink() + target_file.unlink() # list targets (json) process = subprocess.run(BBCTL_COMMAND + ["scan", "target", "list", "--json"], capture_output=True, text=True) @@ -67,8 +67,8 @@ def test_cli_targetctl(bbot_server_http): targets = [Target(**orjson.loads(line)) for line in process.stdout.splitlines()] assert len(targets) == 2 targets = {t.name: t for t in targets} - assert set(targets["Target 1"].seeds) == {"evilcorp.com", "evilcorp.net"} - assert set(targets["Target 2"].seeds) == {"evilcorp.org"} + assert set(targets["Target 1"].target) == {"evilcorp.com", "evilcorp.net"} + assert set(targets["Target 2"].target) == {"evilcorp.org"} assert targets["Target 1"].strict_dns_scope is False assert targets["Target 2"].strict_dns_scope is True @@ -77,14 +77,14 @@ def test_cli_targetctl(bbot_server_http): assert process.returncode == 0 lines = process.stdout.splitlines() assert len(lines) == 3 - assert lines[0] == "name,description,seeds,whitelist,blacklist,strict_scope,created,modified" + assert lines[0] == "name,description,target,seeds,blacklist,strict_scope,created,modified" assert lines[1].startswith("Target 1,,2,0,0,No,") assert lines[2].startswith("Target 2,,1,0,0,Yes,") # list targets (text) process = subprocess.run(BBCTL_COMMAND + ["scan", "target", "list"], capture_output=True, text=True) assert process.returncode == 0 - assert process.stdout.count("Target") == 2 + assert process.stdout.count("Target") == 3 # delete target1 (must specify name or id) process = subprocess.run( diff --git a/tests/test_cli/test_cli_technologyctl.py b/tests/test_cli/test_cli_technologyctl.py index 88b5261e..d71385a1 100644 --- a/tests/test_cli/test_cli_technologyctl.py +++ b/tests/test_cli/test_cli_technologyctl.py @@ -150,7 +150,7 @@ def test_cli_technologyctl(bbot_server_http, bbot_watchdog, bbot_out_file): # create a new target that matches two technologies target_file = BBOT_SERVER_TEST_DIR / "targets" target_file.write_text("t2.tech.evilcorp.com") - command = BBCTL_COMMAND + ["scan", "target", "create", "--seeds", target_file, "--name", "evilcorp1"] + command = BBCTL_COMMAND + ["scan", "target", "create", "--target", target_file, "--name", "evilcorp1"] process = subprocess.run(command, capture_output=True, text=True) assert process.returncode == 0