Files
git.stella-ops.org/devops/trust-repo-template/scripts/add-target.sh

151 lines
4.1 KiB
Bash

#!/bin/bash
# -----------------------------------------------------------------------------
# add-target.sh
# Sprint: SPRINT_20260125_001_Attestor_tuf_trust_foundation
# Task: TUF-006 - Create TUF repository structure template
# Description: Add a new target file to the TUF repository
# -----------------------------------------------------------------------------
set -euo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
usage() {
echo "Usage: $0 <source-file> <target-name> [options]"
echo ""
echo "Add a target file to the TUF repository."
echo ""
echo "Options:"
echo " --repo DIR Repository directory (default: current directory)"
echo " --custom-hash HASH Override SHA256 hash (for testing only)"
echo " -h, --help Show this help message"
echo ""
echo "Example:"
echo " $0 /path/to/rekor-key.pub rekor-key-v1"
echo " $0 /path/to/services.json sigstore-services-v1 --repo /var/lib/tuf"
exit 1
}
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
SOURCE_FILE=""
TARGET_NAME=""
REPO_DIR="."
CUSTOM_HASH=""
while [[ $# -gt 0 ]]; do
case $1 in
--repo)
REPO_DIR="$2"
shift 2
;;
--custom-hash)
CUSTOM_HASH="$2"
shift 2
;;
-h|--help)
usage
;;
*)
if [[ -z "$SOURCE_FILE" ]]; then
SOURCE_FILE="$1"
elif [[ -z "$TARGET_NAME" ]]; then
TARGET_NAME="$1"
else
log_error "Unknown argument: $1"
usage
fi
shift
;;
esac
done
if [[ -z "$SOURCE_FILE" ]] || [[ -z "$TARGET_NAME" ]]; then
log_error "Source file and target name are required"
usage
fi
if [[ ! -f "$SOURCE_FILE" ]]; then
log_error "Source file not found: $SOURCE_FILE"
exit 1
fi
if [[ ! -f "$REPO_DIR/targets.json" ]]; then
log_error "Not a TUF repository: $REPO_DIR (targets.json not found)"
exit 1
fi
# Calculate file hash and size
FILE_SIZE=$(stat -f%z "$SOURCE_FILE" 2>/dev/null || stat -c%s "$SOURCE_FILE")
if [[ -n "$CUSTOM_HASH" ]]; then
FILE_HASH="$CUSTOM_HASH"
else
FILE_HASH=$(openssl dgst -sha256 -hex "$SOURCE_FILE" | awk '{print $2}')
fi
log_info "Adding target: $TARGET_NAME"
log_info " Source: $SOURCE_FILE"
log_info " Size: $FILE_SIZE bytes"
log_info " SHA256: $FILE_HASH"
# Copy file to targets directory
TARGETS_DIR="$REPO_DIR/targets"
mkdir -p "$TARGETS_DIR"
cp "$SOURCE_FILE" "$TARGETS_DIR/$TARGET_NAME"
# Update targets.json
# This is a simplified implementation - production should use proper JSON manipulation
TARGETS_JSON="$REPO_DIR/targets.json"
# Read current version
CURRENT_VERSION=$(grep -o '"version"[[:space:]]*:[[:space:]]*[0-9]*' "$TARGETS_JSON" | head -1 | grep -o '[0-9]*')
NEW_VERSION=$((CURRENT_VERSION + 1))
# Calculate new expiry (30 days from now)
NEW_EXPIRES=$(date -u -d "+30 days" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+30d +%Y-%m-%dT%H:%M:%SZ)
log_info "Updating targets.json (version $CURRENT_VERSION -> $NEW_VERSION)"
# Create new targets entry
python3 - "$TARGETS_JSON" "$TARGET_NAME" "$FILE_SIZE" "$FILE_HASH" "$NEW_VERSION" "$NEW_EXPIRES" << 'PYTHON_SCRIPT'
import json
import sys
targets_file = sys.argv[1]
target_name = sys.argv[2]
file_size = int(sys.argv[3])
file_hash = sys.argv[4]
new_version = int(sys.argv[5])
new_expires = sys.argv[6]
with open(targets_file, 'r') as f:
data = json.load(f)
data['signed']['version'] = new_version
data['signed']['expires'] = new_expires
data['signed']['targets'][target_name] = {
'length': file_size,
'hashes': {
'sha256': file_hash
}
}
# Clear signatures (need to re-sign)
data['signatures'] = []
with open(targets_file, 'w') as f:
json.dump(data, f, indent=2)
print(f"Updated {targets_file}")
PYTHON_SCRIPT
log_info ""
log_info "Target added successfully!"
log_warn "IMPORTANT: targets.json signatures have been cleared."
log_warn "Run the signing script to re-sign metadata before publishing."