diff --git a/.github/scripts/main.md b/.github/scripts/main.md deleted file mode 100644 index 09767a4c..00000000 --- a/.github/scripts/main.md +++ /dev/null @@ -1,18 +0,0 @@ -# custom-pattern-secrets - -Custom Secret Scanning Patterns repository created and maintained by the GitHub Field Services. - -This repository extends the [list of supported Vendors out of the box](https://docs.github.com/en/enterprise-cloud@latest/code-security/secret-scanning/secret-scanning-patterns) with GitHub's Advanced Security Secret Scanning. - -> :warning: This repository does not guarantee the quality or precision of the patterns which might result in False Positives - -{% for path, config in configs.items() %} -{%- if config.display %} -### [{{ config.name }}]({{ config.path.replace("/patterns.yml", "") }}) -{% for pattern in config.patterns %} -{%- if pattern.experimental == False %} -- {{ pattern.name }} -{%- endif %} -{%- endfor %} -{% endif %} -{%- endfor %} diff --git a/.github/scripts/template.md b/.github/scripts/template.md deleted file mode 100644 index 14e8864b..00000000 --- a/.github/scripts/template.md +++ /dev/null @@ -1,76 +0,0 @@ - -# {{ config.name }} -{%- for pattern in config.patterns %} - -## {{ pattern.name }} - -{% if pattern.experimental -%} -**⚠️ WARNING: THIS RULE IS EXPERIMENTAL AND MIGHT CAUSE A HIGH FALSE POSITIVE RATE (test before commiting to org level) ⚠️** -{%- endif %} -{% if pattern.description -%} -{{ pattern.description }} -{%- endif %} -*version: {{ pattern.regex.version }}* - -{% if pattern.comments -%} -**Comments / Notes:** -{% for comment in pattern.comments %} -- {{ comment }} -{%- endfor %} -{% endif %} - -
-Pattern Format -

- -```regex -{{ pattern.regex.pattern }} -``` - -

-
- -{% if pattern.regex.start -%} -
-Start Pattern -

- -```regex -{{ pattern.regex.start }} -``` - -

-
-{%- endif %} - -{%- if pattern.regex.end -%} -
-End Pattern -

- -```regex -{{ pattern.regex.end }} -``` - -

-
-{%- endif %} - -{%- if pattern.regex.additional_match or pattern.regex.additional_not_match %} -
-Additional Matches -

-Add these additional matches to the [Secret Scanning Custom Pattern](https://docs.github.com/en/enterprise-cloud@latest/code-security/secret-scanning/defining-custom-patterns-for-secret-scanning#example-of-a-custom-pattern-specified-using-additional-requirements). - -{% for match in pattern.regex.additional_match %} -- Match: `{{ match }}` -{%- endfor %} -{%- for match in pattern.regex.additional_not_match %} -- Not Match: `{{ match }}` -{%- endfor %} - -

-
-{%- endif %} -{%- endfor %} diff --git a/.github/scripts/validate.py b/.github/scripts/validate.py deleted file mode 100644 index a36bd7a7..00000000 --- a/.github/scripts/validate.py +++ /dev/null @@ -1,292 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -import json -import yaml -import hashlib -import logging -import argparse -import subprocess -import collections -from typing import List, Dict, Optional -from dataclasses import dataclass, field - -from jinja2 import Environment, FileSystemLoader - -from ghastoolkit import GitHub, SecretScanning, SecretAlert - -__here__ = os.path.dirname(os.path.realpath(__file__)) - -parser = argparse.ArgumentParser(description="Validate a directory of files.") -parser.add_argument("--debug", action="store_true", help="Print debug messages") -parser.add_argument("-p", "--path", default="./", help="Directory to scan") -parser.add_argument( - "--token", default=os.environ.get("GITHUB_TOKEN"), help="GitHub token to use" -) - -parser_modes = parser.add_argument_group("modes") -parser_modes.add_argument("--validate", action="store_true", help="Validation Mode") -parser_modes.add_argument("--snapshot", action="store_true", help="Snapshot Mode") -parser_modes.add_argument("--markdown", action="store_true", help="Markdown Mode") - - -@dataclass -class Regex: - pattern: str - version: Optional[str] = "0.1" - start: Optional[str] = None - end: Optional[str] = None - additional_match: Optional[List[str]] = field(default_factory=list) - additional_not_match: Optional[List[str]] = field(default_factory=list) - - def __post_init__(self): - if self.version is not None: - if isinstance(self.version, (int, float)): - self.version = str(self.version) - if not self.version.startswith("v"): - self.version = "v" + self.version - if self.pattern: - self.pattern = self.pattern.strip() - if self.start: - self.start = self.start.strip() - if self.end: - self.end = self.end.strip() - - -@dataclass -class Pattern: - """A pattern to match against a file.""" - - name: str - description: Optional[str] = None - experimental: bool = False - - regex: Regex = field(default_factory=Regex) - - expected: Optional[List[Dict[str, str]]] = None - - type: Optional[str] = None - comments: List[str] = field(default_factory=list) - - def __post_init__(self): - if isinstance(self.regex, dict): - self.regex = Regex(**self.regex) - - -@dataclass -class PatternsConfig: - """A configuration for a set of patterns.""" - - name: str - - display: bool = True - - patterns: List[Pattern] = field(default_factory=list) - - path: Optional[str] = field(default=None) - - def __post_init__(self): - _tmp = self.patterns - self.patterns = [] - - for pattern in _tmp: - self.patterns.append(Pattern(**pattern)) - - -def createMarkdown(path: str, template: str = "template.md", **data) -> str: - logging.info(f"Creating markdown :: {path}") - env = Environment(loader=FileSystemLoader(__here__)) - template = env.get_template(template) - - output_from_parsed_template = template.render(data) - - with open(path, "w") as f: - f.write(output_from_parsed_template) - return output_from_parsed_template - - -def loadPatternFiles(path: str) -> Dict[str, PatternsConfig]: - """Find all files that match a pattern.""" - logging.info(f"Path being proccessed: {path}") - patterns: Dict[PatternsConfig] = {} - - for root, dirs, files in os.walk(path): - for file in files: - if file == "patterns.yml": - path = os.path.join(root, file) - logging.debug(f"Found patterns file: {path}") - - with open(path) as f: - data = yaml.safe_load(f) - - config = PatternsConfig(path=path, **data) - - logging.debug( - f"Loaded :: PatternsConfig('{config.name}' patterns='{len(config.patterns)})'" - ) - - patterns[path] = config - return patterns - - -def createSnapshot(path: str, results: List[SecretAlert]): - """Create snapshot from SecretScanningAlert to CSV""" - header = [ - "secret_type", - "secret_type_display_name", - "secret", - "path", - "start_line", - "end_line", - "start_column", - "end_column", - ] - - with open(path, "w") as f: - f.write(f"{','.join(header)}\n") - for result in results: - # - for location in result.locations: - # skip non-commit locations - if location.get("type") != "commit": - continue - details = location.get("details", {}) - if details.get("path", "").startswith(".venv"): - continue - - secret = hashlib.sha256(result.secret.encode("utf-8")).hexdigest() - content = f'"{result.secret_type}","{result.secret_type_display_name}","{secret}",' - # location info - content += f'"{details.get("path")}",' - content += f'"{details.get("start_line")}","{details.get("end_line")}",' - content += ( - f'"{details.get("start_column")}","{details.get("end_column")}",' - ) - - f.write(f"{content}\n") - - -def checkExpected(expected: List[Dict[str, str]], results: List[SecretAlert]) -> int: - """Check if the expected results match the actual results""" - issues = 0 - return issues - - -def compareSnapshots(default: str, current: str) -> List[str]: - """Compare two snapshots and return a list of differences""" - logging.debug(f"Comparing snapshots: {default} {current}") - command = [ - "git", - "diff", - "--no-index", - "--no-prefix", - "--color=always", - "--exit-code", - "--", - default, - current, - ] - logging.debug(f"Running command: {command}") - with open(os.devnull, "w") as devnull: - result = subprocess.run(command, capture_output=True) - logging.debug(f"Command result: {result}") - if result.returncode != 0: - return result.stdout.decode("utf-8").split("\n")[4:] - return [] - - -if __name__ == "__main__": - arguments = parser.parse_args() - - logging.basicConfig( - level=logging.DEBUG if arguments.debug else logging.INFO, - format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", - ) - - path = arguments.path - # If a file is provided, point to the directory - if os.path.isfile(path): - path = os.path.dirname(path) - - configs: Dict[str, PatternsConfig] = loadPatternFiles(path) - # Sort by name - configs = collections.OrderedDict(sorted(configs.items())) - - errors = [] - - if len(configs) == 0: - logging.warning("No patterns found") - sys.exit(0) - - GitHub.init( - "advanced-security/secret-scanning-custom-patterns", token=arguments.token - ) - - secret_scanning = SecretScanning() - # todo: caching - try: - all_secrets = secret_scanning.getAlerts(state="open") - except Exception as err: - logging.error(f"Error occured while fetching alerts: {err}") - logging.error(f"Please check your token has the right access and try again") - sys.exit(1) - - logging.info(f"Number of secrets found: {len(all_secrets)}") - - for file_path, pattern_config in configs.items(): - pattern_path = os.path.dirname(pattern_config.path) - - # Markdown mode - if arguments.markdown: - createMarkdown( - os.path.join(pattern_path, "README.md"), config=pattern_config - ) - continue - - for pattern in pattern_config.patterns: - logging.info(f"Checking {pattern.name}") - - snapshot_dir = f"{pattern_path}/__snapshots__" - if not os.path.exists(snapshot_dir): - os.mkdir(snapshot_dir) - - snapshot_path = f"{snapshot_dir}/{pattern.type}.csv" - - # list of secrets for a specific pattern - results = [ - secret for secret in all_secrets if secret.secret_type == pattern.type - ] - logging.info(f"Found secrets :: {len(results)}") - - if arguments.snapshot: - logging.info(f"Creating snapshot for {pattern.name} in {pattern_path}") - createSnapshot(snapshot_path, results) - else: - logging.debug(f"Creating snapshot for '{pattern.name}' for validation") - current_snapshot = snapshot_path.replace(".csv", "-current.csv") - createSnapshot(current_snapshot, results) - - diff = compareSnapshots(snapshot_path, current_snapshot) - if len(diff) > 0: - logging.info(f"Found differences") - for line in diff: - print(line) - errors.append(f"{pattern.name}") - else: - logging.info(f"No differences found") - os.remove(current_snapshot) - - # expected - expected = checkExpected(pattern.expected, results) - - if arguments.markdown: - createMarkdown( - os.path.join("./", "README.md"), - template="main.md", - configs=configs, - ) - - if errors: - logging.error(f"Found {len(errors)} errors") - sys.exit(1) diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 2c0ddfd1..00000000 --- a/Pipfile +++ /dev/null @@ -1,25 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -pyyaml = "*" -jinja2 = "*" -ghastoolkit = "*" - -[dev-packages] - -[requires] -python_version = "3.10" - -[scripts] -main = "python ./.github/scripts/validate.py" -validate = "python ./.github/scripts/validate.py --validate" -snapshot = "python ./.github/scripts/validate.py --snapshot" -markdown = "python ./.github/scripts/validate.py --markdown" -# tests -test = "python ./.github/scripts/validate.py --validate" -# formatting -fmt = "black ./src/ghastoolkit" -lint = "black --check ./src/ghastoolkit" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 9ec7b6e7..00000000 --- a/Pipfile.lock +++ /dev/null @@ -1,272 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "70b0c021bd2c25139c287ff9b249d06314721e257b9b3d4d6b7aae179da44941" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.10" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "certifi": { - "hashes": [ - "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", - "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==2023.7.22" - }, - "charset-normalizer": { - "hashes": [ - "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6", - "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1", - "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e", - "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373", - "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62", - "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230", - "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be", - "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c", - "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0", - "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448", - "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f", - "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649", - "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d", - "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0", - "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706", - "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a", - "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59", - "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23", - "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5", - "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb", - "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e", - "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e", - "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c", - "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28", - "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d", - "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41", - "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974", - "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce", - "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f", - "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1", - "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d", - "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8", - "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017", - "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31", - "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7", - "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8", - "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e", - "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14", - "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd", - "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d", - "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795", - "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b", - "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b", - "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b", - "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203", - "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f", - "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19", - "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1", - "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a", - "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac", - "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9", - "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0", - "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137", - "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f", - "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6", - "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5", - "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909", - "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f", - "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0", - "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324", - "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755", - "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb", - "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854", - "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c", - "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60", - "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84", - "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0", - "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b", - "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1", - "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531", - "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1", - "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11", - "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326", - "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df", - "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.1.0" - }, - "ghastoolkit": { - "hashes": [ - "sha256:12ff41a1948937182323758947a0b27093a5d31e700d8efbb7ae6f96e8f36b24", - "sha256:bee7ca68b3f89790d4324cf349c331361efd229da625e336eb73cded58480f55" - ], - "index": "pypi", - "version": "==0.4.0" - }, - "idna": { - "hashes": [ - "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", - "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0" - ], - "index": "pypi", - "markers": "python_version >= '3.5'", - "version": "==3.7" - }, - "jinja2": { - "hashes": [ - "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa", - "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==3.1.3" - }, - "markupsafe": { - "hashes": [ - "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", - "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", - "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", - "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", - "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", - "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", - "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", - "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", - "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", - "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", - "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", - "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", - "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", - "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", - "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", - "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", - "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", - "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", - "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", - "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", - "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", - "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", - "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", - "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", - "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", - "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", - "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", - "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", - "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", - "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", - "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", - "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", - "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", - "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", - "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", - "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", - "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", - "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", - "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", - "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", - "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", - "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", - "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", - "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", - "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", - "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", - "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", - "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", - "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", - "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", - "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", - "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", - "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", - "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", - "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", - "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", - "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", - "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", - "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2", - "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.3" - }, - "pyyaml": { - "hashes": [ - "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", - "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", - "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", - "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", - "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", - "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", - "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", - "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", - "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", - "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", - "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", - "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", - "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", - "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", - "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", - "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", - "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", - "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", - "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", - "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", - "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", - "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", - "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", - "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", - "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", - "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", - "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", - "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", - "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", - "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", - "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", - "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", - "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", - "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", - "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", - "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", - "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", - "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", - "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", - "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" - ], - "index": "pypi", - "version": "==6.0" - }, - "ratelimit": { - "hashes": [ - "sha256:af8a9b64b821529aca09ebaf6d8d279100d766f19e90b5059ac6a718ca6dee42" - ], - "version": "==2.2.1" - }, - "requests": { - "hashes": [ - "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", - "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" - ], - "markers": "python_version >= '3.7'", - "version": "==2.31.0" - }, - "urllib3": { - "hashes": [ - "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", - "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.0.7" - } - }, - "develop": {} -}