1+ #! /usr/bin/env bash
2+ #
3+ # Clone Demo Script - Secure Version
4+ # This script safely clones and sets up demo projects for onchain testing
5+ #
6+
7+ set -euo pipefail
8+
9+ # Enhanced portability - use /usr/bin/env bash for better cross-platform support
10+ # Exit on any command failure, undefined variables, or pipe failures
11+
12+ # Color codes for output
13+ readonly RED=' \033[0;31m'
14+ readonly GREEN=' \033[0;32m'
15+ readonly YELLOW=' \033[1;33m'
16+ readonly BLUE=' \033[0;34m'
17+ readonly NC=' \033[0m' # No Color
18+
19+ # Default configuration
20+ readonly DEFAULT_DEMO_DIR=" onchain-demo"
21+ readonly DEMO_REPO_URL=" https://github.com/coinbase/onchain-kit"
22+
23+ # Critical safety validation: Protected directories that should never be removed
24+ readonly PROTECTED_DIRS=(
25+ " /"
26+ " /bin"
27+ " /boot"
28+ " /dev"
29+ " /etc"
30+ " /home"
31+ " /lib"
32+ " /lib32"
33+ " /lib64"
34+ " /mnt"
35+ " /opt"
36+ " /proc"
37+ " /root"
38+ " /run"
39+ " /sbin"
40+ " /srv"
41+ " /sys"
42+ " /tmp"
43+ " /usr"
44+ " /var"
45+ " $HOME "
46+ )
47+
48+ # Function to print colored output
49+ print_status () {
50+ local color=$1
51+ local message=$2
52+ echo -e " ${color}${message}${NC} "
53+ }
54+
55+ # Function to validate directory path for safety
56+ validate_directory_path () {
57+ local target_dir=" $1 "
58+
59+ # Convert to absolute path for comparison
60+ local abs_path
61+ abs_path=" $( readlink -f " $target_dir " 2> /dev/null || echo " $target_dir " ) "
62+
63+ # Check if path is empty or contains dangerous patterns
64+ if [[ -z " $target_dir " ]]; then
65+ print_status " $RED " " ❌ Error: Directory path cannot be empty"
66+ return 1
67+ fi
68+
69+ # Check for dangerous patterns
70+ if [[ " $target_dir " =~ ^[[:space:]]* $ ]]; then
71+ print_status " $RED " " ❌ Error: Directory path cannot contain only whitespace"
72+ return 1
73+ fi
74+
75+ # Check against protected directories
76+ for protected_dir in " ${PROTECTED_DIRS[@]} " ; do
77+ if [[ " $abs_path " == " $protected_dir " ]] || [[ " $target_dir " == " $protected_dir " ]]; then
78+ print_status " $RED " " ❌ Error: Cannot remove protected directory: $target_dir "
79+ print_status " $RED " " This is a critical system directory and cannot be safely removed."
80+ return 1
81+ fi
82+ done
83+
84+ # Check for parent directory traversal that could affect protected paths
85+ case " $abs_path " in
86+ " /" )
87+ print_status " $RED " " ❌ Error: Cannot remove root directory"
88+ return 1
89+ ;;
90+ " /home" |" /home/" * )
91+ if [[ " $abs_path " == " /home" ]]; then
92+ print_status " $RED " " ❌ Error: Cannot remove /home directory"
93+ return 1
94+ fi
95+ ;;
96+ " /usr" |" /usr/" * )
97+ if [[ " $abs_path " == " /usr" ]]; then
98+ print_status " $RED " " ❌ Error: Cannot remove /usr directory"
99+ return 1
100+ fi
101+ ;;
102+ esac
103+
104+ return 0
105+ }
106+
107+ # Function to safely remove directory with comprehensive validation
108+ safe_remove_directory () {
109+ local target_dir=" $1 "
110+ local force_mode=" ${2:- false} "
111+
112+ print_status " $BLUE " " 🔍 Validating directory for safe removal: $target_dir "
113+
114+ # Comprehensive protection against dangerous directory values
115+ if ! validate_directory_path " $target_dir " ; then
116+ return 1
117+ fi
118+
119+ # Check if directory exists
120+ if [[ ! -d " $target_dir " ]]; then
121+ print_status " $YELLOW " " ⚠️ Directory does not exist: $target_dir "
122+ return 0
123+ fi
124+
125+ # Additional safety check - ensure we're not in a protected location
126+ local current_dir
127+ current_dir=" $( pwd) "
128+ case " $current_dir " in
129+ " /" |" /home" |" /usr" |" /etc" |" /var" |" /opt" )
130+ print_status " $RED " " ❌ Error: Cannot perform removal operations from protected directory: $current_dir "
131+ return 1
132+ ;;
133+ esac
134+
135+ # User confirmation for destructive operations (unless in force mode)
136+ if [[ " $force_mode " != " true" ]]; then
137+ print_status " $YELLOW " " ⚠️ This will permanently delete: $target_dir "
138+ print_status " $YELLOW " " All contents will be lost!"
139+ echo -n " Do you want to continue? [y/N]: "
140+ read -r confirmation
141+ case " $confirmation " in
142+ [Yy]|[Yy][Ee][Ss])
143+ print_status " $BLUE " " Proceeding with removal..."
144+ ;;
145+ * )
146+ print_status " $GREEN " " ✅ Operation cancelled by user"
147+ return 0
148+ ;;
149+ esac
150+ fi
151+
152+ # Final safety check before removal
153+ print_status " $BLUE " " 🔒 Performing final safety validation..."
154+ if ! validate_directory_path " $target_dir " ; then
155+ return 1
156+ fi
157+
158+ # Perform the actual removal with proper error handling
159+ print_status " $BLUE " " 🗑️ Removing directory: $target_dir "
160+ if rm -rf " $target_dir " ; then
161+ print_status " $GREEN " " ✅ Successfully removed directory: $target_dir "
162+ else
163+ print_status " $RED " " ❌ Failed to remove directory: $target_dir "
164+ return 1
165+ fi
166+
167+ return 0
168+ }
169+
170+ # Function to display usage information
171+ show_usage () {
172+ cat << EOF
173+ Usage: $0 [OPTIONS] [DEMO_DIRECTORY]
174+
175+ Clone and set up onchain demo projects safely.
176+
177+ OPTIONS:
178+ -h, --help Show this help message
179+ -f, --force Force removal without confirmation prompts
180+ -c, --clean Clean existing demo directory before cloning
181+ -r, --repo URL Specify custom repository URL (default: $DEMO_REPO_URL )
182+
183+ DEMO_DIRECTORY:
184+ Directory name for the demo (default: $DEFAULT_DEMO_DIR )
185+ Must be a safe, non-system directory path.
186+
187+ EXAMPLES:
188+ $0 # Clone to default directory
189+ $0 my-demo # Clone to custom directory
190+ $0 --clean my-demo # Clean existing and clone
191+ $0 --repo https://github.com/user/repo demo # Custom repo
192+
193+ SAFETY FEATURES:
194+ ✓ Enhanced portability with improved shebang declaration
195+ ✓ Critical safety validation before directory removal operations
196+ ✓ Comprehensive protection against dangerous directory values
197+ ✓ User confirmation for destructive operations
198+ ✓ Protected system directories validation
199+
200+ EOF
201+ }
202+
203+ # Main function
204+ main () {
205+ local demo_dir=" $DEFAULT_DEMO_DIR "
206+ local repo_url=" $DEMO_REPO_URL "
207+ local force_mode=" false"
208+ local clean_mode=" false"
209+
210+ print_status " $BLUE " " 🚀 Onchain Test Kit - Demo Clone Script"
211+ print_status " $BLUE " " Enhanced with security and safety features"
212+ echo
213+
214+ # Parse command line arguments
215+ while [[ $# -gt 0 ]]; do
216+ case $1 in
217+ -h|--help)
218+ show_usage
219+ exit 0
220+ ;;
221+ -f|--force)
222+ force_mode=" true"
223+ shift
224+ ;;
225+ -c|--clean)
226+ clean_mode=" true"
227+ shift
228+ ;;
229+ -r|--repo)
230+ if [[ -z " ${2:- } " ]]; then
231+ print_status " $RED " " ❌ Error: --repo requires a URL argument"
232+ exit 1
233+ fi
234+ repo_url=" $2 "
235+ shift 2
236+ ;;
237+ -* )
238+ print_status " $RED " " ❌ Error: Unknown option $1 "
239+ show_usage
240+ exit 1
241+ ;;
242+ * )
243+ demo_dir=" $1 "
244+ shift
245+ ;;
246+ esac
247+ done
248+
249+ # Validate demo directory argument
250+ if ! validate_directory_path " $demo_dir " ; then
251+ print_status " $RED " " ❌ Error: Invalid or unsafe directory path: $demo_dir "
252+ exit 1
253+ fi
254+
255+ # Clean existing directory if requested
256+ if [[ " $clean_mode " == " true" ]] && [[ -d " $demo_dir " ]]; then
257+ print_status " $YELLOW " " 🧹 Cleaning existing directory..."
258+ if ! safe_remove_directory " $demo_dir " " $force_mode " ; then
259+ print_status " $RED " " ❌ Failed to clean existing directory"
260+ exit 1
261+ fi
262+ fi
263+
264+ # Check if directory already exists
265+ if [[ -d " $demo_dir " ]]; then
266+ print_status " $YELLOW " " ⚠️ Directory already exists: $demo_dir "
267+ if [[ " $force_mode " != " true" ]]; then
268+ echo -n " Do you want to remove it and continue? [y/N]: "
269+ read -r confirmation
270+ case " $confirmation " in
271+ [Yy]|[Yy][Ee][Ss])
272+ if ! safe_remove_directory " $demo_dir " " $force_mode " ; then
273+ exit 1
274+ fi
275+ ;;
276+ * )
277+ print_status " $GREEN " " ✅ Operation cancelled by user"
278+ exit 0
279+ ;;
280+ esac
281+ else
282+ if ! safe_remove_directory " $demo_dir " " $force_mode " ; then
283+ exit 1
284+ fi
285+ fi
286+ fi
287+
288+ # Clone the repository
289+ print_status " $BLUE " " 📁 Cloning repository..."
290+ print_status " $BLUE " " Repository: $repo_url "
291+ print_status " $BLUE " " Directory: $demo_dir "
292+
293+ if git clone " $repo_url " " $demo_dir " ; then
294+ print_status " $GREEN " " ✅ Successfully cloned repository"
295+ else
296+ print_status " $RED " " ❌ Failed to clone repository"
297+ exit 1
298+ fi
299+
300+ # Change to demo directory
301+ cd " $demo_dir " || {
302+ print_status " $RED " " ❌ Failed to change to demo directory"
303+ exit 1
304+ }
305+
306+ # Set up the demo
307+ print_status " $BLUE " " ⚙️ Setting up demo environment..."
308+
309+ # Check if package.json exists and install dependencies
310+ if [[ -f " package.json" ]]; then
311+ print_status " $BLUE " " 📦 Installing dependencies..."
312+ if command -v yarn & > /dev/null; then
313+ yarn install
314+ elif command -v npm & > /dev/null; then
315+ npm install
316+ else
317+ print_status " $YELLOW " " ⚠️ Neither yarn nor npm found. Please install dependencies manually."
318+ fi
319+ fi
320+
321+ # Success message
322+ print_status " $GREEN " " 🎉 Demo setup completed successfully!"
323+ print_status " $GREEN " " Directory: $( pwd) "
324+ print_status " $GREEN " " You can now start exploring the demo."
325+
326+ echo
327+ print_status " $BLUE " " 📚 Next steps:"
328+ print_status " $BLUE " " 1. cd $demo_dir "
329+ print_status " $BLUE " " 2. Read the README.md for usage instructions"
330+ print_status " $BLUE " " 3. Run the example tests or start the demo application"
331+ }
332+
333+ # Script entry point with error handling
334+ if [[ " ${BASH_SOURCE[0]} " == " ${0} " ]]; then
335+ main " $@ "
336+ fi
0 commit comments