Skip to content

Commit e9e5cee

Browse files
authored
Merge pull request #8 from shubham-stepsecurity/sm/feat/base-lc
feat: dynamically specify directories for scan
2 parents 76b709d + 4ddebb8 commit e9e5cee

3 files changed

Lines changed: 166 additions & 23 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
See [VERSIONING.md](VERSIONING.md) for why the version starts at 1.8.1.
99

10+
## [1.8.2] - 2026-03-17
11+
12+
### Added
13+
- `--search-dirs DIR [DIR...]` flag to scan specific directories instead of `$HOME` (replaces default; repeatable)
14+
- Accepts multiple directories in a single flag: `--search-dirs /tmp /opt /var`
15+
- Supports repeated use: `--search-dirs /tmp --search-dirs /opt`
16+
- Quoted paths with spaces work: `--search-dirs "/path/with spaces"`
17+
1018
## [1.8.1] - 2026-03-10
1119

1220
First open-source release. The scanning engine was previously an internal enterprise tool (v1.0.0-v1.8.1) running in production. This release adds community mode for local-only scanning while keeping the enterprise codebase intact.
@@ -36,4 +44,5 @@ First open-source release. The scanning engine was previously an internal enterp
3644
- Execution log capture and base64 encoding
3745
- Instance locking to prevent concurrent runs
3846

47+
[1.8.2]: https://github.com/step-security/dev-machine-guard/compare/v1.8.1...v1.8.2
3948
[1.8.1]: https://github.com/step-security/dev-machine-guard/releases/tag/v1.8.1

examples/sample-output.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"agent_version": "1.8.1",
2+
"agent_version": "1.8.2",
33
"scan_timestamp": 1741305600,
44
"scan_timestamp_iso": "2026-03-07T00:00:00Z",
55
"device": {
@@ -133,4 +133,4 @@
133133
"mcp_configs_count": 2,
134134
"node_projects_count": 0
135135
}
136-
}
136+
}

stepsecurity-dev-machine-guard.sh

Lines changed: 155 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
# Options:
2828
# --enable-npm-scan Enable Node.js package scanning
2929
# --disable-npm-scan Disable Node.js package scanning
30+
# --search-dirs DIR [DIR...] Search DIRs instead of $HOME (replaces default; repeatable)
3031
# --verbose Show progress messages
3132
# --color=WHEN auto | always | never (default: auto)
3233
# -v, --version Show version
@@ -41,7 +42,7 @@ set -euo pipefail
4142
# SECTION 2: VERSION AND CLI DEFAULTS
4243
#==============================================================================
4344

44-
AGENT_VERSION="1.8.1"
45+
AGENT_VERSION="1.8.2"
4546

4647
# Output configuration (set by CLI flags)
4748
OUTPUT_FORMAT="pretty" # pretty | json | html
@@ -50,6 +51,14 @@ COLOR_MODE="auto" # auto | always | never
5051
QUIET=true # Suppress progress messages by default in community mode
5152
ENABLE_NODE_PACKAGE_SCAN="auto" # auto | true | false
5253

54+
# Directories to search for projects and extensions (bash array)
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") # multiple locations
59+
SEARCH_DIRS=("\$HOME")
60+
_SEARCH_DIRS_SET=false
61+
5362
#==============================================================================
5463
# STEPSECURITY ENTERPRISE CONFIGURATION
5564
# Community users: leave these unchanged. They are only used in enterprise mode.
@@ -524,6 +533,29 @@ get_user_directory() {
524533
return 0
525534
}
526535

