2727# Options:
2828# --enable-npm-scan Enable Node.js package scanning
2929# --disable-npm-scan Disable Node.js package scanning
30+ # --search-dir DIR Add DIR to search paths (repeatable, appends to SEARCH_DIRS)
3031# --verbose Show progress messages
3132# --color=WHEN auto | always | never (default: auto)
3233# -v, --version Show version
@@ -50,6 +51,13 @@ COLOR_MODE="auto" # auto | always | never
5051QUIET=true # Suppress progress messages by default in community mode
5152ENABLE_NODE_PACKAGE_SCAN=" auto" # auto | true | false
5253
54+ # Directories to search for projects and extensions (space-separated)
55+ # Default: user's home directory. Customize as needed, e.g.:
56+ # SEARCH_DIRS="$HOME /Volumes/code" # home + encrypted partition
57+ # SEARCH_DIRS="/Volumes/code" # only encrypted partition
58+ # SEARCH_DIRS="$HOME /Volumes/code /opt/work $HOME/project" # multiple locations
59+ SEARCH_DIRS=" \$ HOME"
60+
5361# ==============================================================================
5462# STEPSECURITY ENTERPRISE CONFIGURATION
5563# Community users: leave these unchanged. They are only used in enterprise mode.
@@ -524,6 +532,29 @@ get_user_directory() {
524532 return 0
525533}
526534
535+ resolve_search_directories () {
536+ local user_home=" $1 "
537+ local resolved_dirs=()
538+
539+ for dir in $SEARCH_DIRS ; do
540+ # Resolve $HOME to the actual user home directory
541+ local resolved=" ${dir/ \$ HOME/ $user_home } "
542+ if [ -d " $resolved " ]; then
543+ resolved_dirs+=(" $resolved " )
544+ else
545+ print_progress " Warning: Search directory not found, skipping: $resolved "
546+ fi
547+ done
548+
549+ if [ ${# resolved_dirs[@]} -eq 0 ]; then
550+ print_progress " Warning: No valid search directories found, falling back to: $user_home "
551+ echo " $user_home "
552+ return
553+ fi
554+
555+ printf ' %s\n' " ${resolved_dirs[@]} "
556+ }
557+
527558# ==============================================================================
528559# LAUNCHD MANAGEMENT
529560# ==============================================================================
@@ -3086,12 +3117,30 @@ run_scan() {
30863117 fi
30873118 step_done " Scanning MCP server configs"
30883119
3089- # Collect extensions
3120+ # Resolve search directories
3121+ local search_dirs
3122+ search_dirs=$( resolve_search_directories " $user_home " )
3123+
3124+ # Collect extensions across all search directories
30903125 step_start " Scanning IDE extensions"
3091- local user_dir=" $user_home "
3092- local all_ext_result=$( collect_all_extensions " $user_dir " )
3093- local ide_extensions=$( echo " $all_ext_result " | head -1)
3094- local ext_count=$( echo " $all_ext_result " | tail -1)
3126+ local ide_extensions=" []"
3127+ local ext_count=0
3128+ while IFS= read -r search_dir; do
3129+ local dir_ext_result=$( collect_all_extensions " $search_dir " )
3130+ local dir_extensions=$( echo " $dir_ext_result " | head -1)
3131+ local dir_ext_count=$( echo " $dir_ext_result " | tail -1)
3132+ # Merge JSON arrays
3133+ if [ " $dir_extensions " != " []" ] && [ -n " $dir_extensions " ]; then
3134+ if [ " $ide_extensions " = " []" ]; then
3135+ ide_extensions=" $dir_extensions "
3136+ else
3137+ local existing_content=$( echo " $ide_extensions " | sed ' s/^\[//;s/\]$//' )
3138+ local new_content=$( echo " $dir_extensions " | sed ' s/^\[//;s/\]$//' )
3139+ ide_extensions=" [${existing_content} ,${new_content} ]"
3140+ fi
3141+ fi
3142+ ext_count=$(( ext_count + dir_ext_count))
3143+ done <<< " $search_dirs"
30953144 step_done " Scanning IDE extensions"
30963145
30973146 # Resolve ENABLE_NODE_PACKAGE_SCAN: in community mode, "auto" means "false"
@@ -3119,10 +3168,36 @@ run_scan() {
31193168 step_done " Scanning global packages"
31203169
31213170 step_start " Scanning Node.js projects"
3122- local node_scan_result=$( scan_node_projects " $user_dir " " $logged_in_user " )
3123- node_projects_file=$( echo " $node_scan_result " | sed -n ' 1p' )
3124- node_projects_count=$( echo " $node_scan_result " | sed -n ' 2p' )
3125- node_scan_duration=$( echo " $node_scan_result " | sed -n ' 3p' )
3171+ # Scan across all search directories, merge results
3172+ local combined_projects_file=$( mktemp)
3173+ echo " []" > " $combined_projects_file "
3174+ local total_node_projects_count=0
3175+ local total_node_scan_duration=0
3176+ while IFS= read -r search_dir; do
3177+ local node_scan_result=$( scan_node_projects " $search_dir " " $logged_in_user " )
3178+ local dir_projects_file=$( echo " $node_scan_result " | sed -n ' 1p' )
3179+ local dir_projects_count=$( echo " $node_scan_result " | sed -n ' 2p' )
3180+ local dir_scan_duration=$( echo " $node_scan_result " | sed -n ' 3p' )
3181+ total_node_projects_count=$(( total_node_projects_count + dir_projects_count))
3182+ total_node_scan_duration=$(( total_node_scan_duration + dir_scan_duration))
3183+ # Merge project files
3184+ if [ -n " $dir_projects_file " ] && [ -f " $dir_projects_file " ]; then
3185+ local existing=$( cat " $combined_projects_file " )
3186+ if [ " $existing " = " []" ]; then
3187+ cat " $dir_projects_file " > " $combined_projects_file "
3188+ else
3189+ local existing_content=$( echo " $existing " | sed ' s/^\[//;s/\]$//' )
3190+ local new_content=$( cat " $dir_projects_file " | sed ' s/^\[//;s/\]$//' )
3191+ if [ -n " $new_content " ]; then
3192+ echo " [${existing_content} ,${new_content} ]" > " $combined_projects_file "
3193+ fi
3194+ fi
3195+ rm -f " $dir_projects_file "
3196+ fi
3197+ done <<< " $search_dirs"
3198+ node_projects_file=" $combined_projects_file "
3199+ node_projects_count=$total_node_projects_count
3200+ node_scan_duration=$total_node_scan_duration
31263201 step_done " Scanning Node.js projects"
31273202 else
31283203 step_skip " Node.js packages (use --enable-npm-scan)"
@@ -3297,14 +3372,29 @@ EOF
32973372 local ide_installations=$( detect_ide_installations " $logged_in_user " )
32983373 echo " "
32993374
3300- # Get user directory to scan
3301- local user_dir=$( get_user_directory)
3375+ # Resolve search directories
3376+ local search_dirs
3377+ search_dirs=$( resolve_search_directories " $user_home " )
33023378 echo " "
33033379
3304- # Collect all IDE extensions
3305- local all_ide_collection_result=$( collect_all_extensions " $user_dir " )
3306- local ide_extensions=$( echo " $all_ide_collection_result " | head -1)
3307- local ide_extensions_count=$( echo " $all_ide_collection_result " | tail -1)
3380+ # Collect all IDE extensions across search directories
3381+ local ide_extensions=" []"
3382+ local ide_extensions_count=0
3383+ while IFS= read -r search_dir; do
3384+ local dir_ext_result=$( collect_all_extensions " $search_dir " )
3385+ local dir_extensions=$( echo " $dir_ext_result " | head -1)
3386+ local dir_ext_count=$( echo " $dir_ext_result " | tail -1)
3387+ if [ " $dir_extensions " != " []" ] && [ -n " $dir_extensions " ]; then
3388+ if [ " $ide_extensions " = " []" ]; then
3389+ ide_extensions=" $dir_extensions "
3390+ else
3391+ local existing_content=$( echo " $ide_extensions " | sed ' s/^\[//;s/\]$//' )
3392+ local new_content=$( echo " $dir_extensions " | sed ' s/^\[//;s/\]$//' )
3393+ ide_extensions=" [${existing_content} ,${new_content} ]"
3394+ fi
3395+ fi
3396+ ide_extensions_count=$(( ide_extensions_count + dir_ext_count))
3397+ done <<< " $search_dirs"
33083398 echo " "
33093399
33103400 # AI Agent Detection (v1.6.0+)
@@ -3375,11 +3465,35 @@ EOF
33753465 node_global_packages_count=$( echo " $global_scan_result " | sed -n ' 2p' )
33763466 echo " "
33773467
3378- # Scan for Node.js projects (returns file path)
3379- local node_scan_result=$( scan_node_projects " $user_dir " " $logged_in_user " )
3380- node_projects_file=$( echo " $node_scan_result " | sed -n ' 1p' )
3381- node_projects_count=$( echo " $node_scan_result " | sed -n ' 2p' )
3382- node_scan_duration=$( echo " $node_scan_result " | sed -n ' 3p' )
3468+ # Scan for Node.js projects across all search directories
3469+ local combined_projects_file=$( mktemp)
3470+ echo " []" > " $combined_projects_file "
3471+ local total_node_projects_count=0
3472+ local total_node_scan_duration=0
3473+ while IFS= read -r search_dir; do
3474+ local node_scan_result=$( scan_node_projects " $search_dir " " $logged_in_user " )
3475+ local dir_projects_file=$( echo " $node_scan_result " | sed -n ' 1p' )
3476+ local dir_projects_count=$( echo " $node_scan_result " | sed -n ' 2p' )
3477+ local dir_scan_duration=$( echo " $node_scan_result " | sed -n ' 3p' )
3478+ total_node_projects_count=$(( total_node_projects_count + dir_projects_count))
3479+ total_node_scan_duration=$(( total_node_scan_duration + dir_scan_duration))
3480+ if [ -n " $dir_projects_file " ] && [ -f " $dir_projects_file " ]; then
3481+ local existing=$( cat " $combined_projects_file " )
3482+ if [ " $existing " = " []" ]; then
3483+ cat " $dir_projects_file " > " $combined_projects_file "
3484+ else
3485+ local existing_content=$( echo " $existing " | sed ' s/^\[//;s/\]$//' )
3486+ local new_content=$( cat " $dir_projects_file " | sed ' s/^\[//;s/\]$//' )
3487+ if [ -n " $new_content " ]; then
3488+ echo " [${existing_content} ,${new_content} ]" > " $combined_projects_file "
3489+ fi
3490+ fi
3491+ rm -f " $dir_projects_file "
3492+ fi
3493+ done <<< " $search_dirs"
3494+ node_projects_file=" $combined_projects_file "
3495+ node_projects_count=$total_node_projects_count
3496+ node_scan_duration=$total_node_scan_duration
33833497 echo " "
33843498
33853499 else
@@ -3493,6 +3607,7 @@ Output formats (community mode, mutually exclusive):
34933607 --html FILE HTML report saved to FILE
34943608
34953609Options:
3610+ --search-dir DIR Add DIR to search paths (repeatable, appends to SEARCH_DIRS)
34963611 --enable-npm-scan Enable Node.js package scanning
34973612 --disable-npm-scan Disable Node.js package scanning
34983613 --verbose Show progress messages (suppressed by default)
@@ -3506,6 +3621,7 @@ Examples:
35063621 $( basename " $0 " ) --json > scan.json # JSON to file
35073622 $( basename " $0 " ) --html report.html # HTML report
35083623 $( basename " $0 " ) --verbose --enable-npm-scan # Verbose with npm scan
3624+ $( basename " $0 " ) --search-dir /Volumes/code # Also search /Volumes/code
35093625 $( basename " $0 " ) send-telemetry # Enterprise telemetry
35103626
35113627https://github.com/step-security/dev-machine-guard
@@ -3561,6 +3677,14 @@ while [ $# -gt 0 ]; do
35613677 fi
35623678 shift
35633679 ;;
3680+ --search-dir)
3681+ if [ -z " ${2:- } " ]; then
3682+ print_error " --search-dir requires a directory path argument"
3683+ exit 1
3684+ fi
3685+ SEARCH_DIRS=" ${SEARCH_DIRS} $2 "
3686+ shift 2
3687+ ;;
35643688 --verbose)
35653689 QUIET=false
35663690 shift
0 commit comments