-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathinstall.sh
More file actions
executable file
·327 lines (278 loc) · 9.23 KB
/
install.sh
File metadata and controls
executable file
·327 lines (278 loc) · 9.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
#!/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:
# # Install latest version
# curl -sSL https://raw.githubusercontent.com/github/gh-aw-firewall/main/install.sh | sudo bash
#
# # Install specific version
# curl -sSL https://raw.githubusercontent.com/github/gh-aw-firewall/main/install.sh | sudo bash -s -- v1.0.0
#
# # Or with environment variable
# curl -sSL https://raw.githubusercontent.com/github/gh-aw-firewall/main/install.sh | sudo AWF_VERSION=v1.0.0 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/github/gh-aw-firewall
# Issue #107: https://github.com/github/gh-aw-firewall/issues/107
REPO="github/gh-aw-firewall"
BINARY_NAME="" # Set dynamically by check_platform
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
}
# Compute SHA256 hash portably (Linux uses sha256sum, macOS uses shasum)
sha256_portable() {
local file="$1"
if command -v sha256sum &> /dev/null; then
sha256sum "$file" | awk '{print $1}'
elif command -v shasum &> /dev/null; then
shasum -a 256 "$file" | awk '{print $1}'
else
error "Neither sha256sum nor shasum found"
exit 1
fi
}
# Check required commands
check_requirements() {
local missing=()
for cmd in curl file; do
if ! command -v "$cmd" &> /dev/null; then
missing+=("$cmd")
fi
done
# Need at least one SHA256 tool
if ! command -v sha256sum &> /dev/null && ! command -v shasum &> /dev/null; then
missing+=("sha256sum or shasum")
fi
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)
case "$os" in
Linux)
case "$arch" in
x86_64|amd64)
BINARY_NAME="awf-linux-x64"
;;
aarch64|arm64)
BINARY_NAME="awf-linux-arm64"
;;
*)
error "Unsupported architecture: $arch (supported: x86_64, aarch64)"
exit 1
;;
esac
;;
Darwin)
case "$arch" in
x86_64)
BINARY_NAME="awf-darwin-x64"
;;
arm64)
BINARY_NAME="awf-darwin-arm64"
;;
*)
error "Unsupported architecture: $arch (supported: x86_64, arm64)"
exit 1
;;
esac
;;
*)
error "Unsupported OS: $os (supported: Linux, macOS)"
exit 1
;;
esac
info "Detected platform: $os $arch (binary: $BINARY_NAME)"
}
# Validate version format (should be like v1.0.0, v1.2.3, etc.)
validate_version() {
local version="$1"
if ! echo "$version" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
error "Invalid version format: $version"
error "Version should be in format: v1.0.0"
exit 1
fi
}
# 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/')
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"
}
# Set version from argument, environment variable, or fetch latest
set_version() {
# Priority: argument > environment variable > fetch latest
if [ -n "$1" ]; then
VERSION="$1"
validate_version "$VERSION"
info "Using specified version: $VERSION"
elif [ -n "$AWF_VERSION" ]; then
VERSION="$AWF_VERSION"
validate_version "$VERSION"
info "Using version from AWF_VERSION: $VERSION"
else
get_latest_version
fi
}
# 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
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=$(sha256_portable "$file" | tr 'A-F' 'a-f')
if [ "$expected_sum" != "$actual_sum" ]; then
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 (from argument, env var, or fetch latest)
set_version "$1"
# 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 executable (ELF on Linux, Mach-O on macOS)
local file_type
file_type=$(file "$TEMP_DIR/$BINARY_NAME")
if echo "$file_type" | grep -q "ELF.*executable"; then
info "Valid Linux ELF executable"
elif echo "$file_type" | grep -q "Mach-O 64-bit"; then
info "Valid macOS Mach-O executable"
else
error "Downloaded file is not a valid executable: $file_type"
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 with all arguments
main "$@"