536+
resolve_search_directories() {
537+
local user_home="$1"
538+
local resolved_dirs=()
539+
540+
for dir in "${SEARCH_DIRS[@]}"; do
541+
# Resolve $HOME to the actual user home directory
542+
local resolved="${dir/\$HOME/$user_home}"
543+
if [ -d "$resolved" ]; then
544+
resolved_dirs+=("$resolved")
545+
else
546+
print_progress "Warning: Search directory not found, skipping: $resolved"
547+
fi
548+
done
549+
550+
if [ ${#resolved_dirs[@]} -eq 0 ]; then
551+
print_progress "Warning: No valid search directories found, falling back to: $user_home"
552+
echo "$user_home"
553+
return
554+
fi
555+
556+
printf '%s\n' "${resolved_dirs[@]}"
557+
}
558+
527559
#==============================================================================
528560
# LAUNCHD MANAGEMENT
529561
#==============================================================================
@@ -3086,12 +3118,30 @@ run_scan() {
30863118
fi
30873119
step_done "Scanning MCP server configs"
30883120

3089-
# Collect extensions
3121+
# Resolve search directories
3122+
local search_dirs
3123+
search_dirs=$(resolve_search_directories "$user_home")
3124+
3125+
# Collect extensions across all search directories
30903126
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)
3127+
local ide_extensions="[]"
3128+
local ext_count=0
3129+
while IFS= read -r search_dir; do
3130+
local dir_ext_result=$(collect_all_extensions "$search_dir")
3131+
local dir_extensions=$(echo "$dir_ext_result" | head -1)
3132+
local dir_ext_count=$(echo "$dir_ext_result" | tail -1)
3133+
# Merge JSON arrays
3134+
if [ "$dir_extensions" != "[]" ] && [ -n "$dir_extensions" ]; then
3135+
if [ "$ide_extensions" = "[]" ]; then
3136+
ide_extensions="$dir_extensions"
3137+
else
3138+
local existing_content=$(echo "$ide_extensions" | sed 's/^\[//;s/\]$//')
3139+
local new_content=$(echo "$dir_extensions" | sed 's/^\[//;s/\]$//')
3140+
ide_extensions="[${existing_content},${new_content}]"
3141+
fi
3142+
fi
3143+
ext_count=$((ext_count + dir_ext_count))
3144+
done <<< "$search_dirs"
30953145
step_done "Scanning IDE extensions"
30963146

30973147
# Resolve ENABLE_NODE_PACKAGE_SCAN: in community mode, "auto" means "false"
@@ -3119,10 +3169,36 @@ run_scan() {
31193169
step_done "Scanning global packages"
31203170

31213171
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')
3172+
# Scan across all search directories, merge results
3173+
local combined_projects_file=$(mktemp)
3174+
echo "[]" > "$combined_projects_file"
3175+
local total_node_projects_count=0
3176+
local total_node_scan_duration=0
3177+
while IFS= read -r search_dir; do
3178+
local node_scan_result=$(scan_node_projects "$search_dir" "$logged_in_user")
3179+
local dir_projects_file=$(echo "$node_scan_result" | sed -n '1p')
3180+
local dir_projects_count=$(echo "$node_scan_result" | sed -n '2p')
3181+
local dir_scan_duration=$(echo "$node_scan_result" | sed -n '3p')
3182+
total_node_projects_count=$((total_node_projects_count + dir_projects_count))
3183+
total_node_scan_duration=$((total_node_scan_duration + dir_scan_duration))
3184+
# Merge project files
3185+
if [ -n "$dir_projects_file" ] && [ -f "$dir_projects_file" ]; then
3186+
local existing=$(cat "$combined_projects_file")
3187+
if [ "$existing" = "[]" ]; then
3188+
cat "$dir_projects_file" > "$combined_projects_file"
3189+
else
3190+
local existing_content=$(echo "$existing" | sed 's/^\[//;s/\]$//')
3191+
local new_content=$(cat "$dir_projects_file" | sed 's/^\[//;s/\]$//')
3192+
if [ -n "$new_content" ]; then
3193+
echo "[${existing_content},${new_content}]" > "$combined_projects_file"
3194+
fi
3195+
fi
3196+
rm -f "$dir_projects_file"
3197+
fi
3198+
done <<< "$search_dirs"
3199+
node_projects_file="$combined_projects_file"
3200+
node_projects_count=$total_node_projects_count
3201+
node_scan_duration=$total_node_scan_duration
31263202
step_done "Scanning Node.js projects"
31273203
else
31283204
step_skip "Node.js packages (use --enable-npm-scan)"
@@ -3297,14 +3373,29 @@ EOF
32973373
local ide_installations=$(detect_ide_installations "$logged_in_user")
32983374
echo ""
32993375

3300-
# Get user directory to scan
3301-
local user_dir=$(get_user_directory)
3376+
# Resolve search directories
3377+
local search_dirs
3378+
search_dirs=$(resolve_search_directories "$user_home")
33023379
echo ""
33033380

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)
3381+
# Collect all IDE extensions across search directories
3382+
local ide_extensions="[]"
3383+
local ide_extensions_count=0
3384+
while IFS= read -r search_dir; do
3385+
local dir_ext_result=$(collect_all_extensions "$search_dir")
3386+
local dir_extensions=$(echo "$dir_ext_result" | head -1)
3387+
local dir_ext_count=$(echo "$dir_ext_result" | tail -1)
3388+
if [ "$dir_extensions" != "[]" ] && [ -n "$dir_extensions" ]; then
3389+
if [ "$ide_extensions" = "[]" ]; then
3390+
ide_extensions="$dir_extensions"
3391+
else
3392+
local existing_content=$(echo "$ide_extensions" | sed 's/^\[//;s/\]$//')
3393+
local new_content=$(echo "$dir_extensions" | sed 's/^\[//;s/\]$//')
3394+
ide_extensions="[${existing_content},${new_content}]"
3395+
fi
3396+
fi
3397+
ide_extensions_count=$((ide_extensions_count + dir_ext_count))
3398+
done <<< "$search_dirs"
33083399
echo ""
33093400

33103401
# AI Agent Detection (v1.6.0+)
@@ -3375,11 +3466,35 @@ EOF
33753466
node_global_packages_count=$(echo "$global_scan_result" | sed -n '2p')
33763467
echo ""
33773468

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')
3469+
# Scan for Node.js projects across all search directories
3470+
local combined_projects_file=$(mktemp)
3471+
echo "[]" > "$combined_projects_file"
3472+
local total_node_projects_count=0
3473+
local total_node_scan_duration=0
3474+
while IFS= read -r search_dir; do
3475+
local node_scan_result=$(scan_node_projects "$search_dir" "$logged_in_user")
3476+
local dir_projects_file=$(echo "$node_scan_result" | sed -n '1p')
3477+
local dir_projects_count=$(echo "$node_scan_result" | sed -n '2p')
3478+
local dir_scan_duration=$(echo "$node_scan_result" | sed -n '3p')
3479+
total_node_projects_count=$((total_node_projects_count + dir_projects_count))
3480+
total_node_scan_duration=$((total_node_scan_duration + dir_scan_duration))
3481+
if [ -n "$dir_projects_file" ] && [ -f "$dir_projects_file" ]; then
3482+
local existing=$(cat "$combined_projects_file")
3483+
if [ "$existing" = "[]" ]; then
3484+
cat "$dir_projects_file" > "$combined_projects_file"
3485+
else
3486+
local existing_content=$(echo "$existing" | sed 's/^\[//;s/\]$//')
3487+
local new_content=$(cat "$dir_projects_file" | sed 's/^\[//;s/\]$//')
3488+
if [ -n "$new_content" ]; then
3489+
echo "[${existing_content},${new_content}]" > "$combined_projects_file"
3490+
fi
3491+
fi
3492+
rm -f "$dir_projects_file"
3493+
fi
3494+
done <<< "$search_dirs"
3495+
node_projects_file="$combined_projects_file"
3496+
node_projects_count=$total_node_projects_count
3497+
node_scan_duration=$total_node_scan_duration
33833498
echo ""
33843499

33853500
else
@@ -3493,6 +3608,7 @@ Output formats (community mode, mutually exclusive):
34933608
--html FILE HTML report saved to FILE
34943609
34953610
Options:
3611+
--search-dirs DIR [DIR...] Search DIRs instead of \$HOME (replaces default; repeatable)
34963612
--enable-npm-scan Enable Node.js package scanning
34973613
--disable-npm-scan Disable Node.js package scanning
34983614
--verbose Show progress messages (suppressed by default)
@@ -3506,6 +3622,9 @@ Examples:
35063622
$(basename "$0") --json > scan.json # JSON to file
35073623
$(basename "$0") --html report.html # HTML report
35083624
$(basename "$0") --verbose --enable-npm-scan # Verbose with npm scan
3625+
$(basename "$0") --search-dirs /Volumes/code # Search only /Volumes/code
3626+
$(basename "$0") --search-dirs /tmp /opt # Multiple dirs, one flag
3627+
$(basename "$0") --search-dirs "/path/with spaces" --search-dirs /opt # Mixed styles
35093628
$(basename "$0") send-telemetry # Enterprise telemetry
35103629
35113630
https://github.com/step-security/dev-machine-guard
@@ -3561,6 +3680,21 @@ while [ $# -gt 0 ]; do
35613680
fi
35623681
shift
35633682
;;
3683+
--search-dirs)
3684+
shift
3685+
if [ $# -eq 0 ] || [[ "$1" == --* ]]; then
3686+
print_error "--search-dirs requires at least one directory path argument"
3687+
exit 1
3688+
fi
3689+
if [ "$_SEARCH_DIRS_SET" = false ]; then
3690+
SEARCH_DIRS=()
3691+
_SEARCH_DIRS_SET=true
3692+
fi
3693+
while [ $# -gt 0 ] && [[ "$1" != --* ]]; do
3694+
SEARCH_DIRS+=("$1")
3695+
shift
3696+
done
3697+
;;
35643698
--verbose)
35653699
QUIET=false
35663700
shift

0 commit comments

Comments
 (0)