-
Notifications
You must be signed in to change notification settings - Fork 2
feat: add install script with sha256 validation #108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
2c9c639
Initial plan
Copilot 8407224
feat: add install script with sha256 validation
Copilot 65de36e
docs: improve install script documentation and add file check
Copilot 3d0ca18
fix: improve install script security and robustness
Copilot a9dd272
fix: accept case-insensitive checksums and improve temp dir handling
Copilot 1177905
feat: add platform checks for OS and architecture in install script
Mossaka 5670663
fix: address shellcheck warnings in install script
Mossaka File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,247 @@ | ||
| #!/bin/bash | ||
| set -e | ||
|
|
||
| # Install script for awf (Agentic Workflow Firewall) | ||
| # | ||
| # This script downloads, verifies, and installs the awf binary with SHA256 validation | ||
| # to protect against corrupted or tampered downloads. | ||
| # | ||
| # Usage: | ||
| # curl -sSL https://raw.githubusercontent.com/githubnext/gh-aw-firewall/main/install.sh | sudo bash | ||
| # | ||
| # Security features: | ||
| # - Uses curl -f to fail on HTTP errors (404, 403, etc.) | ||
| # - Verifies SHA256 checksum from official checksums.txt | ||
| # - Validates downloaded file is a valid ELF executable | ||
| # - Detects HTML error pages that may slip through | ||
| # | ||
| # Requirements: | ||
| # - curl | ||
| # - sha256sum | ||
| # - file | ||
| # - sudo/root access | ||
| # | ||
| # Repository: https://github.com/githubnext/gh-aw-firewall | ||
| # Issue #107: https://github.com/githubnext/gh-aw-firewall/issues/107 | ||
|
|
||
| REPO="githubnext/gh-aw-firewall" | ||
| BINARY_NAME="awf-linux-x64" | ||
| INSTALL_DIR="/usr/local/bin" | ||
| INSTALL_NAME="awf" | ||
|
|
||
| # Colors for output | ||
| RED='\033[0;31m' | ||
| GREEN='\033[0;32m' | ||
| YELLOW='\033[1;33m' | ||
| NC='\033[0m' # No Color | ||
|
|
||
| # Helper functions | ||
| info() { | ||
| echo -e "${GREEN}[INFO]${NC} $1" | ||
| } | ||
|
|
||
| error() { | ||
| echo -e "${RED}[ERROR]${NC} $1" >&2 | ||
| } | ||
|
|
||
| warn() { | ||
| echo -e "${YELLOW}[WARN]${NC} $1" | ||
| } | ||
|
|
||
| # Check if running as root | ||
| check_sudo() { | ||
| if [ "$EUID" -ne 0 ]; then | ||
| error "This script must be run with sudo or as root" | ||
| exit 1 | ||
| fi | ||
| } | ||
|
|
||
| # Check required commands | ||
| check_requirements() { | ||
| local missing=() | ||
|
|
||
| for cmd in curl sha256sum file; do | ||
| if ! command -v "$cmd" &> /dev/null; then | ||
| missing+=("$cmd") | ||
| fi | ||
| done | ||
|
|
||
| if [ ${#missing[@]} -ne 0 ]; then | ||
| error "Missing required commands: ${missing[*]}" | ||
| error "Please install them and try again" | ||
| exit 1 | ||
| fi | ||
| } | ||
|
|
||
| # Check OS and architecture | ||
| check_platform() { | ||
| local os arch | ||
| os=$(uname -s) | ||
| arch=$(uname -m) | ||
|
|
||
| if [ "$os" != "Linux" ]; then | ||
| error "Unsupported OS: $os (this installer supports Linux only)" | ||
| exit 1 | ||
| fi | ||
|
|
||
| case "$arch" in | ||
| x86_64|amd64) | ||
| ;; | ||
| *) | ||
| error "Unsupported architecture: $arch (supported: x86_64)" | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| } | ||
|
|
||
| # Get latest release version | ||
| get_latest_version() { | ||
| info "Fetching latest release version..." | ||
|
|
||
| # Try GitHub API with -f to fail on HTTP errors | ||
| VERSION=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" 2>/dev/null | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') | ||
Mossaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if [ -z "$VERSION" ]; then | ||
| error "Failed to fetch latest version from GitHub API" | ||
| error "Please check your internet connection and try again" | ||
| exit 1 | ||
| fi | ||
|
|
||
| info "Latest version: $VERSION" | ||
| } | ||
|
|
||
| # Download file | ||
| download_file() { | ||
| local url="$1" | ||
| local output="$2" | ||
|
|
||
| info "Downloading from $url..." | ||
|
|
||
| # Use -f to fail on HTTP errors (like 404) | ||
| if ! curl -fsSL "$url" -o "$output"; then | ||
Mossaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| error "Failed to download $url" | ||
| error "Please check if the release exists and try again" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Check if file is not empty | ||
| if [ ! -s "$output" ]; then | ||
| error "Downloaded file is empty" | ||
| rm -f "$output" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Check if file is HTML (common for 404 pages) | ||
| if file "$output" | grep -q "HTML"; then | ||
| error "Downloaded file appears to be an HTML page (possibly 404)" | ||
| error "Please check if the release exists: https://github.com/${REPO}/releases" | ||
| rm -f "$output" | ||
| exit 1 | ||
| fi | ||
| } | ||
|
|
||
| # Verify checksum | ||
| verify_checksum() { | ||
| local file="$1" | ||
| local checksums_file="$2" | ||
|
|
||
| info "Verifying SHA256 checksum..." | ||
|
|
||
| # Extract the checksum for our binary from checksums.txt | ||
| # Format: "checksum filename" (two spaces) - use exact filename match at end of line | ||
| local expected_sum | ||
| expected_sum=$(awk -v fname="$BINARY_NAME" '$2 == fname {print $1; exit}' "$checksums_file") | ||
|
|
||
| if [ -z "$expected_sum" ]; then | ||
| error "Could not find checksum for $BINARY_NAME in checksums.txt" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Validate checksum format (64 hex characters, case-insensitive) | ||
| if ! echo "$expected_sum" | grep -qE '^[a-fA-F0-9]{64}$'; then | ||
| error "Invalid checksum format: $expected_sum" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Normalize checksum case | ||
| expected_sum=$(echo "$expected_sum" | tr 'A-F' 'a-f') | ||
|
|
||
| # Calculate actual checksum | ||
| local actual_sum | ||
| actual_sum=$(sha256sum "$file" | awk '{print $1}' | tr 'A-F' 'a-f') | ||
|
|
||
| if [ "$expected_sum" != "$actual_sum" ]; then | ||
Mossaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| error "Checksum verification failed!" | ||
| error "Expected: $expected_sum" | ||
| error "Got: $actual_sum" | ||
| error "The downloaded file may be corrupted or tampered with" | ||
| exit 1 | ||
| fi | ||
|
|
||
| info "Checksum verification passed ✓" | ||
| } | ||
|
|
||
| # Main installation function | ||
| main() { | ||
| info "Starting awf installation..." | ||
|
|
||
| # Check requirements | ||
| check_sudo | ||
| check_requirements | ||
| check_platform | ||
|
|
||
| # Get version | ||
| get_latest_version | ||
|
|
||
| # Create temp directory with prefix for identification | ||
| # mktemp creates secure temporary directories with proper permissions (0700) | ||
| TEMP_DIR=$(mktemp -d -t awf-install.XXXXXX) | ||
|
|
||
| # Validate temp directory was created | ||
| if [ -z "$TEMP_DIR" ] || [ ! -d "$TEMP_DIR" ]; then | ||
| error "Failed to create temporary directory" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Set up cleanup trap (mktemp already ensures secure location) | ||
| trap 'rm -rf "$TEMP_DIR"' EXIT | ||
|
|
||
| # Download URLs | ||
| BASE_URL="https://github.com/${REPO}/releases/download/${VERSION}" | ||
| BINARY_URL="${BASE_URL}/${BINARY_NAME}" | ||
| CHECKSUMS_URL="${BASE_URL}/checksums.txt" | ||
|
|
||
| # Download binary and checksums | ||
| download_file "$BINARY_URL" "$TEMP_DIR/$BINARY_NAME" | ||
| download_file "$CHECKSUMS_URL" "$TEMP_DIR/checksums.txt" | ||
|
|
||
| # Verify checksum | ||
| verify_checksum "$TEMP_DIR/$BINARY_NAME" "$TEMP_DIR/checksums.txt" | ||
|
|
||
| # Make binary executable | ||
| chmod +x "$TEMP_DIR/$BINARY_NAME" | ||
|
|
||
| # Test if it's a valid ELF executable | ||
| if ! file "$TEMP_DIR/$BINARY_NAME" | grep -q "ELF.*executable"; then | ||
| error "Downloaded file is not a valid Linux executable" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Install binary | ||
| info "Installing to $INSTALL_DIR/$INSTALL_NAME..." | ||
| mv "$TEMP_DIR/$BINARY_NAME" "$INSTALL_DIR/$INSTALL_NAME" | ||
|
|
||
| # Verify installation | ||
| if [ -x "$INSTALL_DIR/$INSTALL_NAME" ]; then | ||
| info "Installation successful! ✓" | ||
| info "" | ||
| info "Run 'awf --help' to get started" | ||
| info "Note: awf requires Docker to be installed and running" | ||
| else | ||
| error "Installation failed - binary not found at $INSTALL_DIR/$INSTALL_NAME" | ||
| exit 1 | ||
| fi | ||
| } | ||
|
|
||
| # Run main function | ||
| main | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.