
# Back to our app: worker.py

Let's follow our app from code to deployment in a container.

During each step we'll analyze the software and show some highlights.

---

## Step 1: The code

```python
import logging
import os
from redis import Redis
import requests
import time
...
redis = Redis("redis")
...
def work_loop(interval=1):
    deadline = 0
    loops_done = 0
    while True:
        if time.time() > deadline:
            log.info("{} units of work done, updating hash counter"
                     .format(loops_done))
            redis.incrby("hashes", loops_done)
            loops_done = 0
            deadline = time.time() + interval
        work_once()
        loops_done += 1

if __name__ == "__main__":
    while True:
        try:
            work_loop()
        except:
            log.exception("In work loop:")
            log.error("Waiting 10s and restarting.")
            time.sleep(10)
```

---

## Shipping the app.

We will distribute our app in a docker container.

The following base images are used to see which would be the best image for our app. We have already determined that the app will run fine with all the mentioned images:

* python:alpine
* python:3.9.18-slim
* python:latest

---

## Building the container images

The container build is done with a small `Dockerfile`.
The only thing that changes is the `FROM` line where different base images are specified:

```Dockerfile
FROM python:latest
RUN pip install redis
RUN pip install requests
COPY worker.py /
CMD ["python", "worker.py"]
```

```Dockerfile
FROM python:alpine
RUN pip install redis
RUN pip install requests
COPY worker.py /
CMD ["python", "worker.py"]
```

---

## Build result

The build result is as follows:

```bash
docker image ls
REPOSITORY  TAG             IMAGE ID       CREATED              SIZE
worker      3.9.18-slim     47f85c518f2d   7 seconds ago        237MB
worker      alpine          890af8c86632   About a minute ago   110MB
worker      latest          9011701a671b   3 minutes ago        1.49GB
```

---

## SBOM creation

SBOM's are create from the source code and the images for further analyses.
The tool used is `syft`, but it could have been another tool as well.

Analysis is done with `grype` because it produces output that fits nice in this presentation.

Let's see how each step adds vulnerabilities. Note that the number of reported CVE's was correct at the time of writing.
Quite likely more CVE's have been discoverd since then.

---

## Source code analysis:

The source code is quite clean. Only one CVE is reported:

```bash
grype --add-cpes-if-none sbom-worker.py.json
 ✔ Vulnerability DB                [no update available]
 ✔ Scanned for vulnerabilities     [1 vulnerability matches]
   ├── by severity: 0 critical, 0 high, 1 medium, 0 low, 0 negligible
   └── by status:   1 fixed, 0 not-fixed, 0 ignored
```

---

## Our first image based on python:latest

This is the most tempting image. It seems to be very complete, but maybe it contains to much?

```bash
grype --add-cpes-if-none sbom-python-latest.json
 ✔ Vulnerability DB                [no update available]
 ✔ Scanned for vulnerabilities     [1700 vulnerability matches]
   ├── by severity: 21 critical, 359 high, 519 medium, 73 low, 721 negligible (7 unknown)
   └── by status:   448 fixed, 1252 not-fixed, 0 ignored
```

Wow... We went from only 1 CVE to 1700...

---

## Can we do better: python:3.9.18-slim

A slim image with more than enough to run our application, but much less than python:default.

```bash
grype --add-cpes-if-none sbom-python-3.9.18-slim.json
 ✔ Vulnerability DB                [no update available]
 ✔ Scanned for vulnerabilities     [101 vulnerability matches]
   ├── by severity: 1 critical, 11 high, 28 medium, 3 low, 55 negligible (3 unknown)
   └── by status:   14 fixed, 87 not-fixed, 0 ignored
```

That's already a huge difference. Especially when you pay attention to the critical and high rated CVE's

---

## Let's try one more image: python:alpine

```bash
grype --add-cpes-if-none sbom-python-alpine.json
 ✔ Vulnerability DB                [no update available]
 ✔ Scanned for vulnerabilities     [21 vulnerability matches]
   ├── by severity: 0 critical, 1 high, 18 medium, 0 low, 0 negligible (2 unknown)
   └── by status:   9 fixed, 12 not-fixed, 0 ignored
```

---

### Summary

The scores are shown in the table below.

| Source | Critial | High | Medium | Low |
|---|---|---|---|---|
| worker.py | 0 | 0 | 1 |  0 |
| python-latest | 21 | 359| 519 | 73 low |
| python:3.9.18-slim | 1 | 11 | 28 | 3 low |
| python:alpine | 0 | 1 | 18 | 0 low |

Any idea which image I prefer to deploy?

---

## Storing SBOM files

If you store these SBOM's files you can quickly evaluate if new CVE's are introduced without scanning every component or image again.

Or you can store them in a database like `Dependency Track` which will periodically evaluate the vulnerabilities and, if configured, send you notifications when your attention is required.

---

## Distributing SBOM files

The federal US government expects vendors to provide SBOM files prior to purchasing software or appliances. And they are not alone.

On `github.com` you'll see them appear as well, waiting for you to download them.

Even the new standard for container registries allows you to store SBOM information. The `docker buildx` command can do this as well.

---

## Final words

When working with SBOM tools make sure you're using good ones. When in doubt compare tools and see if the meet your needs.
Some are good for source code, others can only identify os components and some can do both.
Not all are equally good...
