nabla-c0d3 / sslyze

Fast and powerful SSL/TLS scanning library.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

pem_files path is incorrect when using PyInstaller's onefile bundling

TechSupportJosh opened this issue · comments

commented

Describe the bug
When using PyInstaller's onefile bundling method, and adding pem_files to the bundle, sslyze will attempt to load pem_files from where the bundled executable is executed from, rather than searching in the temporary directory created in /tmp/_MEI....

From what I can tell, this is because sys.executable when using onefile bundling points to the binary that the user called e.g. /home/user/mytool. The function responsible for this doesn't utilise sys._MEIPASS (the directory which points towards the bundle folder, regardless of whether it was one-folder or one-file bundled):

# Getting the path to the trust stores is tricky due to subtle differences on OS X, Linux and Windows
if getattr(sys, "frozen", False):
# py2exe, PyInstaller, cx_Freeze
path = Path(sys.executable).absolute()
else:
path = Path(inspect.getabsfile(_get_script_dir))
if follow_symlinks:
path = Path(realpath(path))
return path.parent

To Reproduce
Steps to reproduce the behavior:

  1. Install sslyze and pyinstaller
pip install sslyze pyinstaller
  1. Create a file test.py with the following content:
from datetime import datetime
from sslyze import (
    ServerScanRequest,
    ServerNetworkLocation,
    Scanner,
    ServerHostnameCouldNotBeResolved,
    ServerScanStatusEnum,
    ScanCommandAttemptStatusEnum,
)
import sys

print("sys.executable:", sys.executable)
print("sys._MEIPASS", sys._MEIPASS if hasattr(sys, "_MEIPASS") else "N/A")
print()

print("=> Starting the scans")
date_scans_started = datetime.utcnow()

# First create the scan requests for each server that we want to scan
try:
    all_scan_requests = [
        ServerScanRequest(
            server_location=ServerNetworkLocation(hostname="cloudflare.com")
        ),
        ServerScanRequest(server_location=ServerNetworkLocation(hostname="google.com")),
    ]
except ServerHostnameCouldNotBeResolved:
    # Handle bad input ie. invalid hostnames
    print("Error resolving the supplied hostnames")
    exit()

# Then queue all the scans
scanner = Scanner()
scanner.queue_scans(all_scan_requests)

# And retrieve and process the results for each server
all_server_scan_results = []
for server_scan_result in scanner.get_results():
    all_server_scan_results.append(server_scan_result)
    print(f"\n\n****Results for {server_scan_result.server_location.hostname}****")

    # Were we able to connect to the server and run the scan?
    if server_scan_result.scan_status == ServerScanStatusEnum.ERROR_NO_CONNECTIVITY:
        # No we weren't
        print(
            f"\nError: Could not connect to {server_scan_result.server_location.hostname}:"
            f" {server_scan_result.connectivity_error_trace}"
        )
        continue

    # Since we were able to run the scan, scan_result is populated
    assert server_scan_result.scan_result

    # Process the result of the certificate info scan command
    certinfo_attempt = server_scan_result.scan_result.certificate_info
    if certinfo_attempt.status == ScanCommandAttemptStatusEnum.ERROR:
        print(certinfo_attempt.status)
        print(certinfo_attempt.error_reason)
        print(certinfo_attempt.error_trace)
    elif certinfo_attempt.status == ScanCommandAttemptStatusEnum.COMPLETED:
        certinfo_result = certinfo_attempt.result
        assert certinfo_result
        print("Certificate worked!")
  1. Build with pyinstaller (specifying the path to the sslyze's pem_stores folder, here I installed into a venv, your path may vary)
pyinstaller --add-data "./venv/lib/python3.10/site-packages/sslyze/plugins/certificate_info/trust_stores/pem_files:pem_files" --onefile ./test.py
  1. Run command and verify that sys.executable points to the binary in the dist folder, rather than the /tmp folder created by PyInstaller:
$ ./test
sys.executable: /home/josh/sslyze_bug_fix/dist/test
sys._MEIPASS /tmp/_MEIqVkqr7

=> Starting the scans


****Results for google.com****
ScanCommandAttemptStatusEnum.ERROR
ScanCommandErrorReasonEnum.BUG_IN_SSLYZE
[Errno 2] No such file or directory: '/home/josh/sslyze_bug_fix/dist/pem_files/apple.yaml'


****Results for cloudflare.com****
ScanCommandAttemptStatusEnum.ERROR
ScanCommandErrorReasonEnum.BUG_IN_SSLYZE
[Errno 2] No such file or directory: '/home/josh/sslyze_bug_fix/dist/pem_files/apple.yaml'

Expected behavior
If the application detects _MEIPASS, it should use this directory rather than the sys.executable parent directory.

Python environment:

  • OS: Ubuntu 22.04.2 LTS
  • Python version: 3.10
commented

Adding this to the top of _get_script_dir seems to fix it, but would be nice if this could be included within the library:

    if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
        return Path(sys._MEIPASS)

Hello,
Unfortunately, only cx_freeze on Windows is supported at the moment, and I have no plans to add support for pyInstaller. Good luck tho!