Skip to content

Commit

Permalink
Merge pull request #77 from facundobatista/unpacking-metadata
Browse files Browse the repository at this point in the history
Save metadata related to the unpacking in the project's dir.
  • Loading branch information
facundobatista authored Jul 11, 2023
2 parents e695fb8 + c6672e2 commit 8d63247
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 8 deletions.
34 changes: 26 additions & 8 deletions pyempaq/unpacker.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import shutil
import subprocess
import sys
import time
import venv
import zipfile
from types import ModuleType
Expand Down Expand Up @@ -91,6 +92,18 @@ def run_command(venv_bin_dir: pathlib.Path, cmd: List[str]) -> subprocess.Comple
return subprocess.run(cmd, env=newenv)


def get_file_hexdigest(filepath: pathlib.Path) -> str:
"""Hash a file and return its hexdigest."""
hasher = hashlib.sha256()
with open(filepath, "rb") as fh:
while True:
data = fh.read(65536)
hasher.update(data)
if not data:
break
return hasher.hexdigest()


def setup_project_directory(
zf: zipfile.ZipFile,
project_dir: pathlib.Path,
Expand Down Expand Up @@ -141,6 +154,17 @@ def setup_project_directory(
log("Virtualenv setup finished")
else:
log("Skipping virtualenv (no requirements)")

# store unpacking metadata
zf_hash = get_file_hexdigest(zf.filename)
metadata = {
"pyz_path": str(zf.filename),
"pyz_hash": zf_hash,
"timestamp": time.time(),
}
(project_dir / "unpacking.json").write_text(json.dumps(metadata))

# save the flag for completeness
(project_dir / COMPLETE_FLAG_FILE).touch()


Expand Down Expand Up @@ -170,14 +194,8 @@ def build_project_install_dir(zip_path: pathlib.Path, metadata: Dict[str, str]):
project_name = metadata["project_name"]

# get the first part of the hash of the file
hasher = hashlib.sha256()
with open(zip_path, "rb") as fh:
while True:
data = fh.read(65536)
hasher.update(data)
if not data:
break
file_hash_partial = hasher.hexdigest()[:20]
hexdigest = get_file_hexdigest(zip_path)
file_hash_partial = hexdigest[:20]

# Python details
py_impl = platform.python_implementation().lower()
Expand Down
27 changes: 27 additions & 0 deletions tests/test_unpacker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
"""Unpacker tests."""

import hashlib
import json
import os
import platform
import time
import zipfile
from pathlib import Path
from subprocess import CompletedProcess
Expand Down Expand Up @@ -237,6 +239,31 @@ def test_projectdir_requirements(tmp_path, logs):
assert "Virtualenv setup finished" in logs.info


def test_projectdir_metadata(tmp_path, logs):
"""Project directory without special requirements."""
# fake a compressed project
compressed_project = tmp_path / "project.zip"
with zipfile.ZipFile(compressed_project, "w") as zf:
zf.writestr("fake_file", b"fake content")

# get its hash
zipfile_hash = hashlib.sha256(compressed_project.read_bytes()).hexdigest()

# unpack
zf = zipfile.ZipFile(compressed_project)
new_dir = tmp_path / "new_dir"
before_timestamp = time.time()
setup_project_directory(zf, new_dir, [])
after_timestamp = time.time()

# check metadata file
unpack_metadata = json.loads((new_dir / "unpacking.json").read_text())
assert unpack_metadata["pyz_path"] == str(compressed_project)
assert unpack_metadata["pyz_hash"] == zipfile_hash
stored_timestamp = unpack_metadata["timestamp"]
assert before_timestamp <= stored_timestamp <= after_timestamp


# --- tests for enforcing the unpacking restrictions


Expand Down

0 comments on commit 8d63247

Please sign in to comment.