renaming extract_otp_secret_keys -> extract_otp_secrets and test file names

cv2_1
scito 1 year ago
parent 10ff533a42
commit 549c128fb7

@ -54,7 +54,7 @@ jobs:
if: (matrix.python-version != '3.x' || matrix.platform != 'ubuntu-latest') if: (matrix.python-version != '3.x' || matrix.platform != 'ubuntu-latest')
# && matrix.platform != 'macos-latest' # && matrix.platform != 'macos-latest'
- name: Test with pytest (with code coverage) - name: Test with pytest (with code coverage)
run: pytest --cov=test_extract_otp_secret_keys_pytest --junitxml=pytest.xml --cov-report=term-missing | tee pytest-coverage.txt run: pytest --cov=extract_otp_secrets_test --junitxml=pytest.xml --cov-report=term-missing | tee pytest-coverage.txt
if: matrix.python-version == '3.x' && matrix.platform == 'ubuntu-latest' if: matrix.python-version == '3.x' && matrix.platform == 'ubuntu-latest'
# https://github.com/marketplace/actions/pytest-coverage-comment # https://github.com/marketplace/actions/pytest-coverage-comment
- name: Pytest coverage comment - name: Pytest coverage comment

@ -54,10 +54,10 @@ jobs:
# builder: ${{ steps.buildx.outputs.name }} # builder: ${{ steps.buildx.outputs.name }}
# Note: tags has to be all lower-case # Note: tags has to be all lower-case
tags: | tags: |
scit0/extract_otp_secret_keys_no_qr_reader:latest scit0/extract_otp_secrets_no_qr_reader:latest
ghcr.io/scito/extract_otp_secret_keys_no_qr_reader:latest ghcr.io/scito/extract_otp_secrets_no_qr_reader:latest
scit0/extract_otp_secret_keys:latest-no-qreader scit0/extract_otp_secrets:latest-no-qreader
ghcr.io/scito/extract_otp_secret_keys:latest-no-qreader ghcr.io/scito/extract_otp_secrets:latest-no-qreader
# build on feature branches, push only on master branch # build on feature branches, push only on master branch
# TODO push: ${{ github.ref == 'refs/heads/master' }} # TODO push: ${{ github.ref == 'refs/heads/master' }}
push: true push: true
@ -74,13 +74,13 @@ jobs:
# builder: ${{ steps.buildx.outputs.name }} # builder: ${{ steps.buildx.outputs.name }}
# Note: tags has to be all lower-case # Note: tags has to be all lower-case
tags: | tags: |
scit0/extract_otp_secret_keys:latest scit0/extract_otp_secrets:latest
ghcr.io/scito/extract_otp_secret_keys:latest ghcr.io/scito/extract_otp_secrets:latest
# build on feature branches, push only on master branch # build on feature branches, push only on master branch
# TODO push: ${{ github.ref == 'refs/heads/master' }} # TODO push: ${{ github.ref == 'refs/heads/master' }}
push: true push: true
- name: Image digest - name: Image digest
run: | run: |
echo "extract_otp_secret_keys: ${{ steps.docker_build_qr_reader.outputs.digest }}" echo "extract_otp_secrets: ${{ steps.docker_build_qr_reader.outputs.digest }}"
echo "extract_otp_secret_keys_no_qr_reader: ${{ steps.docker_build_no_qr_reader.outputs.digest }}" echo "extract_otp_secrets_no_qr_reader: ${{ steps.docker_build_no_qr_reader.outputs.digest }}"

@ -5,20 +5,20 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "Python: extract_otp_secret_keys.py", "name": "Python: extract_otp_secrets.py",
"type": "python", "type": "python",
"request": "launch", "request": "launch",
"program": "extract_otp_secret_keys.py", "program": "extract_otp_secrets.py",
"args": [ "args": [
"example_export.txt" "example_export.txt"
], ],
"console": "integratedTerminal" "console": "integratedTerminal"
}, },
{ {
"name": "Python: extract_otp_secret_keys.py stdin pic", "name": "Python: extract_otp_secrets.py stdin pic",
"type": "python", "type": "python",
"request": "launch", "request": "launch",
"program": "extract_otp_secret_keys.py", "program": "extract_otp_secrets.py",
"args": [ "args": [
"-", "-",
"<", "<",
@ -27,10 +27,10 @@
"console": "integratedTerminal" "console": "integratedTerminal"
}, },
{ {
"name": "Python: extract_otp_secret_keys.py -C", "name": "Python: extract_otp_secrets.py -C",
"type": "python", "type": "python",
"request": "launch", "request": "launch",
"program": "extract_otp_secret_keys.py", "program": "extract_otp_secrets.py",
"args": [ "args": [
"-v", "-v",
], ],

@ -1,8 +1,9 @@
FROM python:3.11-slim-bullseye FROM python:3.11-slim-bullseye
# For debugging # For debugging
# docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro extract_otp_secret_keys # docker build . -t extract_otp_secrets --pull --build-arg RUN_TESTS=false
# docker run --entrypoint /bin/bash -it --rm -v "$(pwd)":/files:ro --device="/dev/video0:/dev/video0" --env="DISPLAY" -v /tmp/.X11-unix:/tmp/.X11-unix:ro extract_otp_secret_keys # docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro extract_otp_secrets
# docker run --entrypoint /bin/bash -it --rm -v "$(pwd)":/files:ro --device="/dev/video0:/dev/video0" --env="DISPLAY" -v /tmp/.X11-unix:/tmp/.X11-unix:ro extract_otp_secrets
WORKDIR /extract WORKDIR /extract
@ -16,6 +17,6 @@ RUN apt-get update && apt-get install -y libzbar0 libsm6 python3-opencv nano \
WORKDIR /files WORKDIR /files
ENTRYPOINT ["python", "/extract/src/extract_otp_secret_keys.py"] ENTRYPOINT ["python", "/extract/src/extract_otp_secrets.py"]
LABEL org.opencontainers.image.source https://github.com/scito/extract_otp_secret_keys LABEL org.opencontainers.image.source https://github.com/scito/extract_otp_secrets

@ -1,8 +1,9 @@
FROM python:3.11-alpine FROM python:3.11-alpine
# For debugging # For debugging
# docker run --entrypoint /bin/sh -it --rm -v "$(pwd)":/files:ro extract_otp_secret_keys_no_qr_reader # docker build . -t extract_otp_secrets_no_qr_reader -f Dockerfile_no_qr_reader --pull --build-arg RUN_TESTS=false
# 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" --relaxed # docker run --entrypoint /bin/sh -it --rm -v "$(pwd)":/files:ro extract_otp_secrets_no_qr_reader
# docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro extract_otp_secrets_no_qr_reader extract_otp_secrets_test.py -k "not qreader" --relaxed
WORKDIR /extract WORKDIR /extract
@ -15,10 +16,10 @@ RUN apk add --no-cache nano zlib jpeg \
&& if [[ "$(apk --print-arch)" == "aarch64" ]]; then apk add --no-cache --virtual .build-deps gcc libc-dev python3-dev py3-setuptools zlib-dev jpeg-dev; fi \ && if [[ "$(apk --print-arch)" == "aarch64" ]]; then apk add --no-cache --virtual .build-deps gcc libc-dev python3-dev py3-setuptools zlib-dev jpeg-dev; fi \
&& pip install --no-cache-dir protobuf qrcode Pillow \ && pip install --no-cache-dir protobuf qrcode Pillow \
&& if [[ "$(apk --print-arch)" == "aarch64" ]]; then apk del .build-deps; fi \ && if [[ "$(apk --print-arch)" == "aarch64" ]]; then apk del .build-deps; fi \
&& if [[ "$RUN_TESTS" == "true" ]]; then /extract/run_pytest.sh tests/test_extract_otp_secret_keys_pytest.py -k "not qreader" --relaxed; else echo "Not running tests..."; fi && if [[ "$RUN_TESTS" == "true" ]]; then /extract/run_pytest.sh tests/extract_otp_secrets_test.py -k "not qreader" --relaxed; else echo "Not running tests..."; fi
WORKDIR /files WORKDIR /files
ENTRYPOINT ["python", "/extract/src/extract_otp_secret_keys.py"] ENTRYPOINT ["python", "/extract/src/extract_otp_secrets.py"]
LABEL org.opencontainers.image.source https://github.com/scito/extract_otp_secret_keys LABEL org.opencontainers.image.source https://github.com/scito/extract_otp_secrets

@ -32,7 +32,7 @@ cd extract_otp_secret_keys
3. Point the QR codes to the camera of your computer 3. Point the QR codes to the camera of your computer
4. Call this script with the file as input: 4. Call this script with the file as input:
python extract_otp_secret_keys.py python extract_otp_secrets.py
### With builtin QR decoder from image files ### With builtin QR decoder from image files
@ -42,7 +42,7 @@ cd extract_otp_secret_keys
5. Transfer the images files to the computer where his script is installed. 5. Transfer the images files to the computer where his script is installed.
6. Call this script with the file as input: 6. Call this script with the file as input:
python extract_otp_secret_keys.py example_export.png python extract_otp_secrets.py example_export.png
### With external QR decoder app from text files ### With external QR decoder app from text files
@ -53,11 +53,11 @@ cd extract_otp_secret_keys
5. Transfer the file to the computer where his script is installed. 5. Transfer the file to the computer where his script is installed.
6. Call this script with the file as input: 6. Call this script with the file as input:
python extract_otp_secret_keys.py example_export.txt python extract_otp_secrets.py example_export.txt
## Program help: arguments and options ## Program help: arguments and options
<pre>usage: extract_otp_secret_keys.py [-h] [--camera NUMBER] [--json FILE] [--csv FILE] [--keepass FILE] [--printqr] [--saveqr DIR] [--verbose | --quiet] [infile ...] <pre>usage: extract_otp_secrets.py [-h] [--camera NUMBER] [--json FILE] [--csv FILE] [--keepass FILE] [--printqr] [--saveqr DIR] [--verbose | --quiet] [infile ...]
Extracts one time password (OTP) secret keys from QR codes, e.g. from Google Authenticator app. Extracts one time password (OTP) secret keys from QR codes, e.g. from Google Authenticator app.
If no infiles are provided, the QR codes are interactively captured from the camera. If no infiles are provided, the QR codes are interactively captured from the camera.
@ -78,11 +78,11 @@ options:
--quiet, -q no stdout output, except output set by - --quiet, -q no stdout output, except output set by -
examples: examples:
python extract_otp_secret_keys.py python extract_otp_secrets.py
python extract_otp_secret_keys.py example_*.txt python extract_otp_secrets.py example_*.txt
python extract_otp_secret_keys.py - < example_export.txt python extract_otp_secrets.py - < example_export.txt
python extract_otp_secret_keys.py --csv - example_*.png | tail -n+2 python extract_otp_secrets.py --csv - example_*.png | tail -n+2
python extract_otp_secret_keys.py = < example_export.png</pre> python extract_otp_secrets.py = < example_export.png</pre>
## Dependencies ## Dependencies
@ -93,7 +93,7 @@ Known to work with
* Python 3.10.8, protobuf 4.21.9, qrcode 7.3.1, and pillow 9.2 * Python 3.10.8, protobuf 4.21.9, qrcode 7.3.1, and pillow 9.2
* Python 3.11.1, protobuf 4.21.12, qrcode 7.3.1, and pillow 9.2 * Python 3.11.1, protobuf 4.21.12, qrcode 7.3.1, and pillow 9.2
For protobuf versions 3.14.0 or similar or Python 3.6, use the extract_otp_secret_keys version 1.4.0. For protobuf versions 3.14.0 or similar or Python 3.6, use the extract_otp_secrets version 1.4.0.
### Shared libs installation for reading QR code images ### Shared libs installation for reading QR code images
@ -126,38 +126,38 @@ The zbar DLLs are included with the Windows Python wheels. On other operating sy
### Printing otp secrets form text file ### Printing otp secrets form text file
python extract_otp_secret_keys.py example_export.txt python extract_otp_secrets.py example_export.txt
### Printing otp secrets from image file ### Printing otp secrets from image file
python extract_otp_secret_keys.py example_export.png python extract_otp_secrets.py example_export.png
### Printing otp secrets multiple files ### Printing otp secrets multiple files
python extract_otp_secret_keys.py example_*.txt python extract_otp_secrets.py example_*.txt
python extract_otp_secret_keys.py example_*.png python extract_otp_secrets.py example_*.png
python extract_otp_secret_keys.py example_export.* python extract_otp_secrets.py example_export.*
python extract_otp_secret_keys.py example_*.txt example_*.png python extract_otp_secrets.py example_*.txt example_*.png
### Printing otp secrets from stdin (text) ### Printing otp secrets from stdin (text)
python extract_otp_secret_keys.py - < example_export.txt python extract_otp_secrets.py - < example_export.txt
### Printing otp secrets from stdin (image) ### Printing otp secrets from stdin (image)
python extract_otp_secret_keys.py = < example_export.png python extract_otp_secrets.py = < example_export.png
### Printing otp secrets csv to stdout ### Printing otp secrets csv to stdout
python extract_otp_secret_keys.py --csv - example_export.txt python extract_otp_secrets.py --csv - example_export.txt
### Printing otp secrets csv to stdout without header line ### Printing otp secrets csv to stdout without header line
python extract_otp_secret_keys.py --csv - example_*.png | tail -n+2 python extract_otp_secrets.py --csv - example_*.png | tail -n+2
### Reading from stdin and printing to stdout ### Reading from stdin and printing to stdout
cat example_*.txt | python extract_otp_secret_keys.py --csv - - | tail -n+2 cat example_*.txt | python extract_otp_secrets.py --csv - - | tail -n+2
## Features ## Features
@ -200,7 +200,7 @@ The zbar DLLs are included with the Windows Python wheels. On other operating sy
* Uses UTF-8 on all platforms * Uses UTF-8 on all platforms
* Supports Python >= 3.7 * Supports Python >= 3.7
* All these features are backed by tests ran nightly * All these features are backed by tests ran nightly
* All functionality in one Python script: extract_otp_secret_keys.py (except protobuf generated code in protobuf_generated_python) * All functionality in one Python script: extract_otp_secrets.py (except protobuf generated code in protobuf_generated_python)
## KeePass ## KeePass
@ -273,27 +273,27 @@ https://github.com/nipunn1313/mypy-protobuf
``` ```
pip install git+https://github.com/scito/extract_otp_secret_keys pip install git+https://github.com/scito/extract_otp_secret_keys
python -m extract_otp_secret_keys python -m extract_otp_secrets
``` ```
#### For development #### For development
``` ```
pip install git+https://github.com/scito/extract_otp_secret_keys@support_img_read pip install git+https://github.com/scito/extract_otp_secret_keys@support_img_read
python -m extract_otp_secret_keys python -m extract_otp_secrets
``` ```
``` ```
# pip install -e git+https://github.com/scito/extract_otp_secret_keys@$(git ls-remote git@github.com:scito/extract_otp_secret_keys@support_img_read.git | head -1 | awk '{print $1;}')#egg=extract_otp_secret_keys # pip install -e git+https://github.com/scito/extract_otp_secret_keys@$(git ls-remote git@github.com:scito/extract_otp_secret_keys@support_img_read.git | head -1 | awk '{print $1;}')#egg=extract_otp_secrets
pip3.11 install -e git+https://github.com/scito/extract_otp_secret_keys.git@$(git ls-remote git@github.com:scito/extract_otp_secret_keys.git | grep support_img_read | head -1 | awk '{print $1;}')#egg=extract_otp_secret_keys pip3.11 install -e git+https://github.com/scito/extract_otp_secret_keys.git@$(git ls-remote git@github.com:scito/extract_otp_secret_keys.git | grep support_img_read | head -1 | awk '{print $1;}')#egg=extract_otp_secrets
python -m extract_otp_secret_keys python -m extract_otp_secrets
``` ```
#### Example #### Example
``` ```
wget https://raw.githubusercontent.com/scito/extract_otp_secret_keys/master/example_export.txt wget https://raw.githubusercontent.com/scito/extract_otp_secret_keys/master/example_export.txt
python -m extract_otp_secret_keys example_export.txt python -m extract_otp_secrets example_export.txt
``` ```
### local pip ### local pip
@ -304,27 +304,27 @@ pip install -e .
### pipenv ### pipenv
You can you use [Pipenv](https://github.com/pypa/pipenv) for running extract_otp_secret_keys. You can you use [Pipenv](https://github.com/pypa/pipenv) for running extract_otp_secrets.
``` ```
pipenv --rm pipenv --rm
pipenv install pipenv install
pipenv shell pipenv shell
python extract_otp_secret_keys.py example_export.txt python extract_otp_secrets.py example_export.txt
``` ```
### Visual Studio Code Remote - Containers / VSCode devcontainer ### Visual Studio Code Remote - Containers / VSCode devcontainer
You can you use [VSCode devcontainer](https://code.visualstudio.com/docs/remote/containers-tutorial) for running extract_otp_secret_keys. You can you use [VSCode devcontainer](https://code.visualstudio.com/docs/remote/containers-tutorial) for running extract_otp_secrets.
Requirement: Docker Requirement: Docker
1. Start VSCode 1. Start VSCode
2. Open extract_otp_secret_keys.code-workspace 2. Open extract_otp_secrets.code-workspace
3. Open VSCode command palette (Ctrl-Shift-P) 3. Open VSCode command palette (Ctrl-Shift-P)
4. Type command "Remote-Containers: Reopen in Container" 4. Type command "Remote-Containers: Reopen in Container"
5. Open integrated bash terminal in VSCode 5. Open integrated bash terminal in VSCode
6. Execute: python extract_otp_secret_keys.py example_export.txt 6. Execute: python extract_otp_secrets.py example_export.txt
### venv ### venv
@ -353,36 +353,36 @@ Install [Docker](https://docs.docker.com/get-docker/).
Build and run the app within the container: Build and run the app within the container:
```bash ```bash
docker build . -t extract_otp_secret_keys --pull docker build . -t extract_otp_secrets --pull
docker run --rm -v "$(pwd)":/files:ro extract_otp_secret_keys example_export.txt docker run --rm -v "$(pwd)":/files:ro extract_otp_secrets example_export.txt
docker run --rm -v "$(pwd)":/files:ro extract_otp_secret_keys example_export.png docker run --rm -v "$(pwd)":/files:ro extract_otp_secrets example_export.png
``` ```
docker run --rm -v "$(pwd)":/files:ro -i extract_otp_secret_keys = < example_export.png docker run --rm -v "$(pwd)":/files:ro -i extract_otp_secrets = < example_export.png
docker run --rm -v "$(pwd)":/files:ro -i --device="/dev/video0:/dev/video0" --env="DISPLAY" -v /tmp/.X11-unix:/tmp/.X11-unix:ro extract_otp_secret_keys docker run --rm -v "$(pwd)":/files:ro -i --device="/dev/video0:/dev/video0" --env="DISPLAY" -v /tmp/.X11-unix:/tmp/.X11-unix:ro extract_otp_secrets
docker run --pull always --rm -v "$(pwd)":/files:ro -i --device="/dev/video0:/dev/video0" --env="DISPLAY" -v /tmp/.X11-unix:/tmp/.X11-unix:ro scit0/extract_otp_secret_keys docker run --pull always --rm -v "$(pwd)":/files:ro -i --device="/dev/video0:/dev/video0" --env="DISPLAY" -v /tmp/.X11-unix:/tmp/.X11-unix:ro scit0/extract_otp_secrets
docker run --entrypoint /bin/bash -it --rm -v "$(pwd)":/files:ro extract_otp_secret_keys docker run --entrypoint /bin/bash -it --rm -v "$(pwd)":/files:ro extract_otp_secrets
docker run --pull always --rm -v "$(pwd)":/files:ro -i scit0/extract_otp_secret_keys docker run --pull always --rm -v "$(pwd)":/files:ro -i scit0/extract_otp_secrets
docker login -uscit0 docker login -uscit0
docker build . -t extract_otp_secret_keys_no_qr_reader -f Dockerfile_no_qr_reader --pull docker build . -t extract_otp_secrets_no_qr_reader -f Dockerfile_no_qr_reader --pull
docker build . -t extract_otp_secret_keys_no_qr_reader -f Dockerfile_no_qr_reader --pull --build-arg RUN_TESTS=false docker build . -t extract_otp_secrets_no_qr_reader -f Dockerfile_no_qr_reader --pull --build-arg RUN_TESTS=false
docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro scit0/extract_otp_secret_keys_no_qr_reader test_extract_otp_secret_keys_pytest.py -k "not qreader" --relaxed docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets_no_qr_reader extract_otp_secrets_test.py -k "not qreader" --relaxed
docker run --rm -v "$(pwd)":/files:ro extract_otp_secret_keys_no_qr_reader example_export.txt docker run --rm -v "$(pwd)":/files:ro extract_otp_secrets_no_qr_reader example_export.txt
docker run --rm -v "$(pwd)":/files:ro -i extract_otp_secret_keys_no_qr_reader - < example_export.txt docker run --rm -v "$(pwd)":/files:ro -i extract_otp_secrets_no_qr_reader - < example_export.txt
docker build . -t extract_otp_secret_keys_no_qr_reader -f Dockerfile_no_qr_reader --pull && 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 -s docker build . -t extract_otp_secrets_no_qr_reader -f Dockerfile_no_qr_reader --pull && docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro extract_otp_secrets_no_qr_reader extract_otp_secrets_test.py -k "not qreader" -vvv --relaxed -s
docker pull scit0/extract_otp_secret_keys docker pull scit0/extract_otp_secrets
docker pull scit0/extract_otp_secret_keys_no_qr_reader docker pull scit0/extract_otp_secrets_no_qr_reader
docker pull ghcr.io/scito/extract_otp_secret_keys docker pull ghcr.io/scito/extract_otp_secrets
docker pull ghcr.io/scito/extract_otp_secret_keys_no_qr_reader docker pull ghcr.io/scito/extract_otp_secrets_no_qr_reader
## Tests ## Tests
### PyTest ### PyTest
There are basic [pytest](https://pytest.org)s, see `test_extract_otp_secret_keys_pytest.py`. There are basic [pytest](https://pytest.org)s, see `extract_otp_secrets_test.py`.
Run tests: Run tests:
@ -406,7 +406,7 @@ https://docs.pytest.org/en/7.1.x/explanation/pythonpath.html#pytest-vs-python-m-
### unittest ### unittest
There are basic [unittest](https://docs.python.org/3.10/library/unittest.html)s, see `test_extract_otp_secret_keys_unittest.py`. There are basic [unittest](https://docs.python.org/3.10/library/unittest.html)s, see `extract_otp_secrets_txt_unit_test.py`.
Run tests: Run tests:
@ -429,7 +429,7 @@ Setup for running the tests in VSCode.
``` ```
pip install -e . pip install -e .
python src/extract_otp_secret_keys.py python src/extract_otp_secrets.py
pip wheel . pip wheel .
# --isolated # --isolated

@ -11,10 +11,10 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "Python: extract_otp_secret_keys.py", "name": "Python: extract_otp_secrets.py",
"type": "python", "type": "python",
"request": "launch", "request": "launch",
"program": "extract_otp_secret_keys.py", "program": "extract_otp_secrets.py",
"args": [ "args": [
"example_export.txt" "example_export.txt"
], ],

@ -7,7 +7,7 @@ requires = [
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[project] [project]
name = "extract_otp_secret_keys" name = "extract_otp_secrets"
# https://pypi.org/classifiers/ # https://pypi.org/classifiers/
classifiers = [ classifiers = [
"Development Status :: 5 - Production/Stable", "Development Status :: 5 - Production/Stable",
@ -46,15 +46,15 @@ readme = "README.md"
authors = [{name = "scito", email = "info@scito.ch"}] authors = [{name = "scito", email = "info@scito.ch"}]
maintainers = [{name = "scito", email = "info@scito.ch"}] maintainers = [{name = "scito", email = "info@scito.ch"}]
requires-python = ">=3.7, <4" requires-python = ">=3.7, <4"
scripts = {extract_otp_secret_keys = "extract_otp_secret_keys:sys_main"} scripts = {extract_otp_secrets = "extract_otp_secrets:sys_main"}
urls = {Project-URL = "https://github.com/scito/extract_otp_secret_keys", Bug-Reports = "https://github.com/scito/extract_otp_secret_keys/issues", Source = "https://github.com/scito/extract_otp_secret_keys" } urls = {Project-URL = "https://github.com/scito/extract_otp_secret_keys", Bug-Reports = "https://github.com/scito/extract_otp_secret_keys/issues", Source = "https://github.com/scito/extract_otp_secret_keys" }
# [tool.setuptools] # [tool.setuptools]
# Still in beta, once it is stable move config from setup.cfg to pyproject.toml # Still in beta, once it is stable move config from setup.cfg to pyproject.toml
# py-modules = ["extract_otp_secret_keys", "protobuf_generated_python.protobuf_generated_python"] # py-modules = ["extract_otp_secrets", "protobuf_generated_python.protobuf_generated_python"]
# [tool.setuptools.dynamic] # [tool.setuptools.dynamic]
# version = {attr = "extract_otp_secret_keys.VERSION"} # version = {attr = "extract_otp_secrets.VERSION"}
[tool.setuptools-git-versioning] [tool.setuptools-git-versioning]
enabled = true enabled = true

@ -1,9 +1,9 @@
[metadata] [metadata]
name = extract_otp_secret_keys name = extract_otp_secrets
[options] [options]
python_requires = >=3.7, <4 python_requires = >=3.7, <4
py_modules = extract_otp_secret_keys, protobuf_generated_python.google_auth_pb2 py_modules = extract_otp_secrets, protobuf_generated_python.google_auth_pb2
package_dir = package_dir =
=src =src
# packages=find: # packages=find:

@ -5,7 +5,7 @@
# 2. Read QR codes with QR code reader (e.g. with a second device) # 2. Read QR codes with QR code reader (e.g. with a second device)
# 3. Save the captured QR codes in a text file. Save each QR code on a new line. (The captured QR codes look like "otpauth-migration://offline?data=...") # 3. Save the captured QR codes in a text file. Save each QR code on a new line. (The captured QR codes look like "otpauth-migration://offline?data=...")
# 4. Call this script with the file as input: # 4. Call this script with the file as input:
# python extract_otp_secret_keys.py example_export.txt # python extract_otp_secrets.py example_export.txt
# #
# Requirement: # Requirement:
# The protobuf package of Google for proto3 is required for running this script. # The protobuf package of Google for proto3 is required for running this script.
@ -41,7 +41,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations # for compatibility with Python < 3.11 from __future__ import annotations # for compatibility with PYTHON < 3.11
import argparse import argparse
import base64 import base64
import csv import csv
@ -124,11 +124,11 @@ def parse_args(sys_args: list[str]) -> Args:
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."
example_text = """examples: example_text = """examples:
python extract_otp_secret_keys.py python extract_otp_secrets.py
python extract_otp_secret_keys.py example_*.txt python extract_otp_secrets.py example_*.txt
python extract_otp_secret_keys.py - < example_export.txt python extract_otp_secrets.py - < example_export.txt
python extract_otp_secret_keys.py --csv - example_*.png | tail -n+2 python extract_otp_secrets.py --csv - example_*.png | tail -n+2
python extract_otp_secret_keys.py = < example_export.png""" python extract_otp_secrets.py = < example_export.png"""
arg_parser = argparse.ArgumentParser(formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=52), arg_parser = argparse.ArgumentParser(formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=52),
description=description_text, description=description_text,

@ -1,4 +1,4 @@
# Unit test for extract_otp_secret_keys.py # Unit test for extract_otp_secrets.py
# Run tests: # Run tests:
# python -m unittest # python -m unittest
@ -18,17 +18,17 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations # for compatibility with Python < 3.11 from __future__ import annotations # for compatibility with PYTHON < 3.11
import unittest import unittest
import extract_otp_secret_keys import extract_otp_secrets
from utils import Capturing from utils import Capturing
class TestQRImageExtract(unittest.TestCase): class TestQRImageExtract(unittest.TestCase):
def test_img_qr_reader_happy_path(self) -> None: def test_img_qr_reader_happy_path(self) -> None:
with Capturing() as actual_output: with Capturing() as actual_output:
extract_otp_secret_keys.main(['tests/data/test_googleauth_export.png']) extract_otp_secrets.main(['tests/data/test_googleauth_export.png'])
expected_output =\ expected_output =\
['Name: Test1:test1@example1.com', 'Secret: JBSWY3DPEHPK3PXP', 'Issuer: Test1', 'Type: totp', '', ['Name: Test1:test1@example1.com', 'Secret: JBSWY3DPEHPK3PXP', 'Issuer: Test1', 'Type: totp', '',
@ -40,7 +40,7 @@ class TestQRImageExtract(unittest.TestCase):
def test_img_qr_reader_no_qr_code_in_image(self) -> None: def test_img_qr_reader_no_qr_code_in_image(self) -> None:
with Capturing() as actual_output: with Capturing() as actual_output:
with self.assertRaises(SystemExit) as context: with self.assertRaises(SystemExit) as context:
extract_otp_secret_keys.main(['tests/data/lena_std.tif']) extract_otp_secrets.main(['tests/data/lena_std.tif'])
expected_output = ['', 'ERROR: Unable to read QR Code from file.', 'input file: tests/data/lena_std.tif'] expected_output = ['', 'ERROR: Unable to read QR Code from file.', 'input file: tests/data/lena_std.tif']
@ -50,7 +50,7 @@ class TestQRImageExtract(unittest.TestCase):
def test_img_qr_reader_nonexistent_file(self) -> None: def test_img_qr_reader_nonexistent_file(self) -> None:
with Capturing() as actual_output: with Capturing() as actual_output:
with self.assertRaises(SystemExit) as context: with self.assertRaises(SystemExit) as context:
extract_otp_secret_keys.main(['nonexistent.bmp']) extract_otp_secrets.main(['nonexistent.bmp'])
expected_output = ['', 'ERROR: Input file provided is non-existent or not a file.', 'input file: nonexistent.bmp'] expected_output = ['', 'ERROR: Input file provided is non-existent or not a file.', 'input file: nonexistent.bmp']
@ -60,7 +60,7 @@ class TestQRImageExtract(unittest.TestCase):
def test_img_qr_reader_non_image_file(self) -> None: def test_img_qr_reader_non_image_file(self) -> None:
with Capturing() as actual_output: with Capturing() as actual_output:
with self.assertRaises(SystemExit) as context: with self.assertRaises(SystemExit) as context:
extract_otp_secret_keys.main(['tests/data/text_masquerading_as_image.jpeg']) extract_otp_secrets.main(['tests/data/text_masquerading_as_image.jpeg'])
expected_output = [ expected_output = [
'', '',

@ -1,4 +1,4 @@
# pytest for extract_otp_secret_keys.py # pytest for extract_otp_secrets.py
# Run tests: # Run tests:
# pytest # pytest
@ -18,7 +18,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations # for compatibility with Python < 3.11 from __future__ import annotations # for compatibility with PYTHON < 3.11
import io import io
import os import os
import pathlib import pathlib
@ -27,18 +27,18 @@ import sys
import pytest import pytest
from pytest_mock import MockerFixture from pytest_mock import MockerFixture
import extract_otp_secret_keys import extract_otp_secrets
from utils import (file_exits, quick_and_dirty_workaround_encoding_problem, from utils import (file_exits, quick_and_dirty_workaround_encoding_problem,
read_binary_file_as_stream, read_csv, read_csv_str, read_binary_file_as_stream, read_csv, read_csv_str,
read_file_to_str, read_json, read_json_str, read_file_to_str, read_json, read_json_str,
replace_escaped_octal_utf8_bytes_with_str) replace_escaped_octal_utf8_bytes_with_str)
qreader_available: bool = extract_otp_secret_keys.qreader_available qreader_available: bool = extract_otp_secrets.qreader_available
def test_extract_stdout(capsys: pytest.CaptureFixture[str]) -> None: def test_extract_stdout(capsys: pytest.CaptureFixture[str]) -> None:
# Act # Act
extract_otp_secret_keys.main(['example_export.txt']) extract_otp_secrets.main(['example_export.txt'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -50,7 +50,7 @@ def test_extract_stdout(capsys: pytest.CaptureFixture[str]) -> None:
def test_extract_non_existent_file(capsys: pytest.CaptureFixture[str]) -> None: def test_extract_non_existent_file(capsys: pytest.CaptureFixture[str]) -> None:
# Act # Act
with pytest.raises(SystemExit) as e: with pytest.raises(SystemExit) as e:
extract_otp_secret_keys.main(['non_existent_file.txt']) extract_otp_secrets.main(['non_existent_file.txt'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -68,7 +68,7 @@ def test_extract_stdin_stdout(capsys: pytest.CaptureFixture[str], monkeypatch: p
monkeypatch.setattr('sys.stdin', io.StringIO(read_file_to_str('example_export.txt'))) monkeypatch.setattr('sys.stdin', io.StringIO(read_file_to_str('example_export.txt')))
# Act # Act
extract_otp_secret_keys.main(['-']) extract_otp_secrets.main(['-'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -82,7 +82,7 @@ def test_extract_stdin_empty(capsys: pytest.CaptureFixture[str], monkeypatch: py
monkeypatch.setattr('sys.stdin', io.StringIO()) monkeypatch.setattr('sys.stdin', io.StringIO())
# Act # Act
extract_otp_secret_keys.main(['-']) extract_otp_secrets.main(['-'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -96,7 +96,7 @@ def test_extract_empty_file_no_qreader(capsys: pytest.CaptureFixture[str]) -> No
if qreader_available: if qreader_available:
# Act # Act
with pytest.raises(SystemExit) as e: with pytest.raises(SystemExit) as e:
extract_otp_secret_keys.main(['tests/data/empty_file.txt']) extract_otp_secrets.main(['tests/data/empty_file.txt'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -109,7 +109,7 @@ def test_extract_empty_file_no_qreader(capsys: pytest.CaptureFixture[str]) -> No
assert e.type == SystemExit assert e.type == SystemExit
else: else:
# Act # Act
extract_otp_secret_keys.main(['tests/data/empty_file.txt']) extract_otp_secrets.main(['tests/data/empty_file.txt'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -124,7 +124,7 @@ def test_extract_stdin_img_empty(capsys: pytest.CaptureFixture[str], monkeypatch
monkeypatch.setattr('sys.stdin', io.BytesIO()) monkeypatch.setattr('sys.stdin', io.BytesIO())
# Act # Act
extract_otp_secret_keys.main(['=']) extract_otp_secrets.main(['='])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -138,7 +138,7 @@ def test_extract_csv(capsys: pytest.CaptureFixture[str], tmp_path: pathlib.Path)
output_file = str(tmp_path / 'test_example_output.csv') output_file = str(tmp_path / 'test_example_output.csv')
# Act # Act
extract_otp_secret_keys.main(['-q', '-c', output_file, 'example_export.txt']) extract_otp_secrets.main(['-q', '-c', output_file, 'example_export.txt'])
# Assert # Assert
expected_csv = read_csv('example_output.csv') expected_csv = read_csv('example_output.csv')
@ -154,7 +154,7 @@ def test_extract_csv(capsys: pytest.CaptureFixture[str], tmp_path: pathlib.Path)
def test_extract_csv_stdout(capsys: pytest.CaptureFixture[str]) -> None: def test_extract_csv_stdout(capsys: pytest.CaptureFixture[str]) -> None:
# Act # Act
extract_otp_secret_keys.main(['-c', '-', 'example_export.txt']) extract_otp_secrets.main(['-c', '-', 'example_export.txt'])
# Assert # Assert
assert not file_exits('test_example_output.csv') assert not file_exits('test_example_output.csv')
@ -173,7 +173,7 @@ def test_extract_stdin_and_csv_stdout(capsys: pytest.CaptureFixture[str], monkey
monkeypatch.setattr('sys.stdin', io.StringIO(read_file_to_str('example_export.txt'))) monkeypatch.setattr('sys.stdin', io.StringIO(read_file_to_str('example_export.txt')))
# Act # Act
extract_otp_secret_keys.main(['-c', '-', '-']) extract_otp_secrets.main(['-c', '-', '-'])
# Assert # Assert
assert not file_exits('test_example_output.csv') assert not file_exits('test_example_output.csv')
@ -193,7 +193,7 @@ def test_keepass_csv(capsys: pytest.CaptureFixture[str], tmp_path: pathlib.Path)
file_name = str(tmp_path / 'test_example_keepass_output.csv') file_name = str(tmp_path / 'test_example_keepass_output.csv')
# Act # Act
extract_otp_secret_keys.main(['-q', '-k', file_name, 'example_export.txt']) extract_otp_secrets.main(['-q', '-k', file_name, 'example_export.txt'])
# Assert # Assert
expected_totp_csv = read_csv('example_keepass_output.totp.csv') expected_totp_csv = read_csv('example_keepass_output.totp.csv')
@ -214,7 +214,7 @@ def test_keepass_csv(capsys: pytest.CaptureFixture[str], tmp_path: pathlib.Path)
def test_keepass_csv_stdout(capsys: pytest.CaptureFixture[str]) -> None: def test_keepass_csv_stdout(capsys: pytest.CaptureFixture[str]) -> None:
'''Two csv files .totp and .htop are generated.''' '''Two csv files .totp and .htop are generated.'''
# Act # Act
extract_otp_secret_keys.main(['-k', '-', 'tests/data/example_export_only_totp.txt']) extract_otp_secrets.main(['-k', '-', 'tests/data/example_export_only_totp.txt'])
# Assert # Assert
expected_totp_csv = read_csv('example_keepass_output.totp.csv') expected_totp_csv = read_csv('example_keepass_output.totp.csv')
@ -232,7 +232,7 @@ def test_keepass_csv_stdout(capsys: pytest.CaptureFixture[str]) -> None:
def test_single_keepass_csv(capsys: pytest.CaptureFixture[str], tmp_path: pathlib.Path) -> None: def test_single_keepass_csv(capsys: pytest.CaptureFixture[str], tmp_path: pathlib.Path) -> None:
'''Does not add .totp or .hotp pre-suffix''' '''Does not add .totp or .hotp pre-suffix'''
# Act # Act
extract_otp_secret_keys.main(['-q', '-k', str(tmp_path / 'test_example_keepass_output.csv'), 'tests/data/example_export_only_totp.txt']) extract_otp_secrets.main(['-q', '-k', str(tmp_path / 'test_example_keepass_output.csv'), 'tests/data/example_export_only_totp.txt'])
# Assert # Assert
expected_totp_csv = read_csv('example_keepass_output.totp.csv') expected_totp_csv = read_csv('example_keepass_output.totp.csv')
@ -253,7 +253,7 @@ def test_extract_json(capsys: pytest.CaptureFixture[str], tmp_path: pathlib.Path
output_file = str(tmp_path / 'test_example_output.json') output_file = str(tmp_path / 'test_example_output.json')
# Act # Act
extract_otp_secret_keys.main(['-q', '-j', output_file, 'example_export.txt']) extract_otp_secrets.main(['-q', '-j', output_file, 'example_export.txt'])
# Assert # Assert
expected_json = read_json('example_output.json') expected_json = read_json('example_output.json')
@ -269,7 +269,7 @@ def test_extract_json(capsys: pytest.CaptureFixture[str], tmp_path: pathlib.Path
def test_extract_json_stdout(capsys: pytest.CaptureFixture[str]) -> None: def test_extract_json_stdout(capsys: pytest.CaptureFixture[str]) -> None:
# Act # Act
extract_otp_secret_keys.main(['-j', '-', 'example_export.txt']) extract_otp_secrets.main(['-j', '-', 'example_export.txt'])
# Assert # Assert
expected_json = read_json('example_output.json') expected_json = read_json('example_output.json')
@ -283,7 +283,7 @@ def test_extract_json_stdout(capsys: pytest.CaptureFixture[str]) -> None:
def test_extract_not_encoded_plus(capsys: pytest.CaptureFixture[str]) -> None: def test_extract_not_encoded_plus(capsys: pytest.CaptureFixture[str]) -> None:
# Act # Act
extract_otp_secret_keys.main(['tests/data/test_plus_problem_export.txt']) extract_otp_secrets.main(['tests/data/test_plus_problem_export.txt'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -316,7 +316,7 @@ Type: totp
def test_extract_printqr(capsys: pytest.CaptureFixture[str]) -> None: def test_extract_printqr(capsys: pytest.CaptureFixture[str]) -> None:
# Act # Act
extract_otp_secret_keys.main(['-p', 'example_export.txt']) extract_otp_secrets.main(['-p', 'example_export.txt'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -329,7 +329,7 @@ def test_extract_printqr(capsys: pytest.CaptureFixture[str]) -> None:
def test_extract_saveqr(capsys: pytest.CaptureFixture[str], tmp_path: pathlib.Path) -> None: def test_extract_saveqr(capsys: pytest.CaptureFixture[str], tmp_path: pathlib.Path) -> None:
# Act # Act
extract_otp_secret_keys.main(['-q', '-s', str(tmp_path), 'example_export.txt']) extract_otp_secrets.main(['-q', '-s', str(tmp_path), 'example_export.txt'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -350,7 +350,7 @@ def test_normalize_bytes() -> None:
def test_extract_verbose(capsys: pytest.CaptureFixture[str], relaxed: bool) -> None: def test_extract_verbose(capsys: pytest.CaptureFixture[str], relaxed: bool) -> None:
# Act # Act
extract_otp_secret_keys.main(['-v', 'example_export.txt']) extract_otp_secrets.main(['-v', 'example_export.txt'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -372,7 +372,7 @@ def test_extract_verbose(capsys: pytest.CaptureFixture[str], relaxed: bool) -> N
def test_extract_debug(capsys: pytest.CaptureFixture[str]) -> None: def test_extract_debug(capsys: pytest.CaptureFixture[str]) -> None:
# Act # Act
extract_otp_secret_keys.main(['-vvv', 'example_export.txt']) extract_otp_secrets.main(['-vvv', 'example_export.txt'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -387,7 +387,7 @@ def test_extract_debug(capsys: pytest.CaptureFixture[str]) -> None:
def test_extract_help(capsys: pytest.CaptureFixture[str]) -> None: def test_extract_help(capsys: pytest.CaptureFixture[str]) -> None:
with pytest.raises(SystemExit) as e: with pytest.raises(SystemExit) as e:
# Act # Act
extract_otp_secret_keys.main(['-h']) extract_otp_secrets.main(['-h'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -403,10 +403,10 @@ def test_extract_no_arguments(capsys: pytest.CaptureFixture[str], mocker: Mocker
if qreader_available: if qreader_available:
# Arrange # Arrange
otps = read_json('example_output.json') otps = read_json('example_output.json')
mocker.patch('extract_otp_secret_keys.extract_otps_from_camera', return_value=otps) mocker.patch('extract_otp_secrets.extract_otps_from_camera', return_value=otps)
# Act # Act
extract_otp_secret_keys.main(['-c', '-']) extract_otp_secrets.main(['-c', '-'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -419,7 +419,7 @@ def test_extract_no_arguments(capsys: pytest.CaptureFixture[str], mocker: Mocker
else: else:
# Act # Act
with pytest.raises(SystemExit) as e: with pytest.raises(SystemExit) as e:
extract_otp_secret_keys.main([]) extract_otp_secrets.main([])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -435,7 +435,7 @@ def test_extract_no_arguments(capsys: pytest.CaptureFixture[str], mocker: Mocker
def test_verbose_and_quiet(capsys: pytest.CaptureFixture[str]) -> None: def test_verbose_and_quiet(capsys: pytest.CaptureFixture[str]) -> None:
with pytest.raises(SystemExit) as e: with pytest.raises(SystemExit) as e:
# Act # Act
extract_otp_secret_keys.main(['-v', '-q', 'example_export.txt']) extract_otp_secrets.main(['-v', '-q', 'example_export.txt'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -450,7 +450,7 @@ def test_verbose_and_quiet(capsys: pytest.CaptureFixture[str]) -> None:
def test_wrong_data(capsys: pytest.CaptureFixture[str]) -> None: def test_wrong_data(capsys: pytest.CaptureFixture[str]) -> None:
with pytest.raises(SystemExit) as e: with pytest.raises(SystemExit) as e:
# Act # Act
extract_otp_secret_keys.main(['tests/data/test_export_wrong_data.txt']) extract_otp_secrets.main(['tests/data/test_export_wrong_data.txt'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -469,7 +469,7 @@ data=XXXX
def test_wrong_content(capsys: pytest.CaptureFixture[str]) -> None: def test_wrong_content(capsys: pytest.CaptureFixture[str]) -> None:
with pytest.raises(SystemExit) as e: with pytest.raises(SystemExit) as e:
# Act # Act
extract_otp_secret_keys.main(['tests/data/test_export_wrong_content.txt']) extract_otp_secrets.main(['tests/data/test_export_wrong_content.txt'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -494,7 +494,7 @@ Probably a wrong file was given
def test_wrong_prefix(capsys: pytest.CaptureFixture[str]) -> None: def test_wrong_prefix(capsys: pytest.CaptureFixture[str]) -> None:
# Act # Act
extract_otp_secret_keys.main(['tests/data/test_export_wrong_prefix.txt']) extract_otp_secrets.main(['tests/data/test_export_wrong_prefix.txt'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -518,15 +518,15 @@ Type: totp
def test_add_pre_suffix(capsys: pytest.CaptureFixture[str]) -> None: def test_add_pre_suffix(capsys: pytest.CaptureFixture[str]) -> None:
assert extract_otp_secret_keys.add_pre_suffix("name.csv", "totp") == "name.totp.csv" assert extract_otp_secrets.add_pre_suffix("name.csv", "totp") == "name.totp.csv"
assert extract_otp_secret_keys.add_pre_suffix("name.csv", "") == "name..csv" assert extract_otp_secrets.add_pre_suffix("name.csv", "") == "name..csv"
assert extract_otp_secret_keys.add_pre_suffix("name", "totp") == "name.totp" assert extract_otp_secrets.add_pre_suffix("name", "totp") == "name.totp"
@pytest.mark.qreader @pytest.mark.qreader
def test_img_qr_reader_from_file_happy_path(capsys: pytest.CaptureFixture[str]) -> None: def test_img_qr_reader_from_file_happy_path(capsys: pytest.CaptureFixture[str]) -> None:
# Act # Act
extract_otp_secret_keys.main(['tests/data/test_googleauth_export.png']) extract_otp_secrets.main(['tests/data/test_googleauth_export.png'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -538,7 +538,7 @@ def test_img_qr_reader_from_file_happy_path(capsys: pytest.CaptureFixture[str])
@pytest.mark.qreader @pytest.mark.qreader
def test_extract_multiple_files_and_mixed(capsys: pytest.CaptureFixture[str]) -> None: def test_extract_multiple_files_and_mixed(capsys: pytest.CaptureFixture[str]) -> None:
# Act # Act
extract_otp_secret_keys.main([ extract_otp_secrets.main([
'example_export.txt', 'example_export.txt',
'tests/data/test_googleauth_export.png', 'tests/data/test_googleauth_export.png',
'example_export.txt', 'example_export.txt',
@ -558,7 +558,7 @@ def test_img_qr_reader_from_stdin(capsys: pytest.CaptureFixture[str], monkeypatc
monkeypatch.setattr('sys.stdin', read_binary_file_as_stream('tests/data/test_googleauth_export.png')) monkeypatch.setattr('sys.stdin', read_binary_file_as_stream('tests/data/test_googleauth_export.png'))
# Act # Act
extract_otp_secret_keys.main(['=']) extract_otp_secrets.main(['='])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -592,7 +592,7 @@ def test_img_qr_reader_from_stdin_wrong_symbol(capsys: pytest.CaptureFixture[str
# Act # Act
with pytest.raises(SystemExit) as e: with pytest.raises(SystemExit) as e:
extract_otp_secret_keys.main(['-']) extract_otp_secrets.main(['-'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -612,7 +612,7 @@ def test_extract_stdin_stdout_wrong_symbol(capsys: pytest.CaptureFixture[str], m
# Act # Act
with pytest.raises(SystemExit) as e: with pytest.raises(SystemExit) as e:
extract_otp_secret_keys.main(['=']) extract_otp_secrets.main(['='])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -629,7 +629,7 @@ def test_extract_stdin_stdout_wrong_symbol(capsys: pytest.CaptureFixture[str], m
def test_img_qr_reader_no_qr_code_in_image(capsys: pytest.CaptureFixture[str]) -> None: def test_img_qr_reader_no_qr_code_in_image(capsys: pytest.CaptureFixture[str]) -> None:
# Act # Act
with pytest.raises(SystemExit) as e: with pytest.raises(SystemExit) as e:
extract_otp_secret_keys.main(['tests/data/lena_std.tif']) extract_otp_secrets.main(['tests/data/lena_std.tif'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -646,7 +646,7 @@ def test_img_qr_reader_no_qr_code_in_image(capsys: pytest.CaptureFixture[str]) -
def test_img_qr_reader_nonexistent_file(capsys: pytest.CaptureFixture[str]) -> None: def test_img_qr_reader_nonexistent_file(capsys: pytest.CaptureFixture[str]) -> None:
# Act # Act
with pytest.raises(SystemExit) as e: with pytest.raises(SystemExit) as e:
extract_otp_secret_keys.main(['nonexistent.bmp']) extract_otp_secrets.main(['nonexistent.bmp'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
@ -662,7 +662,7 @@ def test_img_qr_reader_nonexistent_file(capsys: pytest.CaptureFixture[str]) -> N
def test_non_image_file(capsys: pytest.CaptureFixture[str]) -> None: def test_non_image_file(capsys: pytest.CaptureFixture[str]) -> None:
# Act # Act
with pytest.raises(SystemExit) as e: with pytest.raises(SystemExit) as e:
extract_otp_secret_keys.main(['tests/data/text_masquerading_as_image.jpeg']) extract_otp_secrets.main(['tests/data/text_masquerading_as_image.jpeg'])
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()

@ -1,4 +1,4 @@
# Unit test for extract_otp_secret_keys.py # Unit test for extract_otp_secrets.py
# Run tests: # Run tests:
# python -m unittest # python -m unittest
@ -18,14 +18,14 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations # for compatibility with Python < 3.11 from __future__ import annotations # for compatibility with PYTHON < 3.11
import io import io
import os import os
import sys import sys
import unittest import unittest
from contextlib import redirect_stdout from contextlib import redirect_stdout
import extract_otp_secret_keys import extract_otp_secrets
from utils import (Capturing, read_csv, read_file_to_str, read_json, from utils import (Capturing, read_csv, read_file_to_str, read_json,
remove_dir_with_files, remove_file) remove_dir_with_files, remove_file)
@ -33,7 +33,7 @@ from utils import (Capturing, read_csv, read_file_to_str, read_json,
class TestExtract(unittest.TestCase): class TestExtract(unittest.TestCase):
def test_extract_csv(self) -> None: def test_extract_csv(self) -> None:
extract_otp_secret_keys.main(['-q', '-c', 'test_example_output.csv', 'example_export.txt']) extract_otp_secrets.main(['-q', '-c', 'test_example_output.csv', 'example_export.txt'])
expected_csv = read_csv('example_output.csv') expected_csv = read_csv('example_output.csv')
actual_csv = read_csv('test_example_output.csv') actual_csv = read_csv('test_example_output.csv')
@ -41,7 +41,7 @@ class TestExtract(unittest.TestCase):
self.assertEqual(actual_csv, expected_csv) self.assertEqual(actual_csv, expected_csv)
def test_extract_json(self) -> None: def test_extract_json(self) -> None:
extract_otp_secret_keys.main(['-q', '-j', 'test_example_output.json', 'example_export.txt']) extract_otp_secrets.main(['-q', '-j', 'test_example_output.json', 'example_export.txt'])
expected_json = read_json('example_output.json') expected_json = read_json('example_output.json')
actual_json = read_json('test_example_output.json') actual_json = read_json('test_example_output.json')
@ -50,7 +50,7 @@ class TestExtract(unittest.TestCase):
def test_extract_stdout_1(self) -> None: def test_extract_stdout_1(self) -> None:
with Capturing() as output: with Capturing() as output:
extract_otp_secret_keys.main(['example_export.txt']) extract_otp_secrets.main(['example_export.txt'])
expected_output = [ expected_output = [
'Name: pi@raspberrypi', 'Name: pi@raspberrypi',
@ -87,7 +87,7 @@ class TestExtract(unittest.TestCase):
def test_extract_stdout_2(self) -> None: def test_extract_stdout_2(self) -> None:
out = io.StringIO() out = io.StringIO()
with redirect_stdout(out): with redirect_stdout(out):
extract_otp_secret_keys.main(['example_export.txt']) extract_otp_secrets.main(['example_export.txt'])
actual_output = out.getvalue() actual_output = out.getvalue()
expected_output = '''Name: pi@raspberrypi expected_output = '''Name: pi@raspberrypi
@ -123,7 +123,7 @@ Type: totp
def test_extract_not_encoded_plus(self) -> None: def test_extract_not_encoded_plus(self) -> None:
out = io.StringIO() out = io.StringIO()
with redirect_stdout(out): with redirect_stdout(out):
extract_otp_secret_keys.main(['tests/data/test_plus_problem_export.txt']) extract_otp_secrets.main(['tests/data/test_plus_problem_export.txt'])
actual_output = out.getvalue() actual_output = out.getvalue()
expected_output = '''Name: SerenityLabs:test1@serenitylabs.co.uk expected_output = '''Name: SerenityLabs:test1@serenitylabs.co.uk
@ -152,7 +152,7 @@ Type: totp
def test_extract_printqr(self) -> None: def test_extract_printqr(self) -> None:
out = io.StringIO() out = io.StringIO()
with redirect_stdout(out): with redirect_stdout(out):
extract_otp_secret_keys.main(['-p', 'example_export.txt']) extract_otp_secrets.main(['-p', 'example_export.txt'])
actual_output = out.getvalue() actual_output = out.getvalue()
expected_output = read_file_to_str('tests/data/printqr_output.txt') expected_output = read_file_to_str('tests/data/printqr_output.txt')
@ -160,7 +160,7 @@ Type: totp
self.assertEqual(actual_output, expected_output) self.assertEqual(actual_output, expected_output)
def test_extract_saveqr(self) -> None: def test_extract_saveqr(self) -> None:
extract_otp_secret_keys.main(['-q', '-s', 'testout/qr/', 'example_export.txt']) extract_otp_secrets.main(['-q', '-s', 'testout/qr/', 'example_export.txt'])
self.assertTrue(os.path.isfile('testout/qr/1-piraspberrypi-raspberrypi.png')) self.assertTrue(os.path.isfile('testout/qr/1-piraspberrypi-raspberrypi.png'))
self.assertTrue(os.path.isfile('testout/qr/2-piraspberrypi.png')) self.assertTrue(os.path.isfile('testout/qr/2-piraspberrypi.png'))
@ -171,7 +171,7 @@ Type: totp
if sys.implementation.name == 'pypy': self.skipTest("Encoding problems in verbose mode in pypy.") if sys.implementation.name == 'pypy': self.skipTest("Encoding problems in verbose mode in pypy.")
out = io.StringIO() out = io.StringIO()
with redirect_stdout(out): with redirect_stdout(out):
extract_otp_secret_keys.main(['-v', 'example_export.txt']) extract_otp_secrets.main(['-v', 'example_export.txt'])
actual_output = out.getvalue() actual_output = out.getvalue()
expected_output = read_file_to_str('tests/data/print_verbose_output.txt') expected_output = read_file_to_str('tests/data/print_verbose_output.txt')
@ -181,7 +181,7 @@ Type: totp
def test_extract_debug(self) -> None: def test_extract_debug(self) -> None:
out = io.StringIO() out = io.StringIO()
with redirect_stdout(out): with redirect_stdout(out):
extract_otp_secret_keys.main(['-vvv', 'example_export.txt']) extract_otp_secrets.main(['-vvv', 'example_export.txt'])
actual_output = out.getvalue() actual_output = out.getvalue()
expected_stdout = read_file_to_str('tests/data/print_verbose_output.txt') expected_stdout = read_file_to_str('tests/data/print_verbose_output.txt')
@ -193,7 +193,7 @@ Type: totp
out = io.StringIO() out = io.StringIO()
with redirect_stdout(out): with redirect_stdout(out):
try: try:
extract_otp_secret_keys.main(['-h']) extract_otp_secrets.main(['-h'])
self.fail("Must abort") self.fail("Must abort")
except SystemExit as e: except SystemExit as e:
self.assertEqual(e.code, 0) self.assertEqual(e.code, 0)
@ -207,7 +207,7 @@ Type: totp
out = io.StringIO() out = io.StringIO()
with redirect_stdout(out): with redirect_stdout(out):
with self.assertRaises(SystemExit) as context: with self.assertRaises(SystemExit) as context:
extract_otp_secret_keys.main(['-h']) extract_otp_secrets.main(['-h'])
actual_output = out.getvalue() actual_output = out.getvalue()
@ -218,7 +218,7 @@ Type: totp
def test_extract_help_3(self) -> None: def test_extract_help_3(self) -> None:
with Capturing() as actual_output: with Capturing() as actual_output:
with self.assertRaises(SystemExit) as context: with self.assertRaises(SystemExit) as context:
extract_otp_secret_keys.main(['-h']) extract_otp_secrets.main(['-h'])
self.assertGreater(len(actual_output), 0) self.assertGreater(len(actual_output), 0)
self.assertTrue("-h, --help" in "\n".join(actual_output) and "--verbose, -v" in "\n".join(actual_output)) self.assertTrue("-h, --help" in "\n".join(actual_output) and "--verbose, -v" in "\n".join(actual_output))

@ -13,7 +13,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations # for compatibility with Python < 3.11 from __future__ import annotations # for compatibility with PYTHON < 3.11
import csv import csv
import glob import glob
import io import io

@ -256,30 +256,30 @@ cmd="$PIP install -e ."
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="extract_otp_secret_keys example_export.txt" cmd="extract_otp_secrets example_export.txt"
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="extract_otp_secret_keys - < example_export.txt" cmd="extract_otp_secrets - < example_export.txt"
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"
# Test # Test
cmd="$PYTHON src/extract_otp_secret_keys.py example_export.txt" cmd="$PYTHON src/extract_otp_secrets.py example_export.txt"
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="$PYTHON src/extract_otp_secret_keys.py - < example_export.txt" cmd="$PYTHON src/extract_otp_secrets.py - < example_export.txt"
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"
COVERAGE_OUT="tests/reports/pytest-coverage.txt" COVERAGE_OUT="tests/reports/pytest-coverage.txt"
cmd="mkdir -p tests/reports; pytest --cov=test_extract_otp_secret_keys_pytest --junitxml=tests/reports/pytest.xml --cov-report html:tests/reports/html --cov-report=term-missing tests/ | tee $COVERAGE_OUT" cmd="mkdir -p tests/reports; pytest --cov=extract_otp_secrets_test --junitxml=tests/reports/pytest.xml --cov-report html:tests/reports/html --cov-report=term-missing tests/ | tee $COVERAGE_OUT"
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="$PIPENV run pytest --cov=test_extract_otp_secret_keys_pytest tests/" cmd="$PIPENV run pytest --cov=extract_otp_secrets_test tests/"
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"
@ -292,35 +292,35 @@ TOTAL_COVERAGE=$(cat $COVERAGE_OUT | grep 'TOTAL' | perl -ne 'print "$&" if /\b(
# Build docker # Build docker
cmd="docker build . -t extract_otp_secret_keys_no_qr_reader -f Dockerfile_no_qr_reader --pull" cmd="docker build . -t extract_otp_secrets_no_qr_reader -f Dockerfile_no_qr_reader --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 --rm -v \"$(pwd)\":/files:ro extract_otp_secret_keys_no_qr_reader example_export.txt" cmd="docker run --rm -v \"$(pwd)\":/files:ro extract_otp_secrets_no_qr_reader example_export.txt"
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 --rm -i -v \"$(pwd)\":/files:ro extract_otp_secret_keys_no_qr_reader - < example_export.txt" cmd="docker run --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets_no_qr_reader - < example_export.txt"
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 tests/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_secrets_no_qr_reader tests/extract_otp_secrets_test.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"
cmd="docker build . -t extract_otp_secret_keys --pull" cmd="docker build . -t extract_otp_secrets --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 --rm -v \"$(pwd)\":/files:ro extract_otp_secret_keys example_export.txt" cmd="docker run --rm -v \"$(pwd)\":/files:ro extract_otp_secrets example_export.txt"
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 --rm -i -v \"$(pwd)\":/files:ro extract_otp_secret_keys - < example_export.txt" cmd="docker run --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets - < example_export.txt"
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_secrets"
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"
@ -328,7 +328,7 @@ cmd="docker image prune || echo 'No docker image pruning'"
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="$PYTHON src/extract_otp_secret_keys.py &" cmd="$PYTHON src/extract_otp_secrets.py &"
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"

Loading…
Cancel
Save