From c428ba01d616d4a087fe50a6ddda7bfd9ac96cd0 Mon Sep 17 00:00:00 2001 From: Mikel Olasagasti Uranga Date: Wed, 16 Mar 2022 14:30:17 +0100 Subject: [PATCH] Run dos2unix to have homogeneous endlines Converted by running `find . -type f -print0 | xargs -0 dos2unix` --- .github/release-drafter.yml | 6 +- .github/workflows/python.yaml | 40 ++--- name_that_hash/HashTypeObj.py | 86 +++++------ name_that_hash/check_hashes.py | 76 +++++----- name_that_hash/hash_info.py | 42 +++--- name_that_hash/prettifier.py | 268 ++++++++++++++++----------------- noxfile.py | 132 ++++++++-------- packages/setup.py | 68 ++++----- pyproject.toml | 44 +++--- tests/mocks/hashes.txt | 4 +- tests/test_click.py | 142 ++++++++--------- 11 files changed, 454 insertions(+), 454 deletions(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 96b0f95..0a5d792 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,4 +1,4 @@ -template: | - ## What’s Changed - +template: | + ## What’s Changed + $CHANGES \ No newline at end of file diff --git a/.github/workflows/python.yaml b/.github/workflows/python.yaml index 124e7c1..439ef22 100644 --- a/.github/workflows/python.yaml +++ b/.github/workflows/python.yaml @@ -1,20 +1,20 @@ -# .github/workflows/tests.yml -name: Tests -on: pull_request -jobs: - tests: - strategy: - matrix: - python-version: ['3.6', '3.7', '3.8', '3.9'] - os: [ubuntu-latest, windows-latest, macos-latest] - runs-on: ${{ matrix.os }} - name: Python ${{ matrix.python-version }} - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v1 - with: - python-version: ${{ matrix.python-version }} - architecture: x64 - - run: pip install nox==2019.11.9 - - run: pip install poetry==1.0.5 - - run: nox +# .github/workflows/tests.yml +name: Tests +on: pull_request +jobs: + tests: + strategy: + matrix: + python-version: ['3.6', '3.7', '3.8', '3.9'] + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + name: Python ${{ matrix.python-version }} + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + architecture: x64 + - run: pip install nox==2019.11.9 + - run: pip install poetry==1.0.5 + - run: nox diff --git a/name_that_hash/HashTypeObj.py b/name_that_hash/HashTypeObj.py index 70bd46c..bd76116 100644 --- a/name_that_hash/HashTypeObj.py +++ b/name_that_hash/HashTypeObj.py @@ -1,43 +1,43 @@ -class HashType: - """ - Every hash given to our program will be associated with one object. - This object contains the possible type of hash - and provides ways to print that hash. - """ - - def __init__(self, chash: str, nth, hash_info, kwargs): - self.chash = chash - self.nth = nth - - self.popular = hash_info.popular - - # prototypes is given as a generator - if "extreme" in kwargs and kwargs["extreme"]: - self.prototypes = nth.identify_all(chash) - else: - self.prototypes = nth.identify(chash) - self.prototypes = self.sort_by_popular() - - self.hash_obj = {self.chash: self.prototypes} - - def get_prototypes(self): - return self.prototypes - - def sort_by_popular(self): - """ - Sorts the list by popular + everything else. - - We do this using the self.popular set. Sets have O(1) lookup, so it's cheap. - If one named_tuple is in the popular set, we add it to the populars list and remove it from prototypes. - - We then return populars list + prototypes. - """ - - to_ret = [] - populars = [] - for i in self.prototypes: - if i.name in self.popular: - populars.append(i.__dict__) - else: - to_ret.append(i.__dict__) - return populars + to_ret +class HashType: + """ + Every hash given to our program will be associated with one object. + This object contains the possible type of hash + and provides ways to print that hash. + """ + + def __init__(self, chash: str, nth, hash_info, kwargs): + self.chash = chash + self.nth = nth + + self.popular = hash_info.popular + + # prototypes is given as a generator + if "extreme" in kwargs and kwargs["extreme"]: + self.prototypes = nth.identify_all(chash) + else: + self.prototypes = nth.identify(chash) + self.prototypes = self.sort_by_popular() + + self.hash_obj = {self.chash: self.prototypes} + + def get_prototypes(self): + return self.prototypes + + def sort_by_popular(self): + """ + Sorts the list by popular + everything else. + + We do this using the self.popular set. Sets have O(1) lookup, so it's cheap. + If one named_tuple is in the popular set, we add it to the populars list and remove it from prototypes. + + We then return populars list + prototypes. + """ + + to_ret = [] + populars = [] + for i in self.prototypes: + if i.name in self.popular: + populars.append(i.__dict__) + else: + to_ret.append(i.__dict__) + return populars + to_ret diff --git a/name_that_hash/check_hashes.py b/name_that_hash/check_hashes.py index f2a8953..b60ff78 100644 --- a/name_that_hash/check_hashes.py +++ b/name_that_hash/check_hashes.py @@ -1,38 +1,38 @@ -import base64 -import logging - -from name_that_hash import HashTypeObj, hash_info - - -class HashChecker: - """ - Call this with an input to identify hashes - """ - - def __init__(self, kwargs, nth): - self.kwargs = kwargs - self.hashinfo_obj = hash_info.HashInformation() - self.nth = nth - self.output = [] - - def file_input(self, f): - for nr, line in enumerate(f, start=1): - line = str(line).strip() - if not line: - logging.debug(f"Skipped empty line {nr}") - continue - self.single_hash(line) - logging.debug(f"{self.output}") - - def single_hash(self, chash: str): - if "base64" in self.kwargs and self.kwargs["base64"]: - logging.debug("decoding as base64") - - try: - # b64decode returns Bytes obj - chash = base64.b64decode(chash).decode("utf-8") - except: - logging.debug("Failed to base64 decode") - self.output.append( - HashTypeObj.HashType(chash, self.nth, self.hashinfo_obj, self.kwargs) - ) +import base64 +import logging + +from name_that_hash import HashTypeObj, hash_info + + +class HashChecker: + """ + Call this with an input to identify hashes + """ + + def __init__(self, kwargs, nth): + self.kwargs = kwargs + self.hashinfo_obj = hash_info.HashInformation() + self.nth = nth + self.output = [] + + def file_input(self, f): + for nr, line in enumerate(f, start=1): + line = str(line).strip() + if not line: + logging.debug(f"Skipped empty line {nr}") + continue + self.single_hash(line) + logging.debug(f"{self.output}") + + def single_hash(self, chash: str): + if "base64" in self.kwargs and self.kwargs["base64"]: + logging.debug("decoding as base64") + + try: + # b64decode returns Bytes obj + chash = base64.b64decode(chash).decode("utf-8") + except: + logging.debug("Failed to base64 decode") + self.output.append( + HashTypeObj.HashType(chash, self.nth, self.hashinfo_obj, self.kwargs) + ) diff --git a/name_that_hash/hash_info.py b/name_that_hash/hash_info.py index 882058d..036b7fb 100644 --- a/name_that_hash/hash_info.py +++ b/name_that_hash/hash_info.py @@ -1,21 +1,21 @@ -class HashInformation: - def __init__(self): - self.popular = set( - [ - "MD5", - "MD4", - "NTLM", - "SHA-256", - "SHA-512", - "Keccak-256", - "Keccak-512", - "Blake2", - "bcrypt", - "SHA-1", - "HMAC-SHA1 (key = $salt)", - "CryptoCurrency(PrivateKey)", - "SHA-338", - "Domain Cached Credentials", - "Domain Cached Credentials 2", - ] - ) +class HashInformation: + def __init__(self): + self.popular = set( + [ + "MD5", + "MD4", + "NTLM", + "SHA-256", + "SHA-512", + "Keccak-256", + "Keccak-512", + "Blake2", + "bcrypt", + "SHA-1", + "HMAC-SHA1 (key = $salt)", + "CryptoCurrency(PrivateKey)", + "SHA-338", + "Domain Cached Credentials", + "Domain Cached Credentials 2", + ] + ) diff --git a/name_that_hash/prettifier.py b/name_that_hash/prettifier.py index b0f0a4a..255b49d 100644 --- a/name_that_hash/prettifier.py +++ b/name_that_hash/prettifier.py @@ -1,134 +1,134 @@ -import json -import logging -from typing import NamedTuple, List - -from rich.console import Console - -from name_that_hash import hash_info - - -# we need a global console to control highlighting / printing -console = Console(highlighter=False) - -class Prettifier: - """ - This classes entire existence is to output stuff. - """ - - def __init__(self, kwargs, api=False): - """ - Takes arguments as list so we can do A11Y stuff etc - """ - if api is not True: - self.a11y = kwargs["accessible"] - self.john = kwargs["no_john"] - self.hashcat = kwargs["no_hashcat"] - self.args = kwargs - self.hashinfo_obj = hash_info.HashInformation() - - if not "popular_only" in self.args: - self.args["popular_only"] = False - - def greppable_output(self, objs: List): - logging.debug("Greppable output") - - """ - takes the prototypes and turns it into json - returns the json - - Doesn't print it, it prints in main - """ - return json.dumps(self.turn_hash_objs_into_dict(objs), indent=2) - - def turn_hash_objs_into_dict(self, objs: List): - outputs_as_dict = {} - - for y in objs: - outputs_as_dict.update(y.hash_obj) - logging.debug(f"Output_as_dicts is now {outputs_as_dict}") - - if self.args["popular_only"]: - return self.get_popular_only(outputs_as_dict) - return outputs_as_dict - - def get_popular_only(self, outputs_as_dict): - popular_only = {} - for hash in list(outputs_as_dict.keys()): - popular_only[hash] = [] - for hash_type in outputs_as_dict[hash]: - if hash_type["name"] in self.hashinfo_obj.popular: - popular_only[hash].append(hash_type) - return popular_only - - def pretty_print(self, objs): - logging.debug("In pretty printing") - """ - prints it prettily in the format: - most popular hashes - 1. - 2. - 3. - 4. - - - then everything else on one line. - """ - for i in objs: - logging.debug(i) - self.pretty_print_one(i) - - def pretty_print_one(self, objs: List): - out = f"\n[bold magenta]{objs.chash}[/bold magenta]\n" - - # It didn't find any hashes. - if len(objs.prototypes) == 0: - out += "[bold #FF0000]No hashes found.[/bold #FF0000]" - console.print(out) - return out - - out += "\n[bold underline #5f5fff]Most Likely[/bold underline #5f5fff] \n" - start = objs.prototypes[0:4] - rest = objs.prototypes[4:] - - for i in start: - out += self.turn_named_tuple_pretty_print(i) + "\n" - - # It has hashes, but not many so don't print least likely. - if len(objs.prototypes) <= 5: - console.print(out) - return out - - # return if accessible is on - if not self.a11y: - out += "\n[bold underline #5f5fff]Least Likely[/bold underline #5f5fff]\n" - - for i in rest: - out += self.turn_named_tuple_pretty_print(i) + " " - - console.print(out) - return out - - def turn_named_tuple_pretty_print(self, nt: NamedTuple): - # This colour is red - out = f"[bold #ff5f00]{nt['name']}[/bold #ff5f00], " - - hc = nt["hashcat"] - john = nt["john"] - des = nt["description"] - - if not self.hashcat: - if hc is not None and john: - out += f"HC: {hc} " - elif hc is not None: - out += f"HC: {hc} " - - if not self.john: - if john is not None and des: - out += f"JtR: {john} " - elif john is not None: - out += f"JtR: {john}" - if des: - # Orange - out += f"[#8787D7]Summary: {des}[/#8787D7]" - - return out +import json +import logging +from typing import NamedTuple, List + +from rich.console import Console + +from name_that_hash import hash_info + + +# we need a global console to control highlighting / printing +console = Console(highlighter=False) + +class Prettifier: + """ + This classes entire existence is to output stuff. + """ + + def __init__(self, kwargs, api=False): + """ + Takes arguments as list so we can do A11Y stuff etc + """ + if api is not True: + self.a11y = kwargs["accessible"] + self.john = kwargs["no_john"] + self.hashcat = kwargs["no_hashcat"] + self.args = kwargs + self.hashinfo_obj = hash_info.HashInformation() + + if not "popular_only" in self.args: + self.args["popular_only"] = False + + def greppable_output(self, objs: List): + logging.debug("Greppable output") + + """ + takes the prototypes and turns it into json + returns the json + + Doesn't print it, it prints in main + """ + return json.dumps(self.turn_hash_objs_into_dict(objs), indent=2) + + def turn_hash_objs_into_dict(self, objs: List): + outputs_as_dict = {} + + for y in objs: + outputs_as_dict.update(y.hash_obj) + logging.debug(f"Output_as_dicts is now {outputs_as_dict}") + + if self.args["popular_only"]: + return self.get_popular_only(outputs_as_dict) + return outputs_as_dict + + def get_popular_only(self, outputs_as_dict): + popular_only = {} + for hash in list(outputs_as_dict.keys()): + popular_only[hash] = [] + for hash_type in outputs_as_dict[hash]: + if hash_type["name"] in self.hashinfo_obj.popular: + popular_only[hash].append(hash_type) + return popular_only + + def pretty_print(self, objs): + logging.debug("In pretty printing") + """ + prints it prettily in the format: + most popular hashes + 1. + 2. + 3. + 4. + + + then everything else on one line. + """ + for i in objs: + logging.debug(i) + self.pretty_print_one(i) + + def pretty_print_one(self, objs: List): + out = f"\n[bold magenta]{objs.chash}[/bold magenta]\n" + + # It didn't find any hashes. + if len(objs.prototypes) == 0: + out += "[bold #FF0000]No hashes found.[/bold #FF0000]" + console.print(out) + return out + + out += "\n[bold underline #5f5fff]Most Likely[/bold underline #5f5fff] \n" + start = objs.prototypes[0:4] + rest = objs.prototypes[4:] + + for i in start: + out += self.turn_named_tuple_pretty_print(i) + "\n" + + # It has hashes, but not many so don't print least likely. + if len(objs.prototypes) <= 5: + console.print(out) + return out + + # return if accessible is on + if not self.a11y: + out += "\n[bold underline #5f5fff]Least Likely[/bold underline #5f5fff]\n" + + for i in rest: + out += self.turn_named_tuple_pretty_print(i) + " " + + console.print(out) + return out + + def turn_named_tuple_pretty_print(self, nt: NamedTuple): + # This colour is red + out = f"[bold #ff5f00]{nt['name']}[/bold #ff5f00], " + + hc = nt["hashcat"] + john = nt["john"] + des = nt["description"] + + if not self.hashcat: + if hc is not None and john: + out += f"HC: {hc} " + elif hc is not None: + out += f"HC: {hc} " + + if not self.john: + if john is not None and des: + out += f"JtR: {john} " + elif john is not None: + out += f"JtR: {john}" + if des: + # Orange + out += f"[#8787D7]Summary: {des}[/#8787D7]" + + return out diff --git a/noxfile.py b/noxfile.py index 68a6ed4..29ac9fe 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,66 +1,66 @@ -""" -The file for Nox -""" -from typing import Any - -import nox -from nox.sessions import Session - -locations = "name_that_hash/", "tests/", "docs/" -nox.options.sessions = ["tests"] -package = "nth" - - -def install_with_constraints(session: Session, *args: str, **kwargs: Any) -> None: - """Install packages constrained by Poetry's lock file. - This function is a wrapper for nox.sessions.Session.install. It - invokes pip to install packages inside of the session's virtualenv. - Additionally, pip is passed a constraints file generated from - Poetry's lock file, to ensure that the packages are pinned to the - versions specified in poetry.lock. This allows you to manage the - packages as Poetry development dependencies. - Arguments: - session: The Session object. - args: Command-line arguments for pip. - kwargs: Additional keyword arguments for Session.install. - """ - session.run( - "poetry", - "export", - "--dev", - "--format=requirements.txt", - "--output=requirements.txt", - external=True, - ) - session.install("--constraint=requirements.txt", *args, **kwargs) - - -# noxfile.py -@nox.session -def black(session): - args = session.posargs or locations - session.install("black") - session.run("black", *args) - - -@nox.session(python="3.8") -def coverage(session: Session) -> None: - """Upload coverage data.""" - install_with_constraints(session, "coverage[toml]", "codecov") - session.run("coverage", "xml", "--fail-under=0") - session.run("codecov", *session.posargs) - - -# noxfile.py -@nox.session -def docs(session: Session) -> None: - """Build the documentation.""" - install_with_constraints(session, "sphinx") - session.run("sphinx-build", "docs", "docs/_build") - - -@nox.session -def tests(session): - session.run("pip", "install", "click", external=True) - session.run("poetry", "install", external=True) - session.run("poetry", "run", "pytest") +""" +The file for Nox +""" +from typing import Any + +import nox +from nox.sessions import Session + +locations = "name_that_hash/", "tests/", "docs/" +nox.options.sessions = ["tests"] +package = "nth" + + +def install_with_constraints(session: Session, *args: str, **kwargs: Any) -> None: + """Install packages constrained by Poetry's lock file. + This function is a wrapper for nox.sessions.Session.install. It + invokes pip to install packages inside of the session's virtualenv. + Additionally, pip is passed a constraints file generated from + Poetry's lock file, to ensure that the packages are pinned to the + versions specified in poetry.lock. This allows you to manage the + packages as Poetry development dependencies. + Arguments: + session: The Session object. + args: Command-line arguments for pip. + kwargs: Additional keyword arguments for Session.install. + """ + session.run( + "poetry", + "export", + "--dev", + "--format=requirements.txt", + "--output=requirements.txt", + external=True, + ) + session.install("--constraint=requirements.txt", *args, **kwargs) + + +# noxfile.py +@nox.session +def black(session): + args = session.posargs or locations + session.install("black") + session.run("black", *args) + + +@nox.session(python="3.8") +def coverage(session: Session) -> None: + """Upload coverage data.""" + install_with_constraints(session, "coverage[toml]", "codecov") + session.run("coverage", "xml", "--fail-under=0") + session.run("codecov", *session.posargs) + + +# noxfile.py +@nox.session +def docs(session: Session) -> None: + """Build the documentation.""" + install_with_constraints(session, "sphinx") + session.run("sphinx-build", "docs", "docs/_build") + + +@nox.session +def tests(session): + session.run("pip", "install", "click", external=True) + session.run("poetry", "install", external=True) + session.run("poetry", "run", "pytest") diff --git a/packages/setup.py b/packages/setup.py index 38cf19e..d261643 100644 --- a/packages/setup.py +++ b/packages/setup.py @@ -1,34 +1,34 @@ -# -*- coding: utf-8 -*- - -# DO NOT EDIT THIS FILE! -# This file has been autogenerated by dephell <3 -# https://github.com/dephell/dephell - -try: - from setuptools import setup -except ImportError: - from distutils.core import setup - -readme = "" - -setup( - long_description=readme, - name="name-that-hash", - version="1.7.2", - description="The Modern Hash Identification System", - python_requires="==3.*,>=3.6.0", - author="brandon", - author_email="brandon@skerritt.blog", - license="GPL-3.0-or-later", - entry_points={ - "console_scripts": [ - "nth = name_that_hash.runner:main", - "name-that-hash = name_that_hash.runner:main", - ] - }, - packages=["name_that_hash"], - package_dir={"": "."}, - package_data={}, - install_requires=["click==7.*,>=7.1.2", "rich<11.0,>=9.9"], - extras_require={"dev": ["pytest==6.*,>=6.2.0"]}, -) +# -*- coding: utf-8 -*- + +# DO NOT EDIT THIS FILE! +# This file has been autogenerated by dephell <3 +# https://github.com/dephell/dephell + +try: + from setuptools import setup +except ImportError: + from distutils.core import setup + +readme = "" + +setup( + long_description=readme, + name="name-that-hash", + version="1.7.2", + description="The Modern Hash Identification System", + python_requires="==3.*,>=3.6.0", + author="brandon", + author_email="brandon@skerritt.blog", + license="GPL-3.0-or-later", + entry_points={ + "console_scripts": [ + "nth = name_that_hash.runner:main", + "name-that-hash = name_that_hash.runner:main", + ] + }, + packages=["name_that_hash"], + package_dir={"": "."}, + package_data={}, + install_requires=["click==7.*,>=7.1.2", "rich<11.0,>=9.9"], + extras_require={"dev": ["pytest==6.*,>=6.2.0"]}, +) diff --git a/pyproject.toml b/pyproject.toml index 8bc4477..219d6d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,22 +1,22 @@ -[tool.poetry] -name = "name-that-hash" -version = "1.10.0" -description = "The Modern Hash Identification System" -authors = ["brandon "] -license = "GPL-3.0-or-later" - -[tool.poetry.dependencies] -python = "^3.6" -click = ">=7.1.2,<9.0.0" -rich = ">=9.9,<11.0" - -[tool.poetry.dev-dependencies] -pytest = "^6.2" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" - -[tool.poetry.scripts] -nth = "name_that_hash.runner:main" -name-that-hash = "name_that_hash.runner:main" +[tool.poetry] +name = "name-that-hash" +version = "1.10.0" +description = "The Modern Hash Identification System" +authors = ["brandon "] +license = "GPL-3.0-or-later" + +[tool.poetry.dependencies] +python = "^3.6" +click = ">=7.1.2,<9.0.0" +rich = ">=9.9,<11.0" + +[tool.poetry.dev-dependencies] +pytest = "^6.2" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry.scripts] +nth = "name_that_hash.runner:main" +name-that-hash = "name_that_hash.runner:main" diff --git a/tests/mocks/hashes.txt b/tests/mocks/hashes.txt index c302a67..2f9329b 100644 --- a/tests/mocks/hashes.txt +++ b/tests/mocks/hashes.txt @@ -1,2 +1,2 @@ -NWJhYTYxZTRjOWI5M2YzZjA2ODIyNTBiNmNmODMzMWI3ZWU2OGZkOA== -b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86 +NWJhYTYxZTRjOWI5M2YzZjA2ODIyNTBiNmNmODMzMWI3ZWU2OGZkOA== +b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86 diff --git a/tests/test_click.py b/tests/test_click.py index 0f13a94..0715864 100644 --- a/tests/test_click.py +++ b/tests/test_click.py @@ -1,71 +1,71 @@ -from click.testing import CliRunner - -from name_that_hash.runner import main - - -def test_it_runs(): - runner = CliRunner() - result = runner.invoke( - main, - [ - "-t", - "b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86", - ], - ) - assert result.exit_code == 0 - assert "SHA-512" in result.output - - -def test_greppable(): - runner = CliRunner() - result = runner.invoke( - main, - [ - "-t", - "b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86", - "-g", - ], - ) - assert result.exit_code == 0 - assert "Twitter" not in result.output - - -def test_greppable_no_text(): - runner = CliRunner() - result = runner.invoke(main, ["-t", "", "-g"]) - assert "Usage" not in result.output - - -def test_greppable_b64(): - runner = CliRunner() - result = runner.invoke( - main, ["-t", "NWY0ZGNjM2I1YWE3NjVkNjFkODMyN2RlYjg4MmNmOTk=", "--base64", "-g"] - ) - assert "MD5" in result.output - - -def test__b64(): - runner = CliRunner() - result = runner.invoke( - main, ["-t", "NWY0ZGNjM2I1YWE3NjVkNjFkODMyN2RlYjg4MmNmOTk=", "-b64"] - ) - assert "MD5" in result.output - - -def test_file_input(): - runner = CliRunner() - result = runner.invoke(main, ["-f", "tests/mocks/hashes.txt", "-b64", "-g"]) - assert "SHA-1" in result.output - assert "SHA-512" in result.output - - -def test_kerberos_works(): - runner = CliRunner() - result = runner.invoke( - main, - [ - "-t", - "$krb5tgs$23$*user$realm$test/spn*$63386d22d359fe42230300d56852c9eb$891ad31d09ab89c6b3b8c5e5de6c06a7f49fd559d7a9a3c32576c8fedf705376cea582ab5938f7fc8bc741acf05c5990741b36ef4311fe3562a41b70a4ec6ecba849905f2385bb3799d92499909658c7287c49160276bca0006c350b0db4fd387adc27c01e9e9ad0c20ed53a7e6356dee2452e35eca2a6a1d1432796fc5c19d068978df74d3d0baf35c77de12456bf1144b6a750d11f55805f5a16ece2975246e2d026dce997fba34ac8757312e9e4e6272de35e20d52fb668c5ed", - ], - ) - assert "Kerberos" in result.output +from click.testing import CliRunner + +from name_that_hash.runner import main + + +def test_it_runs(): + runner = CliRunner() + result = runner.invoke( + main, + [ + "-t", + "b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86", + ], + ) + assert result.exit_code == 0 + assert "SHA-512" in result.output + + +def test_greppable(): + runner = CliRunner() + result = runner.invoke( + main, + [ + "-t", + "b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86", + "-g", + ], + ) + assert result.exit_code == 0 + assert "Twitter" not in result.output + + +def test_greppable_no_text(): + runner = CliRunner() + result = runner.invoke(main, ["-t", "", "-g"]) + assert "Usage" not in result.output + + +def test_greppable_b64(): + runner = CliRunner() + result = runner.invoke( + main, ["-t", "NWY0ZGNjM2I1YWE3NjVkNjFkODMyN2RlYjg4MmNmOTk=", "--base64", "-g"] + ) + assert "MD5" in result.output + + +def test__b64(): + runner = CliRunner() + result = runner.invoke( + main, ["-t", "NWY0ZGNjM2I1YWE3NjVkNjFkODMyN2RlYjg4MmNmOTk=", "-b64"] + ) + assert "MD5" in result.output + + +def test_file_input(): + runner = CliRunner() + result = runner.invoke(main, ["-f", "tests/mocks/hashes.txt", "-b64", "-g"]) + assert "SHA-1" in result.output + assert "SHA-512" in result.output + + +def test_kerberos_works(): + runner = CliRunner() + result = runner.invoke( + main, + [ + "-t", + "$krb5tgs$23$*user$realm$test/spn*$63386d22d359fe42230300d56852c9eb$891ad31d09ab89c6b3b8c5e5de6c06a7f49fd559d7a9a3c32576c8fedf705376cea582ab5938f7fc8bc741acf05c5990741b36ef4311fe3562a41b70a4ec6ecba849905f2385bb3799d92499909658c7287c49160276bca0006c350b0db4fd387adc27c01e9e9ad0c20ed53a7e6356dee2452e35eca2a6a1d1432796fc5c19d068978df74d3d0baf35c77de12456bf1144b6a750d11f55805f5a16ece2975246e2d026dce997fba34ac8757312e9e4e6272de35e20d52fb668c5ed", + ], + ) + assert "Kerberos" in result.output