# PROOF OF CONCEPT
The script `compile_and_poison.py` compiles `benign.py` from the package `this_is_a_package` and `malicious.py` using [py_compile](https://docs.python.org/3/library/py_compile.html) with a given invalidation mode (see below). Afterward, it simply moves the malicious body into the benign cache but keeps the benign header. The project in `./test` depends on `this_is_a_package` and imports the function `hello()` from the module `benign`.

## Results
| Package Manager | TIMESTAMP | CHECKED_HASH | UNCHECKED_HASH | Comment |
|--|--|--|--|--|
| [pip](https://pip.pypa.io/en/stable/) | ❌ | ❌ | ❌ | In default it compiles everything on install (unless `--no-compile` is provided) |
| [conda](https://docs.conda.io/projects/conda/en/stable/) | ❌ | ❌ | ❌ | uses pip |
| [pipenv](https://pipenv.pypa.io/en/latest/) | ❌ | ❌ | ❌ | wrapper around pip |
| [pip-tools](https://github.com/jazzband/pip-tools) | ❌ | ❌ | ❌ | wrapper around pip |
| [hatch](https://hatch.pypa.io/latest/) | ❌ | ❌ | ❌ | uses pip |
| [pip](https://pip.pypa.io/en/stable/) `--no-compile` | ❌ | ✅ | ✅ | |
| [poerty](https://python-poetry.org/) | ❌ | ✅ | ✅ | |
| [uv](https://docs.astral.sh/uv) | ❌ | ✅ | ✅ | |
| [pdm](https://pdm-project.org/en/latest/usage/dependency/) | ❌ | ✅ | ✅ | |

## WHAT DO?
### PREPARATION (DONE ONCE)
- Install [twine](https://twine.readthedocs.io/en/stable/)
- The manual steps below are automatized in `prepare.sh`
- Set the [twine environment variables](https://twine.readthedocs.io/en/stable/#environment-variables): `TWINE_USERNAME`, `TWINE_PASSWORD`, and `TWINE_REPOSITORY_URL`
- use `compile_and_poison.py {TIMESTAMP, CHECKED_HASH, UNCHECKED_HASH}` to create manipulated hash
- use `python -m build this_is_a_package` to create distribution artifacts
- use `python -m twine upload this_is_a_package/dist/*` to upload artifacts to your GitLab package registry

### EVALUATE
- for package manager in {pip, uv, poerty, hatch, pdm, ...}:
  - remove .venv
  - install package from Gitlab registry
  - run `main.py`
  - check if output == 'Hello Evil!'

### AVAILABLE MODES

### `py_compile.PycInvalidationMode.TIMESTAMP`
> The .pyc file includes the timestamp and size of the source file, which Python will compare against the metadata of the source file at runtime to determine if the .pyc file needs to be regenerated.

### `py_compile.PycInvalidationMode.CHECKED_HASH`
> The .pyc file includes a hash of the source file content, which Python will compare against the source at runtime to determine if the .pyc file needs to be regenerated.

### `py_compile.PycInvalidationMode.UNCHECKED_HASH`
> Like CHECKED_HASH, the .pyc file includes a hash of the source file content. However, Python will at runtime assume the .pyc file is up to date and not validate the .pyc against the source file at all.
