fix linting

This commit is contained in:
scito 2022-12-29 15:52:17 +01:00
parent b89a338246
commit f4389ca8a3
9 changed files with 53 additions and 39 deletions

View file

@ -2,7 +2,7 @@
"python.testing.pytestArgs": [ "python.testing.pytestArgs": [
"." "."
], ],
"python.testing.unittestEnabled": false, "python.testing.unittestEnabled": true,
"python.testing.pytestEnabled": true, "python.testing.pytestEnabled": true,
"cSpell.words": [ "cSpell.words": [
"devbox", "devbox",
@ -16,5 +16,9 @@
"qrcode", "qrcode",
"TOTP", "TOTP",
"venv" "venv"
] ],
"search.exclude": {
"**/build": true,
"**/dist": true
},
} }

View file

@ -60,7 +60,7 @@ import protobuf_generated_python.google_auth_pb2 # type: ignore
try: try:
import cv2 # type: ignore import cv2 # type: ignore
import numpy import numpy # type: ignore
try: try:
import pyzbar.pyzbar as zbar # type: ignore import pyzbar.pyzbar as zbar # type: ignore
@ -72,7 +72,7 @@ On Linux and macOS libzbar0 must be installed.
See in README.md for the installation of the libzbar0. See in README.md for the installation of the libzbar0.
Exception: {e}""") Exception: {e}""")
qreader_available = True qreader_available = True
except ImportError as e: except ImportError:
qreader_available = False qreader_available = False
@ -95,7 +95,6 @@ def main(sys_args):
def parse_args(sys_args): def parse_args(sys_args):
global verbose, quiet global verbose, quiet
formatter = lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=52)
description_text = "Extracts one time password (OTP) secret keys from QR codes, e.g. from Google Authenticator app." description_text = "Extracts one time password (OTP) secret keys from QR codes, e.g. from Google Authenticator app."
if qreader_available: if qreader_available:
description_text += "\nIf no infiles are provided, the QR codes are interactively captured from the camera." description_text += "\nIf no infiles are provided, the QR codes are interactively captured from the camera."
@ -106,7 +105,7 @@ python extract_otp_secret_keys.py - < example_export.txt
python extract_otp_secret_keys.py --csv - example_*.png | tail -n+2 python extract_otp_secret_keys.py --csv - example_*.png | tail -n+2
python extract_otp_secret_keys.py = < example_export.png""" python extract_otp_secret_keys.py = < example_export.png"""
arg_parser = argparse.ArgumentParser(formatter_class=formatter, arg_parser = argparse.ArgumentParser(formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=52),
description=description_text, description=description_text,
epilog=example_text) epilog=example_text)
arg_parser.add_argument('infile', help="""a) file or - for stdin with 'otpauth-migration://...' URLs separated by newlines, lines starting with # are ignored; arg_parser.add_argument('infile', help="""a) file or - for stdin with 'otpauth-migration://...' URLs separated by newlines, lines starting with # are ignored;
@ -285,7 +284,7 @@ def extract_otp_from_otp_url(otpauth_migration_url, otps, i, j, infile, args):
j += 1 j += 1
if verbose: print(f"\n{j}. Secret Key") if verbose: print(f"\n{j}. Secret Key")
secret = convert_secret_from_bytes_to_base32_str(raw_otp.secret) secret = convert_secret_from_bytes_to_base32_str(raw_otp.secret)
otp_type_enum = get_enum_name_by_number(raw_otp, 'type') if verbose: print('OTP enum type:', get_enum_name_by_number(raw_otp, 'type'))
otp_type = get_otp_type_str_from_code(raw_otp.type) otp_type = get_otp_type_str_from_code(raw_otp.type)
otp_url = build_otp_url(secret, raw_otp) otp_url = build_otp_url(secret, raw_otp)
otp = { otp = {
@ -349,7 +348,7 @@ def get_payload_from_otp_url(otpauth_migration_url, i, input_source):
if verbose > 2: print(f"\nDEBUG: parsed_url={parsed_url}") if verbose > 2: print(f"\nDEBUG: parsed_url={parsed_url}")
try: try:
params = urlparse.parse_qs(parsed_url.query, strict_parsing=True) params = urlparse.parse_qs(parsed_url.query, strict_parsing=True)
except: # Not necessary for Python >= 3.11 except Exception: # Not necessary for Python >= 3.11
params = [] params = []
if verbose > 2: print(f"\nDEBUG: querystring params={params}") if verbose > 2: print(f"\nDEBUG: querystring params={params}")
if 'data' not in params: if 'data' not in params:
@ -362,7 +361,7 @@ def get_payload_from_otp_url(otpauth_migration_url, i, input_source):
payload = protobuf_generated_python.google_auth_pb2.MigrationPayload() payload = protobuf_generated_python.google_auth_pb2.MigrationPayload()
try: try:
payload.ParseFromString(data) payload.ParseFromString(data)
except: except Exception:
abort(f"\nERROR: Cannot decode otpauth-migration migration payload.\n" abort(f"\nERROR: Cannot decode otpauth-migration migration payload.\n"
f"data={data_base64}") f"data={data_base64}")
if verbose: if verbose:

View file

@ -42,6 +42,7 @@ batch_id: -1320898453
1. Secret Key 1. Secret Key
OTP enum type: OTP_TOTP
Name: pi@raspberrypi Name: pi@raspberrypi
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
Issuer: raspberrypi Issuer: raspberrypi
@ -66,6 +67,7 @@ batch_id: -2094403140
2. Secret Key 2. Secret Key
OTP enum type: OTP_TOTP
Name: pi@raspberrypi Name: pi@raspberrypi
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
Type: totp Type: totp
@ -98,6 +100,7 @@ batch_id: -1822886384
3. Secret Key 3. Secret Key
OTP enum type: OTP_TOTP
Name: pi@raspberrypi Name: pi@raspberrypi
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
Type: totp Type: totp
@ -105,6 +108,7 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
4. Secret Key 4. Secret Key
OTP enum type: OTP_TOTP
Name: pi@raspberrypi Name: pi@raspberrypi
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
Issuer: raspberrypi Issuer: raspberrypi
@ -130,6 +134,7 @@ batch_id: -1558849573
5. Secret Key 5. Secret Key
OTP enum type: OTP_HOTP
Name: hotp demo Name: hotp demo
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
Type: hotp Type: hotp
@ -155,6 +160,7 @@ batch_id: -171198419
6. Secret Key 6. Secret Key
OTP enum type: OTP_TOTP
Name: encoding: ¿äÄéÉ? (demo) Name: encoding: ¿äÄéÉ? (demo)
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
Type: totp Type: totp

View file

@ -20,13 +20,15 @@
import io import io
import os import os
import re
import sys import sys
import pytest import pytest
import extract_otp_secret_keys import extract_otp_secret_keys
from utils import * from utils import (file_exits, quick_and_dirty_workaround_encoding_problem,
read_binary_file_as_stream, read_csv, read_csv_str,
read_file_to_str, read_json, read_json_str,
replace_escaped_octal_utf8_bytes_with_str)
qreader_available = extract_otp_secret_keys.qreader_available qreader_available = extract_otp_secret_keys.qreader_available
@ -213,7 +215,6 @@ def test_keepass_csv_stdout(capsys):
# Assert # Assert
expected_totp_csv = read_csv('example_keepass_output.totp.csv') expected_totp_csv = read_csv('example_keepass_output.totp.csv')
expected_hotp_csv = read_csv('example_keepass_output.hotp.csv')
assert not file_exits('test_example_keepass_output.totp.csv') assert not file_exits('test_example_keepass_output.totp.csv')
assert not file_exits('test_example_keepass_output.hotp.csv') assert not file_exits('test_example_keepass_output.hotp.csv')
assert not file_exits('test_example_keepass_output.csv') assert not file_exits('test_example_keepass_output.csv')
@ -340,7 +341,8 @@ def test_extract_saveqr(capsys, tmp_path):
def test_normalize_bytes(): def test_normalize_bytes():
assert replace_escaped_octal_utf8_bytes_with_str('Before\\\\302\\\\277\\\\303\nname: enc: \\302\\277\\303\\244\\303\\204\\303\\251\\303\\211?\nAfter') == 'Before\\\\302\\\\277\\\\303\nname: enc: ¿äÄéÉ?\nAfter' assert replace_escaped_octal_utf8_bytes_with_str(
'Before\\\\302\\\\277\\\\303\nname: enc: \\302\\277\\303\\244\\303\\204\\303\\251\\303\\211?\nAfter') == 'Before\\\\302\\\\277\\\\303\nname: enc: ¿äÄéÉ?\nAfter'
def test_extract_verbose(capsys, relaxed): def test_extract_verbose(capsys, relaxed):
@ -352,6 +354,9 @@ def test_extract_verbose(capsys, relaxed):
expected_stdout = read_file_to_str('test/print_verbose_output.txt') expected_stdout = read_file_to_str('test/print_verbose_output.txt')
if not qreader_available:
expected_stdout = expected_stdout.replace('QReader installed: True', 'QReader installed: False')
if relaxed or sys.implementation.name == 'pypy': if relaxed or sys.implementation.name == 'pypy':
print('\nRelaxed mode\n') print('\nRelaxed mode\n')
@ -390,6 +395,7 @@ def test_extract_help(capsys):
assert e.type == SystemExit assert e.type == SystemExit
assert e.value.code == 0 assert e.value.code == 0
def test_extract_no_arguments(capsys, mocker): def test_extract_no_arguments(capsys, mocker):
if qreader_available: if qreader_available:
# Arrange # Arrange
@ -554,8 +560,7 @@ def test_img_qr_reader_from_stdin(capsys, monkeypatch):
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
expected_stdout =\ expected_stdout = '''Name: Test1:test1@example1.com
'''Name: Test1:test1@example1.com
Secret: JBSWY3DPEHPK3PXP Secret: JBSWY3DPEHPK3PXP
Issuer: Test1 Issuer: Test1
Type: totp Type: totp
@ -705,8 +710,7 @@ Type: totp
''' '''
EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT_PNG =\ EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT_PNG = '''Name: Test1:test1@example1.com
'''Name: Test1:test1@example1.com
Secret: JBSWY3DPEHPK3PXP Secret: JBSWY3DPEHPK3PXP
Issuer: Test1 Issuer: Test1
Type: totp Type: totp

View file

@ -23,6 +23,7 @@ from utils import Capturing
import extract_otp_secret_keys import extract_otp_secret_keys
class TestQRImageExtract(unittest.TestCase): class TestQRImageExtract(unittest.TestCase):
def test_img_qr_reader_happy_path(self): def test_img_qr_reader_happy_path(self):
with Capturing() as actual_output: with Capturing() as actual_output:
@ -40,8 +41,7 @@ class TestQRImageExtract(unittest.TestCase):
with self.assertRaises(SystemExit) as context: with self.assertRaises(SystemExit) as context:
extract_otp_secret_keys.main(['test/lena_std.tif']) extract_otp_secret_keys.main(['test/lena_std.tif'])
expected_output =\ expected_output = ['', 'ERROR: Unable to read QR Code from file.', 'input file: test/lena_std.tif']
['', 'ERROR: Unable to read QR Code from file.', 'input file: test/lena_std.tif']
self.assertEqual(actual_output, expected_output) self.assertEqual(actual_output, expected_output)
self.assertEqual(context.exception.code, 1) self.assertEqual(context.exception.code, 1)
@ -51,8 +51,7 @@ class TestQRImageExtract(unittest.TestCase):
with self.assertRaises(SystemExit) as context: with self.assertRaises(SystemExit) as context:
extract_otp_secret_keys.main(['test/nonexistent.bmp']) extract_otp_secret_keys.main(['test/nonexistent.bmp'])
expected_output =\ expected_output = ['', 'ERROR: Input file provided is non-existent or not a file.', 'input file: test/nonexistent.bmp']
['', 'ERROR: Input file provided is non-existent or not a file.', 'input file: test/nonexistent.bmp']
self.assertEqual(actual_output, expected_output) self.assertEqual(actual_output, expected_output)
self.assertEqual(context.exception.code, 1) self.assertEqual(context.exception.code, 1)

View file

@ -209,7 +209,7 @@ cmd="$FLAKE8 . --count --select=E9,F63,F7,F82 --show-source --statistics"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd" eval "$cmd"
cmd="$FLAKE8 . --count --exit-zero --max-complexity=10 --max-line-length=200 --statistics" cmd="$FLAKE8 . --count --exit-zero --max-complexity=10 --max-line-length=200 --statistics --exclude=.git,__pycache__,docs/source/conf.py,old,build,dist,protobuf_generated_python"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd" eval "$cmd"
@ -239,7 +239,7 @@ cmd="docker build . -t extract_otp_secret_keys_no_qr_reader -f Dockerfile_no_qr_
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd" eval "$cmd"
cmd="docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro extract_otp_secret_keys_no_qr_reader test_extract_otp_secret_keys_pytest.py -k 'not qreader' -vvv --relaxed" cmd="docker run --entrypoint /extract/run_pytest.sh --rm -v \"$(pwd)\":/files:ro extract_otp_secret_keys_no_qr_reader test_extract_otp_secret_keys_pytest.py -k 'not qreader' -vvv --relaxed"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd" eval "$cmd"
@ -247,7 +247,7 @@ cmd="docker build . -t extract_otp_secret_keys --pull"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd" eval "$cmd"
cmd="docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro extract_otp_secret_keys" cmd="docker run --entrypoint /extract/run_pytest.sh --rm -v \"$(pwd)\":/files:ro extract_otp_secret_keys"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd" eval "$cmd"

View file

@ -104,11 +104,13 @@ def read_file_to_str(filename):
"""Returns a str.""" """Returns a str."""
return "".join(read_file_to_list(filename)) return "".join(read_file_to_list(filename))
def read_binary_file_as_stream(filename): def read_binary_file_as_stream(filename):
"""Returns binary file content.""" """Returns binary file content."""
with open(filename, "rb",) as infile: with open(filename, "rb",) as infile:
return io.BytesIO(infile.read()) return io.BytesIO(infile.read())
def replace_escaped_octal_utf8_bytes_with_str(str): def replace_escaped_octal_utf8_bytes_with_str(str):
encoded_name_strings = re.findall(r'name: .*$', str, flags=re.MULTILINE) encoded_name_strings = re.findall(r'name: .*$', str, flags=re.MULTILINE)
for encoded_name_string in encoded_name_strings: for encoded_name_string in encoded_name_strings: