From 372c6e32d6aaa2b4bd2967845a77747b554bd607 Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Mon, 27 Apr 2026 19:12:23 +0200 Subject: [PATCH] feat(scripts): Add flash-multiboot.sh download/flash tool - Download latest multiboot image from GitHub releases - SHA256 checksum verification - Flash to USB with progress and confirmation - Supports --list, --download, --release, --force options - Document in scripts/README.md Co-Authored-By: Claude Opus 4.5 --- scripts/README.md | 24 +++ scripts/flash-multiboot.sh | 298 +++++++++++++++++++++++++++++++++++++ 2 files changed, 322 insertions(+) create mode 100755 scripts/flash-multiboot.sh diff --git a/scripts/README.md b/scripts/README.md index 0e692be2..b5bd7c8f 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -88,6 +88,30 @@ bash local-repo-add.sh ../output/debs/secubox-core_1.0.0.deb --- +## Flash & Download + +| Script | Description | +|--------|-------------| +| `flash-multiboot.sh` | Download & flash multiboot USB | + +### Usage + +```bash +# List available releases +bash flash-multiboot.sh --list + +# Flash latest multiboot to USB +sudo bash flash-multiboot.sh /dev/sdb + +# Download specific release without flashing +bash flash-multiboot.sh --release multiboot-v2.2.4-live --download + +# Flash with force (no confirmation) +sudo bash flash-multiboot.sh --force /dev/sdb +``` + +--- + ## VM & Testing | Script | Description | diff --git a/scripts/flash-multiboot.sh b/scripts/flash-multiboot.sh new file mode 100755 index 00000000..13395cc4 --- /dev/null +++ b/scripts/flash-multiboot.sh @@ -0,0 +1,298 @@ +#!/usr/bin/env bash +# SecuBox-DEB :: flash-multiboot.sh +# Download and flash latest multiboot image to USB +# CyberMind — Gérald Kerma +set -euo pipefail + +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +readonly CACHE_DIR="${PROJECT_DIR}/output/cache" +readonly REPO="CyberMind-FR/secubox-deb" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +log() { echo -e "${GREEN}[INFO]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } + +usage() { + cat </dev/null || missing+=("$cmd") + done + if [[ ${#missing[@]} -gt 0 ]]; then + error "Missing dependencies: ${missing[*]}" + exit 1 + fi + + # Check gh auth + if ! gh auth status &>/dev/null; then + error "GitHub CLI not authenticated. Run: gh auth login" + exit 1 + fi +} + +list_releases() { + log "Available multiboot releases:" + echo + gh release list --repo "$REPO" --limit 10 | grep -i multiboot || { + warn "No multiboot releases found" + echo + log "All releases:" + gh release list --repo "$REPO" --limit 5 + } +} + +list_devices() { + log "Available USB devices:" + echo + lsblk -d -o NAME,SIZE,MODEL,TRAN | grep -E "^NAME|usb" || { + warn "No USB devices found" + lsblk -d -o NAME,SIZE,MODEL,TRAN + } +} + +get_latest_multiboot() { + gh release list --repo "$REPO" --limit 20 --json tagName,isPrerelease \ + --jq '.[] | select(.tagName | contains("multiboot")) | .tagName' \ + | head -1 +} + +download_image() { + local tag="$1" + local dest_dir="$2" + + mkdir -p "$dest_dir" + + log "Fetching release info for: $tag" + + # Get asset names + local assets + assets=$(gh release view "$tag" --repo "$REPO" --json assets --jq '.assets[].name') + + local img_file sha_file + img_file=$(echo "$assets" | grep -E '\.img\.xz$' | head -1) + sha_file=$(echo "$assets" | grep -E '\.sha256$' | head -1) + + if [[ -z "$img_file" ]]; then + error "No .img.xz file found in release $tag" + exit 1 + fi + + local img_path="${dest_dir}/${img_file}" + local sha_path="${dest_dir}/${sha_file}" + + # Download if not cached + if [[ -f "$img_path" ]]; then + log "Image already cached: $img_path" + else + log "Downloading: $img_file" + gh release download "$tag" --repo "$REPO" --pattern "$img_file" --dir "$dest_dir" + fi + + # Download checksum + if [[ -n "$sha_file" ]]; then + log "Downloading checksum: $sha_file" + gh release download "$tag" --repo "$REPO" --pattern "$sha_file" --dir "$dest_dir" --clobber + + # Verify checksum + log "Verifying checksum..." + (cd "$dest_dir" && sha256sum -c "$sha_file") || { + error "Checksum verification failed!" + exit 1 + } + log "Checksum OK" + fi + + echo "$img_path" +} + +flash_image() { + local img_xz="$1" + local device="$2" + local force="${3:-false}" + + # Validate device + if [[ ! -b "$device" ]]; then + error "Not a block device: $device" + exit 1 + fi + + # Check if mounted + if mount | grep -q "^${device}"; then + error "Device $device has mounted partitions. Unmount first." + mount | grep "^${device}" + exit 1 + fi + + # Get device info + local dev_info + dev_info=$(lsblk -d -o NAME,SIZE,MODEL "$device" 2>/dev/null | tail -1) + + # Confirmation + if [[ "$force" != "true" ]]; then + echo + echo -e "${RED}╔════════════════════════════════════════════════════════════╗${NC}" + echo -e "${RED}║ WARNING: ALL DATA ON THIS DEVICE WILL BE DESTROYED! ║${NC}" + echo -e "${RED}╚════════════════════════════════════════════════════════════╝${NC}" + echo + echo "Device: $device" + echo "Info: $dev_info" + echo "Image: $(basename "$img_xz")" + echo + read -rp "Type 'YES' to confirm: " confirm + if [[ "$confirm" != "YES" ]]; then + error "Aborted by user" + exit 1 + fi + fi + + # Flash + log "Flashing to $device..." + log "This may take several minutes..." + + # Use pv if available for progress, otherwise dd status + if command -v pv &>/dev/null; then + xz -dc "$img_xz" | pv | sudo dd of="$device" bs=4M conv=fsync status=none + else + xz -dc "$img_xz" | sudo dd of="$device" bs=4M conv=fsync status=progress + fi + + # Sync + log "Syncing..." + sync + + log "Flash complete!" + echo + log "Device partitions:" + lsblk "$device" +} + +main() { + local release="" + local device="" + local download_only=false + local force=false + local clean=false + local list=false + + # Parse arguments + while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) usage 0 ;; + -l|--list) list=true; shift ;; + -r|--release) release="$2"; shift 2 ;; + -d|--download) download_only=true; shift ;; + -f|--force) force=true; shift ;; + -c|--clean) clean=true; shift ;; + -*) error "Unknown option: $1"; usage 1 ;; + *) device="$1"; shift ;; + esac + done + + check_deps + + # List mode + if [[ "$list" == "true" ]]; then + list_releases + exit 0 + fi + + # Get release + if [[ -z "$release" ]]; then + release=$(get_latest_multiboot) + if [[ -z "$release" ]]; then + error "No multiboot release found" + exit 1 + fi + log "Latest multiboot release: $release" + fi + + # Download + local img_path + img_path=$(download_image "$release" "$CACHE_DIR") + + if [[ "$download_only" == "true" ]]; then + log "Download complete: $img_path" + exit 0 + fi + + # Need device for flashing + if [[ -z "$device" ]]; then + echo + list_devices + echo + error "No device specified. Use: $(basename "$0") /dev/sdX" + exit 1 + fi + + # Check root for flashing + if [[ $EUID -ne 0 ]] && ! sudo -n true 2>/dev/null; then + warn "Root privileges required for flashing" + fi + + # Flash + flash_image "$img_path" "$device" "$force" + + # Clean cache + if [[ "$clean" == "true" ]]; then + log "Cleaning cache..." + rm -f "$img_path" "${img_path%.img.xz}.sha256" + fi + + echo + echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}" + echo -e "${GREEN}║ SecuBox Multiboot USB Ready! ║${NC}" + echo -e "${GREEN}║ Boot from USB to start SecuBox Live ║${NC}" + echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}" +} + +main "$@"