#!/usr/bin/env bash
# Scans a Docker image for vulnerabilities using the ECR scan tool.
# Usage: scan.sh <image-name:tag> [awsume-profile] [project-dir]
#
# ECR_SCAN_DIR must be set to the ecr_scan/ directory inside python-deployment-utils.
# See the docker-image-scan skill for instructions on locating or cloning the repo.
#
# project-dir: optional path to the project root. If a Jenkinsfile is found there
# containing a dockerScan(vulnerabilitiesToIgnore: [...]) call, those CVE IDs are
# written as VULNERABILITIES_TO_IGNORE into the scanner's .env before the scan runs.
set -euo pipefail

if [[ $# -lt 1 ]]; then
    echo "Usage: $(basename "$0") <image-name:tag> [awsume-profile] [project-dir]" >&2
    exit 1
fi

IMAGE="$1"
AWSSUME_PROFILE="${2:-shared}"
PROJECT_DIR="${3:-}"
TARBALL=""

_cleanup() { [[ -n "${TARBALL}" ]] && rm -f "${TARBALL}"; }
trap _cleanup EXIT

# ── Validate ECR_SCAN_DIR ─────────────────────────────────────────────────────

if [[ -z "${ECR_SCAN_DIR:-}" || ! -d "${ECR_SCAN_DIR}" ]]; then
    echo "ERROR: ECR_SCAN_DIR is not set or does not exist." >&2
    echo "Set it to the ecr_scan/ directory inside python-deployment-utils, e.g.:" >&2
    echo "  export ECR_SCAN_DIR=~/code/forks/python-deployment-utils/ecr_scan" >&2
    echo "See the docker-image-scan skill for setup instructions." >&2
    exit 1
fi

SCAN_DIR="${ECR_SCAN_DIR}"

echo "Using scanner at: ${SCAN_DIR}"

# ── Configure .env ────────────────────────────────────────────────────────────

ENV_FILE="${SCAN_DIR}/.env"
if [[ ! -f "${ENV_FILE}" ]]; then
    [[ -f "${SCAN_DIR}/.env.shadow" ]] || { echo "ERROR: no .env or .env.shadow in ${SCAN_DIR}" >&2; exit 1; }
    cp "${SCAN_DIR}/.env.shadow" "${ENV_FILE}"
    echo "Created .env from .env.shadow"
fi

# IMAGE_PATH is always forced to ./image.tar. Integer fields are filled with
# defaults only if absent or empty. If a Jenkinsfile is found by walking up from
# PROJECT_DIR and contains vulnerabilitiesToIgnore, those CVE IDs are written as
# VULNERABILITIES_TO_IGNORE into the .env.
python3 - "$ENV_FILE" "${PROJECT_DIR:-}" <<'PYEOF'
import sys, re, os
env_file = sys.argv[1]
project_dir = sys.argv[2] if len(sys.argv) > 2 else ""

int_defaults = {"CRITICAL_ERROR_DAYS": "7", "HIGH_ERROR_DAYS": "14", "NON_BLOCKING_ERROR_DAYS": "90"}

# Walk up from project_dir to find the nearest Jenkinsfile
def find_jenkinsfile(start):
    if not start:
        return None
    current = os.path.abspath(start)
    while True:
        candidate = os.path.join(current, "Jenkinsfile")
        if os.path.isfile(candidate):
            return candidate
        parent = os.path.dirname(current)
        if parent == current:
            return None
        current = parent

# Parse vulnerabilitiesToIgnore from Jenkinsfile (if present)
ignore_value = None
jenkinsfile = find_jenkinsfile(project_dir)
if jenkinsfile:
    with open(jenkinsfile) as f:
        content = f.read()
    m = re.search(r'vulnerabilitiesToIgnore\s*:\s*\[([^\]]+)\]', content)
    if m:
        items = re.findall(r"[\"']([^\"']+)[\"']", m.group(1))
        if items:
            ignore_value = ','.join(items)
            print(f"Injecting VULNERABILITIES_TO_IGNORE from Jenkinsfile: {ignore_value}", file=sys.stderr)

with open(env_file) as f:
    lines = f.readlines()
seen = set()
out = []
for line in lines:
    m = re.match(r'^(\w+)=(.*)', line.rstrip())
    if m:
        var, val = m.group(1), m.group(2).strip()
        seen.add(var)
        if var == "IMAGE_PATH":
            line = "IMAGE_PATH=./image.tar\n"
        elif var == "VULNERABILITIES_TO_IGNORE" and ignore_value is not None:
            line = f"VULNERABILITIES_TO_IGNORE={ignore_value}\n"
        elif var in int_defaults and not val:
            line = f"{var}={int_defaults[var]}\n"
    out.append(line if line.endswith('\n') else line + '\n')
for var, default in int_defaults.items():
    if var not in seen:
        out.append(f"{var}={default}\n")
if "IMAGE_PATH" not in seen:
    out.append("IMAGE_PATH=./image.tar\n")
if ignore_value is not None and "VULNERABILITIES_TO_IGNORE" not in seen:
    out.append(f"VULNERABILITIES_TO_IGNORE={ignore_value}\n")
with open(env_file, 'w') as f:
    f.writelines(out)
PYEOF

# ── Save image tarball ────────────────────────────────────────────────────────

TARBALL="${SCAN_DIR}/image.tar"
echo "Saving ${IMAGE} → ${TARBALL}"
docker save "${IMAGE}" -o "${TARBALL}"

# ── Authenticate and scan ─────────────────────────────────────────────────────

eval "$(awsume "${AWSUME_PROFILE}" -s)"
cd "${SCAN_DIR}"
docker compose run --build --rm docker-image-scanner
