#!/usr/bin/env python3 """Extract the Debian + OS-dependency layers of the pinned ZAP archive, offline. Retain the complete package database and licenses; exclude application/pip/ZAP layers. The output is a Docker archive used only as the per-tool build base. """ import hashlib import io import json from pathlib import Path import sys import tarfile root = Path(__file__).resolve().parents[1] output = Path(sys.argv[1]) with tarfile.open(root / 'images/zap-stable-amd64.tar') as src: manifest = json.load(src.extractfile('manifest.json'))[0] config = json.load(src.extractfile(manifest['Config'])) assert config['architecture'] == 'amd64' and config['os'] == 'linux' nonempty = [h for h in config['history'] if not h.get('empty_layer')] assert 'debian.sh' in nonempty[0]['created_by'] assert 'apt-get install' in nonempty[1]['created_by'] assert 'python3-pip' in nonempty[1]['created_by'] and 'git' in nonempty[1]['created_by'] layers = manifest['Layers'][:2] history = [] count = 0 for h in config['history']: history.append(h) count += not h.get('empty_layer', False) if count == 2: break config = { 'created': config['created'], 'architecture': 'amd64', 'os': 'linux', 'config': {'Env': ['PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', 'HOME=/root', 'LANG=C.UTF-8'], 'User': 'root', 'WorkingDir': '/tmp', 'Cmd': ['/bin/bash'], 'Labels': {'org.opencontainers.image.description': 'Offline Debian runtime from the first two pinned ZAP layers; no ZAP application'}}, 'rootfs': {'type': 'layers', 'diff_ids': config['rootfs']['diff_ids'][:2]}, 'history': history, } data = json.dumps(config, separators=(',', ':')).encode() name = hashlib.sha256(data).hexdigest() + '.json' with tarfile.open(output, 'w') as dest: for layer in layers: member = src.getmember(layer) # Docker archives contain uncompressed layer tar streams. digest = hashlib.sha256() f = src.extractfile(member) for chunk in iter(lambda: f.read(1024 * 1024), b''): digest.update(chunk) expected = config['rootfs']['diff_ids'][layers.index(layer)] assert 'sha256:' + digest.hexdigest() == expected, layer dest.addfile(member, src.extractfile(member)) for path, content in [(name, data), ('manifest.json', json.dumps([{ 'Config': name, 'RepoTags': ['localhost/devsecops/tool-runtime:20260912'], 'Layers': layers }]).encode())]: info = tarfile.TarInfo(path); info.size = len(content); info.mode = 0o644 dest.addfile(info, io.BytesIO(content)) print(output)