mirror of
https://github.com/scito/extract_otp_secrets.git
synced 2025-12-12 17:59:48 +01:00
Compare commits
256 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
716194da48 | ||
|
|
bb41c737ef | ||
|
|
bb4c3da0bf | ||
|
|
e0dac0fd99 | ||
|
|
3040965d79 | ||
|
|
3899f5f826 | ||
|
|
f82c79aa7d | ||
|
|
68ced0f2ec | ||
|
|
d1ce4c0ba4 | ||
|
|
ed7a9f5143 | ||
|
|
8f7c267153 | ||
|
|
0c68efd06e | ||
|
|
5f01b50635 | ||
|
|
cf618281d8 | ||
|
|
e3a1443b9b | ||
|
|
863f3f3128 | ||
|
|
edee97a54c | ||
|
|
e01c81aca1 | ||
|
|
f713a8ef39 | ||
|
|
bfc2c0d853 | ||
|
|
8a8e92bfef | ||
|
|
d26a521997 | ||
|
|
223f1e4b5b | ||
|
|
5ffa2d9dcc | ||
|
|
5c9c680561 | ||
|
|
c9cdc10a73 | ||
|
|
750ca3e13b | ||
|
|
a2bf1533ae | ||
|
|
7fa0e3f555 | ||
|
|
56e74d5ccb | ||
|
|
10737a91c4 | ||
|
|
d29eab943e | ||
|
|
e17fed8704 | ||
|
|
b09d2c6c1f | ||
|
|
208e6e60fc | ||
|
|
1851664c96 | ||
|
|
ab0cd6717a | ||
|
|
ccb52cc8b5 | ||
|
|
41ed2b62d4 | ||
|
|
b6ab6417cb | ||
|
|
e857912c74 | ||
|
|
ac18fbf020 | ||
|
|
494a9f8446 | ||
|
|
cec47852d8 | ||
|
|
d5a8b6f0c0 | ||
|
|
95c34277ca | ||
|
|
ca6ca1f0e6 | ||
|
|
026db54551 | ||
|
|
793b061d3f | ||
|
|
7557ae125a | ||
|
|
f94120b84c | ||
|
|
787a975009 | ||
|
|
95c1cc3cfe | ||
|
|
95176ee9de | ||
|
|
7b379b58a3 | ||
|
|
10a60ab556 | ||
|
|
38e7e8b40b | ||
|
|
6b25cc96e2 | ||
|
|
4799483b2b | ||
|
|
3e6eeffff5 | ||
|
|
00d25e89d9 | ||
|
|
f7453c7720 | ||
|
|
77d799a461 | ||
|
|
9a0d8dbc80 | ||
|
|
9f39cc4508 | ||
|
|
08dbda3b80 | ||
|
|
8317ef0bf2 | ||
|
|
b9c5833374 | ||
|
|
b6f5c41559 | ||
|
|
e665d159f6 | ||
|
|
d9bd792394 | ||
|
|
cb8ef2ae22 | ||
|
|
fa8506935f | ||
|
|
c49ba82f1b | ||
|
|
f11b2d2483 | ||
|
|
894edb4561 | ||
|
|
4c4b033aa4 | ||
|
|
f6a003d707 | ||
|
|
6a2dc80ad4 | ||
|
|
b54974e584 | ||
|
|
a2845556a4 | ||
|
|
7ce765ddb1 | ||
|
|
fc9b36d281 | ||
|
|
556d1350b8 | ||
|
|
a84d52fb38 | ||
|
|
2928168bc2 | ||
|
|
5714d71e7c | ||
|
|
170fbf6edb | ||
|
|
d2993c093c | ||
|
|
c9a2784e56 | ||
|
|
59804c9970 | ||
|
|
5955c1dc69 | ||
|
|
bfb63ebf49 | ||
|
|
396071f15a | ||
|
|
6503b442f1 | ||
|
|
5c5e2096b8 | ||
|
|
eb8bb38aa5 | ||
|
|
d486a94c6f | ||
|
|
ee932c05e3 | ||
|
|
e13a4584fb | ||
|
|
91e2efe7e1 | ||
|
|
b5442a0d94 | ||
|
|
2318c3a185 | ||
|
|
fcfdf1cbf6 | ||
|
|
f07db1b74f | ||
|
|
5d5453231f | ||
|
|
5d7afeeed3 | ||
|
|
d9f760ad97 | ||
|
|
6c54a438cb | ||
|
|
9d4f8c83b4 | ||
|
|
9b8c79b232 | ||
|
|
50151c4f3f | ||
|
|
5c7fe2352e | ||
|
|
7c31f988c8 | ||
|
|
fce36ee279 | ||
|
|
9463dc3e88 | ||
|
|
5986cf32ff | ||
|
|
64980e71df | ||
|
|
b8c7979c78 | ||
|
|
7f34488ad2 | ||
|
|
c553d9ae90 | ||
|
|
8599683cc7 | ||
|
|
d026a245ed | ||
|
|
f12c5df41c | ||
|
|
79c01e5b88 | ||
|
|
60de43cd44 | ||
|
|
c57d6a681f | ||
|
|
dd537ccbf4 | ||
|
|
ed9ab3af07 | ||
|
|
99291ed2be | ||
|
|
3ea20f245c | ||
|
|
bd5be0239d | ||
|
|
ec05910347 | ||
|
|
bc4d2cafc9 | ||
|
|
2f8db3b99d | ||
|
|
860a472e9b | ||
|
|
9f20d10b4b | ||
|
|
fa2b75ac63 | ||
|
|
9d7ce50b94 | ||
|
|
8daec6f46e | ||
|
|
c663a2de8f | ||
|
|
b77e8cc190 | ||
|
|
9fd3333a3a | ||
|
|
0ae5f296e8 | ||
|
|
77cdf7e41f | ||
|
|
febbbe2cf5 | ||
|
|
15654d2e44 | ||
|
|
2cb74cca41 | ||
|
|
c175909227 | ||
|
|
cc363b4c57 | ||
|
|
e196c76db4 | ||
|
|
07416678a0 | ||
|
|
e4d0bb418d | ||
|
|
7069156f54 | ||
|
|
384d98ff8b | ||
|
|
7f314cedd8 | ||
|
|
8da53b5daf | ||
|
|
ce2fe77de7 | ||
|
|
d5d7cf29ae | ||
|
|
612c0594f1 | ||
|
|
29d69a2919 | ||
|
|
437cb47654 | ||
|
|
0b2be8defe | ||
|
|
fa622a1c70 | ||
|
|
16c425ed56 | ||
|
|
e56fef8df8 | ||
|
|
d10f2494f9 | ||
|
|
05a78992ce | ||
|
|
9807e3ed12 | ||
|
|
45bfe117b3 | ||
|
|
a822abbba1 | ||
|
|
1b11c4ccb5 | ||
|
|
46dda83624 | ||
|
|
259273ab16 | ||
|
|
ab878ad737 | ||
|
|
fa9324513d | ||
|
|
16aa2ddf8c | ||
|
|
3fb41a4349 | ||
|
|
7484bbe1b6 | ||
|
|
14d86db936 | ||
|
|
b4790b926a | ||
|
|
66545ac1d3 | ||
|
|
b41250c23a | ||
|
|
4e15725c4d | ||
|
|
d8545ea22b | ||
|
|
1f8d9d171b | ||
|
|
0fc8342dd0 | ||
|
|
d2bf496b2b | ||
|
|
0854a70036 | ||
|
|
f9ce96fb57 | ||
|
|
e676bb7ea7 | ||
|
|
153b1d92c0 | ||
|
|
c8cf5ba12b | ||
|
|
1ba64e6efe | ||
|
|
0ba76da085 | ||
|
|
578eda8744 | ||
|
|
b548908014 | ||
|
|
c730388fd6 | ||
|
|
d387522d1b | ||
|
|
47da32cd22 | ||
|
|
7a45e9a444 | ||
|
|
44ba0a966e | ||
|
|
2ee5480fc8 | ||
|
|
8eb0ca2043 | ||
|
|
35f63099c9 | ||
|
|
7362d80e3d | ||
|
|
18d4992afb | ||
|
|
2849dda044 | ||
|
|
5aaa8b24fc | ||
|
|
c81cd6b87d | ||
|
|
3d2fcae6dc | ||
|
|
85d73e347c | ||
|
|
6dfc23ef77 | ||
|
|
23f708a1d0 | ||
|
|
3d8bda5c41 | ||
|
|
f3f3988c59 | ||
|
|
8f0461f62c | ||
|
|
95f99df3fe | ||
|
|
340c569635 | ||
|
|
157e247416 | ||
|
|
a0bf28f4cc | ||
|
|
fd8b165212 | ||
|
|
a467955b13 | ||
|
|
eea0179b5e | ||
|
|
d20d709d6e | ||
|
|
a0c21273e8 | ||
|
|
c7ab0b8ef4 | ||
|
|
8e70ae9da9 | ||
|
|
511bb4a91c | ||
|
|
15cbd77b07 | ||
|
|
2610afe5d8 | ||
|
|
8a4e2e3641 | ||
|
|
a0cf4246c1 | ||
|
|
b3c0912e1f | ||
|
|
1d8b49efcc | ||
|
|
f846ee40af | ||
|
|
f8b0283bdd | ||
|
|
bcbf189289 | ||
|
|
7c295e0963 | ||
|
|
6b72fad3c7 | ||
|
|
c937bede32 | ||
|
|
7402263dfe | ||
|
|
b721571101 | ||
|
|
72bac4d951 | ||
|
|
6c16d28194 | ||
|
|
87d2d4a05d | ||
|
|
ad3f184a56 | ||
|
|
994f96f749 | ||
|
|
8177a97d39 | ||
|
|
0c0ac817b1 | ||
|
|
5133711179 | ||
|
|
5c02e7d478 | ||
|
|
6932793236 | ||
|
|
9c3edea75c | ||
|
|
0ec1314b3d | ||
|
|
414e80c469 |
3
.envrc
Normal file
3
.envrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
source_url "https://raw.githubusercontent.com/cachix/devenv/95f329d49a8a5289d31e0982652f7058a189bfca/direnvrc" "sha256-d+8cBpDfDBj41inrADaJt+bDWhOktwslgoP5YiGJ1v0="
|
||||||
|
|
||||||
|
use devenv
|
||||||
17
.github/copilot-instructions.md
vendored
Normal file
17
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Copilot Instructions
|
||||||
|
|
||||||
|
This project is a web application that allows users to create and manage tasks. The application is built using Python.
|
||||||
|
|
||||||
|
## Coding Standards
|
||||||
|
|
||||||
|
- Use snakeCase for variable and function names.
|
||||||
|
- Use PascalCase for component names.
|
||||||
|
- Use double quotes for strings.
|
||||||
|
- Use 4 spaces for indentation.
|
||||||
|
|
||||||
|
## Tone
|
||||||
|
|
||||||
|
- If I tell you that you are wrong, think about whether or not you think that's true and respond with facts.
|
||||||
|
- Avoid apologizing or making conciliatory statements.
|
||||||
|
- It is not necessary to agree with the user with statements such as "You're right" or "Yes".
|
||||||
|
- Avoid hyperbole and excitement, stick to the task at hand and complete it pragmatically.
|
||||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -8,4 +8,4 @@ updates:
|
|||||||
- package-ecosystem: "pip" # See documentation for possible values
|
- package-ecosystem: "pip" # See documentation for possible values
|
||||||
directory: "/" # Location of package manifests
|
directory: "/" # Location of package manifests
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "daily"
|
||||||
|
|||||||
15
.github/workflows/ci.yml
vendored
15
.github/workflows/ci.yml
vendored
@@ -20,19 +20,20 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.x", "3.11", "3.10", "3.9", "3.8", "3.7"]
|
# 3.x is used to run code coverage
|
||||||
platform: [ubuntu-latest, macos-latest, windows-latest]
|
python-version: ["3.x", "3.13", "3.12", "3.11", "3.10", "3.9"]
|
||||||
|
platform: [ubuntu-latest, macos-latest, windows-latest, ubuntu-24.04-arm, macos-13]
|
||||||
# exclude:
|
# exclude:
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
check-latest: false
|
check-latest: ${{ github.event_name == 'schedule' }}
|
||||||
- name: Install zbar shared lib for QReader (Linux)
|
- name: Install zbar shared lib for QReader (Linux)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
@@ -52,7 +53,6 @@ jobs:
|
|||||||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=200 --statistics
|
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=200 --statistics
|
||||||
if: matrix.python-version != '3.7'
|
|
||||||
- name: Type checking with mypy
|
- name: Type checking with mypy
|
||||||
run: |
|
run: |
|
||||||
mypy --install-types --non-interactive src/*.py tests/*.py
|
mypy --install-types --non-interactive src/*.py tests/*.py
|
||||||
@@ -60,8 +60,7 @@ jobs:
|
|||||||
if: matrix.python-version == '3.x' && matrix.platform == 'ubuntu-latest'
|
if: matrix.python-version == '3.x' && matrix.platform == 'ubuntu-latest'
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: pytest
|
run: pytest
|
||||||
if: (matrix.python-version != '3.x' || matrix.platform != 'ubuntu-latest')
|
if: (matrix.python-version != '3.x' || matrix.platform != 'ubuntu-latest') && (matrix.python-version != '3.10' && 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=extract_otp_secrets_test --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'
|
||||||
|
|||||||
268
.github/workflows/ci_docker.yml
vendored
268
.github/workflows/ci_docker.yml
vendored
@@ -25,14 +25,24 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-push-docker-debian-image:
|
build-and-push-docker-debian-image:
|
||||||
name: Build Docker Bullseye image and push to repositories
|
name: Build Docker Bookworm image and push to repositories
|
||||||
# run only when code is compiling and tests are passing
|
# run only when code is compiling and tests are passing
|
||||||
runs-on: ubuntu-latest
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- DOCKER_ARCH: amd64
|
||||||
|
platform: ubuntu-latest
|
||||||
|
PLATFORM_ARCH: x86_64
|
||||||
|
- DOCKER_ARCH: arm64
|
||||||
|
platform: ubuntu-24.04-arm
|
||||||
|
PLATFORM_ARCH: arm64
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
# steps to perform in job
|
# steps to perform in job
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# avoid building if there are testing errors
|
# avoid building if there are testing errors
|
||||||
- name: Run smoke test
|
- name: Run smoke test
|
||||||
@@ -44,50 +54,49 @@ jobs:
|
|||||||
pytest
|
pytest
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
# setup Docker build action
|
# setup Docker build action
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
# Workaround for failing builds: https://github.com/docker/build-push-action/issues/761#issuecomment-1383822381
|
|
||||||
# TODO remove workaround when fixed
|
|
||||||
with:
|
|
||||||
driver-opts: |
|
|
||||||
image=moby/buildkit:v0.10.6
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
if: github.secret_source == 'Actions'
|
if: github.secret_source == 'Actions'
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Login to Github Packages
|
- name: Login to Github Packages
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
if: github.secret_source == 'Actions'
|
if: github.secret_source == 'Actions'
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GHCR_IO_TOKEN }}
|
password: ${{ secrets.GHCR_IO_TOKEN }}
|
||||||
|
|
||||||
- name: "Build image and push to Docker Hub and GitHub Container Registry"
|
- name: "Build image (Bookworm/Debian 12) and push to Docker Hub and GitHub Container Registry"
|
||||||
id: docker_build_qr_reader_latest
|
id: docker_build_qr_reader_latest
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/${{ matrix.DOCKER_ARCH }}
|
||||||
# relative path to the place where source code with Dockerfile is located
|
# relative path to the place where source code with Dockerfile is located
|
||||||
# TODO file:, move to docker/
|
# TODO file:, move to docker/
|
||||||
context: .
|
context: .
|
||||||
file: docker/Dockerfile
|
file: docker/Dockerfile
|
||||||
# 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
|
||||||
|
build-args: |
|
||||||
|
BASE_IMAGE=python:3.13-slim-bookworm
|
||||||
pull: true
|
pull: true
|
||||||
tags: |
|
tags: |
|
||||||
scit0/extract_otp_secrets:latest
|
docker.io/scit0/extract_otp_secrets:latest-${{ matrix.PLATFORM_ARCH }}
|
||||||
scit0/extract_otp_secrets:bullseye
|
docker.io/scit0/extract_otp_secrets:bookworm-${{ matrix.PLATFORM_ARCH }}
|
||||||
ghcr.io/scito/extract_otp_secrets:latest
|
ghcr.io/scito/extract_otp_secrets:latest-${{ matrix.PLATFORM_ARCH }}
|
||||||
ghcr.io/scito/extract_otp_secrets:bullseye
|
ghcr.io/scito/extract_otp_secrets:bookworm-${{ matrix.PLATFORM_ARCH }}
|
||||||
|
provenance: true
|
||||||
|
sbom: true
|
||||||
# build on feature branches, push only on master branch
|
# build on feature branches, push only on master branch
|
||||||
push: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
push: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||||
|
|
||||||
@@ -98,20 +107,68 @@ jobs:
|
|||||||
echo "${{ steps.docker_build_qr_reader_latest.outputs.digest }}" > digests.txt
|
echo "${{ steps.docker_build_qr_reader_latest.outputs.digest }}" > digests.txt
|
||||||
- name: Save docker digests as artifacts
|
- name: Save docker digests as artifacts
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: debian_digests
|
name: digests_bookworm_${{ matrix.PLATFORM_ARCH }}
|
||||||
path: digests.txt
|
path: digests.txt
|
||||||
|
|
||||||
|
create-multiarch-debian-manifests:
|
||||||
|
name: Create multiarch manifests for Debian image
|
||||||
|
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- build-and-push-docker-debian-image
|
||||||
|
steps:
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
if: github.secret_source == 'Actions'
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to Github Packages
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
if: github.secret_source == 'Actions'
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GHCR_IO_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create multiarch manifests
|
||||||
|
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
for tag in \
|
||||||
|
docker.io/scit0/extract_otp_secrets:latest \
|
||||||
|
ghcr.io/scito/extract_otp_secrets:latest \
|
||||||
|
docker.io/scit0/extract_otp_secrets:bookworm \
|
||||||
|
ghcr.io/scito/extract_otp_secrets:bookworm \
|
||||||
|
; do
|
||||||
|
docker buildx imagetools create -t $tag \
|
||||||
|
$tag-x86_64 \
|
||||||
|
$tag-arm64
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
build-and-push-docker-alpine-image:
|
build-and-push-docker-alpine-image:
|
||||||
name: Build Docker Alpine image and push to repositories
|
name: Build Docker Alpine image and push to repositories
|
||||||
# run only when code is compiling and tests are passing
|
# run only when code is compiling and tests are passing
|
||||||
runs-on: ubuntu-latest
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- DOCKER_ARCH: amd64
|
||||||
|
platform: ubuntu-latest
|
||||||
|
PLATFORM_ARCH: x86_64
|
||||||
|
- DOCKER_ARCH: arm64
|
||||||
|
platform: ubuntu-24.04-arm
|
||||||
|
PLATFORM_ARCH: arm64
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
# steps to perform in job
|
# steps to perform in job
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# avoid building if there are testing errors
|
# avoid building if there are testing errors
|
||||||
- name: Run smoke test
|
- name: Run smoke test
|
||||||
@@ -123,22 +180,22 @@ jobs:
|
|||||||
pytest
|
pytest
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
# setup Docker build action
|
# setup Docker build action
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
if: github.secret_source == 'Actions'
|
if: github.secret_source == 'Actions'
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Login to Github Packages
|
- name: Login to Github Packages
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
if: github.secret_source == 'Actions'
|
if: github.secret_source == 'Actions'
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
@@ -147,20 +204,22 @@ jobs:
|
|||||||
|
|
||||||
- name: "only_txt: Build image and push to Docker Hub and GitHub Container Registry"
|
- name: "only_txt: Build image and push to Docker Hub and GitHub Container Registry"
|
||||||
id: docker_build_only_txt
|
id: docker_build_only_txt
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
|
platforms: linux/${{ matrix.DOCKER_ARCH }}
|
||||||
# relative path to the place where source code with Dockerfile is located
|
# relative path to the place where source code with Dockerfile is located
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
context: .
|
context: .
|
||||||
file: docker/Dockerfile_only_txt
|
file: docker/Dockerfile_only_txt
|
||||||
# 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
|
||||||
pull: true
|
pull: true
|
||||||
tags: |
|
tags: |
|
||||||
scit0/extract_otp_secrets:only-txt
|
docker.io/scit0/extract_otp_secrets:only-txt-${{ matrix.PLATFORM_ARCH }}
|
||||||
scit0/extract_otp_secrets:alpine
|
docker.io/scit0/extract_otp_secrets:alpine-${{ matrix.PLATFORM_ARCH }}
|
||||||
ghcr.io/scito/extract_otp_secrets:only-txt
|
ghcr.io/scito/extract_otp_secrets:only-txt-${{ matrix.PLATFORM_ARCH }}
|
||||||
ghcr.io/scito/extract_otp_secrets:alpine
|
ghcr.io/scito/extract_otp_secrets:alpine-${{ matrix.PLATFORM_ARCH }}
|
||||||
|
provenance: true
|
||||||
|
sbom: true
|
||||||
# build on feature branches, push only on master branch
|
# build on feature branches, push only on master branch
|
||||||
push: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
push: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||||
build-args: |
|
build-args: |
|
||||||
@@ -174,20 +233,70 @@ jobs:
|
|||||||
|
|
||||||
- name: Save docker digests as artifacts
|
- name: Save docker digests as artifacts
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: alpine_digests
|
name: digests_alpine_${{ matrix.PLATFORM_ARCH }}
|
||||||
path: digests.txt
|
path: digests.txt
|
||||||
|
|
||||||
build-and-push-docker-buster-image:
|
create-multiarch-alpine-manifests:
|
||||||
name: Build Docker Buster image (for PyInstsaller) and push to repositories
|
name: Create multiarch manifests for Alpine image
|
||||||
# run only when code is compiling and tests are passing
|
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- build-and-push-docker-alpine-image
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
steps:
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
if: github.secret_source == 'Actions'
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to Github Packages
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
if: github.secret_source == 'Actions'
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GHCR_IO_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create multiarch manifests
|
||||||
|
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
for tag in \
|
||||||
|
docker.io/scit0/extract_otp_secrets:only-txt \
|
||||||
|
docker.io/scit0/extract_otp_secrets:alpine \
|
||||||
|
ghcr.io/scito/extract_otp_secrets:only-txt \
|
||||||
|
ghcr.io/scito/extract_otp_secrets:alpine \
|
||||||
|
; do
|
||||||
|
docker buildx imagetools create -t $tag \
|
||||||
|
$tag-x86_64 \
|
||||||
|
$tag-arm64
|
||||||
|
done
|
||||||
|
|
||||||
|
build-and-push-docker-bullseye-image:
|
||||||
|
name: Build Docker Bullseye image (for PyInstsaller) and push to repositories
|
||||||
|
# run only when code is compiling and tests are passing
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- DOCKER_ARCH: amd64
|
||||||
|
platform: ubuntu-latest
|
||||||
|
PLATFORM_ARCH: x86_64
|
||||||
|
- DOCKER_ARCH: arm64
|
||||||
|
platform: ubuntu-24.04-arm
|
||||||
|
PLATFORM_ARCH: arm64
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
# steps to perform in job
|
# steps to perform in job
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# avoid building if there are testing errors
|
# avoid building if there are testing errors
|
||||||
- name: Run smoke test
|
- name: Run smoke test
|
||||||
@@ -199,50 +308,47 @@ jobs:
|
|||||||
pytest
|
pytest
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
# setup Docker build action
|
# setup Docker build action
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
# Workaround for failing builds: https://github.com/docker/build-push-action/issues/761#issuecomment-1383822381
|
|
||||||
# TODO remove workaround when fixed
|
|
||||||
with:
|
|
||||||
driver-opts: |
|
|
||||||
image=moby/buildkit:v0.10.6
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
if: github.secret_source == 'Actions'
|
if: github.secret_source == 'Actions'
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Login to Github Packages
|
- name: Login to Github Packages
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
if: github.secret_source == 'Actions'
|
if: github.secret_source == 'Actions'
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GHCR_IO_TOKEN }}
|
password: ${{ secrets.GHCR_IO_TOKEN }}
|
||||||
|
|
||||||
- name: "Build image from Buster and push to GitHub Container Registry"
|
- name: "Build image from Bullseye (Debian 11) and push to GitHub Container Registry"
|
||||||
id: docker_build_buster
|
id: docker_build_bullseye
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/${{ matrix.DOCKER_ARCH }}
|
||||||
# relative path to the place where source code with Dockerfile is located
|
# relative path to the place where source code with Dockerfile is located
|
||||||
# TODO file:, move to docker/
|
|
||||||
context: .
|
context: .
|
||||||
file: docker/Dockerfile
|
file: docker/Dockerfile
|
||||||
# builder: ${{ steps.buildx.outputs.name }}
|
# builder: ${{ steps.buildx.outputs.name }}
|
||||||
build-args: |
|
build-args: |
|
||||||
BASE_IMAGE=python:3.11-slim-buster
|
BASE_IMAGE=python:3.13-slim-bullseye
|
||||||
# Note: tags has to be all lower-case
|
# Note: tags has to be all lower-case
|
||||||
pull: true
|
pull: true
|
||||||
tags: |
|
tags: |
|
||||||
scit0/extract_otp_secrets:buster
|
docker.io/scit0/extract_otp_secrets:bullseye-${{ matrix.PLATFORM_ARCH }}
|
||||||
|
ghcr.io/scito/extract_otp_secrets:bullseye-${{ matrix.PLATFORM_ARCH }}
|
||||||
|
provenance: true
|
||||||
|
sbom: true
|
||||||
push: ${{ github.secret_source == 'Actions' }}
|
push: ${{ github.secret_source == 'Actions' }}
|
||||||
|
|
||||||
- name: Image digest
|
- name: Image digest
|
||||||
@@ -252,7 +358,55 @@ jobs:
|
|||||||
echo "${{ steps.docker_build_qr_reader_latest.outputs.digest }}" > digests.txt
|
echo "${{ steps.docker_build_qr_reader_latest.outputs.digest }}" > digests.txt
|
||||||
- name: Save docker digests as artifacts
|
- name: Save docker digests as artifacts
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: buster_digests
|
name: digests_bullseye_${{ matrix.PLATFORM_ARCH }}
|
||||||
path: digests.txt
|
path: digests.txt
|
||||||
|
|
||||||
|
create-multiarch-bullseye-manifests:
|
||||||
|
name: Create multiarch manifests for Bullseye image
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||||
|
needs:
|
||||||
|
- build-and-push-docker-bullseye-image
|
||||||
|
steps:
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
if: github.secret_source == 'Actions'
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to Github Packages
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
if: github.secret_source == 'Actions'
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GHCR_IO_TOKEN }}
|
||||||
|
|
||||||
|
- name:
|
||||||
|
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
for tag in \
|
||||||
|
docker.io/scit0/extract_otp_secrets:bullseye \
|
||||||
|
ghcr.io/scito/extract_otp_secrets:bullseye \
|
||||||
|
; do
|
||||||
|
docker buildx imagetools create -t $tag \
|
||||||
|
$tag-x86_64 \
|
||||||
|
$tag-arm64
|
||||||
|
done
|
||||||
|
|
||||||
|
container-images-clean-up:
|
||||||
|
name: Cleanup old container images
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Delete Container Packages
|
||||||
|
uses: actions/delete-package-versions@v5
|
||||||
|
if: ${{ github.secret_source == 'Actions'}}
|
||||||
|
with:
|
||||||
|
package-name: 'extract_otp_secrets'
|
||||||
|
package-type: 'container'
|
||||||
|
min-versions-to-keep: 1
|
||||||
|
delete-only-untagged-versions: 'true'
|
||||||
|
|||||||
121
.github/workflows/ci_release.yml
vendored
121
.github/workflows/ci_release.yml
vendored
@@ -26,7 +26,8 @@ name: release
|
|||||||
|
|
||||||
# Build matrix:
|
# Build matrix:
|
||||||
# - Linux x86_64 glibc 2.35: ubuntu-latest
|
# - Linux x86_64 glibc 2.35: ubuntu-latest
|
||||||
# - Linux x86_64 glibc 2.34: extract_otp_secrets:buster
|
# - Linux x86_64 glibc 2.31: extract_otp_secrets:bullseye
|
||||||
|
# - Linux x86_64 glibc 2.36: extract_otp_secrets:bookworm
|
||||||
# - Windows x86_64: windows-latest
|
# - Windows x86_64: windows-latest
|
||||||
# - MacOS x86_64: macos-11
|
# - MacOS x86_64: macos-11
|
||||||
# - Linux x86_64 glibc 2.28: extract_otp_secrets:buster
|
# - Linux x86_64 glibc 2.28: extract_otp_secrets:buster
|
||||||
@@ -56,7 +57,7 @@ jobs:
|
|||||||
tag_message: ${{ steps.meta.outputs.tag_message }}
|
tag_message: ${{ steps.meta.outputs.tag_message }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Set meta data
|
- name: Set meta data
|
||||||
id: meta
|
id: meta
|
||||||
# Writing to env with >> $GITHUB_ENV is an alternative
|
# Writing to env with >> $GITHUB_ENV is an alternative
|
||||||
@@ -87,42 +88,45 @@ jobs:
|
|||||||
https://api.github.com/repos/scito/extract_otp_secrets/releases \
|
https://api.github.com/repos/scito/extract_otp_secrets/releases \
|
||||||
--silent \
|
--silent \
|
||||||
--show-error \
|
--show-error \
|
||||||
-d '{"tag_name":"${{ github.ref }}","target_commitish":"master","name":"${{ steps.meta.outputs.version }} - ${{ steps.meta.outputs.date }}","body":"${{ steps.meta.outputs.tag_message }}\n\n## Executables\n\nDownload the executable for your platform and execute it, see [README.md](https://github.com/scito/extract_otp_secrets#readme)\n\n | Executable | Description |\n | --- | --- |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_linux_x86_64 | Linux x86_64/amd64 (glibc >= 2.28) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_linux_arm64 | Linux arm64 (glibc >= 2.28) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_win_x86_64.exe | Windows x86_64/amd64/x64 |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_win_arm64.exe | N/A |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_macos_x86_64.dmg | N/A, see [README.md](https://github.com/scito/extract_otp_secrets#readme) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_macos_x86_64.pkg | N/A, see [README.md](https://github.com/scito/extract_otp_secrets#readme) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_macos_x86_64 | MacOS x86_64/amd64 (bare executable, see [README.md](https://github.com/scito/extract_otp_secrets#readme); optional libzbar must be installed manually, see [README.md](https://github.com/scito/extract_otp_secrets#readme)) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_macos_arm64 | N/A |\n","draft":true,"prerelease":false,"generate_release_notes":true}')
|
-d '{"tag_name":"${{ github.ref }}","target_commitish":"master","name":"${{ steps.meta.outputs.version }} - ${{ steps.meta.outputs.date }}","body":"${{ steps.meta.outputs.tag_message }}\n\n## Executables\n\nDownload the executable for your platform and execute it, see [README.md](https://github.com/scito/extract_otp_secrets#readme)\n\n | Executable | Description |\n | --- | --- |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_linux_x86_64 | Linux x86_64/amd64 (glibc >= 2.31) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_linux_arm64 | Linux arm64 (glibc >= 2.31) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_win_x86_64.exe | Windows x86_64/amd64/x64 |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_win_arm64.exe | N/A |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_macos_x86_64.dmg | N/A, see [README.md](https://github.com/scito/extract_otp_secrets#readme) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_macos_x86_64.pkg | N/A, see [README.md](https://github.com/scito/extract_otp_secrets#readme) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_macos_x86_64 | MacOS x86_64/amd64 (bare executable, see [README.md](https://github.com/scito/extract_otp_secrets#readme); optional libzbar must be installed manually, see [README.md](https://github.com/scito/extract_otp_secrets#readme)) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_macos_arm64 | N/A |\n","draft":true,"prerelease":false,"generate_release_notes":true}')
|
||||||
echo upload_url=$(jq '.upload_url' <<< "$response") >> $GITHUB_OUTPUT
|
echo upload_url=$(jq '.upload_url' <<< "$response") >> $GITHUB_OUTPUT
|
||||||
echo $(jq -r '.upload_url' <<< "$response") > release_url.txt
|
echo $(jq -r '.upload_url' <<< "$response") > release_url.txt
|
||||||
echo $(jq -r '.id' <<< "$response") > release_id.txt
|
echo $(jq -r '.id' <<< "$response") > release_id.txt
|
||||||
- name: Save Release URL File for publish
|
- name: Save Release URL File for publish
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release_url
|
name: release_url
|
||||||
path: release_url.txt
|
path: release_url.txt
|
||||||
- name: Save asset upload id for publish
|
- name: Save asset upload id for publish
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release_id
|
name: release_id
|
||||||
path: release_id.txt
|
path: release_id.txt
|
||||||
|
|
||||||
build-linux-executable-in-docker:
|
build-linux-executable-in-docker:
|
||||||
name: Build ${{ matrix.PLATFORM }} release in docker container
|
name: Build ${{ matrix.platform }} release in docker container
|
||||||
# run only when code is compiling and tests are passing
|
# run only when code is compiling and tests are passing
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: create-release
|
needs: create-release
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- PLATFORM: linux/amd64
|
- DOCKER_PLATFORM: linux/amd64
|
||||||
|
platform: ubuntu-latest
|
||||||
EXE: extract_otp_secrets_linux_x86_64
|
EXE: extract_otp_secrets_linux_x86_64
|
||||||
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_x86_64
|
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_x86_64
|
||||||
- PLATFORM: linux/arm64
|
- DOCKER_PLATFORM: linux/arm64
|
||||||
|
platform: ubuntu-24.04-arm
|
||||||
EXE: extract_otp_secrets_linux_arm64
|
EXE: extract_otp_secrets_linux_arm64
|
||||||
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_arm64
|
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_arm64
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
# steps to perform in job
|
# steps to perform in job
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# avoid building if there are testing errors
|
# avoid building if there are testing errors
|
||||||
- name: Run smoke test
|
- name: Run smoke test
|
||||||
@@ -134,27 +138,27 @@ jobs:
|
|||||||
pytest
|
pytest
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
# setup Docker build action
|
# setup Docker build action
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
# Workaround for failing builds: https://github.com/docker/build-push-action/issues/761#issuecomment-1383822381
|
# Workaround for failing builds: https://github.com/docker/build-push-action/issues/761#issuecomment-1383822381
|
||||||
# TODO remove workaround when fixed
|
# TODO remove workaround when fixed
|
||||||
with:
|
with:
|
||||||
driver-opts: |
|
driver-opts: |
|
||||||
image=moby/buildkit:v0.10.6
|
image=moby/buildkit:latest
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
if: github.secret_source == 'Actions'
|
if: github.secret_source == 'Actions'
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Login to Github Packages
|
- name: Login to Github Packages
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
if: github.secret_source == 'Actions'
|
if: github.secret_source == 'Actions'
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
@@ -164,21 +168,21 @@ jobs:
|
|||||||
- name: Image digest
|
- name: Image digest
|
||||||
# TODO upload digests to assets
|
# TODO upload digests to assets
|
||||||
run: |
|
run: |
|
||||||
echo "extract_otp_secrets: ${{ steps.docker_build_buster.outputs.digest }}"
|
echo "extract_otp_secrets: ${{ steps.docker_build_bullseye.outputs.digest }}"
|
||||||
|
|
||||||
# TODO use local docker image https://stackoverflow.com/a/61155718/1663871
|
# TODO use local docker image https://stackoverflow.com/a/61155718/1663871
|
||||||
# https://github.com/multiarch/qemu-user-static
|
# https://github.com/multiarch/qemu-user-static
|
||||||
# https://hub.docker.com/r/multiarch/qemu-user-static/
|
# https://hub.docker.com/r/multiarch/qemu-user-static/
|
||||||
- name: Run Pyinstaller in container for ${{ matrix.EXE }}
|
- name: Run Pyinstaller in container for ${{ matrix.EXE }}
|
||||||
run: |
|
run: |
|
||||||
docker run --pull always --rm --privileged multiarch/qemu-user-static --reset -p yes
|
docker run --pull always --entrypoint /bin/bash --rm -v "$(pwd)":/files -w /files docker.io/scit0/extract_otp_secrets:bullseye -c 'apt-get update && apt-get -y install binutils && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller -y --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name ${{ matrix.EXE }} --distpath /files/dist/ /files/src/extract_otp_secrets.py'
|
||||||
docker run --platform ${{ matrix.PLATFORM }} --pull always --entrypoint /bin/bash --rm -v "$(pwd)":/files -w /files scit0/extract_otp_secrets:buster -c 'apt-get update && apt-get -y install binutils && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller -y --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name ${{ matrix.EXE }} --distpath /files/dist/ /files/src/extract_otp_secrets.py'
|
|
||||||
|
|
||||||
- name: Smoke tests ${{ matrix.PLATFORM }}
|
- name: Smoke tests linux/amd64
|
||||||
if: matrix.PLATFORM == 'linux/amd64'
|
if: matrix.DOCKER_PLATFORM == 'linux/amd64'
|
||||||
run: |
|
run: |
|
||||||
dist/${{ matrix.EXE }} -V
|
dist/${{ matrix.EXE }} -V
|
||||||
dist/${{ matrix.EXE }} -h
|
dist/${{ matrix.EXE }} -h
|
||||||
|
dist/${{ matrix.EXE }} --debug
|
||||||
dist/${{ matrix.EXE }} example_export.png
|
dist/${{ matrix.EXE }} example_export.png
|
||||||
dist/${{ matrix.EXE }} - < example_export.txt
|
dist/${{ matrix.EXE }} - < example_export.txt
|
||||||
dist/${{ matrix.EXE }} --qr ZBAR example_export.png
|
dist/${{ matrix.EXE }} --qr ZBAR example_export.png
|
||||||
@@ -186,19 +190,19 @@ jobs:
|
|||||||
dist/${{ matrix.EXE }} --qr QREADER_DEEP example_export.png
|
dist/${{ matrix.EXE }} --qr QREADER_DEEP example_export.png
|
||||||
dist/${{ matrix.EXE }} --qr CV2 example_export.png
|
dist/${{ matrix.EXE }} --qr CV2 example_export.png
|
||||||
dist/${{ matrix.EXE }} --qr CV2_WECHAT example_export.png
|
dist/${{ matrix.EXE }} --qr CV2_WECHAT example_export.png
|
||||||
- name: Smoke tests ${{ matrix.PLATFORM }}
|
- name: Smoke tests linux/arm64
|
||||||
if: matrix.PLATFORM == 'linux/arm64'
|
if: matrix.DOCKER_PLATFORM == 'linux/arm64'
|
||||||
run: |
|
run: |
|
||||||
docker run --platform ${{ matrix.PLATFORM }} --pull always --entrypoint /bin/bash --rm -v "$(pwd)":/files -w /files scit0/extract_otp_secrets -c 'dist/${{ matrix.EXE }} -V && dist/${{ matrix.EXE }} -h && dist/${{ matrix.EXE }} example_export.png && dist/${{ matrix.EXE }} - < example_export.txt && dist/${{ matrix.EXE }} --qr ZBAR example_export.png && dist/${{ matrix.EXE }} --qr QREADER example_export.png && dist/${{ matrix.EXE }} --qr QREADER_DEEP example_export.png && dist/${{ matrix.EXE }} --qr CV2 example_export.png && dist/${{ matrix.EXE }} --qr CV2_WECHAT example_export.png'
|
docker run --pull always --entrypoint /bin/bash --rm -v "$(pwd)":/files -w /files docker.io/scit0/extract_otp_secrets -c 'dist/${{ matrix.EXE }} -V && dist/${{ matrix.EXE }} -h && dist/${{ matrix.EXE }} example_export.png && dist/${{ matrix.EXE }} - < example_export.txt && dist/${{ matrix.EXE }} --qr ZBAR example_export.png && dist/${{ matrix.EXE }} --qr QREADER example_export.png && dist/${{ matrix.EXE }} --qr QREADER_DEEP example_export.png && dist/${{ matrix.EXE }} --qr CV2 example_export.png && dist/${{ matrix.EXE }} --qr CV2_WECHAT example_export.png'
|
||||||
- name: Load Release URL File from release job
|
- name: Load Release URL File from release job
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release_url
|
name: release_url
|
||||||
- name: Display structure of files
|
- name: Display structure of files
|
||||||
run: ls -R
|
run: ls -R
|
||||||
- name: Upload EXE to artifacts
|
- name: Upload EXE to artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.EXE }}
|
name: ${{ matrix.EXE }}
|
||||||
path: dist/${{ matrix.EXE }}
|
path: dist/${{ matrix.EXE }}
|
||||||
@@ -236,8 +240,24 @@ jobs:
|
|||||||
ASSET_MIME: application/vnd.microsoft.portable-executable
|
ASSET_MIME: application/vnd.microsoft.portable-executable
|
||||||
UPLOAD: true
|
UPLOAD: true
|
||||||
CMD_BUILD: |
|
CMD_BUILD: |
|
||||||
pyinstaller -y --add-data "$($Env:pythonLocation)\__yolo_v3_qr_detector;__yolo_v3_qr_detector" --add-binary "$($Env:pythonLocation)\Lib\site-packages\pyzbar\libiconv.dll;pyzbar" --add-binary "$($Env:pythonLocation)\Lib\site-packages\pyzbar\libzbar-64.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\msvcr120.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\msvcp120.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\vcamp120.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\vcomp120.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\vccorlib120.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120u.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120chs.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120cht.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120deu.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120enu.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120esn.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120fra.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120ita.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120jpn.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120kor.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120rus.dll;pyzbar" --onefile --version-file build\win_file_version_info.txt --name extract_otp_secrets.exe src\extract_otp_secrets.py
|
pyinstaller -y --add-data "$($Env:pythonLocation)\__yolo_v3_qr_detector:__yolo_v3_qr_detector" --add-binary "$($Env:pythonLocation)\Lib\site-packages\pyzbar\libiconv.dll:pyzbar" --add-binary "$($Env:pythonLocation)\Lib\site-packages\pyzbar\libzbar-64.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\msvcr120.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\msvcp120.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\vcamp120.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\vcomp120.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\vccorlib120.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120u.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120chs.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120cht.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120deu.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120enu.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120esn.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120fra.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120ita.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120jpn.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120kor.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120rus.dll:pyzbar" --onefile --version-file build\win_file_version_info.txt --name extract_otp_secrets.exe src\extract_otp_secrets.py
|
||||||
- os: macos-11
|
- os: ubuntu-latest
|
||||||
|
TARGET: linux
|
||||||
|
EXE: extract_otp_secrets_ubuntu
|
||||||
|
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_x86_64_ubuntu_latest
|
||||||
|
ASSET_MIME: application/x-executable
|
||||||
|
UPLOAD: false
|
||||||
|
CMD_BUILD: |
|
||||||
|
pyinstaller -y --add-data $pythonLocation/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_ubuntu src/extract_otp_secrets.py
|
||||||
|
- os: ubuntu-24.04-arm
|
||||||
|
TARGET: linux
|
||||||
|
EXE: extract_otp_secrets_ubuntu_arm64
|
||||||
|
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_arm64_ubuntu_latest
|
||||||
|
ASSET_MIME: application/x-executable
|
||||||
|
UPLOAD: false
|
||||||
|
CMD_BUILD: |
|
||||||
|
pyinstaller -y --add-data $pythonLocation/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_ubuntu_arm64 src/extract_otp_secrets.py
|
||||||
|
- os: macos-13
|
||||||
TARGET: macos
|
TARGET: macos
|
||||||
# https://pyinstaller.org/en/stable/spec-files.html#spec-file-options-for-a-macos-bundle
|
# https://pyinstaller.org/en/stable/spec-files.html#spec-file-options-for-a-macos-bundle
|
||||||
EXE: extract_otp_secrets
|
EXE: extract_otp_secrets
|
||||||
@@ -247,17 +267,23 @@ jobs:
|
|||||||
ASSET_MIME: application/octet-stream
|
ASSET_MIME: application/octet-stream
|
||||||
UPLOAD: true
|
UPLOAD: true
|
||||||
CMD_BUILD: |
|
CMD_BUILD: |
|
||||||
VERSION_STR=$(setuptools-git-versioning) COPYRIGHT_YEARS='2020-2023' envsubst < installer/extract_otp_secrets_macos_template.spec > extract_otp_secrets_macos.spec
|
VERSION_STR=$(setuptools-git-versioning) COPYRIGHT_YEARS='2020-2025' envsubst < installer/extract_otp_secrets_macos_template.spec > extract_otp_secrets_macos.spec
|
||||||
pyinstaller -y extract_otp_secrets_macos.spec
|
pyinstaller -y extract_otp_secrets_macos.spec
|
||||||
installer/build_dmg.sh
|
installer/build_dmg.sh
|
||||||
- os: ubuntu-latest
|
# Disable WARN: Cannot import pyzbar module. This problem is probably due to the missing zbar shared library.
|
||||||
TARGET: linux
|
# - os: macos-14
|
||||||
EXE: extract_otp_secrets_ubuntu
|
# TARGET: macos
|
||||||
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_x86_64_ubuntu_latest
|
# # https://pyinstaller.org/en/stable/spec-files.html#spec-file-options-for-a-macos-bundle
|
||||||
ASSET_MIME: application/x-executable
|
# EXE: extract_otp_secrets
|
||||||
UPLOAD: false
|
# ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_macos_arm64
|
||||||
CMD_BUILD: |
|
# DMG: extract_otp_secrets.dmg
|
||||||
pyinstaller -y --add-data $pythonLocation/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_ubuntu src/extract_otp_secrets.py
|
# ASSET_NAME_DMG: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_macos_arm64.dmg
|
||||||
|
# ASSET_MIME: application/octet-stream
|
||||||
|
# UPLOAD: true
|
||||||
|
# CMD_BUILD: |
|
||||||
|
# VERSION_STR=$(setuptools-git-versioning) COPYRIGHT_YEARS='2020-2025' envsubst < installer/extract_otp_secrets_macos_template.spec > extract_otp_secrets_macos.spec
|
||||||
|
# pyinstaller -y extract_otp_secrets_macos.spec
|
||||||
|
# installer/build_dmg.sh
|
||||||
steps:
|
steps:
|
||||||
- name: Output path
|
- name: Output path
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
@@ -265,14 +291,14 @@ jobs:
|
|||||||
- name: List Windir
|
- name: List Windir
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
run: ls "$($Env:WinDir)\system32"
|
run: ls "$($Env:WinDir)\system32"
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Set macos macos_python_path
|
- name: Set macos macos_python_path
|
||||||
# TODO use variable for Python version
|
# TODO use variable for Python version
|
||||||
run: echo "macos_python_path=/Library/Frameworks/Python.framework/Versions/3.11" >> $GITHUB_ENV
|
run: echo "macos_python_path=/Library/Frameworks/Python.framework/Versions/3.13" >> $GITHUB_ENV
|
||||||
- name: Set up Python 3.11
|
- name: Set up Python 3.13
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: 3.11
|
python-version: 3.13
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- name: Install zbar shared lib for QReader (Linux)
|
- name: Install zbar shared lib for QReader (Linux)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
@@ -292,7 +318,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
mkdir -p build/
|
mkdir -p build/
|
||||||
VERSION_STR=$(setuptools-git-versioning) VERSION_MAJOR=$(cut -d '.' -f 1 <<< "$(setuptools-git-versioning)") VERSION_MINOR=$(cut -d '.' -f 2 <<< "$(setuptools-git-versioning)") VERSION_PATCH=$(echo $(cut -d '.' -f 3 <<< "$(setuptools-git-versioning)") | sed -E -n "s/^([0-9]+).*/\1/p") VERSION_BUILD=$(echo $(cut -d '.' -f 3 <<< "$(setuptools-git-versioning)") | sed -E -n -e"s/^[0-9]+.+/99/p")$(($(git rev-list --count $(git tag | sort -V -r | sed '1!d')..HEAD))) COPYRIGHT_YEARS='2020-2023' envsubst < installer/win_file_version_info_template.txt > build/win_file_version_info.txt
|
VERSION_STR=$(setuptools-git-versioning) VERSION_MAJOR=$(cut -d '.' -f 1 <<< "$(setuptools-git-versioning)") VERSION_MINOR=$(cut -d '.' -f 2 <<< "$(setuptools-git-versioning)") VERSION_PATCH=$(echo $(cut -d '.' -f 3 <<< "$(setuptools-git-versioning)") | sed -E -n "s/^([0-9]+).*/\1/p") VERSION_BUILD=$(echo $(cut -d '.' -f 3 <<< "$(setuptools-git-versioning)") | sed -E -n -e"s/^[0-9]+.+/99/p")$(($(git rev-list --count $(git tag | sort -V -r | sed '1!d')..HEAD))) COPYRIGHT_YEARS='2020-2025' envsubst < installer/win_file_version_info_template.txt > build/win_file_version_info.txt
|
||||||
- name: Build with pyinstaller for ${{ matrix.TARGET }}
|
- name: Build with pyinstaller for ${{ matrix.TARGET }}
|
||||||
env:
|
env:
|
||||||
# Reproducible build: https://pyinstaller.org/en/stable/advanced-topics.html#creating-a-reproducible-build
|
# Reproducible build: https://pyinstaller.org/en/stable/advanced-topics.html#creating-a-reproducible-build
|
||||||
@@ -302,6 +328,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
dist/${{ matrix.EXE }} -V
|
dist/${{ matrix.EXE }} -V
|
||||||
dist/${{ matrix.EXE }} -h
|
dist/${{ matrix.EXE }} -h
|
||||||
|
dist/${{ matrix.EXE }} --debug
|
||||||
dist/${{ matrix.EXE }} example_export.png
|
dist/${{ matrix.EXE }} example_export.png
|
||||||
dist/${{ matrix.EXE }} --qr ZBAR example_export.png
|
dist/${{ matrix.EXE }} --qr ZBAR example_export.png
|
||||||
dist/${{ matrix.EXE }} --qr QREADER example_export.png
|
dist/${{ matrix.EXE }} --qr QREADER example_export.png
|
||||||
@@ -314,12 +341,12 @@ jobs:
|
|||||||
dist/${{ matrix.EXE }} - < example_export.txt
|
dist/${{ matrix.EXE }} - < example_export.txt
|
||||||
- name: Load Release URL File from release job
|
- name: Load Release URL File from release job
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release_url
|
name: release_url
|
||||||
- name: Load Release Id File from release job
|
- name: Load Release Id File from release job
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release_id
|
name: release_id
|
||||||
- name: Display structure of files
|
- name: Display structure of files
|
||||||
@@ -332,12 +359,12 @@ jobs:
|
|||||||
echo "release_id=$(cat release_id.txt)" >> $GITHUB_OUTPUT
|
echo "release_id=$(cat release_id.txt)" >> $GITHUB_OUTPUT
|
||||||
echo "upload_url=https://uploads.github.com/repos/scito/extract_otp_secrets/releases/$(cat release_id.txt)/assets?name=" >> $GITHUB_OUTPUT
|
echo "upload_url=https://uploads.github.com/repos/scito/extract_otp_secrets/releases/$(cat release_id.txt)/assets?name=" >> $GITHUB_OUTPUT
|
||||||
- name: Upload EXE to artifacts
|
- name: Upload EXE to artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.EXE }}
|
name: ${{ matrix.EXE }}
|
||||||
path: dist/${{ matrix.EXE }}
|
path: dist/${{ matrix.EXE }}
|
||||||
- name: Upload DMG to artifacts
|
- name: Upload DMG to artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.DMG }}
|
name: ${{ matrix.DMG }}
|
||||||
@@ -362,7 +389,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Load Release Id File from release job
|
- name: Load Release Id File from release job
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release_id
|
name: release_id
|
||||||
- name: Set meta data
|
- name: Set meta data
|
||||||
|
|||||||
12
.github/workflows/codeql-analysis.yml
vendored
12
.github/workflows/codeql-analysis.yml
vendored
@@ -42,26 +42,26 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
# By default, queries listed here will override any specified in a config file.
|
# By default, queries listed here will override any specified in a config file.
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
|
||||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||||
# queries: security-extended,security-and-quality
|
# queries: security-extended,security-and-quality
|
||||||
|
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v2
|
uses: github/codeql-action/autobuild@v3
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
|
|
||||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||||
|
|
||||||
# - run: |
|
# - run: |
|
||||||
@@ -69,6 +69,6 @@ jobs:
|
|||||||
# ./location_of_script_within_repo/buildscript.sh
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v3
|
||||||
with:
|
with:
|
||||||
category: "/language:${{matrix.language}}"
|
category: "/language:${{matrix.language}}"
|
||||||
|
|||||||
14
.gitignore
vendored
14
.gitignore
vendored
@@ -27,6 +27,20 @@ file_version_info.txt
|
|||||||
assets/*
|
assets/*
|
||||||
extract_otp_secrets_linux_arm64.spec
|
extract_otp_secrets_linux_arm64.spec
|
||||||
extract_otp_secrets_linux_x86_64_bullseye.spec
|
extract_otp_secrets_linux_x86_64_bullseye.spec
|
||||||
|
extract_otp_secrets_linux_x86_64_bookworm.spec
|
||||||
extract_otp_secrets_linux_x86_64.spec
|
extract_otp_secrets_linux_x86_64.spec
|
||||||
extract_otp_secrets.spec
|
extract_otp_secrets.spec
|
||||||
test.txt
|
test.txt
|
||||||
|
extract_otp_secrets.build/
|
||||||
|
extract_otp_secrets.dist/
|
||||||
|
extract_otp_secrets.onefile-build/
|
||||||
|
extract_otp_secrets.bin
|
||||||
|
# Devenv
|
||||||
|
.devenv*
|
||||||
|
devenv.local.nix
|
||||||
|
|
||||||
|
# direnv
|
||||||
|
.direnv
|
||||||
|
|
||||||
|
# pre-commit
|
||||||
|
.pre-commit-config.yaml
|
||||||
|
|||||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -2,7 +2,7 @@
|
|||||||
"python.testing.pytestArgs": [
|
"python.testing.pytestArgs": [
|
||||||
"."
|
"."
|
||||||
],
|
],
|
||||||
"python.testing.unittestEnabled": true,
|
"python.testing.unittestEnabled": false,
|
||||||
"python.testing.pytestEnabled": true,
|
"python.testing.pytestEnabled": true,
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"devbox",
|
"devbox",
|
||||||
|
|||||||
4
Pipfile
4
Pipfile
@@ -4,10 +4,9 @@ verify_ssl = true
|
|||||||
name = "pypi"
|
name = "pypi"
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
colorama = ">=0.4.6"
|
colorama = ">0.4.5"
|
||||||
opencv-contrib-python = "*"
|
opencv-contrib-python = "*"
|
||||||
# for macOS: opencv-contrib-python = "<=4.7.0"
|
# for macOS: opencv-contrib-python = "<=4.7.0"
|
||||||
# for PYTHON <= 3.7: typing_extensions = "*"
|
|
||||||
pillow = "*"
|
pillow = "*"
|
||||||
pyzbar = "*"
|
pyzbar = "*"
|
||||||
protobuf = "*"
|
protobuf = "*"
|
||||||
@@ -20,6 +19,7 @@ flake8 = "*"
|
|||||||
gfm-toc = "*"
|
gfm-toc = "*"
|
||||||
mypy = "*"
|
mypy = "*"
|
||||||
mypy-protobuf = "*"
|
mypy-protobuf = "*"
|
||||||
|
nuitka = "*"
|
||||||
pylint = "*"
|
pylint = "*"
|
||||||
pytest = "*"
|
pytest = "*"
|
||||||
pytest-cov = "*"
|
pytest-cov = "*"
|
||||||
|
|||||||
956
Pipfile.lock
generated
956
Pipfile.lock
generated
File diff suppressed because it is too large
Load Diff
87
README.md
87
README.md
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
[](https://github.com/scito/extract_otp_secrets/actions/workflows/ci.yml)
|
[](https://github.com/scito/extract_otp_secrets/actions/workflows/ci.yml)
|
||||||
[](https://github.com/scito/extract_otp_secrets/actions/workflows/ci_docker.yml)
|
[](https://github.com/scito/extract_otp_secrets/actions/workflows/ci_docker.yml)
|
||||||

|

|
||||||
[](https://github.com/scito/extract_otp_secrets/blob/master/LICENSE)
|
[](https://github.com/scito/extract_otp_secrets/blob/master/LICENSE)
|
||||||
[](https://github.com/scito/extract_otp_secrets/releases/latest)
|
[](https://github.com/scito/extract_otp_secrets/releases/latest)
|
||||||

|

|
||||||
[](https://hub.docker.com/repository/docker/scit0/extract_otp_secrets/general)
|
[](https://hub.docker.com/repository/docker/scit0/extract_otp_secrets/general)
|
||||||
[](https://github.com/scito/extract_otp_secrets/releases/latest)
|
[](https://github.com/scito/extract_otp_secrets/releases/latest)
|
||||||
[](https://github.com/scito/extract_otp_secrets/releases/latest)
|
[](https://github.com/scito/extract_otp_secrets/releases/latest)
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
[](https://stand-with-ukraine.pp.ua)
|
[](https://stand-with-ukraine.pp.ua)
|
||||||
<!-- 
|
<!-- 
|
||||||
[](https://github.com/scito/extract_otp_secrets/blob/master/Pipfile.lock)
|
[](https://github.com/scito/extract_otp_secrets/blob/master/Pipfile.lock)
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- [](https://GitHub.com/scito/extract_otp_secrets/releases/) -->
|
<!-- [](https://GitHub.com/scito/extract_otp_secrets/releases/) -->
|
||||||
|
|
||||||
@@ -36,6 +36,7 @@ The secrets can be exported to JSON or CSV, or printed as QR codes to console or
|
|||||||
|
|
||||||
## Table of contents
|
## Table of contents
|
||||||
|
|
||||||
|
- [Table of contents](#table-of-contents)
|
||||||
- [Download and run binary executable (🆕 since v2.1)](#download-and-run-binary-executable--since-v21)
|
- [Download and run binary executable (🆕 since v2.1)](#download-and-run-binary-executable--since-v21)
|
||||||
- [MacOS](#macos)
|
- [MacOS](#macos)
|
||||||
- [Usage](#usage)
|
- [Usage](#usage)
|
||||||
@@ -46,7 +47,7 @@ The secrets can be exported to JSON or CSV, or printed as QR codes to console or
|
|||||||
- [Installation of optional shared system libraries (recommended)](#installation-of-optional-shared-system-libraries-recommended)
|
- [Installation of optional shared system libraries (recommended)](#installation-of-optional-shared-system-libraries-recommended)
|
||||||
- [Program help: arguments and options](#program-help-arguments-and-options)
|
- [Program help: arguments and options](#program-help-arguments-and-options)
|
||||||
- [Examples](#examples)
|
- [Examples](#examples)
|
||||||
- [Printing otp secrets from text file](#printing-otp-secrets-form-text-file)
|
- [Printing otp secrets from text file](#printing-otp-secrets-from-text-file)
|
||||||
- [Printing otp secrets from image file](#printing-otp-secrets-from-image-file)
|
- [Printing otp secrets from image file](#printing-otp-secrets-from-image-file)
|
||||||
- [Writing otp secrets to csv file](#writing-otp-secrets-to-csv-file)
|
- [Writing otp secrets to csv file](#writing-otp-secrets-to-csv-file)
|
||||||
- [Writing otp secrets to json file](#writing-otp-secrets-to-json-file)
|
- [Writing otp secrets to json file](#writing-otp-secrets-to-json-file)
|
||||||
@@ -66,7 +67,8 @@ The secrets can be exported to JSON or CSV, or printed as QR codes to console or
|
|||||||
- [pipenv](#pipenv)
|
- [pipenv](#pipenv)
|
||||||
- [Visual Studio Code Remote - Containers / VSCode devcontainer](#visual-studio-code-remote---containers--vscode-devcontainer)
|
- [Visual Studio Code Remote - Containers / VSCode devcontainer](#visual-studio-code-remote---containers--vscode-devcontainer)
|
||||||
- [venv](#venv)
|
- [venv](#venv)
|
||||||
- [devbox](#devbox)
|
- [devbox 1](#devbox-1)
|
||||||
|
- [devbox 2](#devbox-2)
|
||||||
- [docker](#docker)
|
- [docker](#docker)
|
||||||
- [More docker examples](#more-docker-examples)
|
- [More docker examples](#more-docker-examples)
|
||||||
- [Tests](#tests)
|
- [Tests](#tests)
|
||||||
@@ -85,6 +87,7 @@ The secrets can be exported to JSON or CSV, or printed as QR codes to console or
|
|||||||
- [Problems and Troubleshooting](#problems-and-troubleshooting)
|
- [Problems and Troubleshooting](#problems-and-troubleshooting)
|
||||||
- [Windows error message](#windows-error-message)
|
- [Windows error message](#windows-error-message)
|
||||||
- [Related projects](#related-projects)
|
- [Related projects](#related-projects)
|
||||||
|
- [Third party documentation](#third-party-documentation)
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## Download and run binary executable (🆕 since v2.1)
|
## Download and run binary executable (🆕 since v2.1)
|
||||||
@@ -119,14 +122,23 @@ However, the bare executable can be executed from the command line:
|
|||||||
1. Download executable for macOS platform from [latest release](https://github.com/scito/extract_otp_secrets/releases/latest), see assets
|
1. Download executable for macOS platform from [latest release](https://github.com/scito/extract_otp_secrets/releases/latest), see assets
|
||||||
2. Open `Terminal` application
|
2. Open `Terminal` application
|
||||||
3. Change to Downloads folder in Terminal: `cd $HOME/Downloads`
|
3. Change to Downloads folder in Terminal: `cd $HOME/Downloads`
|
||||||
4. Set executable bit for the downloaded file: `chmod +x extract_otp_secrets_X.Y.Z_macos_x86_64`
|
4. Remove quarantine bit for the downloaded file: `xattr -r -d com.apple.quarantine extract_otp_secrets_X.Y.Z_macos_x86_64`
|
||||||
5. Start executable from command line: `./extract_otp_secrets_X.Y.Z_macos_x86_64`
|
5. Set executable bit for the downloaded file: `chmod +x extract_otp_secrets_X.Y.Z_macos_x86_64`
|
||||||
|
6. Start executable from command line for the first time: `./extract_otp_secrets_X.Y.Z_macos_x86_64`
|
||||||
|
7. Wait approximately 30 seconds to 1 minute on the first run. Terminal will display the following error:
|
||||||
|
```
|
||||||
|
OpenCV: not authorized to capture video (status 0), requesting...
|
||||||
|
OpenCV: camera failed to properly initialize!
|
||||||
|
```
|
||||||
|
8. macOS will then prompt to request camera access.
|
||||||
|
9. After allowing camera access, rerun the program.
|
||||||
|
10. On the second run, the GUI prompt shows correctly and is fully operable: `./extract_otp_secrets_X.Y.Z_macos_x86_64`
|
||||||
|
|
||||||
:information_source: Replace `X.Y.Z` in above commands with the version number of your downloaded file, e.g. `extract_otp_secrets_2.4.0_macos_x86_64`
|
:information_source: Replace `X.Y.Z` in above commands with the version number of your downloaded file, e.g. `extract_otp_secrets_2.4.0_macos_x86_64`
|
||||||
|
|
||||||
:information_source: If Rosetta2 emulation is installed, these steps work also for M1 and M2 Apple Silicon processors and the program can be executed directly.
|
:information_source: If Rosetta2 emulation is installed, these steps work also for M1 and M2 Apple Silicon processors and the program can be executed directly.
|
||||||
|
|
||||||
> :warning: It seems the GUI mode is not working in Terminal on macOS. In tests no [GUI window](#usage) was opened. (Remarks and hints about macOS are welcome since I do not know macOS.)
|
Tested with extract_otp_secrets_2.8.1_macos_x86_64 on macOS Sequoia 15.1 beta. Source: [#283](https://github.com/scito/extract_otp_secrets/issues/283)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -234,7 +246,7 @@ OpenCV requires [Visual C++ redistributable 2015](https://www.microsoft.com/en-u
|
|||||||
|
|
||||||
## Program help: arguments and options
|
## Program help: arguments and options
|
||||||
|
|
||||||
<pre>usage: extract_otp_secrets.py [-h] [--csv FILE] [--keepass FILE] [--json FILE] [--txt FILE] [--printqr] [--saveqr DIR] [--camera NUMBER] [--qr {ZBAR,QREADER,QREADER_DEEP,CV2,CV2_WECHAT}] [-i] [--no-color] [--version] [-d | -v | -q] [infile ...]
|
<pre>usage: extract_otp_secrets.py [-h] [--csv FILE] [--keepass FILE] [--json FILE] [--txt FILE] [--urls FILE] [--printqr] [--saveqr DIR] [--camera NUMBER] [--qr {ZBAR,QREADER,QREADER_DEEP,CV2,CV2_WECHAT}] [-i] [--no-color] [--version] [-d | -v | -q] [infile ...]
|
||||||
|
|
||||||
Extracts one time password (OTP) secrets from QR codes exported by two-factor authentication (2FA) apps
|
Extracts one time password (OTP) secrets from QR codes exported by two-factor authentication (2FA) apps
|
||||||
If no infiles are provided, a GUI window starts and QR codes are captured from the camera.
|
If no infiles are provided, a GUI window starts and QR codes are captured from the camera.
|
||||||
@@ -245,10 +257,11 @@ positional arguments:
|
|||||||
|
|
||||||
options:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--csv FILE, -c FILE export csv file or - for stdout
|
--csv FILE, -c FILE export csv file, or - for stdout
|
||||||
--keepass FILE, -k FILE export totp/hotp csv file(s) for KeePass, - for stdout
|
--keepass FILE, -k FILE export totp/hotp csv file(s) for KeePass, - for stdout
|
||||||
--json FILE, -j FILE export json file or - for stdout
|
--json FILE, -j FILE export json file or - for stdout
|
||||||
--txt FILE, -t FILE export txt file or - for stdout
|
--txt FILE, -t FILE export txt file or - for stdout
|
||||||
|
--urls FILE, -u FILE export file with list of otpauth urls, or - for stdout
|
||||||
--printqr, -p print QR code(s) as text to the terminal
|
--printqr, -p print QR code(s) as text to the terminal
|
||||||
--saveqr DIR, -s DIR save QR code(s) as images to directory
|
--saveqr DIR, -s DIR save QR code(s) as images to directory
|
||||||
--camera NUMBER, -C NUMBER camera number of system (default camera: 0)
|
--camera NUMBER, -C NUMBER camera number of system (default camera: 0)
|
||||||
@@ -270,7 +283,7 @@ python extract_otp_secrets.py = < example_export.png</pre>
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### Printing otp secrets form text file
|
### Printing otp secrets from text file
|
||||||
|
|
||||||
python src/extract_otp_secrets.py example_export.txt
|
python src/extract_otp_secrets.py example_export.txt
|
||||||
|
|
||||||
@@ -347,13 +360,13 @@ python extract_otp_secrets.py = < example_export.png</pre>
|
|||||||
* Prints errors and warnings to stderr (🆕 since v2.0)
|
* Prints errors and warnings to stderr (🆕 since v2.0)
|
||||||
* Prints colored output (🆕 since v2.0)
|
* Prints colored output (🆕 since v2.0)
|
||||||
* Startable as executable (script, Python, and all dependencies packed in one executable) (🆕 since v2.1)
|
* Startable as executable (script, Python, and all dependencies packed in one executable) (🆕 since v2.1)
|
||||||
* extract_otp_secrets_linux_x86_64 (requires glibc >= 2.28)
|
* extract_otp_secrets_linux_x86_64 (requires glibc >= 2.31)
|
||||||
* extract_otp_secrets_linux_arm64 (requires glibc >= 2.28)
|
* extract_otp_secrets_linux_arm64 (requires glibc >= 2.31)
|
||||||
* extract_otp_secrets_win_x86_64.exe
|
* extract_otp_secrets_win_x86_64.exe
|
||||||
* extract_otp_secrets_macos_x86_64 (optional [libzbar](#installation-of-optional-shared-system-libraries-recommended) needs to be installed manually if needed)
|
* extract_otp_secrets_macos_x86_64 (optional [libzbar](#installation-of-optional-shared-system-libraries-recommended) needs to be installed manually if needed)
|
||||||
* extract_otp_secrets_macos_x86_64.dmg N/A, see [why](#macos)
|
* extract_otp_secrets_macos_x86_64.dmg N/A, see [why](#macos)
|
||||||
* extract_otp_secrets_macos_x86_64.pkg N/A, see [why](#macos)
|
* extract_otp_secrets_macos_x86_64.pkg N/A, see [why](#macos)
|
||||||
* Prebuilt Docker images provided for amd64 and arm64 (🆕 since v2.0)
|
* Prebuilt Docker images provided for amd64 and arm64 on [Docker Hub](https://hub.docker.com/repository/docker/scit0/extract_otp_secrets) and [GitHub Packages](https://github.com/users/scito/packages/container/package/extract_otp_secrets) (🆕 since v2.0)
|
||||||
* Many ways to run the script:
|
* Many ways to run the script:
|
||||||
* Native Python
|
* Native Python
|
||||||
* pipenv
|
* pipenv
|
||||||
@@ -367,7 +380,7 @@ python extract_otp_secrets.py = < example_export.png</pre>
|
|||||||
* macOS
|
* macOS
|
||||||
* Windows
|
* Windows
|
||||||
* Uses UTF-8 on all platforms
|
* Uses UTF-8 on all platforms
|
||||||
* Supports Python >= 3.7
|
* Supports Python >= 3.9
|
||||||
* Installation of shared system libraries is optional (🆕 since v2.3)
|
* Installation of shared system libraries is optional (🆕 since v2.3)
|
||||||
* Provides a debug mode (-d) for analyzing import problems
|
* Provides a debug mode (-d) for analyzing import problems
|
||||||
* Written in modern Python using type hints and following best practices
|
* Written in modern Python using type hints and following best practices
|
||||||
@@ -507,7 +520,7 @@ Alternatively, you can use a python virtual env for the dependencies:
|
|||||||
The requirements\*.txt files contain all the dependencies (also the optional ones).
|
The requirements\*.txt files contain all the dependencies (also the optional ones).
|
||||||
To leave the python virtual env just call `deactivate`.
|
To leave the python virtual env just call `deactivate`.
|
||||||
|
|
||||||
### devbox
|
### devbox 1
|
||||||
|
|
||||||
Install [devbox](https://github.com/jetpack-io/devbox), which is a wrapper for nix. Then enter the environment with Python and the packages installed with:
|
Install [devbox](https://github.com/jetpack-io/devbox), which is a wrapper for nix. Then enter the environment with Python and the packages installed with:
|
||||||
|
|
||||||
@@ -515,6 +528,14 @@ Install [devbox](https://github.com/jetpack-io/devbox), which is a wrapper for n
|
|||||||
devbox shell
|
devbox shell
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### devbox 2
|
||||||
|
|
||||||
|
Install [devbox](https://devenv.sh), which is a wrapper for nix. Then enter the environment with Python and the packages installed with:
|
||||||
|
|
||||||
|
```
|
||||||
|
devenv shell
|
||||||
|
```
|
||||||
|
|
||||||
### docker
|
### docker
|
||||||
|
|
||||||
Install [Docker](https://docs.docker.com/get-docker/).
|
Install [Docker](https://docs.docker.com/get-docker/).
|
||||||
@@ -524,35 +545,35 @@ Prebuilt docker images are available for amd64 and arm64 architectures on [Docke
|
|||||||
Extracting from an QR image file:
|
Extracting from an QR image file:
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.png | docker run --pull always -i --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets =
|
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.png | docker run --network none --pull always -i --rm -v "$(pwd)":/files:ro docker.io/scit0/extract_otp_secrets =
|
||||||
```
|
```
|
||||||
|
|
||||||
Capturing from camera in GUI window (X Window system required on host):
|
Capturing from camera in GUI window (X Window system required on host):
|
||||||
|
|
||||||
```
|
```
|
||||||
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 --network none --pull always --rm -v "$(pwd)":/files:ro -i --device="/dev/video0:/dev/video0" --env="DISPLAY" -v /tmp/.X11-unix:/tmp/.X11-unix:ro docker.io/scit0/extract_otp_secrets
|
||||||
```
|
```
|
||||||
|
|
||||||
If only text processing is required, there is a small Image based on Alpine Linux:
|
If only text processing is required, there is a small Image based on Alpine Linux:
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.txt | docker run --pull always -i --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets:latest-only-txt -
|
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.txt | docker run --network none --pull always -i --rm -v "$(pwd)":/files:ro docker.io/scit0/extract_otp_secrets:latest-only-txt -
|
||||||
```
|
```
|
||||||
|
|
||||||
Docker image from GitHub:
|
Docker image from GitHub:
|
||||||
|
|
||||||
```
|
```
|
||||||
docker login ghcr.io -u USERNAME
|
docker login ghcr.io -u USERNAME
|
||||||
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.png | docker run --pull always -i --rm -v "$(pwd)":/files:ro ghcr.io/scito/extract_otp_secrets =
|
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.png | docker run --network none --pull always -i --rm -v "$(pwd)":/files:ro ghcr.io/scito/extract_otp_secrets =
|
||||||
```
|
```
|
||||||
|
|
||||||
### More docker examples
|
### More docker examples
|
||||||
|
|
||||||
docker run --pull always --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets example_export.png
|
docker run --network none --pull always --rm -v "$(pwd)":/files:ro docker.io/scit0/extract_otp_secrets example_export.png
|
||||||
|
|
||||||
docker run --pull always --rm -i -v "$(pwd)":/files:ro scit0/extract_otp_secrets_only_txt - < example_export.txt
|
docker run --network none --pull always --rm -i -v "$(pwd)":/files:ro docker.io/scit0/extract_otp_secrets:latest-only-txt - < example_export.txt
|
||||||
|
|
||||||
cat example_export.txt | docker run --pull always --rm -i -v "$(pwd)":/files:ro scit0/extract_otp_secrets:latest_only_txt - -c - > example_out.csv
|
cat example_export.txt | docker run --network none --pull always --rm -i -v "$(pwd)":/files:ro docker.io/scit0/extract_otp_secrets:latest-only-txt - -c - > example_out.csv
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
@@ -685,10 +706,15 @@ Build extract_otp_secrets project
|
|||||||
Options:
|
Options:
|
||||||
-i Interactive mode, all steps must be confirmed
|
-i Interactive mode, all steps must be confirmed
|
||||||
-C Ignore version check of protobuf/protoc
|
-C Ignore version check of protobuf/protoc
|
||||||
-E Do not build exe
|
-e Build exe
|
||||||
-D Do not build docker
|
-n Build nuitka exe
|
||||||
|
-L Do not build local (exes)
|
||||||
|
-d Build docker
|
||||||
|
-a Build arm
|
||||||
|
-X Do not build x86_64
|
||||||
|
-B Do not build base
|
||||||
-V Do not run pipenv
|
-V Do not run pipenv
|
||||||
-G Do not start extract_otp_secrets.py in GUI mode
|
-g Start extract_otp_secrets.py in GUI mode
|
||||||
-c Clean everything
|
-c Clean everything
|
||||||
-r Generate result files
|
-r Generate result files
|
||||||
-h, --help Show help and quit
|
-h, --help Show help and quit
|
||||||
@@ -703,7 +729,7 @@ Command for regeneration of Python code from proto3 message definition file (onl
|
|||||||
|
|
||||||
protoc --plugin=protoc-gen-mypy=path/to/protoc-gen-mypy --python_out=src/protobuf_generated_python --mypy_out=src/protobuf_generated_python src/google_auth.proto
|
protoc --plugin=protoc-gen-mypy=path/to/protoc-gen-mypy --python_out=src/protobuf_generated_python --mypy_out=src/protobuf_generated_python src/google_auth.proto
|
||||||
|
|
||||||
The generated protobuf Python code was generated by protoc 22.1 (https://github.com/protocolbuffers/protobuf/releases/tag/v22.1).
|
The generated protobuf Python code was generated by protoc 31.0 (https://github.com/protocolbuffers/protobuf/releases/tag/v31.0).
|
||||||
|
|
||||||
For Python type hint generation the [mypy-protobuf](https://github.com/nipunn1313/mypy-protobuf) package is used.
|
For Python type hint generation the [mypy-protobuf](https://github.com/nipunn1313/mypy-protobuf) package is used.
|
||||||
|
|
||||||
@@ -714,7 +740,7 @@ For Python type hint generation the [mypy-protobuf](https://github.com/nipunn131
|
|||||||
|
|
||||||
## Issues
|
## Issues
|
||||||
|
|
||||||
* Segmentation fault on macOS with CV2 4.7.0: https://github.com/opencv/opencv/issues/23072
|
* Segmentation fault on macOS with CV2 4.7.0: https://github.com/opencv/opencv/issues/23072 (fixed)
|
||||||
* CV2 window does not show icons: https://github.com/opencv/opencv-python/issues/585
|
* CV2 window does not show icons: https://github.com/opencv/opencv-python/issues/585
|
||||||
|
|
||||||
## Problems and Troubleshooting
|
## Problems and Troubleshooting
|
||||||
@@ -754,6 +780,11 @@ FileNotFoundError: Could not find module 'libiconv.dll' (or one of its dependenc
|
|||||||
* [Android OTP Extractor](https://github.com/puddly/android-otp-extractor) can extract your tokens from popular Android OTP apps and export them in a standard format or just display them as QR codes for easy importing. [Requires a _rooted_ Android phone.]
|
* [Android OTP Extractor](https://github.com/puddly/android-otp-extractor) can extract your tokens from popular Android OTP apps and export them in a standard format or just display them as QR codes for easy importing. [Requires a _rooted_ Android phone.]
|
||||||
* [Google Authenticator secret extractor](https://github.com/krissrex/google-authenticator-exporter) is similar project written in JavaScript. It also extracts otp secrets from Google Authenticator.
|
* [Google Authenticator secret extractor](https://github.com/krissrex/google-authenticator-exporter) is similar project written in JavaScript. It also extracts otp secrets from Google Authenticator.
|
||||||
|
|
||||||
|
## Third party documentation
|
||||||
|
|
||||||
|
* [TOTP Secret Extraction from QR codes (medium.com)](https://cavalloj.medium.com/totp-secret-extraction-from-qr-codes-ee097b4c687f)
|
||||||
|
* [Google Authenticator: OTP-Secrets auslesen (stadt-bremerhaven.de)](https://stadt-bremerhaven.de/google-authenticator-otp-secrets-auslesen/)
|
||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
# #StandWithUkraine 🇺🇦
|
# #StandWithUkraine 🇺🇦
|
||||||
|
|||||||
733
build.sh
733
build.sh
@@ -77,10 +77,15 @@ interactive=false
|
|||||||
ignore_version_check=true
|
ignore_version_check=true
|
||||||
clean=false
|
clean=false
|
||||||
clean_flag=""
|
clean_flag=""
|
||||||
build_docker=true
|
build_base=true
|
||||||
build_exe=true
|
build_arm=false
|
||||||
|
build_x86_64=true
|
||||||
|
build_docker=false
|
||||||
|
build_local=true
|
||||||
|
build_exe=false
|
||||||
|
build_nuitka_exe=false
|
||||||
run_pipenv=true
|
run_pipenv=true
|
||||||
run_gui=true
|
run_gui=false
|
||||||
generate_result_files=false
|
generate_result_files=false
|
||||||
PYTHONHASHSEED=31
|
PYTHONHASHSEED=31
|
||||||
|
|
||||||
@@ -94,16 +99,21 @@ while test $# -gt 0; do
|
|||||||
echo "Options:"
|
echo "Options:"
|
||||||
echo "-i Interactive mode, all steps must be confirmed"
|
echo "-i Interactive mode, all steps must be confirmed"
|
||||||
echo "-C Ignore version check of protobuf/protoc"
|
echo "-C Ignore version check of protobuf/protoc"
|
||||||
echo "-E Do not build exe"
|
echo "-e Build exe"
|
||||||
echo "-D Do not build docker"
|
echo "-n Build nuitka exe"
|
||||||
|
echo "-L Do not run protoc and base build locally incl. exes"
|
||||||
|
echo "-d Build docker"
|
||||||
|
echo "-a Build arm"
|
||||||
|
echo "-X Do not build x86_64"
|
||||||
|
echo "-B Do not build base"
|
||||||
echo "-V Do not run pipenv"
|
echo "-V Do not run pipenv"
|
||||||
echo "-G Do not start extract_otp_secrets.py in GUI mode"
|
echo "-g Start extract_otp_secrets.py in GUI mode"
|
||||||
echo "-c Clean everything"
|
echo "-c Clean everything"
|
||||||
echo "-r Generate result files"
|
echo "-r Generate result files"
|
||||||
echo "-h, --help Show help and quit"
|
echo "-h, --help Show help and quit"
|
||||||
quit
|
quit
|
||||||
;;
|
;;
|
||||||
-a)
|
-i)
|
||||||
interactive=true
|
interactive=true
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
@@ -111,20 +121,41 @@ while test $# -gt 0; do
|
|||||||
ignore_version_check=false
|
ignore_version_check=false
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-E)
|
-B)
|
||||||
build_exe=false
|
build_base=false
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-D)
|
-L)
|
||||||
build_docker=false
|
build_local=false
|
||||||
|
build_base=false
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-a)
|
||||||
|
build_arm=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-X)
|
||||||
|
build_x86_64=false
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-e)
|
||||||
|
build_exe=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-n)
|
||||||
|
build_nuitka_exe=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-d)
|
||||||
|
build_docker=true
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-V)
|
-V)
|
||||||
run_pipenv=false
|
run_pipenv=false
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-G)
|
-g)
|
||||||
run_gui=false
|
run_gui=true
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-r)
|
-r)
|
||||||
@@ -186,326 +217,436 @@ if $clean; then
|
|||||||
cmd="sudo pipenv --rm || true"
|
cmd="sudo pipenv --rm || true"
|
||||||
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"
|
||||||
fi
|
|
||||||
|
|
||||||
cmd="$PIP install --use-pep517 -U -r requirements-dev.txt"
|
cmd="mkdir -p dist"
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
|
|
||||||
echo -e "\n\nChecking Protoc version..."
|
|
||||||
VERSION=$(curl -sL https://github.com/protocolbuffers/protobuf/releases/latest | grep -E "<title>" | perl -pe's%.*Protocol Buffers v(\d+\.\d+(\.\d+)?).*%\1%')
|
|
||||||
BASEVERSION=4
|
|
||||||
echo
|
|
||||||
|
|
||||||
OLDVERSION=$(cat $BIN/$DEST/.VERSION.txt || echo "")
|
|
||||||
echo -e "\nProtoc remote version $VERSION\n"
|
|
||||||
echo -e "Protoc local version: $OLDVERSION\n"
|
|
||||||
|
|
||||||
if [ "$OLDVERSION" != "$VERSION" ] || ! $ignore_version_check; then
|
|
||||||
echo "Upgrade protoc from $OLDVERSION to $VERSION"
|
|
||||||
|
|
||||||
NAME="protoc-$VERSION"
|
|
||||||
ARCHIVE="$NAME.zip"
|
|
||||||
|
|
||||||
mkdir -p $DOWNLOADS
|
|
||||||
# https://github.com/protocolbuffers/protobuf/releases/download/v21.6/protoc-21.6-linux-x86_64.zip
|
|
||||||
cmd="wget --trust-server-names https://github.com/protocolbuffers/protobuf/releases/download/v$VERSION/protoc-$VERSION-linux-x86_64.zip -O $DOWNLOADS/$ARCHIVE"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="echo -e '\nSize [Byte]'; stat --printf='%s\n' $DOWNLOADS/$ARCHIVE; echo -e '\nMD5'; md5sum $DOWNLOADS/$ARCHIVE; echo -e '\nSHA256'; sha256sum $DOWNLOADS/$ARCHIVE;"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="mkdir -p $BIN/$NAME; unzip $DOWNLOADS/$ARCHIVE -d $BIN/$NAME"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="echo $VERSION > $BIN/$NAME/.VERSION.txt; echo $VERSION > $BIN/$NAME/.VERSION_$VERSION.txt"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="[ -d $BIN/$DEST.old ] && rm -rf $BIN/$DEST.old || echo 'No old dir to delete'"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="[ -d $BIN/$DEST ] && mv -iT $BIN/$DEST $BIN/$DEST.old || echo 'No previous dir to keep'"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="mv -iT $BIN/$NAME $BIN/$DEST"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="rm $DOWNLOADS/$ARCHIVE"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="$BIN/$DEST/bin/protoc --plugin=protoc-gen-mypy=$HOME/.local/bin/protoc-gen-mypy --python_out=src/protobuf_generated_python --mypy_out=src/protobuf_generated_python --proto_path=src google_auth.proto"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
# Update README.md
|
|
||||||
|
|
||||||
cmd="perl -i -pe 's%proto(buf|c)([- ])(\d\.)?$OLDVERSION%proto\$1\$2\${3}$VERSION%g' README.md && perl -i -pe 's%(protobuf/releases/tag/v)$OLDVERSION%\${1}$VERSION%g' README.md"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
else
|
|
||||||
echo -e "\nVersion has not changed. Quit"
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
# Upgrade pip requirements
|
|
||||||
|
|
||||||
cmd="pip install -U pip"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
$PIP --version
|
|
||||||
|
|
||||||
cmd="$PIP install --use-pep517 -U -r requirements.txt"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
# Lint
|
|
||||||
|
|
||||||
LINT_OUT_FILE="tests/reports/flake8_results.txt"
|
|
||||||
cmd="$FLAKE8 . --count --select=E9,F63,F7,F82 --show-source --statistics | tee $LINT_OUT_FILE"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
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 | tee -a $LINT_OUT_FILE"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
# Type checking
|
|
||||||
|
|
||||||
TYPE_CHECK_OUT_FILE="tests/reports/mypy_results.txt"
|
|
||||||
cmd="$MYPY --install-types --non-interactive src/*.py tests/*.py"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
# change to src as python -m mypy adds the current dir Python sys.path
|
|
||||||
# execute in a subshell in order not to loose the exit code and not to change the dir in the currrent shell
|
|
||||||
cmd="$MYPY --strict src/*.py tests/*.py | tee $TYPE_CHECK_OUT_FILE"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
|
|
||||||
# Generate results files
|
|
||||||
|
|
||||||
if $generate_result_files; then
|
|
||||||
cmd="for color in '' '-n'; do for level in '' '-v' '-vv' '-vvv'; do $PYTHON src/extract_otp_secrets.py example_export.txt \$color \$level > tests/data/print_verbose_output\$color\$level.txt; done; done"
|
|
||||||
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"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# pip -e install
|
if $build_local; then
|
||||||
|
cmd="$PIP install --use-pep517 -U -r requirements-dev.txt"
|
||||||
cmd="$PIP install -U -e ."
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="extract_otp_secrets example_export.txt"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="extract_otp_secrets - < example_export.txt"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
# Test (needs module)
|
|
||||||
|
|
||||||
cmd="$PYTHON src/extract_otp_secrets.py example_export.txt"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="$PYTHON src/extract_otp_secrets.py - < example_export.txt"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
COVERAGE_OUT_FILE="tests/reports/pytest-coverage.txt"
|
|
||||||
cmd="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_FILE"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
# Pipenv
|
|
||||||
|
|
||||||
if $run_pipenv; then
|
|
||||||
cmd="$PIP install -U pipenv"
|
|
||||||
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"
|
||||||
|
|
||||||
$PIPENV --version
|
echo -e "\n\nChecking Protoc version..."
|
||||||
|
VERSION=$(curl -sL https://github.com/protocolbuffers/protobuf/releases/latest | grep -E "<title>" | perl -pe's%.*Protocol Buffers v(\d+\.\d+(\.\d+)?).*%\1%')
|
||||||
|
BASEVERSION=4
|
||||||
|
echo
|
||||||
|
|
||||||
cmd="$PIPENV update && $PIPENV --rm && $PIPENV install"
|
OLDVERSION=$(cat $BIN/$DEST/.VERSION.txt || echo "")
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
echo -e "\nProtoc remote version $VERSION\n"
|
||||||
eval "$cmd"
|
echo -e "Protoc local version: $OLDVERSION\n"
|
||||||
|
|
||||||
$PIPENV run python --version
|
if [ "$OLDVERSION" != "$VERSION" ] || ! $ignore_version_check; then
|
||||||
|
echo "Upgrade protoc from $OLDVERSION to $VERSION"
|
||||||
|
|
||||||
cmd="$PIPENV run pytest --cov=extract_otp_secrets_test tests/"
|
NAME="protoc-$VERSION"
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
ARCHIVE="$NAME.zip"
|
||||||
eval "$cmd"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Build wheel
|
mkdir -p $DOWNLOADS
|
||||||
|
# https://github.com/protocolbuffers/protobuf/releases/download/v21.6/protoc-21.6-linux-x86_64.zip
|
||||||
cmd="$PIP wheel ."
|
cmd="wget --trust-server-names https://github.com/protocolbuffers/protobuf/releases/download/v$VERSION/protoc-$VERSION-linux-x86_64.zip -O $DOWNLOADS/$ARCHIVE"
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
# Build executable
|
|
||||||
|
|
||||||
if $build_exe; then
|
|
||||||
cmd="LOCAL_GLIBC_VERSION=$(ldd --version | sed '1!d' | sed -E 's/.* ([[:digit:]]+\.[[:digit:]]+)$/\1/')"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
echo "local glibc: $LOCAL_GLIBC_VERSION"
|
|
||||||
|
|
||||||
cmd="pyinstaller -y --specpath installer --add-data $HOME/.local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile $clean_flag src/extract_otp_secrets.py"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="dist/extract_otp_secrets -h"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Generate README.md TOC
|
|
||||||
|
|
||||||
cmd="gfm-toc -s 2 -e 3 -t -o README.md > docs/README_TOC.md"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
# Update Code Coverage in README.md
|
|
||||||
|
|
||||||
# https://github.com/marketplace/actions/pytest-coverage-comment
|
|
||||||
# Coverage-95%25-yellowgreen
|
|
||||||
echo -e "Update code coverage in README.md"
|
|
||||||
TOTAL_COVERAGE=$(cat $COVERAGE_OUT_FILE | grep 'TOTAL' | perl -ne 'print "$&" if /\b(\d{1,3})%/') && perl -i -pe "s/coverage-(\d{1,3}%)25-/coverage-${TOTAL_COVERAGE}25-/" README.md
|
|
||||||
|
|
||||||
if $build_docker; then
|
|
||||||
# Build docker
|
|
||||||
|
|
||||||
# Build Dockerfile_only_txt (Alpine)
|
|
||||||
cmd="docker build . -t extract_otp_secrets_only_txt -t extract_otp_secrets:only-txt -t extract_otp_secrets:alpine -f docker/Dockerfile_only_txt --pull --build-arg RUN_TESTS=false"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="docker run --rm -v \"$(pwd)\":/files:ro extract_otp_secrets_only_txt example_export.txt"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="docker run --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets_only_txt - < example_export.txt"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="docker run --entrypoint /extract/run_pytest.sh --rm -v \"$(pwd)\":/files:ro extract_otp_secrets_only_txt extract_otp_secrets_test.py -k 'not qreader' -vvv --relaxed"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
# Build extract_otp_secrets (Debian Bullseye)
|
|
||||||
cmd="docker build . -t extract_otp_secrets -t extract_otp_secrets:bullseye --pull -f docker/Dockerfile --build-arg RUN_TESTS=false"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
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
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="cat example_export.txt | docker run --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets - -c - > example_output.csv"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="docker run --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets = < example_export.png"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
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
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
# Build extract_otp_secrets (Debian Buster)
|
|
||||||
cmd="docker build . -t extract_otp_secrets:buster --pull -f docker/Dockerfile --build-arg RUN_TESTS=false --build-arg BASE_IMAGE=python:3.11-slim-buster"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="docker run --rm -v \"$(pwd)\":/files:ro extract_otp_secrets:buster example_export.txt"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="cat example_export.txt | docker run --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets:buster - -c - > example_output.csv"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="docker run --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets:buster = < example_export.png"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="docker run --entrypoint /extract/run_pytest.sh --rm -v \"$(pwd)\":/files:ro extract_otp_secrets:buster"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
# Build executable from Docker latest
|
|
||||||
# sed "1!d" is workaround for head -n 1 since it head procduces exit code != 0
|
|
||||||
BULLSEYE_GLIBC_VERSION=$(docker run --entrypoint /bin/bash --rm extract_otp_secrets -c 'ldd --version | sed "1!d" | sed -E "s/.* ([[:digit:]]+\.[[:digit:]]+)$/\1/"')
|
|
||||||
echo "Bullseye glibc: $BULLSEYE_GLIBC_VERSION"
|
|
||||||
|
|
||||||
if $build_exe; then
|
|
||||||
cmd="docker run --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets -c 'apt-get update && apt-get -y install binutils && pip install -U pip && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller -y --specpath installer --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_linux_x86_64_bullseye --distpath /files/dist/ /files/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"
|
||||||
|
|
||||||
cmd="dist/extract_otp_secrets_linux_x86_64_bullseye -h"
|
cmd="echo -e '\nSize [Byte]'; stat --printf='%s\n' $DOWNLOADS/$ARCHIVE; echo -e '\nMD5'; md5sum $DOWNLOADS/$ARCHIVE; echo -e '\nSHA256'; sha256sum $DOWNLOADS/$ARCHIVE;"
|
||||||
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"
|
||||||
|
|
||||||
# Build executable from Docker buster
|
cmd="mkdir -p $BIN/$NAME; unzip $DOWNLOADS/$ARCHIVE -d $BIN/$NAME"
|
||||||
BUSTER_GLIBC_VERSION=$(docker run --entrypoint /bin/bash --rm extract_otp_secrets:buster -c 'ldd --version | sed "1!d" | sed -E "s/.* ([[:digit:]]+\.[[:digit:]]+)$/\1/"')
|
|
||||||
echo "Bullseye glibc: $BUSTER_GLIBC_VERSION"
|
|
||||||
|
|
||||||
cmd="docker run --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets:buster -c 'apt-get update && apt-get -y install binutils && pip install -U pip && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller -y --specpath installer --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_linux_x86_64 --distpath /files/dist/ /files/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"
|
||||||
|
|
||||||
cmd="dist/extract_otp_secrets_linux_x86_64 -h"
|
cmd="echo $VERSION > $BIN/$NAME/.VERSION.txt; echo $VERSION > $BIN/$NAME/.VERSION_$VERSION.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"
|
||||||
|
|
||||||
# create Windows win_file_version_info.txt
|
cmd="[ -d $BIN/$DEST.old ] && rm -rf $BIN/$DEST.old || echo 'No old dir to delete'"
|
||||||
cmd="VERSION_STR=$(setuptools-git-versioning) VERSION_MAJOR=$(cut -d '.' -f 1 <<< "$(setuptools-git-versioning)") VERSION_MINOR=$(cut -d '.' -f 2 <<< "$(setuptools-git-versioning)") VERSION_PATCH=$(cut -d '.' -f 3 <<< "$(setuptools-git-versioning)") VERSION_BUILD=$(($(git rev-list --count $(git tag | sort -V -r | sed '1!d')..HEAD))) COPYRIGHT_YEARS='2020-2023' envsubst < installer/win_file_version_info_template.txt > build/win_file_version_info.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"
|
||||||
|
|
||||||
# create macOS extract_otp_secrets_macos.spec from extract_otp_secrets_macos_template.spec
|
cmd="[ -d $BIN/$DEST ] && mv -iT $BIN/$DEST $BIN/$DEST.old || echo 'No previous dir to keep'"
|
||||||
cmd="VERSION_STR=$(setuptools-git-versioning) COPYRIGHT_YEARS='2020-2023' envsubst < installer/extract_otp_secrets_macos_template.spec > build/extract_otp_secrets_macos.spec"
|
|
||||||
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"
|
||||||
|
|
||||||
# build linux/arm64
|
cmd="mv -iT $BIN/$NAME $BIN/$DEST"
|
||||||
# Build extract_otp_secrets (Debian Buster)
|
|
||||||
cmd="docker buildx build --platform=linux/arm64 . -t extract_otp_secrets:buster --pull -f docker/Dockerfile --build-arg RUN_TESTS=false --build-arg BASE_IMAGE=python:3.11-slim-buster"
|
|
||||||
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 --pull always --rm --privileged multiarch/qemu-user-static --reset -p yes"
|
cmd="rm $DOWNLOADS/$ARCHIVE"
|
||||||
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 --platform linux/arm64 --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets:buster -c 'apt-get update && apt-get -y install binutils && pip install -U pip && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller --specpath installer -y --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_linux_arm64 --distpath /files/dist/ /files/src/extract_otp_secrets.py'"
|
cmd="$BIN/$DEST/bin/protoc --plugin=protoc-gen-mypy=$HOME/.local/bin/protoc-gen-mypy --python_out=src/protobuf_generated_python --mypy_out=src/protobuf_generated_python --proto_path=src google_auth.proto"
|
||||||
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="PLATFORM='linux/arm64' && EXE='dist/extract_otp_secrets_linux_arm64' && docker run --platform \"\$PLATFORM\" --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets:buster -c \"\$EXE -V && \$EXE -h && \$EXE example_export.png && \$EXE - < example_export.txt && \$EXE --qr ZBAR example_export.png && \$EXE --qr QREADER example_export.png && \$EXE --qr QREADER_DEEP example_export.png && \$EXE --qr CV2 example_export.png && \$EXE --qr CV2_WECHAT example_export.png\""
|
# Update README.md
|
||||||
|
|
||||||
|
cmd="perl -i -pe 's%proto(buf|c)([- ])(\d\.)?$OLDVERSION%proto\$1\$2\${3}$VERSION%g' README.md && perl -i -pe 's%(protobuf/releases/tag/v)$OLDVERSION%\${1}$VERSION%g' README.md"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
else
|
||||||
|
echo -e "\nVersion has not changed. Quit"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Upgrade pip requirements
|
||||||
|
|
||||||
|
cmd="pip install -U pip"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
$PIP --version
|
||||||
|
|
||||||
|
cmd="$PIP install --use-pep517 -U -r requirements.txt"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
if $build_base; then
|
||||||
|
# Lint
|
||||||
|
|
||||||
|
LINT_OUT_FILE="tests/reports/flake8_results.txt"
|
||||||
|
cmd="$FLAKE8 . --count --select=E9,F63,F7,F82 --show-source --statistics | tee $LINT_OUT_FILE"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
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 | tee -a $LINT_OUT_FILE"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
# Type checking
|
||||||
|
|
||||||
|
TYPE_CHECK_OUT_FILE="tests/reports/mypy_results.txt"
|
||||||
|
cmd="$MYPY --install-types --non-interactive src/*.py tests/*.py"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
# change to src as python -m mypy adds the current dir Python sys.path
|
||||||
|
# execute in a subshell in order not to loose the exit code and not to change the dir in the currrent shell
|
||||||
|
cmd="$MYPY --strict src/*.py tests/*.py | tee $TYPE_CHECK_OUT_FILE"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
|
||||||
|
# Generate results files
|
||||||
|
|
||||||
|
if $generate_result_files; then
|
||||||
|
cmd="for color in '' '-n'; do for level in '' '-v' '-vv' '-vvv'; do $PYTHON src/extract_otp_secrets.py example_export.txt \$color \$level > tests/data/print_verbose_output\$color\$level.txt; done; done"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# pip -e install
|
||||||
|
|
||||||
|
cmd="$PIP install -U -e ."
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="extract_otp_secrets example_export.txt"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="extract_otp_secrets - < example_export.txt"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
# Test (needs module)
|
||||||
|
|
||||||
|
cmd="$PYTHON src/extract_otp_secrets.py example_export.txt"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="$PYTHON src/extract_otp_secrets.py - < example_export.txt"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
COVERAGE_OUT_FILE="tests/reports/pytest-coverage.txt"
|
||||||
|
cmd="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_FILE"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
# Update Code Coverage in README.md
|
||||||
|
|
||||||
|
# https://github.com/marketplace/actions/pytest-coverage-comment
|
||||||
|
# Coverage-95%25-yellowgreen
|
||||||
|
echo -e "Update code coverage in README.md"
|
||||||
|
TOTAL_COVERAGE=$(cat $COVERAGE_OUT_FILE | grep 'TOTAL' | perl -ne 'print "$&" if /\b(\d{1,3})%/') && perl -i -pe "s/coverage-(\d{1,3}%)25-/coverage-${TOTAL_COVERAGE}25-/" README.md
|
||||||
|
|
||||||
|
# Pipenv
|
||||||
|
|
||||||
|
if $run_pipenv; then
|
||||||
|
cmd="$PIP install -U pipenv"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
$PIPENV --version
|
||||||
|
|
||||||
|
cmd="$PIPENV --rm && $PIPENV update && $PIPENV install"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
$PIPENV run python --version
|
||||||
|
|
||||||
|
cmd="$PIPENV run pytest --cov=extract_otp_secrets_test tests/"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build wheel
|
||||||
|
|
||||||
|
cmd="$PIP wheel ."
|
||||||
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"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Build executable
|
||||||
|
if $build_exe; then
|
||||||
|
cmd="LOCAL_GLIBC_VERSION=$(ldd --version | sed '1!d' | sed -E 's/.* ([[:digit:]]+\.[[:digit:]]+)$/\1/')"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
echo "local glibc: $LOCAL_GLIBC_VERSION"
|
||||||
|
|
||||||
|
cmd="time pyinstaller -y --specpath installer --add-data $HOME/.local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile $clean_flag src/extract_otp_secrets.py"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="dist/extract_otp_secrets -h"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="dist/extract_otp_secrets --qr ZBAR example_export.png"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build compiled executable
|
||||||
|
|
||||||
|
if $build_nuitka_exe; then
|
||||||
|
cmd="LOCAL_GLIBC_VERSION=$(ldd --version | sed '1!d' | sed -E 's/.* ([[:digit:]]+\.[[:digit:]]+)$/\1/')"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
echo "local glibc: $LOCAL_GLIBC_VERSION"
|
||||||
|
|
||||||
|
cmd="$PIP install -U pyqt5"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="time $PYTHON -m nuitka --enable-plugin=tk-inter --enable-plugin=pyqt5 --noinclude-default-mode=nofollow --clang --include-data-dir=$HOME/.local/__yolo_v3_qr_detector/=__yolo_v3_qr_detector/ --onefile --output-dir=build/nuitka --output-filename=extract_otp_secrets_compiled src/extract_otp_secrets.py"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="cp build/nuitka/extract_otp_secrets_compiled dist/"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="dist/extract_otp_secrets_compiled -h"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="dist/extract_otp_secrets_compiled --qr ZBAR example_export.png"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate README.md TOC
|
||||||
|
|
||||||
|
cmd="gfm-toc -s 2 -e 3 -t -o README.md > docs/README_TOC.md"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
# create Windows win_file_version_info.txt
|
||||||
|
cmd="VERSION_STR=$(setuptools-git-versioning) VERSION_MAJOR=$(cut -d '.' -f 1 <<< "$(setuptools-git-versioning)") VERSION_MINOR=$(cut -d '.' -f 2 <<< "$(setuptools-git-versioning)") VERSION_PATCH=$(cut -d '.' -f 3 <<< "$(setuptools-git-versioning)") VERSION_BUILD=$(($(git rev-list --count $(git tag | sort -V -r | sed '1!d')..HEAD))) COPYRIGHT_YEARS='2020-2023' envsubst < installer/win_file_version_info_template.txt > build/win_file_version_info.txt"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
# create macOS extract_otp_secrets_macos.spec from extract_otp_secrets_macos_template.spec
|
||||||
|
cmd="VERSION_STR=$(setuptools-git-versioning) COPYRIGHT_YEARS='2020-2023' envsubst < installer/extract_otp_secrets_macos_template.spec > build/extract_otp_secrets_macos.spec"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $build_docker; then
|
||||||
|
# Build docker
|
||||||
|
|
||||||
|
if $build_x86_64; then
|
||||||
|
# Build Dockerfile_only_txt (Alpine)
|
||||||
|
cmd="docker build . -t extract_otp_secrets_only_txt -t extract_otp_secrets:only-txt -t extract_otp_secrets:alpine -f docker/Dockerfile_only_txt --pull --build-arg RUN_TESTS=false"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="docker run --network none --rm -v \"$(pwd)\":/files:ro extract_otp_secrets_only_txt example_export.txt"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="docker run --network none --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets_only_txt - < example_export.txt"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="docker run --entrypoint /extract/run_pytest.sh --rm -v \"$(pwd)\":/files:ro extract_otp_secrets_only_txt extract_otp_secrets_test.py -k 'not qreader' -vvv --relaxed"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
# Build extract_otp_secrets (Debian Bookworm)
|
||||||
|
cmd="docker build . -t extract_otp_secrets -t extract_otp_secrets:bookworm --pull -f docker/Dockerfile --build-arg RUN_TESTS=false"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="docker run --network none --rm -v \"$(pwd)\":/files:ro extract_otp_secrets example_export.txt"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="cat example_export.txt | docker run --network none --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets - -c - > example_output.csv"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="docker run --network none --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets = < example_export.png"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
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
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
# Build extract_otp_secrets (Debian Bullseye)
|
||||||
|
cmd="docker build . -t extract_otp_secrets:bullseye -t extract_otp_secrets:bullseye-x86_64 --pull -f docker/Dockerfile --build-arg RUN_TESTS=false --build-arg BASE_IMAGE=python:3.12-slim-bullseye"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="docker run --network none --rm -v \"$(pwd)\":/files:ro extract_otp_secrets:bullseye example_export.txt"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="cat example_export.txt | docker run --network none --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets:bullseye - -c - > example_output.csv"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="docker run --network none --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets:bullseye = < example_export.png"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="docker run --entrypoint /extract/run_pytest.sh --rm -v \"$(pwd)\":/files:ro extract_otp_secrets:bullseye"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
# Build executable from Docker latest
|
||||||
|
# sed "1!d" is workaround for head -n 1 since it head procduces exit code != 0
|
||||||
|
BOOKWORM_GLIBC_VERSION=$(docker run --network none --entrypoint /bin/bash --rm extract_otp_secrets -c 'ldd --version | sed "1!d" | sed -E "s/.* ([[:digit:]]+\.[[:digit:]]+)$/\1/"')
|
||||||
|
echo "Bookworm glibc: $BOOKWORM_GLIBC_VERSION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $build_arm; then
|
||||||
|
# build linux/arm64
|
||||||
|
cmd="docker run --network none --pull always --rm --privileged multiarch/qemu-user-static --reset -p yes"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
# Build extract_otp_secrets (Debian Bullseye)
|
||||||
|
cmd="docker buildx build --platform=linux/arm64 . -t extract_otp_secrets:bullseye-arm64 --pull -f docker/Dockerfile --build-arg RUN_TESTS=false --build-arg BASE_IMAGE=python:3.12-slim-bullseye"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $build_exe; then
|
||||||
|
if $build_x86_64; then
|
||||||
|
cmd="docker run --platform linux/amd64 --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets -c 'apt-get update && apt-get -y install binutils && pip install -U pip && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller -y --specpath installer --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_linux_x86_64_bookworm --distpath /files/dist/ /files/src/extract_otp_secrets.py'"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="dist/extract_otp_secrets_linux_x86_64_bookworm -h || echo 'Could not run exe; see error message above'"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="dist/extract_otp_secrets_linux_x86_64_bookworm --qr ZBAR example_export.png || echo 'Could not run exe; see error message above'"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
# Build executable from Docker bullseye
|
||||||
|
BULLSEYE_GLIBC_VERSION=$(docker run --network none --entrypoint /bin/bash --rm extract_otp_secrets:bullseye -c 'ldd --version | sed "1!d" | sed -E "s/.* ([[:digit:]]+\.[[:digit:]]+)$/\1/"')
|
||||||
|
echo "Bullseye glibc: $BULLSEYE_GLIBC_VERSION"
|
||||||
|
|
||||||
|
cmd="docker run --platform linux/amd64 --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets:bullseye -c 'apt-get update && apt-get -y install binutils && pip install -U pip && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller -y --specpath installer --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_linux_x86_64 --distpath /files/dist/ /files/src/extract_otp_secrets.py'"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="dist/extract_otp_secrets_linux_x86_64 -h"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="dist/extract_otp_secrets_linux_x86_64 --qr ZBAR example_export.png"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $build_arm; then
|
||||||
|
# build linux/arm64
|
||||||
|
cmd="docker run --platform linux/arm64 --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets:bullseye-arm64 -c 'apt-get update && apt-get -y install binutils && pip install -U pip && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller --specpath installer -y --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_linux_arm64 --distpath /files/dist/ /files/src/extract_otp_secrets.py'"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="PLATFORM='linux/arm64' && EXE='dist/extract_otp_secrets_linux_arm64' && docker run --platform \"\$PLATFORM\" --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets:bullseye-arm64 -c \"\$EXE -V && \$EXE -h && \$EXE example_export.png && \$EXE - < example_export.txt && \$EXE --qr ZBAR example_export.png && \$EXE --qr QREADER example_export.png && \$EXE --qr QREADER_DEEP example_export.png && \$EXE --qr CV2 example_export.png && \$EXE --qr CV2_WECHAT example_export.png\""
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $build_nuitka_exe; then
|
||||||
|
if $build_x86_64; then
|
||||||
|
cmd="docker run --platform linux/amd64 --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets -c 'apt-get update && apt-get -y install binutils build-essential patchelf && pip install -U pip && pip install -U -r /files/requirements.txt && pip install nuitka pyqt5 && PYTHONHASHSEED=31 && python -m nuitka --enable-plugin=tk-inter --enable-plugin=pyqt5 --include-data-dir=/usr/local/__yolo_v3_qr_detector/=__yolo_v3_qr_detector/ --onefile --output-dir=/files/build/docker/nuitka --output-filename=extract_otp_secrets_linux_x86_64_bookworm_compiled /files/src/extract_otp_secrets.py'"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="build/docker/nuitka/extract_otp_secrets_linux_x86_64_bookworm_compiled -h"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="build/docker/nuitka/extract_otp_secrets_linux_x86_64_bookworm_compiled --qr ZBAR example_export.png"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="cp build/docker/nuitka/extract_otp_secrets_linux_x86_64_bookworm_compiled dist/"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
# Build executable from Docker bullseye
|
||||||
|
BULLSEYE_GLIBC_VERSION=$(docker run --network none --entrypoint /bin/bash --rm extract_otp_secrets:bullseye -c 'ldd --version | sed "1!d" | sed -E "s/.* ([[:digit:]]+\.[[:digit:]]+)$/\1/"')
|
||||||
|
echo "Bookworm glibc: $BULLSEYE_GLIBC_VERSION"
|
||||||
|
|
||||||
|
cmd="docker run --platform linux/amd64 --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets:bullseye -c 'apt-get update && apt-get -y install binutils build-essential patchelf && pip install -U pip && pip install -U -r /files/requirements.txt && pip install nuitka pyqt5 && PYTHONHASHSEED=31 && python -m nuitka --enable-plugin=tk-inter --enable-plugin=pyqt5 --include-data-dir=/usr/local/__yolo_v3_qr_detector/=__yolo_v3_qr_detector/ --onefile --output-dir=/files/build/docker/nuitka --output-filename=extract_otp_secrets_linux_x86_64_bullseye_compiled /files/src/extract_otp_secrets.py'"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="build/docker/nuitka/extract_otp_secrets_linux_x86_64_bullseye_compiled -h"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="build/docker/nuitka/extract_otp_secrets_linux_x86_64_bullseye_compiled --qr ZBAR example_export.png"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="cp build/docker/nuitka/extract_otp_secrets_linux_x86_64_bullseye_compiled dist/"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $build_arm; then
|
||||||
|
# build linux/arm64
|
||||||
|
cmd="docker run --platform linux/arm64 --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets:bullseye-arm64 -c 'apt-get update && apt-get -y install binutils build-essential patchelf qt5-default && pip install -U pip && pip install -U -r /files/requirements.txt && pip install nuitka pyqt5 && PYTHONHASHSEED=31 && python -m nuitka --enable-plugin=tk-inter --enable-plugin=pyqt5 --include-data-dir=/usr/local/__yolo_v3_qr_detector/=__yolo_v3_qr_detector/ --onefile --output-dir=/files/build/docker/nuitka --output-filename=extract_otp_secrets_linux_arm64_compiled /files/src/extract_otp_secrets.py'"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="PLATFORM='linux/arm64' && EXE='build/docker/nuitka/extract_otp_secrets_linux_arm64_compiled' && docker run --platform \"\$PLATFORM\" --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets:bullseye-arm64 -c \"\$EXE -V && \$EXE -h && \$EXE example_export.png && \$EXE - < example_export.txt && \$EXE --qr ZBAR example_export.png && \$EXE --qr QREADER example_export.png && \$EXE --qr QREADER_DEEP example_export.png && \$EXE --qr CV2 example_export.png && \$EXE --qr CV2_WECHAT example_export.png\""
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="cp build/docker/nuitka/extract_otp_secrets_linux_arm64_compiled dist/"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Run GUI from Docker
|
# Run GUI from Docker
|
||||||
if $run_gui; then
|
if $build_x86_64 && $run_gui; then
|
||||||
cmd="docker run --rm -v "$(pwd)":/files:ro --device=\"/dev/video0:/dev/video0\" --env=\"DISPLAY\" -v /tmp/.X11-unix:/tmp/.X11-unix:ro extract_otp_secrets &"
|
cmd="docker run --network none --rm -v "$(pwd)":/files:ro --device=\"/dev/video0:/dev/video0\" --env=\"DISPLAY\" -v /tmp/.X11-unix:/tmp/.X11-unix: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"
|
||||||
fi
|
fi
|
||||||
@@ -517,14 +658,18 @@ if $run_gui; then
|
|||||||
eval "$cmd"
|
eval "$cmd"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
line=$(printf '#%.0s' $(eval echo {1..$(( ($COLUMNS - 10) / 2))}))
|
if $build_base; then
|
||||||
echo -e "\n${blueBold}$line RESULTS $line${reset}"
|
line=$(printf '#%.0s' $(eval echo {1..$(( ($COLUMNS - 10) / 2))}))
|
||||||
|
echo -e "\n${blueBold}$line RESULTS $line${reset}"
|
||||||
|
|
||||||
cmd="cat $TYPE_CHECK_OUT_FILE $LINT_OUT_FILE $COVERAGE_OUT_FILE"
|
cmd="cat $TYPE_CHECK_OUT_FILE $LINT_OUT_FILE $COVERAGE_OUT_FILE"
|
||||||
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"
|
||||||
|
fi
|
||||||
|
|
||||||
line=$(printf '#%.0s' $(eval echo {1..$(( ($COLUMNS - 10) / 2))}))
|
line=$(printf '#%.0s' $(eval echo {1..$(( ($COLUMNS - 10) / 2))}))
|
||||||
echo -e "\n${greenBold}$line SUCCESS $line${reset}"
|
echo -e "\n${greenBold}$line SUCCESS $line${reset}"
|
||||||
|
|
||||||
|
git status
|
||||||
|
|
||||||
quit
|
quit
|
||||||
|
|||||||
122
devenv.lock
Normal file
122
devenv.lock
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"devenv": {
|
||||||
|
"locked": {
|
||||||
|
"dir": "src/modules",
|
||||||
|
"lastModified": 1726050924,
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv",
|
||||||
|
"rev": "cf471f691c3765ed431199f61b8bd70530bc4305",
|
||||||
|
"treeHash": "04a5d566820f8fb1955c7c49ccbd3ff95d9129d7",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"dir": "src/modules",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-compat": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1696426674,
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||||
|
"treeHash": "2addb7b71a20a25ea74feeaf5c2f6a6b30898ecb",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gitignore": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"pre-commit-hooks",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1709087332,
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||||
|
"treeHash": "ca14199cabdfe1a06a7b1654c76ed49100a689f9",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1716977621,
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv-nixpkgs",
|
||||||
|
"rev": "4267e705586473d3e5c8d50299e71503f16a6fb6",
|
||||||
|
"treeHash": "6d9f1f7ca0faf1bc2eeb397c78a49623260d3412",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"ref": "rolling",
|
||||||
|
"repo": "devenv-nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-stable": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1725826545,
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "f4c846aee8e1e29062aa8514d5e0ab270f4ec2f9",
|
||||||
|
"treeHash": "8fc49deaed3f2728a7147c38163cc468a117570a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-24.05",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pre-commit-hooks": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"gitignore": "gitignore",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1725513492,
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "pre-commit-hooks.nix",
|
||||||
|
"rev": "7570de7b9b504cfe92025dd1be797bf546f66528",
|
||||||
|
"treeHash": "4b46d77870afecd8f642541cb4f4927326343b59",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "pre-commit-hooks.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"devenv": "devenv",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"pre-commit-hooks": "pre-commit-hooks"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
24
devenv.nix
Normal file
24
devenv.nix
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
inputs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
{
|
||||||
|
cachix.enable = false;
|
||||||
|
|
||||||
|
languages.python = {
|
||||||
|
enable = true;
|
||||||
|
venv = {
|
||||||
|
enable = true;
|
||||||
|
requirements = ./requirements.txt;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
packages = [
|
||||||
|
pkgs.git
|
||||||
|
pkgs.zbar
|
||||||
|
];
|
||||||
|
}
|
||||||
14
devenv.yaml
Normal file
14
devenv.yaml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
inputs:
|
||||||
|
nixpkgs:
|
||||||
|
url: github:cachix/devenv-nixpkgs/rolling
|
||||||
|
|
||||||
|
# If you're using non-OSS software, you can set allowUnfree to true.
|
||||||
|
# allowUnfree: true
|
||||||
|
|
||||||
|
# If you're willing to use a package that's vulnerable
|
||||||
|
# permittedInsecurePackages:
|
||||||
|
# - "openssl-1.1.1w"
|
||||||
|
|
||||||
|
# If you have more than one devenv you can merge them
|
||||||
|
#imports:
|
||||||
|
# - ./backend
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# --build-arg BASE_IMAGE=python:3.11-slim-buster
|
# --build-arg BASE_IMAGE=python:3.11-slim-buster
|
||||||
ARG BASE_IMAGE=python:3.11-slim-bullseye
|
ARG BASE_IMAGE=python:3.13-slim-bookworm
|
||||||
FROM $BASE_IMAGE
|
FROM $BASE_IMAGE
|
||||||
|
|
||||||
# https://docs.docker.com/engine/reference/builder/
|
# https://docs.docker.com/engine/reference/builder/
|
||||||
@@ -16,14 +16,15 @@ COPY requirements*.txt src/ run_pytest.sh pytest.ini tests/ example_*.txt exampl
|
|||||||
|
|
||||||
ARG RUN_TESTS=true
|
ARG RUN_TESTS=true
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN uname -a \
|
||||||
|
&& apt-get update && apt-get install -y --no-install-recommends \
|
||||||
libgl1 \
|
libgl1 \
|
||||||
libglib2.0-0 \
|
libglib2.0-0 \
|
||||||
libsm6 \
|
libsm6 \
|
||||||
libzbar0 \
|
libzbar0 \
|
||||||
python3-tk \
|
python3-tk \
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
&& pip install --no-cache-dir -U -r requirements.txt \
|
&& pip install --no-cache-dir -U pip -r requirements.txt \
|
||||||
&& if [ "$RUN_TESTS" = "true" ]; then /extract/run_pytest.sh; else echo "Not running tests..."; fi \
|
&& if [ "$RUN_TESTS" = "true" ]; then /extract/run_pytest.sh; else echo "Not running tests..."; fi \
|
||||||
&& echo 'test -s /extract/.alias && . /extract/.alias || true' >> ~/.bashrc
|
&& echo 'test -s /extract/.alias && . /extract/.alias || true' >> ~/.bashrc
|
||||||
|
|
||||||
@@ -31,6 +32,6 @@ WORKDIR /files
|
|||||||
|
|
||||||
ENTRYPOINT ["python", "/extract/extract_otp_secrets.py"]
|
ENTRYPOINT ["python", "/extract/extract_otp_secrets.py"]
|
||||||
|
|
||||||
LABEL org.opencontainers.image.source https://github.com/scito/extract_otp_secrets
|
LABEL org.opencontainers.image.source=https://github.com/scito/extract_otp_secrets
|
||||||
LABEL org.opencontainers.image.license GPL-3.0+
|
LABEL org.opencontainers.image.license=GPL-3.0+
|
||||||
LABEL maintainer="Scito https://scito.ch, https://github.com/scito"
|
LABEL maintainer="Scito https://scito.ch, https://github.com/scito"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
ARG BASE_IMAGE=python:3.11-alpine
|
ARG BASE_IMAGE=python:3.13-alpine
|
||||||
FROM $BASE_IMAGE
|
FROM $BASE_IMAGE
|
||||||
|
|
||||||
# https://docs.docker.com/engine/reference/builder/
|
# https://docs.docker.com/engine/reference/builder/
|
||||||
@@ -17,7 +17,8 @@ COPY requirements*.txt src/ run_pytest.sh pytest.ini tests/ example_*.txt exampl
|
|||||||
|
|
||||||
ARG RUN_TESTS=true
|
ARG RUN_TESTS=true
|
||||||
|
|
||||||
RUN apk add --no-cache \
|
RUN uname -a \
|
||||||
|
&& apk add --no-cache \
|
||||||
jpeg \
|
jpeg \
|
||||||
zlib \
|
zlib \
|
||||||
&& echo "Arch: $(apk --print-arch)" \
|
&& echo "Arch: $(apk --print-arch)" \
|
||||||
@@ -30,6 +31,7 @@ RUN apk add --no-cache \
|
|||||||
zlib-dev \
|
zlib-dev \
|
||||||
; fi \
|
; fi \
|
||||||
&& pip install --no-cache-dir -U \
|
&& pip install --no-cache-dir -U \
|
||||||
|
pip \
|
||||||
colorama \
|
colorama \
|
||||||
Pillow \
|
Pillow \
|
||||||
protobuf \
|
protobuf \
|
||||||
@@ -42,6 +44,6 @@ WORKDIR /files
|
|||||||
|
|
||||||
ENTRYPOINT ["python", "/extract/extract_otp_secrets.py"]
|
ENTRYPOINT ["python", "/extract/extract_otp_secrets.py"]
|
||||||
|
|
||||||
LABEL org.opencontainers.image.source https://github.com/scito/extract_otp_secrets
|
LABEL org.opencontainers.image.source=https://github.com/scito/extract_otp_secrets
|
||||||
LABEL org.opencontainers.image.license GPL-3.0+
|
LABEL org.opencontainers.image.license=GPL-3.0+
|
||||||
LABEL maintainer="Scito https://scito.ch, https://github.com/scito"
|
LABEL maintainer="Scito https://scito.ch, https://github.com/scito"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ Generate from file: README.md
|
|||||||
- [Installation of optional shared system libraries (recommended)](#installation-of-optional-shared-system-libraries-recommended)
|
- [Installation of optional shared system libraries (recommended)](#installation-of-optional-shared-system-libraries-recommended)
|
||||||
- [Program help: arguments and options](#program-help-arguments-and-options)
|
- [Program help: arguments and options](#program-help-arguments-and-options)
|
||||||
- [Examples](#examples)
|
- [Examples](#examples)
|
||||||
- [Printing otp secrets form text file](#printing-otp-secrets-form-text-file)
|
- [Printing otp secrets from text file](#printing-otp-secrets-from-text-file)
|
||||||
- [Printing otp secrets from image file](#printing-otp-secrets-from-image-file)
|
- [Printing otp secrets from image file](#printing-otp-secrets-from-image-file)
|
||||||
- [Writing otp secrets to csv file](#writing-otp-secrets-to-csv-file)
|
- [Writing otp secrets to csv file](#writing-otp-secrets-to-csv-file)
|
||||||
- [Writing otp secrets to json file](#writing-otp-secrets-to-json-file)
|
- [Writing otp secrets to json file](#writing-otp-secrets-to-json-file)
|
||||||
@@ -33,7 +33,8 @@ Generate from file: README.md
|
|||||||
- [pipenv](#pipenv)
|
- [pipenv](#pipenv)
|
||||||
- [Visual Studio Code Remote - Containers / VSCode devcontainer](#visual-studio-code-remote---containers--vscode-devcontainer)
|
- [Visual Studio Code Remote - Containers / VSCode devcontainer](#visual-studio-code-remote---containers--vscode-devcontainer)
|
||||||
- [venv](#venv)
|
- [venv](#venv)
|
||||||
- [devbox](#devbox)
|
- [devbox 1](#devbox-1)
|
||||||
|
- [devbox 2](#devbox-2)
|
||||||
- [docker](#docker)
|
- [docker](#docker)
|
||||||
- [More docker examples](#more-docker-examples)
|
- [More docker examples](#more-docker-examples)
|
||||||
- [Tests](#tests)
|
- [Tests](#tests)
|
||||||
@@ -52,5 +53,6 @@ Generate from file: README.md
|
|||||||
- [Problems and Troubleshooting](#problems-and-troubleshooting)
|
- [Problems and Troubleshooting](#problems-and-troubleshooting)
|
||||||
- [Windows error message](#windows-error-message)
|
- [Windows error message](#windows-error-message)
|
||||||
- [Related projects](#related-projects)
|
- [Related projects](#related-projects)
|
||||||
|
- [Third party documentation](#third-party-documentation)
|
||||||
|
|
||||||
Table of contents generated.
|
Table of contents generated.
|
||||||
|
|||||||
3
mypy.ini
3
mypy.ini
@@ -1,4 +1 @@
|
|||||||
[mypy]
|
[mypy]
|
||||||
|
|
||||||
[mypy-protobuf_generated_python.*]
|
|
||||||
ignore_errors = True
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
[build-system]
|
[build-system]
|
||||||
requires = [
|
requires = [
|
||||||
"setuptools>=64.0.0", "wheel>=0.37.0", "pip",
|
"pip",
|
||||||
|
"nuitka",
|
||||||
# https://setuptools-git-versioning.readthedocs.io/en/latest/differences.html
|
# https://setuptools-git-versioning.readthedocs.io/en/latest/differences.html
|
||||||
|
"setuptools>=64.0.0",
|
||||||
"setuptools-git-versioning",
|
"setuptools-git-versioning",
|
||||||
|
"wheel>=0.37.0",
|
||||||
]
|
]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
@@ -18,11 +21,11 @@ classifiers = [
|
|||||||
"Topic :: Utilities",
|
"Topic :: Utilities",
|
||||||
"Topic :: Security",
|
"Topic :: Security",
|
||||||
"Topic :: Multimedia :: Graphics :: Capture :: Digital Camera",
|
"Topic :: Multimedia :: Graphics :: Capture :: Digital Camera",
|
||||||
"Programming Language :: Python :: 3.7",
|
|
||||||
"Programming Language :: Python :: 3.8",
|
|
||||||
"Programming Language :: Python :: 3.9",
|
"Programming Language :: Python :: 3.9",
|
||||||
"Programming Language :: Python :: 3.10",
|
"Programming Language :: Python :: 3.10",
|
||||||
"Programming Language :: Python :: 3.11",
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Programming Language :: Python :: 3.12",
|
||||||
|
"Programming Language :: Python :: 3.13",
|
||||||
"Intended Audience :: End Users/Desktop",
|
"Intended Audience :: End Users/Desktop",
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
"Intended Audience :: System Administrators",
|
"Intended Audience :: System Administrators",
|
||||||
@@ -37,16 +40,12 @@ classifiers = [
|
|||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"colorama>=0.4.6",
|
"colorama>=0.4.6",
|
||||||
"opencv-contrib-python; sys_platform != 'darwin'",
|
"opencv-contrib-python",
|
||||||
"opencv-contrib-python<=4.7.0; sys_platform == 'darwin'",
|
|
||||||
"Pillow",
|
"Pillow",
|
||||||
"protobuf",
|
"protobuf",
|
||||||
"pyzbar",
|
"pyzbar",
|
||||||
"qrcode",
|
"qrcode",
|
||||||
"qreader<2.0.0",
|
"qreader<2.0.0",
|
||||||
# workaround for PYTHON <= 3.7: compatibility
|
|
||||||
"typing_extensions; python_version<='3.7'",
|
|
||||||
"importlib_metadata; python_version<='3.7'",
|
|
||||||
]
|
]
|
||||||
description = "Extracts one time password (OTP) secrets from QR codes exported by two-factor authentication (2FA) apps such as 'Google Authenticator'"
|
description = "Extracts one time password (OTP) secrets from QR codes exported by two-factor authentication (2FA) apps such as 'Google Authenticator'"
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
@@ -55,7 +54,7 @@ license = {text = "GNU General Public License v3 (GPLv3)"}
|
|||||||
readme = "README.md"
|
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.9, <4"
|
||||||
scripts = {extract_otp_secrets = "extract_otp_secrets:sys_main"}
|
scripts = {extract_otp_secrets = "extract_otp_secrets:sys_main"}
|
||||||
urls = {Project-URL = "https://github.com/scito/extract_otp_secrets", Bug-Reports = "https://github.com/scito/extract_otp_secrets/issues", Source = "https://github.com/scito/extract_otp_secrets"}
|
urls = {Project-URL = "https://github.com/scito/extract_otp_secrets", Bug-Reports = "https://github.com/scito/extract_otp_secrets/issues", Source = "https://github.com/scito/extract_otp_secrets"}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ flake8
|
|||||||
gfm-toc
|
gfm-toc
|
||||||
mypy
|
mypy
|
||||||
mypy-protobuf
|
mypy-protobuf
|
||||||
|
nuitka
|
||||||
pyinstaller
|
pyinstaller
|
||||||
pylint
|
pylint
|
||||||
pytest
|
pytest
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
colorama>=0.4.6
|
colorama>=0.4.6
|
||||||
importlib_metadata; python_version<='3.7'
|
opencv-contrib-python
|
||||||
opencv-contrib-python; sys_platform != 'darwin'
|
|
||||||
opencv-contrib-python<=4.7.0; sys_platform == 'darwin'
|
|
||||||
Pillow
|
Pillow
|
||||||
protobuf
|
protobuf
|
||||||
pyzbar
|
pyzbar
|
||||||
qrcode
|
qrcode
|
||||||
qreader<2.0.0
|
qreader<2.0.0
|
||||||
typing_extensions; python_version<='3.7'
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name = extract_otp_secrets
|
name = extract_otp_secrets
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
python_requires = >=3.7, <4
|
python_requires = >=3.9, <4
|
||||||
py_modules = extract_otp_secrets, protobuf_generated_python.google_auth_pb2
|
py_modules = extract_otp_secrets, protobuf_generated_python.google_auth_pb2
|
||||||
package_dir =
|
package_dir =
|
||||||
=src
|
=src
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import argparse
|
|||||||
import base64
|
import base64
|
||||||
import csv
|
import csv
|
||||||
import fileinput
|
import fileinput
|
||||||
|
import glob
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
@@ -43,35 +44,24 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
import urllib.parse as urlparse
|
import urllib.parse as urlparse
|
||||||
from enum import Enum, IntEnum
|
from enum import Enum, IntEnum
|
||||||
from typing import Any, List, Optional, Sequence, TextIO, Tuple, Union, TYPE_CHECKING
|
from importlib.metadata import PackageNotFoundError, version
|
||||||
|
from typing import (Any, Final, List, Optional, Sequence, TextIO, Tuple,
|
||||||
|
TypedDict, Union)
|
||||||
|
|
||||||
import colorama
|
import colorama
|
||||||
from pkg_resources import DistributionNotFound, get_distribution
|
from qrcode import QRCode
|
||||||
from qrcode import QRCode # type: ignore
|
|
||||||
|
|
||||||
import protobuf_generated_python.google_auth_pb2 as pb
|
import protobuf_generated_python.google_auth_pb2 as pb
|
||||||
|
|
||||||
# workaround for PYTHON <= 3.7: compatibility
|
|
||||||
if sys.version_info >= (3, 8):
|
|
||||||
from typing import Final, TypedDict
|
|
||||||
else:
|
|
||||||
from typing_extensions import Final, TypedDict
|
|
||||||
|
|
||||||
# workaround for PYTHON <= 3.7: compatibility
|
|
||||||
if sys.version_info >= (3, 8):
|
|
||||||
from importlib.metadata import PackageNotFoundError, version
|
|
||||||
else:
|
|
||||||
from importlib_metadata import PackageNotFoundError, version
|
|
||||||
|
|
||||||
|
|
||||||
debug_mode = '-d' in sys.argv[1:] or '--debug' in sys.argv[1:]
|
debug_mode = '-d' in sys.argv[1:] or '--debug' in sys.argv[1:]
|
||||||
quiet = '-q' in sys.argv[1:] or '--quiet' in sys.argv[1:]
|
quiet = '-q' in sys.argv[1:] or '--quiet' in sys.argv[1:]
|
||||||
headless: bool = False
|
headless: bool = False
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import cv2 # type: ignore # TODO use cv2 types if available
|
import cv2
|
||||||
import numpy as np # TODO use numpy types if available
|
import numpy as np
|
||||||
|
import cv2.typing
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import tkinter
|
import tkinter
|
||||||
@@ -104,9 +94,8 @@ Exception: {e}\n""", file=sys.stderr)
|
|||||||
FONT_SCALE: Final[float] = 1.3
|
FONT_SCALE: Final[float] = 1.3
|
||||||
FONT_THICKNESS: Final[int] = 1
|
FONT_THICKNESS: Final[int] = 1
|
||||||
FONT_LINE_STYLE: Final[int] = cv2.LINE_AA
|
FONT_LINE_STYLE: Final[int] = cv2.LINE_AA
|
||||||
FONT_COLOR: Final[ColorBGR] = (255, 0, 0)
|
FONT_COLOR: Final[ColorBGR] = 255, 0, 0
|
||||||
BOX_THICKNESS: Final[int] = 5
|
BOX_THICKNESS: Final[int] = 5
|
||||||
# workaround for PYTHON <= 3.7: must use () for assignments
|
|
||||||
WINDOW_X: Final[int] = 0
|
WINDOW_X: Final[int] = 0
|
||||||
WINDOW_Y: Final[int] = 1
|
WINDOW_Y: Final[int] = 1
|
||||||
WINDOW_WIDTH: Final[int] = 2
|
WINDOW_WIDTH: Final[int] = 2
|
||||||
@@ -115,10 +104,10 @@ Exception: {e}\n""", file=sys.stderr)
|
|||||||
TEXT_HEIGHT: Final[int] = 1
|
TEXT_HEIGHT: Final[int] = 1
|
||||||
BORDER: Final[int] = 5
|
BORDER: Final[int] = 5
|
||||||
START_Y: Final[int] = 20
|
START_Y: Final[int] = 20
|
||||||
START_POS_TEXT: Final[Point] = (BORDER, START_Y)
|
START_POS_TEXT: Final[Point] = BORDER, START_Y
|
||||||
NORMAL_COLOR: Final[ColorBGR] = (255, 0, 255)
|
NORMAL_COLOR: Final[ColorBGR] = 255, 0, 255
|
||||||
SUCCESS_COLOR: Final[ColorBGR] = (0, 255, 0)
|
SUCCESS_COLOR: Final[ColorBGR] = 0, 255, 0
|
||||||
FAILURE_COLOR: Final[ColorBGR] = (0, 0, 255)
|
FAILURE_COLOR: Final[ColorBGR] = 0, 0, 255
|
||||||
CHAR_DX: Final[int] = (lambda text: cv2.getTextSize(text, FONT, FONT_SCALE, FONT_THICKNESS)[0][TEXT_WIDTH] // len(text))("28 QR codes capturedMMM")
|
CHAR_DX: Final[int] = (lambda text: cv2.getTextSize(text, FONT, FONT_SCALE, FONT_THICKNESS)[0][TEXT_WIDTH] // len(text))("28 QR codes capturedMMM")
|
||||||
FONT_DY: Final[int] = cv2.getTextSize("M", FONT, FONT_SCALE, FONT_THICKNESS)[0][TEXT_HEIGHT] + 5
|
FONT_DY: Final[int] = cv2.getTextSize("M", FONT, FONT_SCALE, FONT_THICKNESS)[0][TEXT_HEIGHT] + 5
|
||||||
WINDOW_NAME: Final[str] = "Extract OTP Secrets: Capture QR Codes from Camera"
|
WINDOW_NAME: Final[str] = "Extract OTP Secrets: Capture QR Codes from Camera"
|
||||||
@@ -131,12 +120,12 @@ except ImportError as e:
|
|||||||
if debug_mode:
|
if debug_mode:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
# Workaround for PYTHON <= 3.9: Union[int, None] used instead of int | None
|
# Workaround for PYTHON <= 3.9: Generally Union[int, None] used instead of int | None
|
||||||
|
|
||||||
# Types
|
# Types
|
||||||
Args = argparse.Namespace
|
Args = argparse.Namespace
|
||||||
OtpUrl = str
|
OtpUrl = str
|
||||||
# workaround for PYTHON <= 3.7: Otp = TypedDict('Otp', {'name': str, 'secret': str, 'issuer': str, 'type': str, 'counter': int | None, 'url': OtpUrl})
|
# Workaround for PYTHON <= 3.9: Otp = TypedDict('Otp', {'name': str, 'secret': str, 'issuer': str, 'type': str, 'counter': int | None, 'url': OtpUrl})
|
||||||
Otp = TypedDict('Otp', {'name': str, 'secret': str, 'issuer': str, 'type': str, 'counter': Union[int, None], 'url': OtpUrl})
|
Otp = TypedDict('Otp', {'name': str, 'secret': str, 'issuer': str, 'type': str, 'counter': Union[int, None], 'url': OtpUrl})
|
||||||
# workaround for PYTHON <= 3.9: Otps = list[Otp]
|
# workaround for PYTHON <= 3.9: Otps = list[Otp]
|
||||||
Otps = List[Otp]
|
Otps = List[Otp]
|
||||||
@@ -200,6 +189,7 @@ def main(sys_args: list[str]) -> None:
|
|||||||
write_keepass_csv(args.keepass, otps)
|
write_keepass_csv(args.keepass, otps)
|
||||||
write_json(args.json, otps)
|
write_json(args.json, otps)
|
||||||
write_txt(args.txt, otps, True)
|
write_txt(args.txt, otps, True)
|
||||||
|
write_urls(args.urls, otps)
|
||||||
|
|
||||||
|
|
||||||
# workaround for PYTHON <= 3.9 use: pb.MigrationPayload | None
|
# workaround for PYTHON <= 3.9 use: pb.MigrationPayload | None
|
||||||
@@ -276,9 +266,7 @@ def extract_otp_from_otp_url(otpauth_migration_url: str, otps: Otps, urls_count:
|
|||||||
def parse_args(sys_args: list[str]) -> Args:
|
def parse_args(sys_args: list[str]) -> Args:
|
||||||
global verbose, quiet, colored
|
global verbose, quiet, colored
|
||||||
|
|
||||||
# For PYTHON <= 3.7: Use :=
|
cmd = f"python {name}" if (name := os.path.basename(sys.argv[0])).endswith('.py') else f"{name}"
|
||||||
name = os.path.basename(sys.argv[0])
|
|
||||||
cmd = f"python {name}" if name.endswith('.py') else f"{name}"
|
|
||||||
description_text = "Extracts one time password (OTP) secrets from QR codes exported by two-factor authentication (2FA) apps"
|
description_text = "Extracts one time password (OTP) secrets from QR codes exported by two-factor authentication (2FA) apps"
|
||||||
if cv2_available:
|
if cv2_available:
|
||||||
description_text += "\nIf no infiles are provided, a GUI window starts and QR codes are captured from the camera."
|
description_text += "\nIf no infiles are provided, a GUI window starts and QR codes are captured from the camera."
|
||||||
@@ -294,10 +282,11 @@ def parse_args(sys_args: list[str]) -> Args:
|
|||||||
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;
|
||||||
b) image file containing a QR code or = for stdin for an image containing a QR code""", nargs='*' if cv2_available else '+')
|
b) image file containing a QR code or = for stdin for an image containing a QR code""", nargs='*' if cv2_available else '+')
|
||||||
arg_parser.add_argument('--csv', '-c', help='export csv file or - for stdout', metavar=('FILE'))
|
arg_parser.add_argument('--csv', '-c', help='export csv file, or - for stdout', metavar=('FILE'))
|
||||||
arg_parser.add_argument('--keepass', '-k', help='export totp/hotp csv file(s) for KeePass, - for stdout', metavar=('FILE'))
|
arg_parser.add_argument('--keepass', '-k', help='export totp/hotp csv file(s) for KeePass, - for stdout', metavar=('FILE'))
|
||||||
arg_parser.add_argument('--json', '-j', help='export json file or - for stdout', metavar=('FILE'))
|
arg_parser.add_argument('--json', '-j', help='export json file or - for stdout', metavar=('FILE'))
|
||||||
arg_parser.add_argument('--txt', '-t', help='export txt file or - for stdout', metavar=('FILE'))
|
arg_parser.add_argument('--txt', '-t', help='export txt file or - for stdout', metavar=('FILE'))
|
||||||
|
arg_parser.add_argument('--urls', '-u', help='export file with list of otpauth urls, or - for stdout', metavar=('FILE'))
|
||||||
arg_parser.add_argument('--printqr', '-p', help='print QR code(s) as text to the terminal', action='store_true')
|
arg_parser.add_argument('--printqr', '-p', help='print QR code(s) as text to the terminal', action='store_true')
|
||||||
arg_parser.add_argument('--saveqr', '-s', help='save QR code(s) as images to directory', metavar=('DIR'))
|
arg_parser.add_argument('--saveqr', '-s', help='save QR code(s) as images to directory', metavar=('DIR'))
|
||||||
if cv2_available:
|
if cv2_available:
|
||||||
@@ -315,13 +304,14 @@ b) image file containing a QR code or = for stdin for an image containing a QR c
|
|||||||
output_group.add_argument('-q', '--quiet', help='no stdout output, except output set by -', action='store_true')
|
output_group.add_argument('-q', '--quiet', help='no stdout output, except output set by -', action='store_true')
|
||||||
args = arg_parser.parse_args(sys_args)
|
args = arg_parser.parse_args(sys_args)
|
||||||
colored = not args.no_color
|
colored = not args.no_color
|
||||||
if args.csv == '-' or args.json == '-' or args.keepass == '-' or args.txt == '-':
|
if args.csv == '-' or args.json == '-' or args.keepass == '-' or args.txt == '-' or args.urls == '-':
|
||||||
args.quiet = args.q = True
|
args.quiet = args.q = True
|
||||||
|
|
||||||
verbose = args.verbose if args.verbose else LogLevel.NORMAL
|
verbose = args.verbose if args.verbose else LogLevel.NORMAL
|
||||||
if args.debug:
|
if args.debug:
|
||||||
verbose = LogLevel.DEBUG
|
verbose = LogLevel.DEBUG
|
||||||
log_debug('Debug mode start')
|
log_debug('Debug mode start')
|
||||||
|
log_debug(args)
|
||||||
quiet = True if args.quiet else False
|
quiet = True if args.quiet else False
|
||||||
if verbose: print(f"QReader installed: {cv2_available}")
|
if verbose: print(f"QReader installed: {cv2_available}")
|
||||||
if cv2_available:
|
if cv2_available:
|
||||||
@@ -392,7 +382,7 @@ def extract_otps_from_camera(args: Args) -> Otps:
|
|||||||
|
|
||||||
cv2_print_text(img, f"Mode: {qr_mode.name} (Hit SPACE to change)", 0, TextPosition.LEFT, FONT_COLOR, 20)
|
cv2_print_text(img, f"Mode: {qr_mode.name} (Hit SPACE to change)", 0, TextPosition.LEFT, FONT_COLOR, 20)
|
||||||
cv2_print_text(img, "Press ESC to quit", 1, TextPosition.LEFT, FONT_COLOR, 17)
|
cv2_print_text(img, "Press ESC to quit", 1, TextPosition.LEFT, FONT_COLOR, 17)
|
||||||
cv2_print_text(img, "Press C/J/K/T to save as csv/json/keepass/txt file", 2, TextPosition.LEFT, FONT_COLOR, None)
|
cv2_print_text(img, "Press c,j,k,t,u to save as csv/json/keepass/txt/urls file", 2, TextPosition.LEFT, FONT_COLOR, None)
|
||||||
|
|
||||||
cv2_print_text(img, f"{len(otp_urls)} QR code{'s'[:len(otp_urls) != 1]} captured", 0, TextPosition.RIGHT, FONT_COLOR)
|
cv2_print_text(img, f"{len(otp_urls)} QR code{'s'[:len(otp_urls) != 1]} captured", 0, TextPosition.RIGHT, FONT_COLOR)
|
||||||
cv2_print_text(img, f"{len(otps)} otp{'s'[:len(otps) != 1]} extracted", 1, TextPosition.RIGHT, FONT_COLOR)
|
cv2_print_text(img, f"{len(otps)} otp{'s'[:len(otps) != 1]} extracted", 1, TextPosition.RIGHT, FONT_COLOR)
|
||||||
@@ -419,16 +409,15 @@ def get_color(new_otps_count: int, otp_url: str) -> ColorBGR:
|
|||||||
return NORMAL_COLOR
|
return NORMAL_COLOR
|
||||||
|
|
||||||
|
|
||||||
# TODO use cv2 types if available
|
# TODO use proper cv2 types if available
|
||||||
def cv2_draw_box(img: Any, raw_pts: Any, color: ColorBGR) -> Any:
|
def cv2_draw_box(img: cv2.typing.MatLike, raw_pts: cv2.typing.MatLike | list[tuple[Any, Any]], color: ColorBGR) -> np.ndarray[Any, np.dtype[np.int32]]:
|
||||||
pts = np.array([raw_pts], np.int32)
|
pts = np.array([raw_pts], np.int32)
|
||||||
pts = pts.reshape((-1, 1, 2))
|
pts = pts.reshape((-1, 1, 2))
|
||||||
cv2.polylines(img, [pts], True, color, BOX_THICKNESS)
|
cv2.polylines(img, [pts], True, color, BOX_THICKNESS)
|
||||||
return pts
|
return pts
|
||||||
|
|
||||||
|
|
||||||
# TODO use cv2 types if available
|
def cv2_print_text(img: cv2.typing.MatLike, text: str, line_number: int, position: TextPosition, color: ColorBGR, opposite_len: Optional[int] = None) -> None:
|
||||||
def cv2_print_text(img: Any, text: str, line_number: int, position: TextPosition, color: ColorBGR, opposite_len: Optional[int] = None) -> None:
|
|
||||||
window_dim = cv2.getWindowImageRect(WINDOW_NAME)
|
window_dim = cv2.getWindowImageRect(WINDOW_NAME)
|
||||||
out_text = text
|
out_text = text
|
||||||
if opposite_len:
|
if opposite_len:
|
||||||
@@ -499,6 +488,18 @@ def cv2_handle_pressed_keys(qr_mode: QRMode, otps: Otps) -> Tuple[bool, QRMode]:
|
|||||||
tk_root.update()
|
tk_root.update()
|
||||||
if len(file_name) > 0:
|
if len(file_name) > 0:
|
||||||
write_txt(file_name, otps, True)
|
write_txt(file_name, otps, True)
|
||||||
|
elif (key == ord('u') or key == ord('U')) and is_not_headless():
|
||||||
|
if has_no_otps_show_warning(otps):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
file_name = tkinter.filedialog.asksaveasfilename(
|
||||||
|
title="Save extracted otp secrets as list of urls",
|
||||||
|
defaultextension='.txt',
|
||||||
|
filetypes=[('Text', '*.txt'), ('All', '*.*')]
|
||||||
|
)
|
||||||
|
tk_root.update()
|
||||||
|
if len(file_name) > 0:
|
||||||
|
write_urls(file_name, otps)
|
||||||
elif key == 32:
|
elif key == 32:
|
||||||
qr_mode = next_valid_qr_mode(qr_mode, zbar_available)
|
qr_mode = next_valid_qr_mode(qr_mode, zbar_available)
|
||||||
if verbose >= LogLevel.MORE_VERBOSE: print(f"QR reading mode: {qr_mode}")
|
if verbose >= LogLevel.MORE_VERBOSE: print(f"QR reading mode: {qr_mode}")
|
||||||
@@ -527,14 +528,20 @@ def extract_otps_from_files(args: Args) -> Otps:
|
|||||||
|
|
||||||
files_count = urls_count = otps_count = 0
|
files_count = urls_count = otps_count = 0
|
||||||
if verbose: print(f"Input files: {args.infile}")
|
if verbose: print(f"Input files: {args.infile}")
|
||||||
for infile in args.infile:
|
for infile_raw in args.infile:
|
||||||
if verbose >= LogLevel.MORE_VERBOSE: log_verbose(f"Processing infile {infile}")
|
expanded_infiles = glob.glob(infile_raw)
|
||||||
files_count += 1
|
if not expanded_infiles:
|
||||||
for line in get_otp_urls_from_file(infile, args):
|
expanded_infiles = [infile_raw]
|
||||||
if verbose >= LogLevel.MORE_VERBOSE: log_verbose(line)
|
if verbose >= LogLevel.DEBUG: log_debug(f"Could not expand input files, fallback to infile {infile_raw}")
|
||||||
if line.startswith('#') or line == '': continue
|
if verbose >= LogLevel.DEBUG: log_debug(f"Expanded input files: {expanded_infiles}")
|
||||||
urls_count += 1
|
for infile in expanded_infiles:
|
||||||
otps_count += extract_otp_from_otp_url(line, otps, urls_count, infile, args)
|
if verbose >= LogLevel.MORE_VERBOSE: log_verbose(f"Processing infile {infile}")
|
||||||
|
files_count += 1
|
||||||
|
for line in get_otp_urls_from_file(infile, args):
|
||||||
|
if verbose >= LogLevel.MORE_VERBOSE: log_verbose(line)
|
||||||
|
if line.startswith('#') or line == '': continue
|
||||||
|
urls_count += 1
|
||||||
|
otps_count += extract_otp_from_otp_url(line, otps, urls_count, infile, args)
|
||||||
if verbose: print(f"Extracted {otps_count} otp{'s'[:otps_count != 1]} from {urls_count} otp url{'s'[:urls_count != 1]} by reading {files_count} infile{'s'[:files_count != 1]}")
|
if verbose: print(f"Extracted {otps_count} otp{'s'[:otps_count != 1]} from {urls_count} otp url{'s'[:urls_count != 1]} by reading {files_count} infile{'s'[:files_count != 1]}")
|
||||||
return otps
|
return otps
|
||||||
|
|
||||||
@@ -592,7 +599,7 @@ def convert_img_to_otp_urls(filename: str, args: Args) -> OtpUrls:
|
|||||||
stdin = sys.stdin.buffer.read()
|
stdin = sys.stdin.buffer.read()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# Workaround for pytest, since pytest cannot monkeypatch sys.stdin.buffer
|
# Workaround for pytest, since pytest cannot monkeypatch sys.stdin.buffer
|
||||||
stdin = sys.stdin.read() # type: ignore # Workaround for pytest fixtures
|
stdin = sys.stdin.read()
|
||||||
if not stdin:
|
if not stdin:
|
||||||
log_warn("stdin is empty")
|
log_warn("stdin is empty")
|
||||||
try:
|
try:
|
||||||
@@ -679,6 +686,10 @@ def print_otp(otp: Otp, out: Optional[TextIO] = None) -> None:
|
|||||||
print(otp['url'], file=out)
|
print(otp['url'], file=out)
|
||||||
|
|
||||||
|
|
||||||
|
def write_url(otp: Otp, out: Optional[TextIO] = None) -> None:
|
||||||
|
print(otp['url'], file=out)
|
||||||
|
|
||||||
|
|
||||||
def save_qr_image(otp: Otp, dir: str, j: int) -> str:
|
def save_qr_image(otp: Otp, dir: str, j: int) -> str:
|
||||||
if not (os.path.exists(dir)): os.makedirs(dir, exist_ok=True)
|
if not (os.path.exists(dir)): os.makedirs(dir, exist_ok=True)
|
||||||
pattern = re.compile(r'[\W_]+')
|
pattern = re.compile(r'[\W_]+')
|
||||||
@@ -693,7 +704,7 @@ def save_qr_image_file(otp_url: OtpUrl, name: str) -> None:
|
|||||||
qr.add_data(otp_url)
|
qr.add_data(otp_url)
|
||||||
img = qr.make_image(fill_color='black', back_color='white')
|
img = qr.make_image(fill_color='black', back_color='white')
|
||||||
if verbose: print(f"Saving to {name}")
|
if verbose: print(f"Saving to {name}")
|
||||||
img.save(name)
|
img.save(name) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def print_qr(otp_url: str, out: Optional[TextIO] = None) -> None:
|
def print_qr(otp_url: str, out: Optional[TextIO] = None) -> None:
|
||||||
@@ -712,6 +723,14 @@ def write_txt(file: str, otps: Otps, write_qr: bool = False) -> None:
|
|||||||
print(file=outfile)
|
print(file=outfile)
|
||||||
|
|
||||||
|
|
||||||
|
def write_urls(file: str, otps: Otps) -> None:
|
||||||
|
if file and len(file) > 0 and len(otps) > 0:
|
||||||
|
with open_file_or_stdout(file) as outfile:
|
||||||
|
for otp in otps:
|
||||||
|
write_url(otp, outfile)
|
||||||
|
if not quiet: print(f"Exported {len(otps)} otp{'s'[:len(otps) != 1]} to otpauth url list file {file}")
|
||||||
|
|
||||||
|
|
||||||
def write_csv(file: str, otps: Otps) -> None:
|
def write_csv(file: str, otps: Otps) -> None:
|
||||||
if file and len(file) > 0 and len(otps) > 0:
|
if file and len(file) > 0 and len(otps) > 0:
|
||||||
with open_file_or_stdout_for_csv(file) as outfile:
|
with open_file_or_stdout_for_csv(file) as outfile:
|
||||||
@@ -735,8 +754,8 @@ def write_keepass_csv(file: str, otps: Otps) -> None:
|
|||||||
if has_hotp:
|
if has_hotp:
|
||||||
count_hotp_entries = write_keepass_htop_csv(otp_filename_hotp, otps)
|
count_hotp_entries = write_keepass_htop_csv(otp_filename_hotp, otps)
|
||||||
if not quiet:
|
if not quiet:
|
||||||
if count_totp_entries: print(f"Exported {count_totp_entries} totp entrie{'s'[:count_totp_entries != 1]} to keepass csv file {otp_filename_totp}")
|
if has_totp and count_totp_entries: print(f"Exported {count_totp_entries} totp entrie{'s'[:count_totp_entries != 1]} to keepass csv file {otp_filename_totp}")
|
||||||
if count_hotp_entries: print(f"Exported {count_hotp_entries} hotp entrie{'s'[:count_hotp_entries != 1]} to keepass csv file {otp_filename_hotp}")
|
if has_hotp and count_hotp_entries: print(f"Exported {count_hotp_entries} hotp entrie{'s'[:count_hotp_entries != 1]} to keepass csv file {otp_filename_hotp}")
|
||||||
|
|
||||||
|
|
||||||
def write_keepass_totp_csv(file: str, otps: Otps) -> int:
|
def write_keepass_totp_csv(file: str, otps: Otps) -> int:
|
||||||
@@ -898,27 +917,20 @@ def get_raw_version() -> str:
|
|||||||
return __version__
|
return __version__
|
||||||
except PackageNotFoundError:
|
except PackageNotFoundError:
|
||||||
# package is not installed
|
# package is not installed
|
||||||
pass
|
return ''
|
||||||
|
|
||||||
# In some cases importlib cannot properly detect package version, for example it was compiled into executable file, so it uses some custom import mechanism.
|
|
||||||
# Instead, use pkg_resources which is included in setuptools (but has a significant runtime cost)
|
|
||||||
|
|
||||||
try:
|
|
||||||
__version__ = get_distribution("package-name").version
|
|
||||||
return __version__
|
|
||||||
except DistributionNotFound:
|
|
||||||
# package is not installed
|
|
||||||
pass
|
|
||||||
|
|
||||||
return ''
|
|
||||||
|
|
||||||
|
|
||||||
# workaround for PYTHON <= 3.9 use: BaseException | None
|
# workaround for PYTHON <= 3.9 use: BaseException | None
|
||||||
def log_debug(*values: object, sep: Optional[str] = ' ') -> None:
|
def log_debug(*values: object, sep: Optional[str] = ' ') -> None:
|
||||||
|
if os.name == 'nt':
|
||||||
|
# Workaround "Windows fatal exception: access violation"
|
||||||
|
print(f"\nDEBUG: {str(values[0])}")
|
||||||
|
return
|
||||||
|
|
||||||
if colored:
|
if colored:
|
||||||
print(f"{colorama.Fore.CYAN}\nDEBUG: {str(values[0])}", *values[1:], colorama.Fore.RESET, sep)
|
print(f"{colorama.Fore.CYAN}\nDEBUG: {str(values[0])}", *values[1:], colorama.Fore.RESET, sep=sep)
|
||||||
else:
|
else:
|
||||||
print(f"\nDEBUG: {str(values[0])}", *values[1:], sep)
|
print(f"\nDEBUG: {str(values[0])}", *values[1:], sep=sep)
|
||||||
|
|
||||||
|
|
||||||
# workaround for PYTHON <= 3.9 use: BaseException | None
|
# workaround for PYTHON <= 3.9 use: BaseException | None
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
|
# NO CHECKED-IN PROTOBUF GENCODE
|
||||||
# source: google_auth.proto
|
# source: google_auth.proto
|
||||||
|
# Protobuf Python Version: 6.31.0
|
||||||
"""Generated protocol buffer code."""
|
"""Generated protocol buffer code."""
|
||||||
from google.protobuf.internal import builder as _builder
|
|
||||||
from google.protobuf import descriptor as _descriptor
|
from google.protobuf import descriptor as _descriptor
|
||||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
|
from google.protobuf import runtime_version as _runtime_version
|
||||||
from google.protobuf import symbol_database as _symbol_database
|
from google.protobuf import symbol_database as _symbol_database
|
||||||
|
from google.protobuf.internal import builder as _builder
|
||||||
|
_runtime_version.ValidateProtobufRuntimeVersion(
|
||||||
|
_runtime_version.Domain.PUBLIC,
|
||||||
|
6,
|
||||||
|
31,
|
||||||
|
0,
|
||||||
|
'',
|
||||||
|
'google_auth.proto'
|
||||||
|
)
|
||||||
# @@protoc_insertion_point(imports)
|
# @@protoc_insertion_point(imports)
|
||||||
|
|
||||||
_sym_db = _symbol_database.Default()
|
_sym_db = _symbol_database.Default()
|
||||||
@@ -18,9 +29,8 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11google_auth.pr
|
|||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google_auth_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google_auth_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if not _descriptor._USE_C_DESCRIPTORS:
|
||||||
|
DESCRIPTOR._loaded_options = None
|
||||||
DESCRIPTOR._options = None
|
|
||||||
_globals['_MIGRATIONPAYLOAD']._serialized_start=22
|
_globals['_MIGRATIONPAYLOAD']._serialized_start=22
|
||||||
_globals['_MIGRATIONPAYLOAD']._serialized_end=461
|
_globals['_MIGRATIONPAYLOAD']._serialized_end=461
|
||||||
_globals['_MIGRATIONPAYLOAD_OTPPARAMETERS']._serialized_start=176
|
_globals['_MIGRATIONPAYLOAD_OTPPARAMETERS']._serialized_start=176
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
@generated by mypy-protobuf. Do not edit manually!
|
@generated by mypy-protobuf. Do not edit manually!
|
||||||
isort:skip_file
|
isort:skip_file
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import builtins
|
import builtins
|
||||||
import collections.abc
|
import collections.abc
|
||||||
import google.protobuf.descriptor
|
import google.protobuf.descriptor
|
||||||
@@ -18,7 +19,7 @@ else:
|
|||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
||||||
|
|
||||||
@typing_extensions.final
|
@typing.final
|
||||||
class MigrationPayload(google.protobuf.message.Message):
|
class MigrationPayload(google.protobuf.message.Message):
|
||||||
"""Copied from: https://github.com/beemdevelopment/Aegis/blob/master/app/src/main/proto/google_auth.proto"""
|
"""Copied from: https://github.com/beemdevelopment/Aegis/blob/master/app/src/main/proto/google_auth.proto"""
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@ class MigrationPayload(google.protobuf.message.Message):
|
|||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
ValueType = typing.NewType("ValueType", builtins.int)
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
V: typing_extensions.TypeAlias = ValueType
|
||||||
|
|
||||||
class _AlgorithmEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[MigrationPayload._Algorithm.ValueType], builtins.type): # noqa: F821
|
class _AlgorithmEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[MigrationPayload._Algorithm.ValueType], builtins.type):
|
||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
||||||
ALGO_INVALID: MigrationPayload._Algorithm.ValueType # 0
|
ALGO_INVALID: MigrationPayload._Algorithm.ValueType # 0
|
||||||
ALGO_SHA1: MigrationPayload._Algorithm.ValueType # 1
|
ALGO_SHA1: MigrationPayload._Algorithm.ValueType # 1
|
||||||
@@ -41,7 +42,7 @@ class MigrationPayload(google.protobuf.message.Message):
|
|||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
ValueType = typing.NewType("ValueType", builtins.int)
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
V: typing_extensions.TypeAlias = ValueType
|
||||||
|
|
||||||
class _OtpTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[MigrationPayload._OtpType.ValueType], builtins.type): # noqa: F821
|
class _OtpTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[MigrationPayload._OtpType.ValueType], builtins.type):
|
||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
||||||
OTP_INVALID: MigrationPayload._OtpType.ValueType # 0
|
OTP_INVALID: MigrationPayload._OtpType.ValueType # 0
|
||||||
OTP_HOTP: MigrationPayload._OtpType.ValueType # 1
|
OTP_HOTP: MigrationPayload._OtpType.ValueType # 1
|
||||||
@@ -52,7 +53,7 @@ class MigrationPayload(google.protobuf.message.Message):
|
|||||||
OTP_HOTP: MigrationPayload.OtpType.ValueType # 1
|
OTP_HOTP: MigrationPayload.OtpType.ValueType # 1
|
||||||
OTP_TOTP: MigrationPayload.OtpType.ValueType # 2
|
OTP_TOTP: MigrationPayload.OtpType.ValueType # 2
|
||||||
|
|
||||||
@typing_extensions.final
|
@typing.final
|
||||||
class OtpParameters(google.protobuf.message.Message):
|
class OtpParameters(google.protobuf.message.Message):
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
||||||
|
|
||||||
@@ -81,19 +82,19 @@ class MigrationPayload(google.protobuf.message.Message):
|
|||||||
type: global___MigrationPayload.OtpType.ValueType = ...,
|
type: global___MigrationPayload.OtpType.ValueType = ...,
|
||||||
counter: builtins.int = ...,
|
counter: builtins.int = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def ClearField(self, field_name: typing_extensions.Literal["algorithm", b"algorithm", "counter", b"counter", "digits", b"digits", "issuer", b"issuer", "name", b"name", "secret", b"secret", "type", b"type"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["algorithm", b"algorithm", "counter", b"counter", "digits", b"digits", "issuer", b"issuer", "name", b"name", "secret", b"secret", "type", b"type"]) -> None: ...
|
||||||
|
|
||||||
OTP_PARAMETERS_FIELD_NUMBER: builtins.int
|
OTP_PARAMETERS_FIELD_NUMBER: builtins.int
|
||||||
VERSION_FIELD_NUMBER: builtins.int
|
VERSION_FIELD_NUMBER: builtins.int
|
||||||
BATCH_SIZE_FIELD_NUMBER: builtins.int
|
BATCH_SIZE_FIELD_NUMBER: builtins.int
|
||||||
BATCH_INDEX_FIELD_NUMBER: builtins.int
|
BATCH_INDEX_FIELD_NUMBER: builtins.int
|
||||||
BATCH_ID_FIELD_NUMBER: builtins.int
|
BATCH_ID_FIELD_NUMBER: builtins.int
|
||||||
@property
|
|
||||||
def otp_parameters(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___MigrationPayload.OtpParameters]: ...
|
|
||||||
version: builtins.int
|
version: builtins.int
|
||||||
batch_size: builtins.int
|
batch_size: builtins.int
|
||||||
batch_index: builtins.int
|
batch_index: builtins.int
|
||||||
batch_id: builtins.int
|
batch_id: builtins.int
|
||||||
|
@property
|
||||||
|
def otp_parameters(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___MigrationPayload.OtpParameters]: ...
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -103,6 +104,6 @@ class MigrationPayload(google.protobuf.message.Message):
|
|||||||
batch_index: builtins.int = ...,
|
batch_index: builtins.int = ...,
|
||||||
batch_id: builtins.int = ...,
|
batch_id: builtins.int = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def ClearField(self, field_name: typing_extensions.Literal["batch_id", b"batch_id", "batch_index", b"batch_index", "batch_size", b"batch_size", "otp_parameters", b"otp_parameters", "version", b"version"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["batch_id", b"batch_id", "batch_index", b"batch_index", "batch_size", b"batch_size", "otp_parameters", b"otp_parameters", "version", b"version"]) -> None: ...
|
||||||
|
|
||||||
global___MigrationPayload = MigrationPayload
|
global___MigrationPayload = MigrationPayload
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
QReader installed: True
|
QReader installed: True
|
||||||
CV2 version: 4.7.0
|
CV2 version: 4.10.0
|
||||||
QR reading mode: ZBAR
|
QR reading mode: ZBAR
|
||||||
|
|
||||||
Input files: ['example_export.txt']
|
Input files: ['example_export.txt']
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
QReader installed: True
|
QReader installed: True
|
||||||
CV2 version: 4.7.0
|
CV2 version: 4.10.0
|
||||||
QR reading mode: ZBAR
|
QR reading mode: ZBAR
|
||||||
|
|
||||||
Input files: ['example_export.txt']
|
Input files: ['example_export.txt']
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
QReader installed: True
|
QReader installed: True
|
||||||
CV2 version: 4.7.0
|
CV2 version: 4.10.0
|
||||||
QR reading mode: ZBAR
|
QR reading mode: ZBAR
|
||||||
|
|
||||||
Version: extract_otp_secrets 2.0.2.post50+git.158245dd.dirty Linux x86_64 Python 3.11.1 (CPython/called as script)
|
Version: extract_otp_secrets 2.8.4.post4+git.7ce765dd.dirty Linux x86_64 Python 3.11.10 (CPython/called as script)
|
||||||
|
|
||||||
Input files: ['example_export.txt']
|
Input files: ['example_export.txt']
|
||||||
|
|
||||||
|
DEBUG: Expanded input files: ['example_export.txt']
|
||||||
Processing infile example_export.txt
|
Processing infile example_export.txt
|
||||||
Reading lines of example_export.txt
|
Reading lines of example_export.txt
|
||||||
# 2FA example from https://www.raspberrypi.org/blog/setting-up-two-factor-authentication-on-your-raspberry-pi/
|
# 2FA example from https://www.raspberrypi.org/blog/setting-up-two-factor-authentication-on-your-raspberry-pi/
|
||||||
@@ -32,16 +34,17 @@ otpauth-migration://offline?data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8O
|
|||||||
# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
||||||
otpauth-migration://offline?data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B
|
otpauth-migration://offline?data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B
|
||||||
|
|
||||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B', fragment='')
|
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B', fragment='')
|
||||||
|
|
||||||
DEBUG: querystring params={'data': ['CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B']}
|
DEBUG: querystring params={'data': ['CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B']}
|
||||||
|
|
||||||
DEBUG: data_base64=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B
|
DEBUG: data_base64=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B
|
||||||
|
|
||||||
DEBUG: data_base64_fixed=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B
|
DEBUG: data_base64_fixed=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B
|
||||||
|
|
||||||
DEBUG:
|
DEBUG:
|
||||||
1. Payload Line otp_parameters {
|
1. Payload Line
|
||||||
|
otp_parameters {
|
||||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||||
name: "pi@raspberrypi"
|
name: "pi@raspberrypi"
|
||||||
issuer: "raspberrypi"
|
issuer: "raspberrypi"
|
||||||
@@ -52,12 +55,11 @@ DEBUG:
|
|||||||
version: 1
|
version: 1
|
||||||
batch_size: 1
|
batch_size: 1
|
||||||
batch_id: -1320898453
|
batch_id: -1320898453
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1. Secret
|
1. Secret
|
||||||
|
|
||||||
DEBUG: OTP enum type: OTP_TOTP
|
DEBUG: OTP enum type: OTP_TOTP
|
||||||
Name: pi@raspberrypi
|
Name: pi@raspberrypi
|
||||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
Issuer: raspberrypi
|
Issuer: raspberrypi
|
||||||
@@ -68,16 +70,17 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspber
|
|||||||
# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
otpauth-migration://offline?data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4%2F%2F%2F%2F%2FwE%3D
|
otpauth-migration://offline?data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4%2F%2F%2F%2F%2FwE%3D
|
||||||
|
|
||||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4%2F%2F%2F%2F%2FwE%3D', fragment='')
|
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4%2F%2F%2F%2F%2FwE%3D', fragment='')
|
||||||
|
|
||||||
DEBUG: querystring params={'data': ['CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE=']}
|
DEBUG: querystring params={'data': ['CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE=']}
|
||||||
|
|
||||||
DEBUG: data_base64=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE=
|
DEBUG: data_base64=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE=
|
||||||
|
|
||||||
DEBUG: data_base64_fixed=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE=
|
DEBUG: data_base64_fixed=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE=
|
||||||
|
|
||||||
DEBUG:
|
DEBUG:
|
||||||
2. Payload Line otp_parameters {
|
2. Payload Line
|
||||||
|
otp_parameters {
|
||||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||||
name: "pi@raspberrypi"
|
name: "pi@raspberrypi"
|
||||||
algorithm: ALGO_SHA1
|
algorithm: ALGO_SHA1
|
||||||
@@ -87,12 +90,11 @@ DEBUG:
|
|||||||
version: 1
|
version: 1
|
||||||
batch_size: 1
|
batch_size: 1
|
||||||
batch_id: -2094403140
|
batch_id: -2094403140
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2. Secret
|
2. Secret
|
||||||
|
|
||||||
DEBUG: OTP enum type: OTP_TOTP
|
DEBUG: OTP enum type: OTP_TOTP
|
||||||
Name: pi@raspberrypi
|
Name: pi@raspberrypi
|
||||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
Type: totp
|
Type: totp
|
||||||
@@ -103,16 +105,17 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
|||||||
# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
otpauth-migration://offline?data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa%2Bf%2F%2F%2F%2F8B
|
otpauth-migration://offline?data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa%2Bf%2F%2F%2F%2F8B
|
||||||
|
|
||||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa%2Bf%2F%2F%2F%2F8B', fragment='')
|
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa%2Bf%2F%2F%2F%2F8B', fragment='')
|
||||||
|
|
||||||
DEBUG: querystring params={'data': ['CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B']}
|
DEBUG: querystring params={'data': ['CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B']}
|
||||||
|
|
||||||
DEBUG: data_base64=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B
|
DEBUG: data_base64=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B
|
||||||
|
|
||||||
DEBUG: data_base64_fixed=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B
|
DEBUG: data_base64_fixed=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B
|
||||||
|
|
||||||
DEBUG:
|
DEBUG:
|
||||||
3. Payload Line otp_parameters {
|
3. Payload Line
|
||||||
|
otp_parameters {
|
||||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||||
name: "pi@raspberrypi"
|
name: "pi@raspberrypi"
|
||||||
algorithm: ALGO_SHA1
|
algorithm: ALGO_SHA1
|
||||||
@@ -130,12 +133,11 @@ otp_parameters {
|
|||||||
version: 1
|
version: 1
|
||||||
batch_size: 1
|
batch_size: 1
|
||||||
batch_id: -1822886384
|
batch_id: -1822886384
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
3. Secret
|
3. Secret
|
||||||
|
|
||||||
DEBUG: OTP enum type: OTP_TOTP
|
DEBUG: OTP enum type: OTP_TOTP
|
||||||
Name: pi@raspberrypi
|
Name: pi@raspberrypi
|
||||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
Type: totp
|
Type: totp
|
||||||
@@ -144,7 +146,7 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
|||||||
|
|
||||||
4. Secret
|
4. Secret
|
||||||
|
|
||||||
DEBUG: OTP enum type: OTP_TOTP
|
DEBUG: OTP enum type: OTP_TOTP
|
||||||
Name: pi@raspberrypi
|
Name: pi@raspberrypi
|
||||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
Issuer: raspberrypi
|
Issuer: raspberrypi
|
||||||
@@ -155,16 +157,17 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspber
|
|||||||
# otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4
|
# otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4
|
||||||
otpauth-migration://offline?data=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6%2F%2F%2F%2F%2FwE%3D
|
otpauth-migration://offline?data=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6%2F%2F%2F%2F%2FwE%3D
|
||||||
|
|
||||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6%2F%2F%2F%2F%2FwE%3D', fragment='')
|
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6%2F%2F%2F%2F%2FwE%3D', fragment='')
|
||||||
|
|
||||||
DEBUG: querystring params={'data': ['CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE=']}
|
DEBUG: querystring params={'data': ['CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE=']}
|
||||||
|
|
||||||
DEBUG: data_base64=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE=
|
DEBUG: data_base64=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE=
|
||||||
|
|
||||||
DEBUG: data_base64_fixed=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE=
|
DEBUG: data_base64_fixed=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE=
|
||||||
|
|
||||||
DEBUG:
|
DEBUG:
|
||||||
4. Payload Line otp_parameters {
|
4. Payload Line
|
||||||
|
otp_parameters {
|
||||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||||
name: "hotp demo"
|
name: "hotp demo"
|
||||||
algorithm: ALGO_SHA1
|
algorithm: ALGO_SHA1
|
||||||
@@ -175,12 +178,11 @@ DEBUG:
|
|||||||
version: 1
|
version: 1
|
||||||
batch_size: 1
|
batch_size: 1
|
||||||
batch_id: -1558849573
|
batch_id: -1558849573
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
5. Secret
|
5. Secret
|
||||||
|
|
||||||
DEBUG: OTP enum type: OTP_HOTP
|
DEBUG: OTP enum type: OTP_HOTP
|
||||||
Name: hotp demo
|
Name: hotp demo
|
||||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
Type: hotp
|
Type: hotp
|
||||||
@@ -192,16 +194,17 @@ otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4
|
|||||||
# Name: "encoding: ¿äÄéÉ? (demo)"
|
# Name: "encoding: ¿äÄéÉ? (demo)"
|
||||||
otpauth-migration://offline?data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv%2F%2F%2F%2F%2F%2FAQ%3D%3D
|
otpauth-migration://offline?data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv%2F%2F%2F%2F%2F%2FAQ%3D%3D
|
||||||
|
|
||||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv%2F%2F%2F%2F%2F%2FAQ%3D%3D', fragment='')
|
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv%2F%2F%2F%2F%2F%2FAQ%3D%3D', fragment='')
|
||||||
|
|
||||||
DEBUG: querystring params={'data': ['CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ==']}
|
DEBUG: querystring params={'data': ['CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ==']}
|
||||||
|
|
||||||
DEBUG: data_base64=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ==
|
DEBUG: data_base64=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ==
|
||||||
|
|
||||||
DEBUG: data_base64_fixed=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ==
|
DEBUG: data_base64_fixed=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ==
|
||||||
|
|
||||||
DEBUG:
|
DEBUG:
|
||||||
5. Payload Line otp_parameters {
|
5. Payload Line
|
||||||
|
otp_parameters {
|
||||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||||
name: "encoding: ¿äÄéÉ? (demo)"
|
name: "encoding: ¿äÄéÉ? (demo)"
|
||||||
algorithm: ALGO_SHA1
|
algorithm: ALGO_SHA1
|
||||||
@@ -211,12 +214,11 @@ DEBUG:
|
|||||||
version: 1
|
version: 1
|
||||||
batch_size: 1
|
batch_size: 1
|
||||||
batch_id: -171198419
|
batch_id: -171198419
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
6. Secret
|
6. Secret
|
||||||
|
|
||||||
DEBUG: OTP enum type: OTP_TOTP
|
DEBUG: OTP enum type: OTP_TOTP
|
||||||
Name: encoding: ¿äÄéÉ? (demo)
|
Name: encoding: ¿äÄéÉ? (demo)
|
||||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
Type: totp
|
Type: totp
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
QReader installed: True
|
QReader installed: True
|
||||||
CV2 version: 4.7.0
|
CV2 version: 4.10.0
|
||||||
QR reading mode: ZBAR
|
QR reading mode: ZBAR
|
||||||
|
|
||||||
Input files: ['example_export.txt']
|
Input files: ['example_export.txt']
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
QReader installed: True
|
QReader installed: True
|
||||||
CV2 version: 4.7.0
|
CV2 version: 4.10.0
|
||||||
QR reading mode: ZBAR
|
QR reading mode: ZBAR
|
||||||
|
|
||||||
Input files: ['example_export.txt']
|
Input files: ['example_export.txt']
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
QReader installed: True
|
QReader installed: True
|
||||||
CV2 version: 4.7.0
|
CV2 version: 4.10.0
|
||||||
QR reading mode: ZBAR
|
QR reading mode: ZBAR
|
||||||
|
|
||||||
Version: extract_otp_secrets 2.0.2.post50+git.158245dd.dirty Linux x86_64 Python 3.11.1 (CPython/called as script)
|
Version: extract_otp_secrets 2.8.4.post4+git.7ce765dd.dirty Linux x86_64 Python 3.11.10 (CPython/called as script)
|
||||||
|
|
||||||
Input files: ['example_export.txt']
|
Input files: ['example_export.txt']
|
||||||
|
[36m
|
||||||
|
DEBUG: Expanded input files: ['example_export.txt'] [39m
|
||||||
[36mProcessing infile example_export.txt[39m
|
[36mProcessing infile example_export.txt[39m
|
||||||
Reading lines of example_export.txt
|
Reading lines of example_export.txt
|
||||||
[36m# 2FA example from https://www.raspberrypi.org/blog/setting-up-two-factor-authentication-on-your-raspberry-pi/[39m
|
[36m# 2FA example from https://www.raspberrypi.org/blog/setting-up-two-factor-authentication-on-your-raspberry-pi/[39m
|
||||||
@@ -32,16 +34,17 @@ Reading lines of example_export.txt
|
|||||||
[36m# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi[39m
|
[36m# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi[39m
|
||||||
[36motpauth-migration://offline?data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B[39m
|
[36motpauth-migration://offline?data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B[39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B', fragment='') [39m
|
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B', fragment='') [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: querystring params={'data': ['CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B']} [39m
|
DEBUG: querystring params={'data': ['CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B']} [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: data_base64=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B [39m
|
DEBUG: data_base64=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: data_base64_fixed=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B [39m
|
DEBUG: data_base64_fixed=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG:
|
DEBUG:
|
||||||
1. Payload Line otp_parameters {
|
1. Payload Line
|
||||||
|
otp_parameters {
|
||||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||||
name: "pi@raspberrypi"
|
name: "pi@raspberrypi"
|
||||||
issuer: "raspberrypi"
|
issuer: "raspberrypi"
|
||||||
@@ -52,12 +55,12 @@ DEBUG:
|
|||||||
version: 1
|
version: 1
|
||||||
batch_size: 1
|
batch_size: 1
|
||||||
batch_id: -1320898453
|
batch_id: -1320898453
|
||||||
[39m
|
|
||||||
|
|
||||||
|
[39m
|
||||||
|
|
||||||
1. Secret
|
1. Secret
|
||||||
[36m
|
[36m
|
||||||
DEBUG: OTP enum type: OTP_TOTP [39m
|
DEBUG: OTP enum type: OTP_TOTP [39m
|
||||||
Name: pi@raspberrypi
|
Name: pi@raspberrypi
|
||||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
Issuer: raspberrypi
|
Issuer: raspberrypi
|
||||||
@@ -68,16 +71,17 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspber
|
|||||||
[36m# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY[39m
|
[36m# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY[39m
|
||||||
[36motpauth-migration://offline?data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4%2F%2F%2F%2F%2FwE%3D[39m
|
[36motpauth-migration://offline?data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4%2F%2F%2F%2F%2FwE%3D[39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4%2F%2F%2F%2F%2FwE%3D', fragment='') [39m
|
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4%2F%2F%2F%2F%2FwE%3D', fragment='') [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: querystring params={'data': ['CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE=']} [39m
|
DEBUG: querystring params={'data': ['CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE=']} [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: data_base64=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE= [39m
|
DEBUG: data_base64=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE= [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: data_base64_fixed=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE= [39m
|
DEBUG: data_base64_fixed=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE= [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG:
|
DEBUG:
|
||||||
2. Payload Line otp_parameters {
|
2. Payload Line
|
||||||
|
otp_parameters {
|
||||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||||
name: "pi@raspberrypi"
|
name: "pi@raspberrypi"
|
||||||
algorithm: ALGO_SHA1
|
algorithm: ALGO_SHA1
|
||||||
@@ -87,12 +91,12 @@ DEBUG:
|
|||||||
version: 1
|
version: 1
|
||||||
batch_size: 1
|
batch_size: 1
|
||||||
batch_id: -2094403140
|
batch_id: -2094403140
|
||||||
[39m
|
|
||||||
|
|
||||||
|
[39m
|
||||||
|
|
||||||
2. Secret
|
2. Secret
|
||||||
[36m
|
[36m
|
||||||
DEBUG: OTP enum type: OTP_TOTP [39m
|
DEBUG: OTP enum type: OTP_TOTP [39m
|
||||||
Name: pi@raspberrypi
|
Name: pi@raspberrypi
|
||||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
Type: totp
|
Type: totp
|
||||||
@@ -103,16 +107,17 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
|||||||
[36m# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY[39m
|
[36m# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY[39m
|
||||||
[36motpauth-migration://offline?data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa%2Bf%2F%2F%2F%2F8B[39m
|
[36motpauth-migration://offline?data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa%2Bf%2F%2F%2F%2F8B[39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa%2Bf%2F%2F%2F%2F8B', fragment='') [39m
|
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa%2Bf%2F%2F%2F%2F8B', fragment='') [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: querystring params={'data': ['CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B']} [39m
|
DEBUG: querystring params={'data': ['CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B']} [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: data_base64=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B [39m
|
DEBUG: data_base64=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: data_base64_fixed=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B [39m
|
DEBUG: data_base64_fixed=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG:
|
DEBUG:
|
||||||
3. Payload Line otp_parameters {
|
3. Payload Line
|
||||||
|
otp_parameters {
|
||||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||||
name: "pi@raspberrypi"
|
name: "pi@raspberrypi"
|
||||||
algorithm: ALGO_SHA1
|
algorithm: ALGO_SHA1
|
||||||
@@ -130,12 +135,12 @@ otp_parameters {
|
|||||||
version: 1
|
version: 1
|
||||||
batch_size: 1
|
batch_size: 1
|
||||||
batch_id: -1822886384
|
batch_id: -1822886384
|
||||||
[39m
|
|
||||||
|
|
||||||
|
[39m
|
||||||
|
|
||||||
3. Secret
|
3. Secret
|
||||||
[36m
|
[36m
|
||||||
DEBUG: OTP enum type: OTP_TOTP [39m
|
DEBUG: OTP enum type: OTP_TOTP [39m
|
||||||
Name: pi@raspberrypi
|
Name: pi@raspberrypi
|
||||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
Type: totp
|
Type: totp
|
||||||
@@ -144,7 +149,7 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
|||||||
|
|
||||||
4. Secret
|
4. Secret
|
||||||
[36m
|
[36m
|
||||||
DEBUG: OTP enum type: OTP_TOTP [39m
|
DEBUG: OTP enum type: OTP_TOTP [39m
|
||||||
Name: pi@raspberrypi
|
Name: pi@raspberrypi
|
||||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
Issuer: raspberrypi
|
Issuer: raspberrypi
|
||||||
@@ -155,16 +160,17 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspber
|
|||||||
[36m# otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4[39m
|
[36m# otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4[39m
|
||||||
[36motpauth-migration://offline?data=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6%2F%2F%2F%2F%2FwE%3D[39m
|
[36motpauth-migration://offline?data=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6%2F%2F%2F%2F%2FwE%3D[39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6%2F%2F%2F%2F%2FwE%3D', fragment='') [39m
|
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6%2F%2F%2F%2F%2FwE%3D', fragment='') [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: querystring params={'data': ['CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE=']} [39m
|
DEBUG: querystring params={'data': ['CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE=']} [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: data_base64=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE= [39m
|
DEBUG: data_base64=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE= [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: data_base64_fixed=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE= [39m
|
DEBUG: data_base64_fixed=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE= [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG:
|
DEBUG:
|
||||||
4. Payload Line otp_parameters {
|
4. Payload Line
|
||||||
|
otp_parameters {
|
||||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||||
name: "hotp demo"
|
name: "hotp demo"
|
||||||
algorithm: ALGO_SHA1
|
algorithm: ALGO_SHA1
|
||||||
@@ -175,12 +181,12 @@ DEBUG:
|
|||||||
version: 1
|
version: 1
|
||||||
batch_size: 1
|
batch_size: 1
|
||||||
batch_id: -1558849573
|
batch_id: -1558849573
|
||||||
[39m
|
|
||||||
|
|
||||||
|
[39m
|
||||||
|
|
||||||
5. Secret
|
5. Secret
|
||||||
[36m
|
[36m
|
||||||
DEBUG: OTP enum type: OTP_HOTP [39m
|
DEBUG: OTP enum type: OTP_HOTP [39m
|
||||||
Name: hotp demo
|
Name: hotp demo
|
||||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
Type: hotp
|
Type: hotp
|
||||||
@@ -192,16 +198,17 @@ otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4
|
|||||||
[36m# Name: "encoding: ¿äÄéÉ? (demo)"[39m
|
[36m# Name: "encoding: ¿äÄéÉ? (demo)"[39m
|
||||||
[36motpauth-migration://offline?data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv%2F%2F%2F%2F%2F%2FAQ%3D%3D[39m
|
[36motpauth-migration://offline?data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv%2F%2F%2F%2F%2F%2FAQ%3D%3D[39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv%2F%2F%2F%2F%2F%2FAQ%3D%3D', fragment='') [39m
|
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv%2F%2F%2F%2F%2F%2FAQ%3D%3D', fragment='') [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: querystring params={'data': ['CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ==']} [39m
|
DEBUG: querystring params={'data': ['CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ==']} [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: data_base64=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ== [39m
|
DEBUG: data_base64=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ== [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG: data_base64_fixed=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ== [39m
|
DEBUG: data_base64_fixed=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ== [39m
|
||||||
[36m
|
[36m
|
||||||
DEBUG:
|
DEBUG:
|
||||||
5. Payload Line otp_parameters {
|
5. Payload Line
|
||||||
|
otp_parameters {
|
||||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||||
name: "encoding: ¿äÄéÉ? (demo)"
|
name: "encoding: ¿äÄéÉ? (demo)"
|
||||||
algorithm: ALGO_SHA1
|
algorithm: ALGO_SHA1
|
||||||
@@ -211,12 +218,12 @@ DEBUG:
|
|||||||
version: 1
|
version: 1
|
||||||
batch_size: 1
|
batch_size: 1
|
||||||
batch_id: -171198419
|
batch_id: -171198419
|
||||||
[39m
|
|
||||||
|
|
||||||
|
[39m
|
||||||
|
|
||||||
6. Secret
|
6. Secret
|
||||||
[36m
|
[36m
|
||||||
DEBUG: OTP enum type: OTP_TOTP [39m
|
DEBUG: OTP enum type: OTP_TOTP [39m
|
||||||
Name: encoding: ¿äÄéÉ? (demo)
|
Name: encoding: ¿äÄéÉ? (demo)
|
||||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
Type: totp
|
Type: totp
|
||||||
|
|||||||
6
tests/data/url_list_output.txt
Normal file
6
tests/data/url_list_output.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
||||||
|
otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
|
otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
|
otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
||||||
|
otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4
|
||||||
|
otpauth://totp/encoding%3A%20%C2%BF%C3%A4%C3%84%C3%A9%C3%89%3F%20%28demo%29?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||||
@@ -39,7 +39,7 @@ from utils import (count_files_in_dir, file_exits, read_binary_file_as_stream,
|
|||||||
import extract_otp_secrets
|
import extract_otp_secrets
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import cv2 # type: ignore
|
import cv2
|
||||||
from extract_otp_secrets import SUCCESS_COLOR, FAILURE_COLOR, FONT, FONT_SCALE, FONT_COLOR, FONT_THICKNESS, FONT_LINE_STYLE
|
from extract_otp_secrets import SUCCESS_COLOR, FAILURE_COLOR, FONT, FONT_SCALE, FONT_COLOR, FONT_THICKNESS, FONT_LINE_STYLE
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# ignore
|
# ignore
|
||||||
@@ -418,6 +418,37 @@ def test_extract_txt_stdout_only_comments(capsys: pytest.CaptureFixture[str]) ->
|
|||||||
assert captured.err == ''
|
assert captured.err == ''
|
||||||
|
|
||||||
|
|
||||||
|
def test_extract_urls(capsys: pytest.CaptureFixture[str], tmp_path: pathlib.Path) -> None:
|
||||||
|
# Arrange
|
||||||
|
output_file = str(tmp_path / 'test_example_url_list.txt')
|
||||||
|
|
||||||
|
# Act
|
||||||
|
extract_otp_secrets.main(['-q', '-u', output_file, 'example_export.txt'])
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
expected_txt = read_file_to_str('tests/data/url_list_output.txt')
|
||||||
|
actual_txt = read_file_to_str(output_file)
|
||||||
|
|
||||||
|
assert actual_txt == expected_txt
|
||||||
|
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
|
||||||
|
assert captured.out == ''
|
||||||
|
assert captured.err == ''
|
||||||
|
|
||||||
|
|
||||||
|
def test_extract_urls_stdout(capsys: pytest.CaptureFixture[str]) -> None:
|
||||||
|
# Act
|
||||||
|
extract_otp_secrets.main(['-u', '-', 'example_export.txt'])
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
expected_txt = read_file_to_str('tests/data/url_list_output.txt')
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
|
||||||
|
assert captured.out == expected_txt
|
||||||
|
assert captured.err == ''
|
||||||
|
|
||||||
|
|
||||||
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_secrets.main(['tests/data/test_plus_problem_export.txt'])
|
extract_otp_secrets.main(['tests/data/test_plus_problem_export.txt'])
|
||||||
@@ -528,7 +559,7 @@ def test_normalize_bytes() -> None:
|
|||||||
# Generate verbose output:
|
# Generate verbose output:
|
||||||
# for color in '' '-n'; do for level in '' '-v' '-vv' '-vvv'; do python3.11 src/extract_otp_secrets.py example_export.txt $color $level > tests/data/print_verbose_output$color$level.txt; done; done
|
# for color in '' '-n'; do for level in '' '-v' '-vv' '-vvv'; do python3.11 src/extract_otp_secrets.py example_export.txt $color $level > tests/data/print_verbose_output$color$level.txt; done; done
|
||||||
# workaround for PYTHON <= 3.10
|
# workaround for PYTHON <= 3.10
|
||||||
@pytest.mark.skipif(sys.version_info < (3, 10), reason="fileinput.input encoding exists since PYTHON 3.10")
|
@pytest.mark.skipif(sys.version_info < (3, 10) or sys.platform.startswith("win"), reason="fileinput.input encoding exists since PYTHON 3.10 OR Windows fatal exception: access violation")
|
||||||
@pytest.mark.parametrize("verbose_level", ['', '-v', '-vv', '-vvv'])
|
@pytest.mark.parametrize("verbose_level", ['', '-v', '-vv', '-vvv'])
|
||||||
@pytest.mark.parametrize("color", ['', '-n'])
|
@pytest.mark.parametrize("color", ['', '-n'])
|
||||||
def test_extract_verbose(verbose_level: str, color: str, capsys: pytest.CaptureFixture[str], relaxed: bool) -> None:
|
def test_extract_verbose(verbose_level: str, color: str, capsys: pytest.CaptureFixture[str], relaxed: bool) -> None:
|
||||||
@@ -818,7 +849,14 @@ data=XXXX
|
|||||||
Exception: unpack requires a buffer of 4 bytes
|
Exception: unpack requires a buffer of 4 bytes
|
||||||
'''
|
'''
|
||||||
|
|
||||||
assert captured.err == first_expected_stderr or captured.err == second_expected_stderr
|
#
|
||||||
|
third_expected_stderr = '''
|
||||||
|
ERROR: Cannot decode otpauth-migration migration payload.
|
||||||
|
data=XXXX
|
||||||
|
Exception: Error parsing message with type 'MigrationPayload'
|
||||||
|
'''
|
||||||
|
|
||||||
|
assert captured.err == first_expected_stderr or captured.err == second_expected_stderr or captured.err == third_expected_stderr
|
||||||
assert captured.out == ''
|
assert captured.out == ''
|
||||||
assert e.value.code == 1
|
assert e.value.code == 1
|
||||||
assert e.type == SystemExit
|
assert e.type == SystemExit
|
||||||
@@ -831,19 +869,8 @@ def test_wrong_content(capsys: pytest.CaptureFixture[str]) -> None:
|
|||||||
# Assert
|
# Assert
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
|
|
||||||
expected_stderr = '''
|
|
||||||
WARN: input is not a otpauth-migration:// url
|
|
||||||
source: tests/data/test_export_wrong_content.txt
|
|
||||||
input: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
|
||||||
Maybe a wrong file was given
|
|
||||||
|
|
||||||
ERROR: could not parse query parameter in input url
|
|
||||||
source: tests/data/test_export_wrong_content.txt
|
|
||||||
url: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
|
||||||
'''
|
|
||||||
|
|
||||||
assert captured.out == ''
|
assert captured.out == ''
|
||||||
assert captured.err == expected_stderr
|
assert captured.err == EXPECTED_STDERR_OTP_URL_WRONG
|
||||||
|
|
||||||
|
|
||||||
def test_one_wrong_file(capsys: pytest.CaptureFixture[str]) -> None:
|
def test_one_wrong_file(capsys: pytest.CaptureFixture[str]) -> None:
|
||||||
@@ -853,19 +880,8 @@ def test_one_wrong_file(capsys: pytest.CaptureFixture[str]) -> None:
|
|||||||
# Assert
|
# Assert
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
|
|
||||||
expected_stderr = '''
|
|
||||||
WARN: input is not a otpauth-migration:// url
|
|
||||||
source: tests/data/test_export_wrong_content.txt
|
|
||||||
input: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
|
||||||
Maybe a wrong file was given
|
|
||||||
|
|
||||||
ERROR: could not parse query parameter in input url
|
|
||||||
source: tests/data/test_export_wrong_content.txt
|
|
||||||
url: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
|
||||||
'''
|
|
||||||
|
|
||||||
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT
|
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT
|
||||||
assert captured.err == expected_stderr
|
assert captured.err == EXPECTED_STDERR_OTP_URL_WRONG
|
||||||
|
|
||||||
|
|
||||||
def test_one_wrong_file_colored(capsys: pytest.CaptureFixture[str]) -> None:
|
def test_one_wrong_file_colored(capsys: pytest.CaptureFixture[str]) -> None:
|
||||||
@@ -875,19 +891,8 @@ def test_one_wrong_file_colored(capsys: pytest.CaptureFixture[str]) -> None:
|
|||||||
# Assert
|
# Assert
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
|
|
||||||
expected_stderr = f'''{colorama.Fore.RED}
|
|
||||||
WARN: input is not a otpauth-migration:// url
|
|
||||||
source: tests/data/test_export_wrong_content.txt
|
|
||||||
input: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
|
||||||
Maybe a wrong file was given{colorama.Fore.RESET}
|
|
||||||
{colorama.Fore.RED}
|
|
||||||
ERROR: could not parse query parameter in input url
|
|
||||||
source: tests/data/test_export_wrong_content.txt
|
|
||||||
url: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.{colorama.Fore.RESET}
|
|
||||||
'''
|
|
||||||
|
|
||||||
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT
|
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT
|
||||||
assert captured.err == expected_stderr
|
assert captured.err == EXPECTED_STDERR_COLORED_OTP_URL_WRONG
|
||||||
|
|
||||||
|
|
||||||
def test_one_wrong_line(capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch) -> None:
|
def test_one_wrong_line(capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
@@ -959,6 +964,46 @@ def test_img_qr_reader_from_file_happy_path(capsys: pytest.CaptureFixture[str])
|
|||||||
assert captured.err == ''
|
assert captured.err == ''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.qreader
|
||||||
|
def test_img_qr_reader_but_no_otp_from_file(capsys: pytest.CaptureFixture[str]) -> None:
|
||||||
|
# Act
|
||||||
|
extract_otp_secrets.main(['-n', 'tests/data/qr_but_without_otp.png'])
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
|
||||||
|
assert captured.out == ''
|
||||||
|
assert captured.err == EXPECTED_STDERR_NO_OTP_URL
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.qreader
|
||||||
|
def test_img_qr_reader_from_wildcard(capsys: pytest.CaptureFixture[str]) -> None:
|
||||||
|
# Act
|
||||||
|
extract_otp_secrets.main(['-n', 'tests/data/*.png'])
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
|
||||||
|
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT_PNG
|
||||||
|
assert normalize_testfile_path(captured.err) == EXPECTED_STDERR_NO_OTP_URL
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_testfile_path(text: str) -> str:
|
||||||
|
return text.replace('tests/data\\', 'tests/data/') if sys.platform.startswith("win") else text
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.qreader
|
||||||
|
def test_img_qr_reader_from_multiple_files(capsys: pytest.CaptureFixture[str]) -> None:
|
||||||
|
# Act
|
||||||
|
extract_otp_secrets.main(['-n', 'tests/data/test_googleauth_export.png', 'tests/data/text_masquerading_as_image.jpeg'])
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
|
||||||
|
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT_PNG
|
||||||
|
assert captured.err == EXPECTED_STDERR_BAD_IMAGE
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.qreader
|
@pytest.mark.qreader
|
||||||
def test_img_qr_reader_by_parameter(capsys: pytest.CaptureFixture[str], qr_mode: str) -> None:
|
def test_img_qr_reader_by_parameter(capsys: pytest.CaptureFixture[str], qr_mode: str) -> None:
|
||||||
# Act
|
# Act
|
||||||
@@ -1003,24 +1048,7 @@ def test_img_qr_reader_from_stdin(capsys: pytest.CaptureFixture[str], monkeypatc
|
|||||||
# Assert
|
# Assert
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
|
|
||||||
expected_stdout = '''Name: Test1:test1@example1.com
|
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT_PNG
|
||||||
Secret: JBSWY3DPEHPK3PXP
|
|
||||||
Issuer: Test1
|
|
||||||
Type: totp
|
|
||||||
|
|
||||||
Name: Test2:test2@example2.com
|
|
||||||
Secret: JBSWY3DPEHPK3PXQ
|
|
||||||
Issuer: Test2
|
|
||||||
Type: totp
|
|
||||||
|
|
||||||
Name: Test3:test3@example3.com
|
|
||||||
Secret: JBSWY3DPEHPK3PXR
|
|
||||||
Issuer: Test3
|
|
||||||
Type: totp
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
assert captured.out == expected_stdout
|
|
||||||
assert captured.err == ''
|
assert captured.err == ''
|
||||||
|
|
||||||
|
|
||||||
@@ -1105,19 +1133,9 @@ def test_non_image_file(capsys: pytest.CaptureFixture[str]) -> None:
|
|||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
expected_stderr = '''
|
|
||||||
WARN: input is not a otpauth-migration:// url
|
|
||||||
source: tests/data/text_masquerading_as_image.jpeg
|
|
||||||
input: This is just a text file masquerading as an image file.
|
|
||||||
Maybe a wrong file was given
|
|
||||||
|
|
||||||
ERROR: could not parse query parameter in input url
|
|
||||||
source: tests/data/text_masquerading_as_image.jpeg
|
|
||||||
url: This is just a text file masquerading as an image file.
|
|
||||||
'''
|
|
||||||
|
|
||||||
assert captured.err == expected_stderr
|
|
||||||
assert captured.out == ''
|
assert captured.out == ''
|
||||||
|
assert captured.err == EXPECTED_STDERR_BAD_IMAGE
|
||||||
|
|
||||||
|
|
||||||
def test_next_valid_qr_mode() -> None:
|
def test_next_valid_qr_mode() -> None:
|
||||||
@@ -1171,3 +1189,47 @@ Issuer: Test3
|
|||||||
Type: totp
|
Type: totp
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
EXPECTED_STDERR_OTP_URL_WRONG = '''
|
||||||
|
WARN: input is not a otpauth-migration:// url
|
||||||
|
source: tests/data/test_export_wrong_content.txt
|
||||||
|
input: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||||
|
Maybe a wrong file was given
|
||||||
|
|
||||||
|
ERROR: could not parse query parameter in input url
|
||||||
|
source: tests/data/test_export_wrong_content.txt
|
||||||
|
url: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXPECTED_STDERR_COLORED_OTP_URL_WRONG = f'''{colorama.Fore.RED}
|
||||||
|
WARN: input is not a otpauth-migration:// url
|
||||||
|
source: tests/data/test_export_wrong_content.txt
|
||||||
|
input: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||||
|
Maybe a wrong file was given{colorama.Fore.RESET}
|
||||||
|
{colorama.Fore.RED}
|
||||||
|
ERROR: could not parse query parameter in input url
|
||||||
|
source: tests/data/test_export_wrong_content.txt
|
||||||
|
url: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.{colorama.Fore.RESET}
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXPECTED_STDERR_NO_OTP_URL = '''
|
||||||
|
WARN: input is not a otpauth-migration:// url
|
||||||
|
source: tests/data/qr_but_without_otp.png
|
||||||
|
input: NOT A otpauth-migration:// URL
|
||||||
|
Maybe a wrong file was given
|
||||||
|
|
||||||
|
ERROR: could not parse query parameter in input url
|
||||||
|
source: tests/data/qr_but_without_otp.png
|
||||||
|
url: NOT A otpauth-migration:// URL
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXPECTED_STDERR_BAD_IMAGE = '''
|
||||||
|
WARN: input is not a otpauth-migration:// url
|
||||||
|
source: tests/data/text_masquerading_as_image.jpeg
|
||||||
|
input: This is just a text file masquerading as an image file.
|
||||||
|
Maybe a wrong file was given
|
||||||
|
|
||||||
|
ERROR: could not parse query parameter in input url
|
||||||
|
source: tests/data/text_masquerading_as_image.jpeg
|
||||||
|
url: This is just a text file masquerading as an image file.
|
||||||
|
'''
|
||||||
|
|||||||
Reference in New Issue
Block a user