mirror of
https://github.com/scito/extract_otp_secret_keys.git
synced 2025-12-06 06:44:57 +01:00
use only cv2_draw_box, move core functions to top
- improve camera test
- add more tests
- improve README
- add "How to export otp secrets from Google Authenticator app"
- reorder: put usage before installation
- add "Full local build"
This commit is contained in:
parent
36fd0c0bb6
commit
2ed923591e
12 changed files with 343 additions and 212 deletions
|
|
@ -40,6 +40,7 @@ import extract_otp_secrets
|
|||
|
||||
try:
|
||||
import cv2 # type: ignore
|
||||
from extract_otp_secrets import SUCCESS_COLOR, FAILURE_COLOR, FONT, FONT_SCALE, FONT_COLOR, FONT_THICKNESS, FONT_LINE_STYLE
|
||||
except ImportError:
|
||||
# ignore
|
||||
pass
|
||||
|
|
@ -106,6 +107,20 @@ def test_extract_stdin_empty(capsys: pytest.CaptureFixture[str], monkeypatch: py
|
|||
assert captured.err == '\nWARN: stdin is empty\n'
|
||||
|
||||
|
||||
def test_extract_stdin_only_comments(capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
# Arrange
|
||||
monkeypatch.setattr('sys.stdin', io.StringIO("\n\n# comment 1\n\n\n#comment 2"))
|
||||
|
||||
# Act
|
||||
extract_otp_secrets.main(['-n', '-'])
|
||||
|
||||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert captured.out == ''
|
||||
assert captured.err == ''
|
||||
|
||||
|
||||
def test_extract_empty_file_no_qreader(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
if qreader_available:
|
||||
# Act
|
||||
|
|
@ -132,6 +147,17 @@ def test_extract_empty_file_no_qreader(capsys: pytest.CaptureFixture[str]) -> No
|
|||
assert captured.out == ''
|
||||
|
||||
|
||||
def test_extract_only_comments_file_no_qreader(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
# Act
|
||||
extract_otp_secrets.main(['-n', 'tests/data/only_comments.txt'])
|
||||
|
||||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert captured.err == ''
|
||||
assert captured.out == ''
|
||||
|
||||
|
||||
@pytest.mark.qreader
|
||||
def test_extract_stdin_img_empty(capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
# Arrange
|
||||
|
|
@ -147,6 +173,24 @@ def test_extract_stdin_img_empty(capsys: pytest.CaptureFixture[str], monkeypatch
|
|||
assert captured.err == '\nWARN: stdin is empty\n'
|
||||
|
||||
|
||||
@pytest.mark.qreader
|
||||
def test_extract_stdin_img_garbage(capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
# Arrange
|
||||
monkeypatch.setattr('sys.stdin', io.BytesIO("garbage".encode('utf-8')))
|
||||
|
||||
# Act
|
||||
with pytest.raises(SystemExit) as e:
|
||||
extract_otp_secrets.main(['-n', '='])
|
||||
|
||||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert captured.out == ''
|
||||
assert captured.err == '\nERROR: Unable to open file for reading.\ninput file: =\n'
|
||||
assert e.type == SystemExit
|
||||
assert e.value.code == 1
|
||||
|
||||
|
||||
def test_extract_csv(capsys: pytest.CaptureFixture[str], tmp_path: pathlib.Path) -> None:
|
||||
# Arrange
|
||||
output_file = str(tmp_path / 'test_example_output.csv')
|
||||
|
|
@ -182,6 +226,19 @@ def test_extract_csv_stdout(capsys: pytest.CaptureFixture[str]) -> None:
|
|||
assert captured.err == ''
|
||||
|
||||
|
||||
def test_extract_csv_stdout_only_comments(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
# Act
|
||||
extract_otp_secrets.main(['-c', '-', 'tests/data/only_comments.txt'])
|
||||
|
||||
# Assert
|
||||
assert not file_exits('test_example_output.csv')
|
||||
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert captured.out == ''
|
||||
assert captured.err == ''
|
||||
|
||||
|
||||
def test_extract_stdin_and_csv_stdout(capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
# Arrange
|
||||
monkeypatch.setattr('sys.stdin', io.StringIO(read_file_to_str('example_export.txt')))
|
||||
|
|
@ -307,6 +364,18 @@ def test_extract_json_stdout(capsys: pytest.CaptureFixture[str]) -> None:
|
|||
assert captured.err == ''
|
||||
|
||||
|
||||
def test_extract_json_stdout_only_comments(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
# Act
|
||||
extract_otp_secrets.main(['-j', '-', 'tests/data/only_comments.txt'])
|
||||
|
||||
# Assert
|
||||
assert not file_exits('test_example_output.json')
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert captured.out == '[]'
|
||||
assert captured.err == ''
|
||||
|
||||
|
||||
def test_extract_not_encoded_plus(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
# Act
|
||||
extract_otp_secrets.main(['tests/data/test_plus_problem_export.txt'])
|
||||
|
|
@ -544,25 +613,36 @@ class MockCam:
|
|||
pass
|
||||
|
||||
|
||||
@pytest.mark.parametrize("qr_reader", [
|
||||
None,
|
||||
'ZBAR',
|
||||
'QREADER',
|
||||
'QREADER_DEEP',
|
||||
'CV2',
|
||||
'CV2_WECHAT'
|
||||
@pytest.mark.parametrize("qr_reader,file,success", [
|
||||
(None, 'example_export.png', True),
|
||||
('ZBAR', 'example_export.png', True),
|
||||
('QREADER', 'example_export.png', True),
|
||||
('QREADER_DEEP', 'example_export.png', True),
|
||||
('CV2', 'example_export.png', True),
|
||||
('CV2_WECHAT', 'example_export.png', True),
|
||||
(None, 'tests/data/qr_but_without_otp.png', False),
|
||||
('ZBAR', 'tests/data/qr_but_without_otp.png', False),
|
||||
('QREADER', 'tests/data/qr_but_without_otp.png', False),
|
||||
('QREADER_DEEP', 'tests/data/qr_but_without_otp.png', False),
|
||||
('CV2', 'tests/data/qr_but_without_otp.png', False),
|
||||
('CV2_WECHAT', 'tests/data/qr_but_without_otp.png', False),
|
||||
(None, 'tests/data/lena_std.tif', None),
|
||||
('ZBAR', 'tests/data/lena_std.tif', None),
|
||||
('QREADER', 'tests/data/lena_std.tif', None),
|
||||
('QREADER_DEEP', 'tests/data/lena_std.tif', None),
|
||||
('CV2', 'tests/data/lena_std.tif', None),
|
||||
('CV2_WECHAT', 'tests/data/lena_std.tif', None),
|
||||
])
|
||||
def test_extract_otps_from_camera(qr_reader: Optional[str], capsys: pytest.CaptureFixture[str], mocker: MockerFixture) -> None:
|
||||
def test_extract_otps_from_camera(qr_reader: Optional[str], file: str, success: bool, capsys: pytest.CaptureFixture[str], mocker: MockerFixture) -> None:
|
||||
if qreader_available:
|
||||
# Arrange
|
||||
mockCam = MockCam()
|
||||
mockCam = MockCam([file])
|
||||
mocker.patch('cv2.VideoCapture', return_value=mockCam)
|
||||
mocker.patch('cv2.namedWindow')
|
||||
mocker.patch('cv2.rectangle')
|
||||
mocker.patch('cv2.polylines')
|
||||
mocked_polylines = mocker.patch('cv2.polylines')
|
||||
mocker.patch('cv2.imshow')
|
||||
mocker.patch('cv2.getTextSize', return_value=([8, 200], False))
|
||||
mocker.patch('cv2.putText')
|
||||
mocked_putText = mocker.patch('cv2.putText')
|
||||
mocker.patch('cv2.getWindowImageRect', return_value=[0, 0, 640, 480])
|
||||
mocker.patch('cv2.waitKey', return_value=27)
|
||||
mocker.patch('cv2.getWindowProperty', return_value=False)
|
||||
|
|
@ -578,8 +658,21 @@ def test_extract_otps_from_camera(qr_reader: Optional[str], capsys: pytest.Captu
|
|||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT_PNG
|
||||
assert captured.err == ''
|
||||
if success:
|
||||
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT_PNG
|
||||
assert captured.err == ''
|
||||
mocked_polylines.assert_called_with(mocker.ANY, mocker.ANY, True, SUCCESS_COLOR, mocker.ANY)
|
||||
mocked_putText.assert_called_with(mocker.ANY, "3 otps extracted", mocker.ANY, FONT, FONT_SCALE, FONT_COLOR, FONT_THICKNESS, FONT_LINE_STYLE)
|
||||
elif success is None:
|
||||
assert captured.out == ''
|
||||
assert captured.err == ''
|
||||
mocked_polylines.assert_not_called()
|
||||
mocked_putText.assert_called_with(mocker.ANY, "0 otps extracted", mocker.ANY, FONT, FONT_SCALE, FONT_COLOR, FONT_THICKNESS, FONT_LINE_STYLE)
|
||||
else:
|
||||
assert captured.out == ''
|
||||
assert captured.err != ''
|
||||
mocked_polylines.assert_called_with(mocker.ANY, mocker.ANY, True, FAILURE_COLOR, mocker.ANY)
|
||||
mocked_putText.assert_called_with(mocker.ANY, "0 otps extracted", mocker.ANY, FONT, FONT_SCALE, FONT_COLOR, FONT_THICKNESS, FONT_LINE_STYLE)
|
||||
else:
|
||||
# Act
|
||||
with pytest.raises(SystemExit) as e:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue