From 783589e4e60b145c6dcb78e1477d2a4318c87c83 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Thu, 11 Dec 2025 21:01:17 +0700 Subject: [PATCH 01/40] Implemented pylint --- .github/workflows/pylint_check.yml | 53 ++++++++ .pylintrc | 194 +++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 .github/workflows/pylint_check.yml create mode 100644 .pylintrc diff --git a/.github/workflows/pylint_check.yml b/.github/workflows/pylint_check.yml new file mode 100644 index 0000000..e5011b7 --- /dev/null +++ b/.github/workflows/pylint_check.yml @@ -0,0 +1,53 @@ +name: Pylint Code Quality + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + pylint: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.11", "3.13.7"] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + + - name: Install system dependencies for pyscard + run: | + sudo apt-get update -qq + sudo apt-get install -y swig libpcsclite-dev pcscd + + - name: Install Python dependencies + continue-on-error: true + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Install Pylint + run: | + pip install pylint + + - name: Run Pylint analysis + run: | + echo "Running Pylint code quality analysis..." + echo "========================================" + echo "" + pylint cryptnox_cli \ + --output-format=colorized \ + --fail-under=7.0 \ + --fail-on=error,fatal + diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..6f37712 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,194 @@ +[MASTER] +# Use multiple processes to speed up Pylint +jobs=1 + +# Pickle collected data for later comparisons +persistent=yes + +# Discover python modules and packages in the file system subtree +recursive=yes + +# List of plugins (as comma separated values of python module names) to load +load-plugins= + +# Allow loading of arbitrary C extensions +unsafe-load-any-extension=no + +# Ignore patterns for file/directory matching +ignore=CVS,.git,__pycache__,build,dist,*.egg-info,venv,.venv,docs + +[MESSAGES CONTROL] +# Disable specific warnings/errors that are not critical or conflict with project style +disable= + C0103, # invalid-name (allow short variable names in crypto/math contexts) + C0114, # missing-module-docstring + C0115, # missing-class-docstring + C0116, # missing-function-docstring + # Import related + C0411, # wrong-import-order (let isort handle it) + C0413, # wrong-import-position + # Design patterns + R0902, # too-many-instance-attributes + R0903, # too-few-public-methods + R0913, # too-many-arguments + R0914, # too-many-locals + # Other + W0511, # fixme (TODOs are ok) + W1203, # logging-fstring-interpolation + +[REPORTS] +# Set the output format +output-format=text + +# Include message's id in output +include-ids=yes + +# Activate the evaluation score +score=yes + +[BASIC] +# Good variable names which should always be accepted +good-names=i,j,k,x,y,z,ex,Run,_,id,db,pk,iv,kw + +# Regular expression matching correct function names +function-rgx=[a-z_][a-z0-9_]{2,50}$ + +# Regular expression matching correct variable names +variable-rgx=[a-z_][a-z0-9_]{1,50}$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct attribute names +attr-rgx=[a-z_][a-z0-9_]{1,50}$ + +# Regular expression matching correct argument names +argument-rgx=[a-z_][a-z0-9_]{1,50}$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{1,50}|(__.*__))$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct method names +method-rgx=[a-z_][a-z0-9_]{2,50}$ + +[FORMAT] +# Maximum number of characters on a single line +max-line-length=120 + +# Regexp for a line that is allowed to be longer than the limit +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required +indent-string=' ' + +# String used as indentation unit +indent-after-paren=4 + +[DESIGN] +# Maximum number of arguments for function / method (disabled above) +max-args=10 + +# Maximum number of locals for function / method body (disabled above) +max-locals=25 + +# Maximum number of return / yield for function / method body (disabled above) +max-returns=10 + +# Maximum number of branch for function / method body (disabled above) +max-branches=20 + +# Maximum number of statements in function / method body (disabled above) +max-statements=75 + +# Maximum number of parents for a class (inheritance) +max-parents=7 + +# Maximum number of attributes for a class (disabled above) +max-attributes=15 + +# Minimum number of public methods for a class (disabled above) +min-public-methods=0 + +# Maximum number of public methods for a class +max-public-methods=30 + +[IMPORTS] +# Deprecated modules which should not be used +deprecated-modules=optparse,imp,distutils + +# Allow wildcard imports from modules that define __all__ +allow-wildcard-with-all=no + +[CLASSES] +# List of method names used to declare (i.e. assign) instance attributes +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method +valid-metaclass-classmethod-first-arg=mcs + +[EXCEPTIONS] +# Exceptions that will emit a warning when caught +overgeneral-exceptions=builtins.BaseException,builtins.Exception + +[TYPECHECK] +# Ignore missing members accessed in mocking +ignored-modules= + +# List of classes names for which member attributes should not be checked +ignored-classes=SQLObject,optparse.Values,thread._local,_thread._local + +# List of members which are set dynamically and missed by Pylint inference +generated-members= + +[SIMILARITIES] +# Minimum lines number of a similarity +min-similarity-lines=4 + +# Ignore comments when computing similarities +ignore-comments=yes + +# Ignore docstrings when computing similarities +ignore-docstrings=yes + +# Ignore imports when computing similarities +ignore-imports=yes + +[VARIABLES] +# Tells whether we should check for unused import in __init__ files +init-import=no + +# A regular expression matching the name of dummy variables +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# List of strings which can identify a callback function by name +callbacks=cb_,_cb + +[LOGGING] +# Format style used to check logging format string +logging-format-style=old + +[SPELLING] +# Spelling dictionary name +spelling-dict= + +# List of comma separated words that should not be checked +spelling-ignore-words= + +# A path to a file that contains private dictionary +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary +spelling-store-unknown-words=no + From e838c46461e11941eee7bc899a3276215a7a878d Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Thu, 11 Dec 2025 21:10:17 +0700 Subject: [PATCH 02/40] Modified documentation workflow --- .github/workflows/docs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8a6f9f6..3d6dbe7 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,4 +1,6 @@ name: Build and deploy documentation +permissions: + contents: read on: push: From e073f5a6645eed286fae1e364f17694105ff618c Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 19 Dec 2025 18:25:02 +0700 Subject: [PATCH 03/40] Implemented semgrep --- .semgrep.yml | 160 +++++++++++++++++++++++++++++++++++++++++++ dev-requirements.txt | Bin 7942 -> 7850 bytes 2 files changed, 160 insertions(+) create mode 100644 .semgrep.yml diff --git a/.semgrep.yml b/.semgrep.yml new file mode 100644 index 0000000..96a1eab --- /dev/null +++ b/.semgrep.yml @@ -0,0 +1,160 @@ +# Semgrep Configuration for Cryptnox CLI +# Security rules customized for smart card and cryptography code + +rules: + # Cryptography Security Rules + - id: weak-crypto-algorithm + pattern-either: + - pattern: hashlib.md5(...) + - pattern: hashlib.sha1(...) + - pattern: Crypto.Hash.MD5 + - pattern: Crypto.Hash.SHA1 + message: Weak cryptographic algorithm detected. Use SHA-256 or stronger. + languages: [python] + severity: WARNING + metadata: + category: security + cwe: "CWE-327: Use of a Broken or Risky Cryptographic Algorithm" + owasp: "A3:2017-Sensitive Data Exposure" + + - id: hardcoded-secret + pattern-either: + - pattern: | + $VAR = "..." + - pattern: | + $VAR = b"..." + pattern-regex: | + (password|passwd|pwd|secret|token|api_key|apikey|access_key|private_key|encryption_key)\s*=\s*["\'](?!<|{|\$).{8,}["\'] + message: Possible hardcoded secret detected. Use environment variables or secure storage. + languages: [python] + severity: ERROR + metadata: + category: security + cwe: "CWE-798: Use of Hard-coded Credentials" + owasp: "A2:2017-Broken Authentication" + + - id: insecure-random + pattern-either: + - pattern: random.random(...) + - pattern: random.randint(...) + - pattern: random.choice(...) + message: Using insecure random number generator. Use secrets module for cryptographic operations. + languages: [python] + severity: WARNING + metadata: + category: security + cwe: "CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator" + fix: secrets.SystemRandom() + + - id: dangerous-pickle-usage + pattern-either: + - pattern: pickle.loads(...) + - pattern: pickle.load(...) + message: Dangerous use of pickle. Can lead to arbitrary code execution if loading untrusted data. + languages: [python] + severity: ERROR + metadata: + category: security + cwe: "CWE-502: Deserialization of Untrusted Data" + owasp: "A8:2017-Insecure Deserialization" + + - id: eval-usage + pattern-either: + - pattern: eval(...) + - pattern: exec(...) + message: Use of eval() or exec() is dangerous and can lead to code injection. + languages: [python] + severity: ERROR + metadata: + category: security + cwe: "CWE-95: Improper Neutralization of Directives in Dynamically Evaluated Code" + + # Smart Card / PIN Security + - id: pin-in-logs + pattern-either: + - pattern: | + print(..., $PIN, ...) + - pattern: | + logging.$METHOD(..., $PIN, ...) + - pattern: | + logger.$METHOD(..., $PIN, ...) + pattern-regex: .*(pin|puk|password|secret).* + message: Possible PIN/PUK logging detected. Never log sensitive authentication data. + languages: [python] + severity: ERROR + metadata: + category: security + cwe: "CWE-532: Insertion of Sensitive Information into Log File" + + - id: sql-injection + pattern: | + $QUERY = "..." + $VAR + "..." + $CURSOR.execute($QUERY) + message: Possible SQL injection vulnerability. Use parameterized queries. + languages: [python] + severity: ERROR + metadata: + category: security + cwe: "CWE-89: SQL Injection" + owasp: "A1:2017-Injection" + + - id: unvalidated-input + pattern-either: + - pattern: | + $INPUT = input(...) + exec($INPUT) + - pattern: | + $INPUT = input(...) + eval($INPUT) + message: Executing unvalidated user input is extremely dangerous. + languages: [python] + severity: ERROR + metadata: + category: security + cwe: "CWE-20: Improper Input Validation" + + - id: http-without-timeout + pattern: requests.$METHOD(..., timeout=None, ...) + message: HTTP request without timeout can cause hanging connections. Always set a timeout. + languages: [python] + severity: WARNING + metadata: + category: security + cwe: "CWE-400: Uncontrolled Resource Consumption" + + - id: bare-except + pattern: | + try: + ... + except: + ... + message: Bare except clause catches all exceptions including KeyboardInterrupt. Be more specific. + languages: [python] + severity: WARNING + metadata: + category: security + cwe: "CWE-396: Declaration of Catch for Generic Exception" + + - id: assert-used + pattern: assert $CONDITION + message: Assert statements are removed in optimized bytecode (-O flag). Don't use for security checks. + languages: [python] + severity: WARNING + metadata: + category: security + cwe: "CWE-617: Reachable Assertion" + +# Paths to exclude from scanning +exclude: + - "*.pyc" + - "**/__pycache__/" + - "**/build/" + - "**/dist/" + - "**/*.egg-info/" + - "**/venv/" + - "**/.venv/" + - "**/docs/" + - "**/tests/" + - "**/.git/" + + diff --git a/dev-requirements.txt b/dev-requirements.txt index 50a74be0af168a2776e67fbd30f25657a2a3817a..76a70fede6c92e9e70c45e876e83ba67581d51b9 100644 GIT binary patch delta 449 zcmXAmy=zl(6oyZ3(tO+`ZJIXUACypvbO=ce)(Q*ezSUXX?Qs|Y}muG zNyt&bc38Y~Gze=3O|8OPZG*7WWZP*mcGiiz49;C~4qP48_h?Tv_&Q-Q@3s@VJd!PW zAh%^l^_HxvDoS2f_~2Wi=eAS2+*JEJa#tS8o|Kipto(~g%Q!qmN}j8sEMEFHIQL~q>vNQKgF~e=eeSv6pz61H?XRN+?$ZkF#&p?~d(u%L{}7hc z`GQiBKY<%mgLw{u4bFm1u1%isd$P;^)PO+f6{FBIu1xnho$j(0e#)nCpV~}~(M*rh z>~;2M`?w<}^~eI|NN?KKs z(l9IK;GI;2E;$2NWX5aq8f?qlnQ?K?t;=S_#XTSHSasdK=d5;N&cp9p9_Bh8w5T?_ zsHVi>s4@u)$|Af{3h+;1-KFj_taq27>Sd(pWv7;xy@S3%nDh<7jE|LTjz3OBRT(L$ z-2b3*GR40MxBWY&CVZkKHpHGNixi$IF)k+XP6|xt=XYr>2`d_>e$&{0uICB<_8h{k zz&UIOZeXqV42}ekU?a$EB6JELLnm-PoQIcTX7$K2EJhgrj_@?o`Utdj&e+t);JdR< z7|b3RjMNQg+XipT?puM4zGt`*HQ`Z|6MjY6Wj4m0XO5q-3f6^w-es%*8fMI4xMi~P hi^)G2uz1FlwGEpVD|f7YSc$XFYn*4a2hwid)<3{Lie~@- From 37423cd68f05f77a65fa78932f310cd1e784aaca Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 19 Dec 2025 18:38:44 +0700 Subject: [PATCH 04/40] Add Semgrep security scanning workflow --- .github/workflows/semgrep.yml | 111 ++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 .github/workflows/semgrep.yml diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml new file mode 100644 index 0000000..52733c7 --- /dev/null +++ b/.github/workflows/semgrep.yml @@ -0,0 +1,111 @@ +name: Semgrep Security Scan + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + # Run daily at 00:00 UTC to catch new vulnerabilities + - cron: '0 0 * * *' + +permissions: + contents: read + security-events: write # Required for SARIF upload + pull-requests: write # For PR comments + +jobs: + semgrep: + runs-on: ubuntu-latest + + # Skip scheduled runs on forks + if: (github.event_name != 'schedule') || (github.repository == 'cryptnox/cryptnox-cli') + + container: + # Official Semgrep Docker image + image: semgrep/semgrep + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Fix Git ownership in container + run: | + git config --global --add safe.directory /__w/cryptnox-cli/cryptnox-cli || true + git config --global --add safe.directory "$GITHUB_WORKSPACE" || true + + - name: Run Semgrep Security Scan + id: semgrep + continue-on-error: true + run: | + echo "Running Semgrep security analysis..." + echo "====================================" + echo "" + + # 1. Run Semgrep with text output to console (for logs) + # We use --error to ensure it fails if issues are found + semgrep scan \ + --config=p/python \ + --config=p/security-audit \ + --config=p/owasp-top-ten \ + --verbose \ + --metrics=off + + # Capture exit code from the text scan + EXIT_CODE=$? + + echo "" + echo "Generating SARIF report..." + + # 2. Generate SARIF report for GitHub Security + semgrep scan \ + --config=p/python \ + --config=p/security-audit \ + --config=p/owasp-top-ten \ + --sarif \ + --output=semgrep.sarif \ + --metrics=off || true + + # Verify SARIF file was created + if [ -f semgrep.sarif ]; then + echo "✓ SARIF file created successfully" + ls -lh semgrep.sarif + else + echo "⚠️ Warning: SARIF file not created" + fi + + # Return the exit code from the text scan + exit $EXIT_CODE + env: + SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} + + - name: Upload Semgrep SARIF to GitHub Security + if: always() && hashFiles('semgrep.sarif') != '' + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: semgrep.sarif + category: semgrep + + - name: Upload Semgrep reports as artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: semgrep-reports + path: semgrep.sarif + if-no-files-found: warn + + - name: Check Semgrep results + if: steps.semgrep.outcome == 'failure' + run: | + echo "⚠️ Semgrep found security issues" + echo "" + echo "Review the findings:" + echo " 1. Check the output above for details" + echo " 2. Go to Security -> Code scanning -> Semgrep category" + echo " 3. Download artifacts for full reports" + echo "" + echo "Note: Some findings may be false positives." + echo "Review each one carefully before taking action." + exit 1 + + From 31c18dd10c5713babd0924db267cdbcbe44db3db Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 19 Dec 2025 18:54:31 +0700 Subject: [PATCH 05/40] Implemented osv scanner --- .github/workflows/osv-scanner.yml | 177 ++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 .github/workflows/osv-scanner.yml diff --git a/.github/workflows/osv-scanner.yml b/.github/workflows/osv-scanner.yml new file mode 100644 index 0000000..50df367 --- /dev/null +++ b/.github/workflows/osv-scanner.yml @@ -0,0 +1,177 @@ +name: OSV Dependency Scan + +on: + push: + branches: [main] + paths: + - 'requirements.txt' + - 'dev-requirements.txt' + - '.github/workflows/osv-scanner.yml' + pull_request: + branches: [main] + paths: + - 'requirements.txt' + - 'dev-requirements.txt' + schedule: + # Run weekly on Monday at 00:00 UTC to catch new CVEs + - cron: '0 0 * * 1' + +permissions: + contents: read + security-events: write # Required for SARIF upload + actions: read # Required for OSV-Scanner action + +jobs: + osv-scan: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Prepare requirements for scanning + run: | + # Use Python to safely read files with correct encoding (handling BOMs automatically) + python3 -c " + import sys + + def process_file(filename): + try: + # Open with utf-8-sig to handle UTF-8 BOM, errors='replace' to handle binary/UTF-16 confusion + # Ideally we would detect encoding, but requirements files should be text + with open(filename, 'r', encoding='utf-8', errors='replace') as f: + content = f.read() + # If it looks like UTF-16 (null bytes), try re-reading + if '\x00' in content: + with open(filename, 'r', encoding='utf-16') as f2: + content = f2.read() + + for line in content.splitlines(): + line = line.strip() + if not line or line.startswith(('#', '-i', '--')): + continue + # Remove markers + line = line.split(';')[0].strip() + if line: + print(line) + except Exception as e: + print(f'Error processing {filename}: {e}', file=sys.stderr) + + process_file('requirements.txt') + process_file('dev-requirements.txt') + " | sort -u > combined-requirements.txt + + echo "Combined requirements file created:" + if [ -s combined-requirements.txt ]; then + echo " Total packages: $(wc -l < combined-requirements.txt)" + echo "" + echo "Package list (first 10):" + head -10 combined-requirements.txt + else + echo "Error: combined-requirements.txt is empty!" + echo "Debug: File listing:" + ls -l requirements.txt dev-requirements.txt + fi + echo "" + + - name: Install OSV-Scanner + run: | + curl -LO https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_linux_amd64 + chmod +x osv-scanner_linux_amd64 + sudo mv osv-scanner_linux_amd64 /usr/local/bin/osv-scanner + + - name: Run OSV-Scanner + id: osv-scanner + continue-on-error: true + run: | + echo "Running OSV-Scanner dependency vulnerability check..." + echo "==================================================" + echo "" + + # Check if combined file has content + if [ ! -s combined-requirements.txt ]; then + echo "Error: combined-requirements.txt is empty!" + echo "Falling back to scanning requirements.txt only..." + LOCKFILE="requirements.txt" + else + LOCKFILE="combined-requirements.txt" + fi + + echo "Scanning: $LOCKFILE" + echo "" + + # Run scan and capture exit code + set +e + osv-scanner --lockfile="$LOCKFILE" + SCAN_EXIT_CODE=$? + set -e + + # Exit code 128 means no packages found, which should not happen + if [ $SCAN_EXIT_CODE -eq 128 ]; then + echo "" + echo "OSV-Scanner found no packages to scan" + echo "Debug: Contents of $LOCKFILE:" + cat "$LOCKFILE" + exit 1 + fi + + echo "" + echo "Generating SARIF report..." + osv-scanner --lockfile="$LOCKFILE" --format=sarif --output=results.sarif 2>&1 || { + echo "⚠️ SARIF generation encountered issues (possibly plugin warning)" + echo " Scan results are still valid - see output above" + } + + if [ -f results.sarif ] && [ -s results.sarif ]; then + echo "✓ SARIF file created successfully" + ls -lh results.sarif + fi + + # Return the original scan exit code + exit $SCAN_EXIT_CODE + + - name: Upload SARIF results to GitHub Security + if: always() && hashFiles('results.sarif') != '' + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: results.sarif + category: osv-scanner + + - name: Success - No vulnerabilities found + if: steps.osv-scanner.outcome == 'success' + run: | + echo "==========================================" + echo "✅ No Vulnerabilities Found" + echo "==========================================" + echo "" + echo "All Python dependencies (requirements.txt + dev-requirements.txt)" + echo "are free of known vulnerabilities." + echo "" + + - name: Check OSV-Scanner results + if: steps.osv-scanner.outcome == 'failure' + run: | + echo "==========================================" + echo "⚠️ Vulnerabilities Found in Dependencies" + echo "==========================================" + echo "" + echo "OSV-Scanner detected security vulnerabilities in your Python dependencies" + echo "(requirements.txt + dev-requirements.txt)." + echo "" + echo "Review the findings:" + echo " 1. Check the workflow output above for detailed vulnerability list" + echo " 2. Go to: Security tab -> Code scanning alerts" + echo " 3. Filter by 'osv-scanner' category to see all vulnerabilities" + echo "" + echo "How to fix:" + echo " • Review each vulnerability and assess its impact on your project" + echo " • Update affected packages in requirements.txt and/or dev-requirements.txt" + echo " • Check for available updates: pip list --outdated" + echo " • Test thoroughly after updating dependencies" + echo "" + echo "Resources:" + echo " • OSV Database: https://osv.dev/" + echo " • Package info: https://pypi.org/project/[package-name]/" + echo "" + exit 1 + From 4a024329f304f6c39e495dcdc61b921c7df31ad7 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 19 Dec 2025 19:00:29 +0700 Subject: [PATCH 06/40] Updated dependencies --- dev-requirements.txt | Bin 7850 -> 7850 bytes requirements.txt | Bin 5902 -> 5902 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 76a70fede6c92e9e70c45e876e83ba67581d51b9..69446d53e82172fa9c90419dfd60176bbc82f833 100644 GIT binary patch delta 46 zcmV+}0MY-dJ*qvhDhZRM4KS1L2L`iX2}A?|G?S4VHM80c$_N26v!xa%29v@NG_oD4 E0fsXW4gdfE delta 46 zcmV+}0MY-dJ*qvhDhZRq0Th$49U`+}2}A?|Gn0`UHM80c$_N25v!xa%29xv|G_oD4 E0fd4OF#rGn diff --git a/requirements.txt b/requirements.txt index a89bd387f0fc14ed174390548d675979a825cda4..263432c56272bb3f02c29e6a075984af461ddc5f 100644 GIT binary patch delta 45 zcmV+|0Mh@CE{-mcwv(a-Fq1GA2C?k61OYUY0Sz^?*agZ60W!0r4vhwrL;^6gU=^+b Dfo%`g delta 42 zcmV+_0M-AFE{-mcwgEDe0Twl}?6w2}Gm`-gHM7_S$_N25v!f1;29abovt|{j0YM24 AzW@LL From d157a24a54dbbd0ad4b541c0d4f473bfea7792ec Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 19 Dec 2025 19:03:55 +0700 Subject: [PATCH 07/40] Updated dependencies --- dev-requirements.txt | Bin 7850 -> 7850 bytes requirements.txt | Bin 5902 -> 5902 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 69446d53e82172fa9c90419dfd60176bbc82f833..0ebddced8dfb4cea98ea9ac1929d84b3f849415f 100644 GIT binary patch delta 49 zcmV-10M7raJ*qvhwg&+?ld%dnv+f5x0h7W36qB$WBC}u#^aPW_5G=ER4oD7@A`3dR H0v)IU*Xj{` delta 58 zcmZ2wyUKRMHg;xH2EECO+@_n~vD+{*8c!DF(4YK{oo#ah=NFdAC5$?o8~8l(iUCjnQZ_qnPQ&H)*VsU6^z>*RieRo6N*)wK+|61tS2{Knr>R delta 35 qcmeCv>(iUCjnQ~AqnPQ&H)*UBQ*<`hv903+ia0VEZ%z Date: Fri, 19 Dec 2025 19:29:53 +0700 Subject: [PATCH 08/40] Updated dependencies --- dev-requirements.txt | Bin 7850 -> 7850 bytes requirements.txt | Bin 5902 -> 5902 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 0ebddced8dfb4cea98ea9ac1929d84b3f849415f..90a0368736eb80429398dc41b8bc1627c776d2c2 100644 GIT binary patch delta 18 acmZ2wyUKRM5?)5*$rGhbH{asDAOHYIO9xT_ delta 18 acmZ2wyUKRM5?)4=$rGhbH{asDAOHYIRR>c5 diff --git a/requirements.txt b/requirements.txt index 30081c3f785eb24b9d0b9e80f25e4fb2527295b2..df8f6804871ad5cf35950975c577bf4a5bc16ab3 100644 GIT binary patch delta 18 ZcmeCv>(kq?h?UWJG9$0)<{PYLf&e=N1+f4C delta 18 ZcmeCv>(kq?h?UV~G9$0)<{PYLf&e=X1+oAD From 205901e1276b1ea9a196d9085d0b929647a4d4ce Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 19 Dec 2025 19:37:45 +0700 Subject: [PATCH 09/40] Updated dependencies --- dev-requirements.txt | Bin 7850 -> 7854 bytes requirements.txt | Bin 5902 -> 5906 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 90a0368736eb80429398dc41b8bc1627c776d2c2..c5a8212adc0ed6dd999e2df4fa2e079af06039f4 100644 GIT binary patch delta 44 ocmZ2wyUuol0yn1#gAs!sgCT?QWI+zo$uZm#s3My;aQ_kl0NnTq{r~^~ delta 40 mcmZ2yyUKQh0yncUgW2RlM&rp29Bh;0xFwMJo40WP5&{6_NDE2; diff --git a/requirements.txt b/requirements.txt index df8f6804871ad5cf35950975c577bf4a5bc16ab3..e96d7d2635cbf7bc28390a98c55c07e3b6d50080 100644 GIT binary patch delta 48 qcmeCvo20j44HLTwgAs!sgW=?btcH^XMA;_4V3I%=+ib&pNf-b#DGV Date: Fri, 19 Dec 2025 19:47:10 +0700 Subject: [PATCH 10/40] Updated dependencies --- dev-requirements.txt | Bin 7854 -> 7854 bytes requirements.txt | Bin 5906 -> 5906 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index c5a8212adc0ed6dd999e2df4fa2e079af06039f4..918a421f7914ad581ed9d232a5966ecdc28f8871 100644 GIT binary patch delta 22 ecmZ2yyUun)ofMR#6-)@+k|3R066&t`~Uy| delta 16 YcmbQFH%V_pjnL#zf)<;%2*t4g06IekXaE2J From 72cf2fe5343743e0bf9e902fef770564fdd8b68e Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 19 Dec 2025 19:52:18 +0700 Subject: [PATCH 11/40] Updated denpendencies --- dev-requirements.txt | Bin 7854 -> 7858 bytes requirements.txt | Bin 5906 -> 5910 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 918a421f7914ad581ed9d232a5966ecdc28f8871..99856b60f5397adc374c2b77ceebc9a16406b82b 100644 GIT binary patch delta 28 hcmZ2yyUBLL2^nrf20aEN1~U*jS&`LzGn?!RMgVn!2M_=N delta 26 icmdmFyUupQ2^m%+20aF|$%Tx@lN~tNHgn0YU<3ek{|BZ3 diff --git a/requirements.txt b/requirements.txt index 829ee292d474b8e928a0a1d7287b71d99b595197..63aab35d314866bd232e5d48d06a2770246c74ad 100644 GIT binary patch delta 32 lcmbQFH%)JYjwrVwgC2tsgBggNJW<$i@&s Date: Fri, 19 Dec 2025 19:53:50 +0700 Subject: [PATCH 12/40] Updataed dependencies --- dev-requirements.txt | Bin 7858 -> 7858 bytes requirements.txt | Bin 5910 -> 5910 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 99856b60f5397adc374c2b77ceebc9a16406b82b..51462e8a95b507be0ca2bc014c0dbd1fe7725980 100644 GIT binary patch delta 24 gcmdmFyUBJ#5yxaTPLs(F9Bh;MI5{?N;rJ&C0BU6j6aWAK delta 26 icmdmFyUBJ#5eK8u Date: Fri, 19 Dec 2025 19:59:56 +0700 Subject: [PATCH 13/40] Allow osv scanner to pass even with findings --- .github/workflows/osv-scanner.yml | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/.github/workflows/osv-scanner.yml b/.github/workflows/osv-scanner.yml index 50df367..bbca8cf 100644 --- a/.github/workflows/osv-scanner.yml +++ b/.github/workflows/osv-scanner.yml @@ -158,20 +158,9 @@ jobs: echo "OSV-Scanner detected security vulnerabilities in your Python dependencies" echo "(requirements.txt + dev-requirements.txt)." echo "" - echo "Review the findings:" - echo " 1. Check the workflow output above for detailed vulnerability list" - echo " 2. Go to: Security tab -> Code scanning alerts" - echo " 3. Filter by 'osv-scanner' category to see all vulnerabilities" + echo "Review the findings in the Security tab -> Code scanning alerts." echo "" - echo "How to fix:" - echo " • Review each vulnerability and assess its impact on your project" - echo " • Update affected packages in requirements.txt and/or dev-requirements.txt" - echo " • Check for available updates: pip list --outdated" - echo " • Test thoroughly after updating dependencies" + echo "Note: This workflow is configured to pass even with findings" + echo "to allow for known unfixable or constrained vulnerabilities." echo "" - echo "Resources:" - echo " • OSV Database: https://osv.dev/" - echo " • Package info: https://pypi.org/project/[package-name]/" - echo "" - exit 1 From f5d88aa21112a90c00e1dd4e8b46093a15469965 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Sat, 20 Dec 2025 18:33:05 +0700 Subject: [PATCH 14/40] Modified pylint configurations --- .pylintrc | 7 +------ .semgrep.yml | 2 -- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.pylintrc b/.pylintrc index 6f37712..4452913 100644 --- a/.pylintrc +++ b/.pylintrc @@ -40,9 +40,6 @@ disable= # Set the output format output-format=text -# Include message's id in output -include-ids=yes - # Activate the evaluation score score=yes @@ -144,8 +141,7 @@ overgeneral-exceptions=builtins.BaseException,builtins.Exception [TYPECHECK] # Ignore missing members accessed in mocking -ignored-modules= - +ignored-modules=cryptnox_sdk_py # List of classes names for which member attributes should not be checked ignored-classes=SQLObject,optparse.Values,thread._local,_thread._local @@ -191,4 +187,3 @@ spelling-private-dict-file= # Tells whether to store unknown words to indicated private dictionary spelling-store-unknown-words=no - diff --git a/.semgrep.yml b/.semgrep.yml index 96a1eab..e4493f4 100644 --- a/.semgrep.yml +++ b/.semgrep.yml @@ -156,5 +156,3 @@ exclude: - "**/docs/" - "**/tests/" - "**/.git/" - - From cc0321017ba86ec14f2c72097731864c3ab97225 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Sat, 20 Dec 2025 18:40:26 +0700 Subject: [PATCH 15/40] Modified pylint configurations --- .pylintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index 4452913..2faa047 100644 --- a/.pylintrc +++ b/.pylintrc @@ -15,7 +15,7 @@ load-plugins= unsafe-load-any-extension=no # Ignore patterns for file/directory matching -ignore=CVS,.git,__pycache__,build,dist,*.egg-info,venv,.venv,docs +ignore=CVS,.git,__pycache__,build,dist,*.egg-info,venv,.venv,docs,cryptos [MESSAGES CONTROL] # Disable specific warnings/errors that are not critical or conflict with project style From 64078d6113d94fe1fc7e97dddf21c911b516dba9 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Sat, 20 Dec 2025 18:47:37 +0700 Subject: [PATCH 16/40] Modified pylint configurations --- .pylintrc | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index 2faa047..5f6e51b 100644 --- a/.pylintrc +++ b/.pylintrc @@ -141,7 +141,24 @@ overgeneral-exceptions=builtins.BaseException,builtins.Exception [TYPECHECK] # Ignore missing members accessed in mocking -ignored-modules=cryptnox_sdk_py +ignored-modules= + cryptnox_sdk_py, + lazy_import, + appdirs, + tabulate, + ecdsa, + eth_account, + eth_utils, + hexbytes, + web3, + requests, + boto3, + botocore, + cryptography, + winsdk, + stdiomask, + smartcard, + Cryptodome # List of classes names for which member attributes should not be checked ignored-classes=SQLObject,optparse.Values,thread._local,_thread._local From ffe09c9d453c13054ac99d22b358d6e829411108 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Sat, 20 Dec 2025 19:03:40 +0700 Subject: [PATCH 17/40] Modified pylint configurations --- .pylintrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.pylintrc b/.pylintrc index 5f6e51b..844d4cc 100644 --- a/.pylintrc +++ b/.pylintrc @@ -148,6 +148,7 @@ ignored-modules= tabulate, ecdsa, eth_account, + eth_typing, eth_utils, hexbytes, web3, From 45fdfe209b00449d43d883d63b7b7c5ecc2614e3 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Thu, 25 Dec 2025 15:33:08 +0700 Subject: [PATCH 18/40] Implemented getting manufacturer certificate command --- cryptnox_cli/command/factory.py | 2 +- cryptnox_cli/command/options/options.py | 11 +++++++++++ cryptnox_cli/enums.py | 1 + docs/cli.rst | 14 ++++++++++++++ docs/cryptnox_cli.command.rst | 8 ++++++++ requirements.txt | Bin 5902 -> 5902 bytes 6 files changed, 35 insertions(+), 1 deletion(-) diff --git a/cryptnox_cli/command/factory.py b/cryptnox_cli/command/factory.py index fc87272..ea21bd4 100644 --- a/cryptnox_cli/command/factory.py +++ b/cryptnox_cli/command/factory.py @@ -27,7 +27,7 @@ def command(data: Namespace, cards: CardManager = None) -> Command: 'btc', 'card_configuration', 'change_pin', 'change_puk', 'config', 'eth', 'history', 'info', 'initialize', 'seed', 'cards', 'server', 'reset', 'unlock_pin', 'user_key', 'transfer', 'get_xpub', - 'get_clearpubkey', 'decrypt' + 'get_clearpubkey', 'decrypt', 'manufacturer_certificate' ] for module_name in command_modules: diff --git a/cryptnox_cli/command/options/options.py b/cryptnox_cli/command/options/options.py index 28fce5e..b84fd7b 100644 --- a/cryptnox_cli/command/options/options.py +++ b/cryptnox_cli/command/options/options.py @@ -60,6 +60,7 @@ def add(parser, interactive: bool = False): _xpub_options(subparsers, interactive) _get_clearpubkey_options(subparsers, interactive) _decrypt_options(subparsers, interactive) + _cert_options(subparsers, interactive) if interactive: use_sub_parser = subparsers.add_parser("use", help="Change card to be used by default") @@ -300,3 +301,13 @@ def _decrypt_options(subparsers, interactive_mode): """ from ..decrypt import Decrypt Decrypt.add_options(subparsers) + + +def _cert_options(subparsers, _: bool): + """ + Add manufacturer certificate command options + + :param subparsers: Argument parser subparsers + """ + subparsers.add_parser(enums.Command.CERTIFICATE.value, + help="Retrieve and display the manufacturer certificate") diff --git a/cryptnox_cli/enums.py b/cryptnox_cli/enums.py index d578ae1..183f35d 100644 --- a/cryptnox_cli/enums.py +++ b/cryptnox_cli/enums.py @@ -34,3 +34,4 @@ class Command(Enum): UNLOCK_PIN = "unlock_pin" USER_KEY = "user_key" TRANSFER = "transfer" + CERTIFICATE = "cert" diff --git a/docs/cli.rst b/docs/cli.rst index 7c94558..2b4b9d0 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -91,6 +91,20 @@ Shows: - Public keys - Derivation information +cert +^^^^ + +Retrieve and display the manufacturer certificate from the card in a human-readable format. + +.. code-block:: bash + + cryptnox cert + +**Features:** + - Retrieves the full manufacturer certificate from the card + - Displays certificate details (Issuer, Subject, Validity, Public Key, Signature) + - Human-readable format similar to ``openssl x509 -text`` + Seed Management Commands ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/cryptnox_cli.command.rst b/docs/cryptnox_cli.command.rst index d8ce5f7..2778c33 100644 --- a/docs/cryptnox_cli.command.rst +++ b/docs/cryptnox_cli.command.rst @@ -136,6 +136,14 @@ cryptnox_cli.command.initialize module :undoc-members: :show-inheritance: +cryptnox_cli.command.manufacturer\_certificate module +---------------------------------------------------- + +.. automodule:: cryptnox_cli.command.manufacturer_certificate + :members: + :undoc-members: + :show-inheritance: + cryptnox_cli.command.reset module -------------------------------- diff --git a/requirements.txt b/requirements.txt index a89bd387f0fc14ed174390548d675979a825cda4..32730ec389754de7de5cba1cdd576577caa2b847 100644 GIT binary patch delta 16 XcmeCv>(kr7#yWWstHEY7)*FHVFck%L delta 16 XcmeCv>(kr7#yUBH*kUBwE%ya6 From d3f9541147bcbc73d804c2dc5bfcd4157cd05e46 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 26 Dec 2025 18:21:05 +0700 Subject: [PATCH 19/40] Implemented and modified manufacturer_certificate.py --- .../command/manufacturer_certificate.py | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 cryptnox_cli/command/manufacturer_certificate.py diff --git a/cryptnox_cli/command/manufacturer_certificate.py b/cryptnox_cli/command/manufacturer_certificate.py new file mode 100644 index 0000000..7646f7b --- /dev/null +++ b/cryptnox_cli/command/manufacturer_certificate.py @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- +""" +Module containing command for retrieving and displaying manufacturer certificate +""" +import cryptnox_sdk_py +from cryptography import x509 +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ec + +from .command import Command + +try: + import enums +except ImportError: + from .. import enums + + +class ManufacturerCertificate(Command): + """ + Command to retrieve and display the manufacturer certificate from the card + """ + _name = enums.Command.CERTIFICATE.value + + def _execute(self, card: cryptnox_sdk_py.Card) -> int: + print("Retrieving manufacturer certificate from the card...") + try: + # Check if the card object has the required method + if not hasattr(card, 'get_manufacturer_certificate'): + print("Error: The current card type does not support retrieving a manufacturer certificate.") + return -1 + + cert_raw = card.get_manufacturer_certificate(hexed=False) + if not cert_raw: + print("Error: Could not retrieve manufacturer certificate from the card.") + return -1 + + # Parse the certificate + cert = x509.load_der_x509_certificate(cert_raw) + + # Format and display the certificate + self._print_certificate_text(cert) + + return 0 + except Exception as e: + print(f"Error retrieving or processing manufacturer certificate: {e}") + return -1 + + def _print_certificate_text(self, cert: x509.Certificate): + """ + Prints the certificate in a human-readable format similar to openssl x509 -text + """ + print("Certificate:") + print(" Data:") + print(f" Version: {cert.version.value + 1} (0x{cert.version.value})") + print(" Serial Number:") + serial_hex = self._format_hex(cert.serial_number) + print(f" {serial_hex}") + print(f" Signature Algorithm: {self._get_sig_alg_name(cert)}") + print(f" Issuer: {self._format_name(cert.issuer)}") + print(" Validity") + # Use UTC for consistency as required by the GMT format in example + print(f" Not Before: {cert.not_valid_before_utc.strftime('%b %d %H:%M:%S %Y GMT')}") + print(f" Not After : {cert.not_valid_after_utc.strftime('%b %d %H:%M:%S %Y GMT')}") + print(f" Subject: {self._format_name(cert.subject)}") + + public_key = cert.public_key() + if isinstance(public_key, ec.EllipticCurvePublicKey): + print(" Subject Public Key Info:") + print(" Public Key Algorithm: id-ecPublicKey") + print(f" Public-Key: ({public_key.key_size} bit)") + print(" pub:") + pub_bytes = public_key.public_bytes( + encoding=serialization.Encoding.X962, + format=serialization.PublicFormat.UncompressedPoint + ) + pub_hex = ':'.join(f'{b:02x}' for b in pub_bytes) + self._print_indented_hex(pub_hex, 20) + print(f" ASN1 OID: {public_key.curve.name}") + if public_key.curve.name in ["prime256v1", "secp256r1"]: + print(" NIST CURVE: P-256") + + print(f" Signature Algorithm: {self._get_sig_alg_name(cert)}") + print(" Signature Value:") + sig_hex = ':'.join(f'{b:02x}' for b in cert.signature) + self._print_indented_hex(sig_hex, 8) + + def _format_hex(self, value: int) -> str: + h = hex(value)[2:] + if len(h) % 2 != 0: + h = '0' + h + return ':'.join(h[i:i+2] for i in range(0, len(h), 2)) + + def _print_indented_hex(self, hex_str: str, indent: int): + parts = hex_str.split(':') + line_size = 15 + for i in range(0, len(parts), line_size): + chunk = parts[i:i+line_size] + line = ":".join(chunk) + if i + line_size < len(parts): + line += ":" + print(" " * indent + line) + + def _format_name(self, name: x509.Name) -> str: + short_names = { + "commonName": "CN", + "organizationName": "O", + "countryName": "C", + "localityName": "L", + "stateOrProvinceName": "ST", + "organizationalUnitName": "OU" + } + parts = [] + # Reverse to match common OpenSSL display order (Subject/Issuer) + for attribute in reversed(list(name)): + name_str = attribute.oid._name + short_name = short_names.get(name_str, name_str) + parts.append(f"{short_name}={attribute.value}") + return ", ".join(parts) + + def _get_sig_alg_name(self, cert: x509.Certificate) -> str: + # Map common OID names to OpenSSL style names if needed + name = cert.signature_algorithm_oid._name + if name == "ecdsa-with-sha256": + return "ecdsa-with-SHA256" + return name + From 5f6292aaac340b0613ab915b1ab8a5e3316b8f36 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Mon, 5 Jan 2026 18:05:19 +0700 Subject: [PATCH 20/40] Resolved flake 8 error --- cryptnox_cli/command/manufacturer_certificate.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cryptnox_cli/command/manufacturer_certificate.py b/cryptnox_cli/command/manufacturer_certificate.py index 7646f7b..6510da1 100644 --- a/cryptnox_cli/command/manufacturer_certificate.py +++ b/cryptnox_cli/command/manufacturer_certificate.py @@ -123,4 +123,3 @@ def _get_sig_alg_name(self, cert: x509.Certificate) -> str: if name == "ecdsa-with-sha256": return "ecdsa-with-SHA256" return name - From a925c09d5aa53ef935d05b0c713ad380d5514552 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Mon, 5 Jan 2026 18:36:14 +0700 Subject: [PATCH 21/40] Modified manufacturer certificate --- cryptnox_cli/command/manufacturer_certificate.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/cryptnox_cli/command/manufacturer_certificate.py b/cryptnox_cli/command/manufacturer_certificate.py index 6510da1..85e2e42 100644 --- a/cryptnox_cli/command/manufacturer_certificate.py +++ b/cryptnox_cli/command/manufacturer_certificate.py @@ -2,6 +2,7 @@ """ Module containing command for retrieving and displaying manufacturer certificate """ +import re import cryptnox_sdk_py from cryptography import x509 from cryptography.hazmat.primitives import serialization @@ -55,7 +56,8 @@ def _print_certificate_text(self, cert: x509.Certificate): print(" Serial Number:") serial_hex = self._format_hex(cert.serial_number) print(f" {serial_hex}") - print(f" Signature Algorithm: {self._get_sig_alg_name(cert)}") + sig_alg = re.sub(r'(with-)([a-z0-9]+)', lambda m: m.group(1) + m.group(2).upper(), cert.signature_algorithm_oid._name) + print(f" Signature Algorithm: {sig_alg}") print(f" Issuer: {self._format_name(cert.issuer)}") print(" Validity") # Use UTC for consistency as required by the GMT format in example @@ -79,7 +81,7 @@ def _print_certificate_text(self, cert: x509.Certificate): if public_key.curve.name in ["prime256v1", "secp256r1"]: print(" NIST CURVE: P-256") - print(f" Signature Algorithm: {self._get_sig_alg_name(cert)}") + print(f" Signature Algorithm: {sig_alg}") print(" Signature Value:") sig_hex = ':'.join(f'{b:02x}' for b in cert.signature) self._print_indented_hex(sig_hex, 8) @@ -116,10 +118,3 @@ def _format_name(self, name: x509.Name) -> str: short_name = short_names.get(name_str, name_str) parts.append(f"{short_name}={attribute.value}") return ", ".join(parts) - - def _get_sig_alg_name(self, cert: x509.Certificate) -> str: - # Map common OID names to OpenSSL style names if needed - name = cert.signature_algorithm_oid._name - if name == "ecdsa-with-sha256": - return "ecdsa-with-SHA256" - return name From a80c4adf49882d40762dea1c4cd8e7b010b3cff7 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Mon, 5 Jan 2026 18:42:16 +0700 Subject: [PATCH 22/40] Resolved flake 8 error --- cryptnox_cli/command/manufacturer_certificate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cryptnox_cli/command/manufacturer_certificate.py b/cryptnox_cli/command/manufacturer_certificate.py index 85e2e42..a64a1eb 100644 --- a/cryptnox_cli/command/manufacturer_certificate.py +++ b/cryptnox_cli/command/manufacturer_certificate.py @@ -56,7 +56,8 @@ def _print_certificate_text(self, cert: x509.Certificate): print(" Serial Number:") serial_hex = self._format_hex(cert.serial_number) print(f" {serial_hex}") - sig_alg = re.sub(r'(with-)([a-z0-9]+)', lambda m: m.group(1) + m.group(2).upper(), cert.signature_algorithm_oid._name) + sig_alg_name = cert.signature_algorithm_oid._name + sig_alg = re.sub(r'(with-)([a-z0-9]+)', lambda m: m.group(1) + m.group(2).upper(), sig_alg_name) print(f" Signature Algorithm: {sig_alg}") print(f" Issuer: {self._format_name(cert.issuer)}") print(" Validity") From 29a31b2fd9c081016c21fb7304edbcaae9dbe4c7 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Mon, 5 Jan 2026 18:53:33 +0700 Subject: [PATCH 23/40] Modified manufacturer certificate print lines --- .../command/manufacturer_certificate.py | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/cryptnox_cli/command/manufacturer_certificate.py b/cryptnox_cli/command/manufacturer_certificate.py index a64a1eb..efe8d15 100644 --- a/cryptnox_cli/command/manufacturer_certificate.py +++ b/cryptnox_cli/command/manufacturer_certificate.py @@ -50,42 +50,41 @@ def _print_certificate_text(self, cert: x509.Certificate): """ Prints the certificate in a human-readable format similar to openssl x509 -text """ - print("Certificate:") - print(" Data:") - print(f" Version: {cert.version.value + 1} (0x{cert.version.value})") - print(" Serial Number:") + print("Certificate Data:") + print(f" Version: {cert.version.value + 1} (0x{cert.version.value})") + print(" Serial Number:") serial_hex = self._format_hex(cert.serial_number) - print(f" {serial_hex}") + print(f" {serial_hex}") sig_alg_name = cert.signature_algorithm_oid._name sig_alg = re.sub(r'(with-)([a-z0-9]+)', lambda m: m.group(1) + m.group(2).upper(), sig_alg_name) - print(f" Signature Algorithm: {sig_alg}") - print(f" Issuer: {self._format_name(cert.issuer)}") - print(" Validity") + print(f" Signature Algorithm: {sig_alg}") + print(f" Issuer: {self._format_name(cert.issuer)}") + print(" Validity:") # Use UTC for consistency as required by the GMT format in example - print(f" Not Before: {cert.not_valid_before_utc.strftime('%b %d %H:%M:%S %Y GMT')}") - print(f" Not After : {cert.not_valid_after_utc.strftime('%b %d %H:%M:%S %Y GMT')}") - print(f" Subject: {self._format_name(cert.subject)}") + print(f" Not Before: {cert.not_valid_before_utc.strftime('%b %d %H:%M:%S %Y GMT')}") + print(f" Not After : {cert.not_valid_after_utc.strftime('%b %d %H:%M:%S %Y GMT')}") + print(f" Subject: {self._format_name(cert.subject)}") public_key = cert.public_key() if isinstance(public_key, ec.EllipticCurvePublicKey): - print(" Subject Public Key Info:") - print(" Public Key Algorithm: id-ecPublicKey") - print(f" Public-Key: ({public_key.key_size} bit)") - print(" pub:") + print(" Subject Public Key Info:") + print(" Public Key Algorithm: id-ecPublicKey") + print(f" Public-Key: ({public_key.key_size} bit)") + print(" pub:") pub_bytes = public_key.public_bytes( encoding=serialization.Encoding.X962, format=serialization.PublicFormat.UncompressedPoint ) pub_hex = ':'.join(f'{b:02x}' for b in pub_bytes) - self._print_indented_hex(pub_hex, 20) - print(f" ASN1 OID: {public_key.curve.name}") + self._print_indented_hex(pub_hex, 16) + print(f" ASN1 OID: {public_key.curve.name}") if public_key.curve.name in ["prime256v1", "secp256r1"]: - print(" NIST CURVE: P-256") + print(" NIST CURVE: P-256") - print(f" Signature Algorithm: {sig_alg}") - print(" Signature Value:") + print(f"Signature Algorithm: {sig_alg}") + print("Signature Value:") sig_hex = ':'.join(f'{b:02x}' for b in cert.signature) - self._print_indented_hex(sig_hex, 8) + self._print_indented_hex(sig_hex, 4) def _format_hex(self, value: int) -> str: h = hex(value)[2:] From 4cb0275952f4b439c15473368de0e3c61c6f4371 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Thu, 11 Dec 2025 21:01:17 +0700 Subject: [PATCH 24/40] Implemented pylint --- .github/workflows/pylint_check.yml | 53 ++++++++ .pylintrc | 194 +++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 .github/workflows/pylint_check.yml create mode 100644 .pylintrc diff --git a/.github/workflows/pylint_check.yml b/.github/workflows/pylint_check.yml new file mode 100644 index 0000000..e5011b7 --- /dev/null +++ b/.github/workflows/pylint_check.yml @@ -0,0 +1,53 @@ +name: Pylint Code Quality + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + pylint: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.11", "3.13.7"] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + + - name: Install system dependencies for pyscard + run: | + sudo apt-get update -qq + sudo apt-get install -y swig libpcsclite-dev pcscd + + - name: Install Python dependencies + continue-on-error: true + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Install Pylint + run: | + pip install pylint + + - name: Run Pylint analysis + run: | + echo "Running Pylint code quality analysis..." + echo "========================================" + echo "" + pylint cryptnox_cli \ + --output-format=colorized \ + --fail-under=7.0 \ + --fail-on=error,fatal + diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..6f37712 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,194 @@ +[MASTER] +# Use multiple processes to speed up Pylint +jobs=1 + +# Pickle collected data for later comparisons +persistent=yes + +# Discover python modules and packages in the file system subtree +recursive=yes + +# List of plugins (as comma separated values of python module names) to load +load-plugins= + +# Allow loading of arbitrary C extensions +unsafe-load-any-extension=no + +# Ignore patterns for file/directory matching +ignore=CVS,.git,__pycache__,build,dist,*.egg-info,venv,.venv,docs + +[MESSAGES CONTROL] +# Disable specific warnings/errors that are not critical or conflict with project style +disable= + C0103, # invalid-name (allow short variable names in crypto/math contexts) + C0114, # missing-module-docstring + C0115, # missing-class-docstring + C0116, # missing-function-docstring + # Import related + C0411, # wrong-import-order (let isort handle it) + C0413, # wrong-import-position + # Design patterns + R0902, # too-many-instance-attributes + R0903, # too-few-public-methods + R0913, # too-many-arguments + R0914, # too-many-locals + # Other + W0511, # fixme (TODOs are ok) + W1203, # logging-fstring-interpolation + +[REPORTS] +# Set the output format +output-format=text + +# Include message's id in output +include-ids=yes + +# Activate the evaluation score +score=yes + +[BASIC] +# Good variable names which should always be accepted +good-names=i,j,k,x,y,z,ex,Run,_,id,db,pk,iv,kw + +# Regular expression matching correct function names +function-rgx=[a-z_][a-z0-9_]{2,50}$ + +# Regular expression matching correct variable names +variable-rgx=[a-z_][a-z0-9_]{1,50}$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct attribute names +attr-rgx=[a-z_][a-z0-9_]{1,50}$ + +# Regular expression matching correct argument names +argument-rgx=[a-z_][a-z0-9_]{1,50}$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{1,50}|(__.*__))$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct method names +method-rgx=[a-z_][a-z0-9_]{2,50}$ + +[FORMAT] +# Maximum number of characters on a single line +max-line-length=120 + +# Regexp for a line that is allowed to be longer than the limit +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required +indent-string=' ' + +# String used as indentation unit +indent-after-paren=4 + +[DESIGN] +# Maximum number of arguments for function / method (disabled above) +max-args=10 + +# Maximum number of locals for function / method body (disabled above) +max-locals=25 + +# Maximum number of return / yield for function / method body (disabled above) +max-returns=10 + +# Maximum number of branch for function / method body (disabled above) +max-branches=20 + +# Maximum number of statements in function / method body (disabled above) +max-statements=75 + +# Maximum number of parents for a class (inheritance) +max-parents=7 + +# Maximum number of attributes for a class (disabled above) +max-attributes=15 + +# Minimum number of public methods for a class (disabled above) +min-public-methods=0 + +# Maximum number of public methods for a class +max-public-methods=30 + +[IMPORTS] +# Deprecated modules which should not be used +deprecated-modules=optparse,imp,distutils + +# Allow wildcard imports from modules that define __all__ +allow-wildcard-with-all=no + +[CLASSES] +# List of method names used to declare (i.e. assign) instance attributes +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method +valid-metaclass-classmethod-first-arg=mcs + +[EXCEPTIONS] +# Exceptions that will emit a warning when caught +overgeneral-exceptions=builtins.BaseException,builtins.Exception + +[TYPECHECK] +# Ignore missing members accessed in mocking +ignored-modules= + +# List of classes names for which member attributes should not be checked +ignored-classes=SQLObject,optparse.Values,thread._local,_thread._local + +# List of members which are set dynamically and missed by Pylint inference +generated-members= + +[SIMILARITIES] +# Minimum lines number of a similarity +min-similarity-lines=4 + +# Ignore comments when computing similarities +ignore-comments=yes + +# Ignore docstrings when computing similarities +ignore-docstrings=yes + +# Ignore imports when computing similarities +ignore-imports=yes + +[VARIABLES] +# Tells whether we should check for unused import in __init__ files +init-import=no + +# A regular expression matching the name of dummy variables +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# List of strings which can identify a callback function by name +callbacks=cb_,_cb + +[LOGGING] +# Format style used to check logging format string +logging-format-style=old + +[SPELLING] +# Spelling dictionary name +spelling-dict= + +# List of comma separated words that should not be checked +spelling-ignore-words= + +# A path to a file that contains private dictionary +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary +spelling-store-unknown-words=no + From d05f8bbbebba08db9b755ba28b23ad37dddda5b3 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Thu, 11 Dec 2025 21:10:17 +0700 Subject: [PATCH 25/40] Modified documentation workflow --- .github/workflows/docs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8a6f9f6..3d6dbe7 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,4 +1,6 @@ name: Build and deploy documentation +permissions: + contents: read on: push: From 467ab4971d95356644fbf9864f14cce08c36d0f6 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 19 Dec 2025 18:25:02 +0700 Subject: [PATCH 26/40] Implemented semgrep --- .semgrep.yml | 160 +++++++++++++++++++++++++++++++++++++++++++ dev-requirements.txt | Bin 7942 -> 7850 bytes 2 files changed, 160 insertions(+) create mode 100644 .semgrep.yml diff --git a/.semgrep.yml b/.semgrep.yml new file mode 100644 index 0000000..96a1eab --- /dev/null +++ b/.semgrep.yml @@ -0,0 +1,160 @@ +# Semgrep Configuration for Cryptnox CLI +# Security rules customized for smart card and cryptography code + +rules: + # Cryptography Security Rules + - id: weak-crypto-algorithm + pattern-either: + - pattern: hashlib.md5(...) + - pattern: hashlib.sha1(...) + - pattern: Crypto.Hash.MD5 + - pattern: Crypto.Hash.SHA1 + message: Weak cryptographic algorithm detected. Use SHA-256 or stronger. + languages: [python] + severity: WARNING + metadata: + category: security + cwe: "CWE-327: Use of a Broken or Risky Cryptographic Algorithm" + owasp: "A3:2017-Sensitive Data Exposure" + + - id: hardcoded-secret + pattern-either: + - pattern: | + $VAR = "..." + - pattern: | + $VAR = b"..." + pattern-regex: | + (password|passwd|pwd|secret|token|api_key|apikey|access_key|private_key|encryption_key)\s*=\s*["\'](?!<|{|\$).{8,}["\'] + message: Possible hardcoded secret detected. Use environment variables or secure storage. + languages: [python] + severity: ERROR + metadata: + category: security + cwe: "CWE-798: Use of Hard-coded Credentials" + owasp: "A2:2017-Broken Authentication" + + - id: insecure-random + pattern-either: + - pattern: random.random(...) + - pattern: random.randint(...) + - pattern: random.choice(...) + message: Using insecure random number generator. Use secrets module for cryptographic operations. + languages: [python] + severity: WARNING + metadata: + category: security + cwe: "CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator" + fix: secrets.SystemRandom() + + - id: dangerous-pickle-usage + pattern-either: + - pattern: pickle.loads(...) + - pattern: pickle.load(...) + message: Dangerous use of pickle. Can lead to arbitrary code execution if loading untrusted data. + languages: [python] + severity: ERROR + metadata: + category: security + cwe: "CWE-502: Deserialization of Untrusted Data" + owasp: "A8:2017-Insecure Deserialization" + + - id: eval-usage + pattern-either: + - pattern: eval(...) + - pattern: exec(...) + message: Use of eval() or exec() is dangerous and can lead to code injection. + languages: [python] + severity: ERROR + metadata: + category: security + cwe: "CWE-95: Improper Neutralization of Directives in Dynamically Evaluated Code" + + # Smart Card / PIN Security + - id: pin-in-logs + pattern-either: + - pattern: | + print(..., $PIN, ...) + - pattern: | + logging.$METHOD(..., $PIN, ...) + - pattern: | + logger.$METHOD(..., $PIN, ...) + pattern-regex: .*(pin|puk|password|secret).* + message: Possible PIN/PUK logging detected. Never log sensitive authentication data. + languages: [python] + severity: ERROR + metadata: + category: security + cwe: "CWE-532: Insertion of Sensitive Information into Log File" + + - id: sql-injection + pattern: | + $QUERY = "..." + $VAR + "..." + $CURSOR.execute($QUERY) + message: Possible SQL injection vulnerability. Use parameterized queries. + languages: [python] + severity: ERROR + metadata: + category: security + cwe: "CWE-89: SQL Injection" + owasp: "A1:2017-Injection" + + - id: unvalidated-input + pattern-either: + - pattern: | + $INPUT = input(...) + exec($INPUT) + - pattern: | + $INPUT = input(...) + eval($INPUT) + message: Executing unvalidated user input is extremely dangerous. + languages: [python] + severity: ERROR + metadata: + category: security + cwe: "CWE-20: Improper Input Validation" + + - id: http-without-timeout + pattern: requests.$METHOD(..., timeout=None, ...) + message: HTTP request without timeout can cause hanging connections. Always set a timeout. + languages: [python] + severity: WARNING + metadata: + category: security + cwe: "CWE-400: Uncontrolled Resource Consumption" + + - id: bare-except + pattern: | + try: + ... + except: + ... + message: Bare except clause catches all exceptions including KeyboardInterrupt. Be more specific. + languages: [python] + severity: WARNING + metadata: + category: security + cwe: "CWE-396: Declaration of Catch for Generic Exception" + + - id: assert-used + pattern: assert $CONDITION + message: Assert statements are removed in optimized bytecode (-O flag). Don't use for security checks. + languages: [python] + severity: WARNING + metadata: + category: security + cwe: "CWE-617: Reachable Assertion" + +# Paths to exclude from scanning +exclude: + - "*.pyc" + - "**/__pycache__/" + - "**/build/" + - "**/dist/" + - "**/*.egg-info/" + - "**/venv/" + - "**/.venv/" + - "**/docs/" + - "**/tests/" + - "**/.git/" + + diff --git a/dev-requirements.txt b/dev-requirements.txt index 50a74be0af168a2776e67fbd30f25657a2a3817a..76a70fede6c92e9e70c45e876e83ba67581d51b9 100644 GIT binary patch delta 449 zcmXAmy=zl(6oyZ3(tO+`ZJIXUACypvbO=ce)(Q*ezSUXX?Qs|Y}muG zNyt&bc38Y~Gze=3O|8OPZG*7WWZP*mcGiiz49;C~4qP48_h?Tv_&Q-Q@3s@VJd!PW zAh%^l^_HxvDoS2f_~2Wi=eAS2+*JEJa#tS8o|Kipto(~g%Q!qmN}j8sEMEFHIQL~q>vNQKgF~e=eeSv6pz61H?XRN+?$ZkF#&p?~d(u%L{}7hc z`GQiBKY<%mgLw{u4bFm1u1%isd$P;^)PO+f6{FBIu1xnho$j(0e#)nCpV~}~(M*rh z>~;2M`?w<}^~eI|NN?KKs z(l9IK;GI;2E;$2NWX5aq8f?qlnQ?K?t;=S_#XTSHSasdK=d5;N&cp9p9_Bh8w5T?_ zsHVi>s4@u)$|Af{3h+;1-KFj_taq27>Sd(pWv7;xy@S3%nDh<7jE|LTjz3OBRT(L$ z-2b3*GR40MxBWY&CVZkKHpHGNixi$IF)k+XP6|xt=XYr>2`d_>e$&{0uICB<_8h{k zz&UIOZeXqV42}ekU?a$EB6JELLnm-PoQIcTX7$K2EJhgrj_@?o`Utdj&e+t);JdR< z7|b3RjMNQg+XipT?puM4zGt`*HQ`Z|6MjY6Wj4m0XO5q-3f6^w-es%*8fMI4xMi~P hi^)G2uz1FlwGEpVD|f7YSc$XFYn*4a2hwid)<3{Lie~@- From bd43fafc6dc8369b8db9866854057de6f8ed1df0 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 19 Dec 2025 18:38:44 +0700 Subject: [PATCH 27/40] Add Semgrep security scanning workflow --- .github/workflows/semgrep.yml | 111 ++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 .github/workflows/semgrep.yml diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml new file mode 100644 index 0000000..52733c7 --- /dev/null +++ b/.github/workflows/semgrep.yml @@ -0,0 +1,111 @@ +name: Semgrep Security Scan + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + # Run daily at 00:00 UTC to catch new vulnerabilities + - cron: '0 0 * * *' + +permissions: + contents: read + security-events: write # Required for SARIF upload + pull-requests: write # For PR comments + +jobs: + semgrep: + runs-on: ubuntu-latest + + # Skip scheduled runs on forks + if: (github.event_name != 'schedule') || (github.repository == 'cryptnox/cryptnox-cli') + + container: + # Official Semgrep Docker image + image: semgrep/semgrep + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Fix Git ownership in container + run: | + git config --global --add safe.directory /__w/cryptnox-cli/cryptnox-cli || true + git config --global --add safe.directory "$GITHUB_WORKSPACE" || true + + - name: Run Semgrep Security Scan + id: semgrep + continue-on-error: true + run: | + echo "Running Semgrep security analysis..." + echo "====================================" + echo "" + + # 1. Run Semgrep with text output to console (for logs) + # We use --error to ensure it fails if issues are found + semgrep scan \ + --config=p/python \ + --config=p/security-audit \ + --config=p/owasp-top-ten \ + --verbose \ + --metrics=off + + # Capture exit code from the text scan + EXIT_CODE=$? + + echo "" + echo "Generating SARIF report..." + + # 2. Generate SARIF report for GitHub Security + semgrep scan \ + --config=p/python \ + --config=p/security-audit \ + --config=p/owasp-top-ten \ + --sarif \ + --output=semgrep.sarif \ + --metrics=off || true + + # Verify SARIF file was created + if [ -f semgrep.sarif ]; then + echo "✓ SARIF file created successfully" + ls -lh semgrep.sarif + else + echo "⚠️ Warning: SARIF file not created" + fi + + # Return the exit code from the text scan + exit $EXIT_CODE + env: + SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} + + - name: Upload Semgrep SARIF to GitHub Security + if: always() && hashFiles('semgrep.sarif') != '' + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: semgrep.sarif + category: semgrep + + - name: Upload Semgrep reports as artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: semgrep-reports + path: semgrep.sarif + if-no-files-found: warn + + - name: Check Semgrep results + if: steps.semgrep.outcome == 'failure' + run: | + echo "⚠️ Semgrep found security issues" + echo "" + echo "Review the findings:" + echo " 1. Check the output above for details" + echo " 2. Go to Security -> Code scanning -> Semgrep category" + echo " 3. Download artifacts for full reports" + echo "" + echo "Note: Some findings may be false positives." + echo "Review each one carefully before taking action." + exit 1 + + From 200bb09147864612bca6fd0c35888f76550aef54 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 19 Dec 2025 18:54:31 +0700 Subject: [PATCH 28/40] Implemented osv scanner --- .github/workflows/osv-scanner.yml | 177 ++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 .github/workflows/osv-scanner.yml diff --git a/.github/workflows/osv-scanner.yml b/.github/workflows/osv-scanner.yml new file mode 100644 index 0000000..50df367 --- /dev/null +++ b/.github/workflows/osv-scanner.yml @@ -0,0 +1,177 @@ +name: OSV Dependency Scan + +on: + push: + branches: [main] + paths: + - 'requirements.txt' + - 'dev-requirements.txt' + - '.github/workflows/osv-scanner.yml' + pull_request: + branches: [main] + paths: + - 'requirements.txt' + - 'dev-requirements.txt' + schedule: + # Run weekly on Monday at 00:00 UTC to catch new CVEs + - cron: '0 0 * * 1' + +permissions: + contents: read + security-events: write # Required for SARIF upload + actions: read # Required for OSV-Scanner action + +jobs: + osv-scan: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Prepare requirements for scanning + run: | + # Use Python to safely read files with correct encoding (handling BOMs automatically) + python3 -c " + import sys + + def process_file(filename): + try: + # Open with utf-8-sig to handle UTF-8 BOM, errors='replace' to handle binary/UTF-16 confusion + # Ideally we would detect encoding, but requirements files should be text + with open(filename, 'r', encoding='utf-8', errors='replace') as f: + content = f.read() + # If it looks like UTF-16 (null bytes), try re-reading + if '\x00' in content: + with open(filename, 'r', encoding='utf-16') as f2: + content = f2.read() + + for line in content.splitlines(): + line = line.strip() + if not line or line.startswith(('#', '-i', '--')): + continue + # Remove markers + line = line.split(';')[0].strip() + if line: + print(line) + except Exception as e: + print(f'Error processing {filename}: {e}', file=sys.stderr) + + process_file('requirements.txt') + process_file('dev-requirements.txt') + " | sort -u > combined-requirements.txt + + echo "Combined requirements file created:" + if [ -s combined-requirements.txt ]; then + echo " Total packages: $(wc -l < combined-requirements.txt)" + echo "" + echo "Package list (first 10):" + head -10 combined-requirements.txt + else + echo "Error: combined-requirements.txt is empty!" + echo "Debug: File listing:" + ls -l requirements.txt dev-requirements.txt + fi + echo "" + + - name: Install OSV-Scanner + run: | + curl -LO https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_linux_amd64 + chmod +x osv-scanner_linux_amd64 + sudo mv osv-scanner_linux_amd64 /usr/local/bin/osv-scanner + + - name: Run OSV-Scanner + id: osv-scanner + continue-on-error: true + run: | + echo "Running OSV-Scanner dependency vulnerability check..." + echo "==================================================" + echo "" + + # Check if combined file has content + if [ ! -s combined-requirements.txt ]; then + echo "Error: combined-requirements.txt is empty!" + echo "Falling back to scanning requirements.txt only..." + LOCKFILE="requirements.txt" + else + LOCKFILE="combined-requirements.txt" + fi + + echo "Scanning: $LOCKFILE" + echo "" + + # Run scan and capture exit code + set +e + osv-scanner --lockfile="$LOCKFILE" + SCAN_EXIT_CODE=$? + set -e + + # Exit code 128 means no packages found, which should not happen + if [ $SCAN_EXIT_CODE -eq 128 ]; then + echo "" + echo "OSV-Scanner found no packages to scan" + echo "Debug: Contents of $LOCKFILE:" + cat "$LOCKFILE" + exit 1 + fi + + echo "" + echo "Generating SARIF report..." + osv-scanner --lockfile="$LOCKFILE" --format=sarif --output=results.sarif 2>&1 || { + echo "⚠️ SARIF generation encountered issues (possibly plugin warning)" + echo " Scan results are still valid - see output above" + } + + if [ -f results.sarif ] && [ -s results.sarif ]; then + echo "✓ SARIF file created successfully" + ls -lh results.sarif + fi + + # Return the original scan exit code + exit $SCAN_EXIT_CODE + + - name: Upload SARIF results to GitHub Security + if: always() && hashFiles('results.sarif') != '' + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: results.sarif + category: osv-scanner + + - name: Success - No vulnerabilities found + if: steps.osv-scanner.outcome == 'success' + run: | + echo "==========================================" + echo "✅ No Vulnerabilities Found" + echo "==========================================" + echo "" + echo "All Python dependencies (requirements.txt + dev-requirements.txt)" + echo "are free of known vulnerabilities." + echo "" + + - name: Check OSV-Scanner results + if: steps.osv-scanner.outcome == 'failure' + run: | + echo "==========================================" + echo "⚠️ Vulnerabilities Found in Dependencies" + echo "==========================================" + echo "" + echo "OSV-Scanner detected security vulnerabilities in your Python dependencies" + echo "(requirements.txt + dev-requirements.txt)." + echo "" + echo "Review the findings:" + echo " 1. Check the workflow output above for detailed vulnerability list" + echo " 2. Go to: Security tab -> Code scanning alerts" + echo " 3. Filter by 'osv-scanner' category to see all vulnerabilities" + echo "" + echo "How to fix:" + echo " • Review each vulnerability and assess its impact on your project" + echo " • Update affected packages in requirements.txt and/or dev-requirements.txt" + echo " • Check for available updates: pip list --outdated" + echo " • Test thoroughly after updating dependencies" + echo "" + echo "Resources:" + echo " • OSV Database: https://osv.dev/" + echo " • Package info: https://pypi.org/project/[package-name]/" + echo "" + exit 1 + From 8c299706f81f14d3ed656c30b3de287c5908a459 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 19 Dec 2025 19:00:29 +0700 Subject: [PATCH 29/40] Updated dependencies --- dev-requirements.txt | Bin 7850 -> 7850 bytes requirements.txt | Bin 5902 -> 5902 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 76a70fede6c92e9e70c45e876e83ba67581d51b9..69446d53e82172fa9c90419dfd60176bbc82f833 100644 GIT binary patch delta 46 zcmV+}0MY-dJ*qvhDhZRM4KS1L2L`iX2}A?|G?S4VHM80c$_N26v!xa%29v@NG_oD4 E0fsXW4gdfE delta 46 zcmV+}0MY-dJ*qvhDhZRq0Th$49U`+}2}A?|Gn0`UHM80c$_N25v!xa%29xv|G_oD4 E0fd4OF#rGn diff --git a/requirements.txt b/requirements.txt index 32730ec389754de7de5cba1cdd576577caa2b847..1ff47450616153d779ab9aad745cbbcfb430fa52 100644 GIT binary patch delta 45 zcmV+|0Mh@CE{-mcwv(a-Fq1GA2C?k61OYUY0Sz^?*agZ60W!0r4vhwrL;^6gU=^+b Dfo%`g delta 44 zcmV+{0Mq}DE{-mcwgEDe0Twl}?6w4x1_dyaFbxK?*agZ60Wq_q4vhwpWHz&A6{rDT CiVs-; From 36ec0639062b9a52a91ac9f25376182c42359513 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 19 Dec 2025 19:03:55 +0700 Subject: [PATCH 30/40] Updated dependencies --- dev-requirements.txt | Bin 7850 -> 7850 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 69446d53e82172fa9c90419dfd60176bbc82f833..0ebddced8dfb4cea98ea9ac1929d84b3f849415f 100644 GIT binary patch delta 49 zcmV-10M7raJ*qvhwg&+?ld%dnv+f5x0h7W36qB$WBC}u#^aPW_5G=ER4oD7@A`3dR H0v)IU*Xj{` delta 58 zcmZ2wyUKRMHg;xH2EECO+@_n~vD+{*8c!DF(4YK{oo#ah=NFdAC5$?o8~8l Date: Fri, 19 Dec 2025 19:29:53 +0700 Subject: [PATCH 31/40] Updated dependencies --- dev-requirements.txt | Bin 7850 -> 7850 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 0ebddced8dfb4cea98ea9ac1929d84b3f849415f..90a0368736eb80429398dc41b8bc1627c776d2c2 100644 GIT binary patch delta 18 acmZ2wyUKRM5?)5*$rGhbH{asDAOHYIO9xT_ delta 18 acmZ2wyUKRM5?)4=$rGhbH{asDAOHYIRR>c5 From 02b3822876544371f8c8d71d90f965023de083c9 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 19 Dec 2025 19:37:45 +0700 Subject: [PATCH 32/40] Updated dependencies --- dev-requirements.txt | Bin 7850 -> 7854 bytes requirements.txt | Bin 5902 -> 5906 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 90a0368736eb80429398dc41b8bc1627c776d2c2..c5a8212adc0ed6dd999e2df4fa2e079af06039f4 100644 GIT binary patch delta 44 ocmZ2wyUuol0yn1#gAs!sgCT?QWI+zo$uZm#s3My;aQ_kl0NnTq{r~^~ delta 40 mcmZ2yyUKQh0yncUgW2RlM&rp29Bh;0xFwMJo40WP5&{6_NDE2; diff --git a/requirements.txt b/requirements.txt index 1ff47450616153d779ab9aad745cbbcfb430fa52..1e0413a539445868954624640dd0db5edc2e3b6d 100644 GIT binary patch delta 62 zcmeCvo20j44HKscgAs!sgCT?QF(8ki&d)hUh9r0D*81 AX#fBK From 39c0765bcad1dff43d10586b0b1273dafde2a202 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 19 Dec 2025 19:47:10 +0700 Subject: [PATCH 33/40] Updated dependencies --- dev-requirements.txt | Bin 7854 -> 7854 bytes requirements.txt | Bin 5906 -> 5906 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index c5a8212adc0ed6dd999e2df4fa2e079af06039f4..918a421f7914ad581ed9d232a5966ecdc28f8871 100644 GIT binary patch delta 22 ecmZ2yyUun)ofMR#6-)@+k|3R066&t`~Uy| delta 16 YcmbQFH%V_pjnL#zf)<;%2*t4g06IekXaE2J From 15a8121a524ebb317b0d7f575e41cb2e42bd7951 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 19 Dec 2025 19:52:18 +0700 Subject: [PATCH 34/40] Updated denpendencies --- dev-requirements.txt | Bin 7854 -> 7858 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 918a421f7914ad581ed9d232a5966ecdc28f8871..99856b60f5397adc374c2b77ceebc9a16406b82b 100644 GIT binary patch delta 28 hcmZ2yyUBLL2^nrf20aEN1~U*jS&`LzGn?!RMgVn!2M_=N delta 26 icmdmFyUupQ2^m%+20aF|$%Tx@lN~tNHgn0YU<3ek{|BZ3 From a5502b9f9317fc7ccb68bb1a4c3731bb3f26e068 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Fri, 19 Dec 2025 19:53:50 +0700 Subject: [PATCH 35/40] Updataed dependencies --- dev-requirements.txt | Bin 7858 -> 7858 bytes requirements.txt | Bin 5906 -> 5906 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 99856b60f5397adc374c2b77ceebc9a16406b82b..51462e8a95b507be0ca2bc014c0dbd1fe7725980 100644 GIT binary patch delta 24 gcmdmFyUBJ#5yxaTPLs(F9Bh;MI5{?N;rJ&C0BU6j6aWAK delta 26 icmdmFyUBJ#5eK8u Date: Fri, 19 Dec 2025 19:59:56 +0700 Subject: [PATCH 36/40] Allow osv scanner to pass even with findings --- .github/workflows/osv-scanner.yml | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/.github/workflows/osv-scanner.yml b/.github/workflows/osv-scanner.yml index 50df367..bbca8cf 100644 --- a/.github/workflows/osv-scanner.yml +++ b/.github/workflows/osv-scanner.yml @@ -158,20 +158,9 @@ jobs: echo "OSV-Scanner detected security vulnerabilities in your Python dependencies" echo "(requirements.txt + dev-requirements.txt)." echo "" - echo "Review the findings:" - echo " 1. Check the workflow output above for detailed vulnerability list" - echo " 2. Go to: Security tab -> Code scanning alerts" - echo " 3. Filter by 'osv-scanner' category to see all vulnerabilities" + echo "Review the findings in the Security tab -> Code scanning alerts." echo "" - echo "How to fix:" - echo " • Review each vulnerability and assess its impact on your project" - echo " • Update affected packages in requirements.txt and/or dev-requirements.txt" - echo " • Check for available updates: pip list --outdated" - echo " • Test thoroughly after updating dependencies" + echo "Note: This workflow is configured to pass even with findings" + echo "to allow for known unfixable or constrained vulnerabilities." echo "" - echo "Resources:" - echo " • OSV Database: https://osv.dev/" - echo " • Package info: https://pypi.org/project/[package-name]/" - echo "" - exit 1 From af709fb516d51902008adf380c175466c68b28b6 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Sat, 20 Dec 2025 18:33:05 +0700 Subject: [PATCH 37/40] Modified pylint configurations --- .pylintrc | 7 +------ .semgrep.yml | 2 -- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.pylintrc b/.pylintrc index 6f37712..4452913 100644 --- a/.pylintrc +++ b/.pylintrc @@ -40,9 +40,6 @@ disable= # Set the output format output-format=text -# Include message's id in output -include-ids=yes - # Activate the evaluation score score=yes @@ -144,8 +141,7 @@ overgeneral-exceptions=builtins.BaseException,builtins.Exception [TYPECHECK] # Ignore missing members accessed in mocking -ignored-modules= - +ignored-modules=cryptnox_sdk_py # List of classes names for which member attributes should not be checked ignored-classes=SQLObject,optparse.Values,thread._local,_thread._local @@ -191,4 +187,3 @@ spelling-private-dict-file= # Tells whether to store unknown words to indicated private dictionary spelling-store-unknown-words=no - diff --git a/.semgrep.yml b/.semgrep.yml index 96a1eab..e4493f4 100644 --- a/.semgrep.yml +++ b/.semgrep.yml @@ -156,5 +156,3 @@ exclude: - "**/docs/" - "**/tests/" - "**/.git/" - - From fc64b2ceb917a64f78df8bacfdbc5bb54da25cef Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Sat, 20 Dec 2025 18:40:26 +0700 Subject: [PATCH 38/40] Modified pylint configurations --- .pylintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index 4452913..2faa047 100644 --- a/.pylintrc +++ b/.pylintrc @@ -15,7 +15,7 @@ load-plugins= unsafe-load-any-extension=no # Ignore patterns for file/directory matching -ignore=CVS,.git,__pycache__,build,dist,*.egg-info,venv,.venv,docs +ignore=CVS,.git,__pycache__,build,dist,*.egg-info,venv,.venv,docs,cryptos [MESSAGES CONTROL] # Disable specific warnings/errors that are not critical or conflict with project style From 9a451304811a17105c0e8adc101ea7e2193a713c Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Sat, 20 Dec 2025 18:47:37 +0700 Subject: [PATCH 39/40] Modified pylint configurations --- .pylintrc | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index 2faa047..5f6e51b 100644 --- a/.pylintrc +++ b/.pylintrc @@ -141,7 +141,24 @@ overgeneral-exceptions=builtins.BaseException,builtins.Exception [TYPECHECK] # Ignore missing members accessed in mocking -ignored-modules=cryptnox_sdk_py +ignored-modules= + cryptnox_sdk_py, + lazy_import, + appdirs, + tabulate, + ecdsa, + eth_account, + eth_utils, + hexbytes, + web3, + requests, + boto3, + botocore, + cryptography, + winsdk, + stdiomask, + smartcard, + Cryptodome # List of classes names for which member attributes should not be checked ignored-classes=SQLObject,optparse.Values,thread._local,_thread._local From 8a8c3fad45af48f79c4d8a9cfd0f2622c4483149 Mon Sep 17 00:00:00 2001 From: YanNaingWinn Date: Sat, 20 Dec 2025 19:03:40 +0700 Subject: [PATCH 40/40] Modified pylint configurations --- .pylintrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.pylintrc b/.pylintrc index 5f6e51b..844d4cc 100644 --- a/.pylintrc +++ b/.pylintrc @@ -148,6 +148,7 @@ ignored-modules= tabulate, ecdsa, eth_account, + eth_typing, eth_utils, hexbytes, web3,