3a8e5d90ef
- Rewrite fhelp: add 'start' onboarding, recipe fallback chain (our files → cheat → tldr), 'workflow' dynamic loader, tier badges - Add welcome.sh: unified English welcome for bash/zsh/fish - Replace German README with concise English version - Add Zsh F1/Ctrl+/ widget for inline help while typing - Configure navi Ctrl+G widget for interactive cheatsheet browsing - Fix dangerous 'alias help=fhelp' (was breaking bash builtin) - Add 'h' and 'analyse' as safe aliases Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
511 lines
16 KiB
Bash
Executable File
511 lines
16 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Enhanced File Analysis Help System
|
|
# Integrates multiple help sources: custom cheat sheets, tldr, tool database, and workflows
|
|
|
|
# Color definitions
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
BLUE='\033[0;34m'
|
|
YELLOW='\033[1;33m'
|
|
CYAN='\033[0;36m'
|
|
MAGENTA='\033[0;35m'
|
|
NC='\033[0m'
|
|
|
|
# Help system paths
|
|
TOOLS_DB="/opt/remnux-docs/tools.db"
|
|
CHEAT_DIR="/opt/cheatsheets"
|
|
WORKFLOW_DIR="/opt/remnux-docs/workflows"
|
|
TLDR_CACHE="/home/remnux/.local/share/tldr"
|
|
|
|
# Resolve cheat file names from a user-provided tool name
|
|
# Tries several variants: exact, without .py, with .py, hyphen/underscore alternatives
|
|
resolve_cheat_file() {
|
|
local name="$1"
|
|
local base=$(echo "$name" | sed 's/\.[Pp][Yy]$//')
|
|
|
|
# candidates to try in order
|
|
local candidates=(
|
|
"$name"
|
|
"$base"
|
|
"${base}.py"
|
|
"${base//_/}"
|
|
"${base//-/_}"
|
|
"${base//_/-}"
|
|
)
|
|
|
|
for cand in "${candidates[@]}"; do
|
|
if [[ -f "$CHEAT_DIR/personal/$cand" ]]; then
|
|
echo "$CHEAT_DIR/personal/$cand"
|
|
return 0
|
|
fi
|
|
if [[ -f "$CHEAT_DIR/personal/${cand}.cheat" ]]; then
|
|
echo "$CHEAT_DIR/personal/${cand}.cheat"
|
|
return 0
|
|
fi
|
|
if [[ -f "$CHEAT_DIR/${cand}.cheat" ]]; then
|
|
echo "$CHEAT_DIR/${cand}.cheat"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
show_main_help() {
|
|
echo -e "${CYAN}REMnux Analysis Container Help System${NC}"
|
|
echo "======================================="
|
|
echo ""
|
|
echo -e "${GREEN}Getting Started:${NC}"
|
|
echo " fhelp start - Quick start guide (30 seconds)"
|
|
echo ""
|
|
echo -e "${GREEN}Find Tools:${NC}"
|
|
echo " fhelp tools <keyword> - Search by name or category"
|
|
echo " fhelp tools --interactive - Interactive browser (fzf)"
|
|
echo " Ctrl+G - Interactive cheatsheet browser (navi)"
|
|
echo ""
|
|
echo -e "${GREEN}Get Examples:${NC}"
|
|
echo " fhelp cheat <tool> - Usage examples for a specific tool"
|
|
echo " fhelp quick <command> - Quick tldr examples"
|
|
echo " F1 / Ctrl+/ - Help for command you're typing (zsh)"
|
|
echo ""
|
|
echo -e "${GREEN}Analysis Workflows:${NC}"
|
|
echo " fhelp workflow - List all 8 analysis workflows"
|
|
echo " fhelp workflow <name> - Show step-by-step workflow"
|
|
echo ""
|
|
echo -e "${GREEN}Other:${NC}"
|
|
echo " fhelp coverage - Help coverage statistics"
|
|
echo " fhelp examples - Browse all cheat sheets"
|
|
echo " fhelp --offline - Verify offline capabilities"
|
|
echo ""
|
|
echo -e "${YELLOW}Shortcuts:${NC} analyse, h, ? (all run fhelp)"
|
|
echo ""
|
|
echo -e "${YELLOW}Examples:${NC}"
|
|
echo " fhelp tools pdf # Find PDF analysis tools"
|
|
echo " fhelp cheat pdfid.py # pdfid.py usage examples"
|
|
echo " fhelp workflow static # Static analysis workflow"
|
|
}
|
|
|
|
show_start() {
|
|
echo -e "${CYAN}Quick Start Guide${NC}"
|
|
echo "================="
|
|
echo ""
|
|
|
|
# Count tools
|
|
local tool_count=0
|
|
local rich_count=0
|
|
if [[ -f "$TOOLS_DB" ]]; then
|
|
tool_count=$(wc -l < "$TOOLS_DB" 2>/dev/null || echo 0)
|
|
rich_count=$(grep -c '|rich$' "$TOOLS_DB" 2>/dev/null || echo 0)
|
|
fi
|
|
|
|
echo -e " This container has ${GREEN}${tool_count} analysis tools${NC} installed."
|
|
echo -e " ${GREEN}${rich_count}${NC} have detailed help with FOR610 lab examples."
|
|
echo ""
|
|
echo -e "${YELLOW}1. Find a tool:${NC}"
|
|
echo " fhelp tools pdf # search by keyword"
|
|
echo " fhelp tools --interactive # browse with fuzzy search"
|
|
echo ""
|
|
echo -e "${YELLOW}2. Get usage examples:${NC}"
|
|
echo " fhelp cheat pdfid.py # cheat sheet with examples"
|
|
echo " fhelp cheat oledump.py # Office document analysis"
|
|
echo " fhelp cheat capa # malware capabilities"
|
|
echo ""
|
|
echo -e "${YELLOW}3. Follow a workflow:${NC}"
|
|
echo " fhelp workflow # list all workflows"
|
|
echo " fhelp workflow static # static analysis steps"
|
|
echo " fhelp workflow document # document analysis steps"
|
|
echo ""
|
|
echo -e "${YELLOW}4. Interactive help:${NC}"
|
|
echo -e " ${GREEN}Ctrl+G${NC} # browse cheatsheets (navi)"
|
|
echo -e " ${GREEN}F1${NC} or ${GREEN}Ctrl+/${NC} # help for command you're typing (zsh)"
|
|
echo ""
|
|
echo -e "${YELLOW}5. Tool tiers:${NC}"
|
|
echo -e " ${GREEN}[FOR610]${NC} Rich help with lab examples and workflows"
|
|
echo -e " ${BLUE}[docs]${NC} Standard help from REMnux documentation"
|
|
echo -e " ${YELLOW}[basic]${NC} Minimal help (try: tool --help)"
|
|
echo ""
|
|
echo "Mount your files to /work/ and start analyzing!"
|
|
}
|
|
|
|
show_cheat() {
|
|
local tool="$1"
|
|
|
|
if [[ -z "$tool" ]]; then
|
|
echo -e "${RED}Please specify a tool name${NC}"
|
|
echo "Usage: fhelp cheat <tool>"
|
|
return 1
|
|
fi
|
|
|
|
# Check for specific workflow cheat sheets first
|
|
local cheat_file=""
|
|
case "$tool" in
|
|
"pdf"|"pdf-analysis")
|
|
cheat_file="$CHEAT_DIR/pdf-analysis.cheat"
|
|
;;
|
|
"malware"|"malware-analysis")
|
|
cheat_file="$CHEAT_DIR/malware-analysis.cheat"
|
|
;;
|
|
"system"|"system-utilities")
|
|
cheat_file="$CHEAT_DIR/system-utilities.cheat"
|
|
;;
|
|
*)
|
|
cheat_file=""
|
|
;;
|
|
esac
|
|
|
|
# If not a workflow cheat, try to resolve tool-specific cheat
|
|
if [[ -z "$cheat_file" || ! -f "$cheat_file" ]]; then
|
|
cheat_file=$(resolve_cheat_file "$tool") || cheat_file=""
|
|
fi
|
|
|
|
if [[ -n "$cheat_file" && -f "$cheat_file" ]]; then
|
|
echo -e "${CYAN}Cheat Sheet: ${YELLOW}$tool${NC}"
|
|
echo "$(printf '=%.0s' $(seq 1 $((${#tool} + 14))))"
|
|
echo ""
|
|
# Display cheat file content (skip YAML frontmatter if present)
|
|
awk '/^---$/{if(++c==2) start=1; next} start || !/^---$/ && c!=1' "$cheat_file"
|
|
elif command -v cheat >/dev/null 2>&1 && cheat "$tool" >/dev/null 2>&1; then
|
|
# Fallback: try the cheat command
|
|
echo -e "${CYAN}Cheat Sheet (cheat): ${YELLOW}$tool${NC}"
|
|
echo "$(printf '=%.0s' $(seq 1 $((${#tool} + 22))))"
|
|
echo ""
|
|
cheat "$tool"
|
|
elif command -v tldr >/dev/null 2>&1 && tldr "$tool" >/dev/null 2>&1; then
|
|
# Fallback: try tldr
|
|
echo -e "${CYAN}Quick Reference (tldr): ${YELLOW}$tool${NC}"
|
|
echo "$(printf '=%.0s' $(seq 1 $((${#tool} + 24))))"
|
|
echo ""
|
|
tldr "$tool"
|
|
else
|
|
echo -e "${YELLOW}No help found for '$tool'${NC}"
|
|
echo ""
|
|
# Suggest similar tools
|
|
if [[ -f "$TOOLS_DB" ]]; then
|
|
local matches=$(grep -i "$tool" "$TOOLS_DB" 2>/dev/null | head -5)
|
|
if [[ -n "$matches" ]]; then
|
|
echo "Did you mean one of these?"
|
|
echo "$matches" | while IFS='|' read -r name desc cat usage tier; do
|
|
local badge=""
|
|
case "$tier" in
|
|
rich) badge="${GREEN}[FOR610]${NC}" ;;
|
|
standard) badge="${BLUE}[docs]${NC}" ;;
|
|
*) badge="${YELLOW}[basic]${NC}" ;;
|
|
esac
|
|
echo -e " ${GREEN}$name${NC} $badge - $desc"
|
|
done
|
|
fi
|
|
fi
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
show_quick() {
|
|
local command="$1"
|
|
|
|
if [[ -z "$command" ]]; then
|
|
echo -e "${RED}Please specify a command name${NC}"
|
|
echo "Usage: fhelp quick <command>"
|
|
return 1
|
|
fi
|
|
|
|
echo -e "${CYAN}Quick examples for: ${YELLOW}$command${NC}"
|
|
echo ""
|
|
|
|
if command -v tldr >/dev/null 2>&1; then
|
|
if ! tldr "$command" 2>/dev/null; then
|
|
echo -e "${YELLOW}No tldr page found for '$command'${NC}"
|
|
echo "Try: fhelp cheat $command"
|
|
fi
|
|
else
|
|
echo -e "${RED}tldr command not available${NC}"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
show_tools() {
|
|
local search_term="$1"
|
|
local option="$2"
|
|
|
|
case "$search_term" in
|
|
"--interactive")
|
|
if command -v find-tool >/dev/null 2>&1; then
|
|
find-tool --interactive
|
|
else
|
|
echo -e "${RED}find-tool not available${NC}"
|
|
fi
|
|
return
|
|
;;
|
|
"--list")
|
|
if command -v find-tool >/dev/null 2>&1; then
|
|
find-tool --list
|
|
else
|
|
echo -e "${RED}find-tool not available${NC}"
|
|
fi
|
|
return
|
|
;;
|
|
esac
|
|
|
|
if [[ -z "$search_term" ]]; then
|
|
echo -e "${RED}Please provide a search term${NC}"
|
|
echo "Usage: fhelp tools <search_term>"
|
|
echo " fhelp tools --interactive"
|
|
echo " fhelp tools --list"
|
|
return 1
|
|
fi
|
|
|
|
echo -e "${CYAN}Searching analysis tools for '${YELLOW}$search_term${CYAN}'...${NC}"
|
|
echo ""
|
|
|
|
if command -v find-tool >/dev/null 2>&1; then
|
|
find-tool "$search_term"
|
|
elif [[ -f "$TOOLS_DB" ]]; then
|
|
# Fallback: direct grep on tools.db
|
|
local results=$(grep -i "$search_term" "$TOOLS_DB" 2>/dev/null)
|
|
if [[ -n "$results" ]]; then
|
|
echo "$results" | while IFS='|' read -r name desc cat usage tier; do
|
|
local tier_badge=""
|
|
case "$tier" in
|
|
rich) tier_badge="${GREEN}[FOR610]${NC}" ;;
|
|
standard) tier_badge="${BLUE}[docs]${NC}" ;;
|
|
basic) tier_badge="${YELLOW}[basic]${NC}" ;;
|
|
*) tier_badge="" ;;
|
|
esac
|
|
echo -e " ${GREEN}$name${NC} $tier_badge"
|
|
echo " $desc"
|
|
echo " Usage: $usage"
|
|
echo ""
|
|
done
|
|
else
|
|
echo "No tools found matching '$search_term'"
|
|
fi
|
|
else
|
|
echo -e "${RED}No tools database available${NC}"
|
|
fi
|
|
}
|
|
|
|
show_examples() {
|
|
echo -e "${CYAN}Available Command Examples${NC}"
|
|
echo ""
|
|
|
|
if [[ -d "$CHEAT_DIR/personal" ]]; then
|
|
local count=$(ls -1 "$CHEAT_DIR/personal/"*.cheat 2>/dev/null | wc -l)
|
|
echo -e "${GREEN}Per-tool cheat sheets: $count${NC} (use: fhelp cheat <name>)"
|
|
echo ""
|
|
# Show a sample of tools grouped by first letter
|
|
ls -1 "$CHEAT_DIR/personal/"*.cheat 2>/dev/null | sed 's|.*/||; s|\.cheat$||' | head -30 | sed 's/^/ /'
|
|
if [[ $count -gt 30 ]]; then
|
|
echo " ... and $((count - 30)) more"
|
|
fi
|
|
echo ""
|
|
fi
|
|
|
|
if [[ -d "$CHEAT_DIR" ]]; then
|
|
echo -e "${GREEN}Workflow cheat sheets:${NC}"
|
|
ls -1 "$CHEAT_DIR"/*.cheat 2>/dev/null | sed 's|.*/||; s|\.cheat$||' | sed 's/^/ /'
|
|
echo ""
|
|
fi
|
|
|
|
echo -e "${GREEN}Analysis workflows:${NC} (use: fhelp workflow <name>)"
|
|
if [[ -d "$WORKFLOW_DIR" ]]; then
|
|
ls -1 "$WORKFLOW_DIR"/*.txt 2>/dev/null | sed 's|.*/||; s|\.txt$||' | grep -v index | sed 's/^/ /'
|
|
else
|
|
echo " static-analysis, behavioral-analysis, network-interception"
|
|
echo " document-analysis, javascript-deobfuscation, unpacking"
|
|
echo " code-injection, dotnet-analysis"
|
|
fi
|
|
}
|
|
|
|
show_workflow() {
|
|
local name="$1"
|
|
|
|
if [[ -z "$name" ]]; then
|
|
# Show workflow index
|
|
if [[ -f "$WORKFLOW_DIR/index.txt" ]]; then
|
|
cat "$WORKFLOW_DIR/index.txt"
|
|
else
|
|
echo -e "${CYAN}Available Analysis Workflows${NC}"
|
|
echo "=============================="
|
|
echo ""
|
|
echo " static-analysis-workflow Static Properties Analysis"
|
|
echo " behavioral-analysis-workflow Behavioral Analysis"
|
|
echo " network-interception-workflow Network Interception"
|
|
echo " document-analysis-workflow Malicious Document Analysis"
|
|
echo " javascript-deobfuscation-workflow JavaScript Deobfuscation"
|
|
echo " unpacking-workflow Unpacking Packed Executables"
|
|
echo " code-injection-workflow Code Injection Analysis"
|
|
echo " dotnet-analysis-workflow .NET Malware Analysis"
|
|
echo ""
|
|
echo "Usage: fhelp workflow <name>"
|
|
echo "Example: fhelp workflow static-analysis"
|
|
fi
|
|
return
|
|
fi
|
|
|
|
# Normalize name: allow partial matches
|
|
local wf_file=""
|
|
|
|
# Try exact match first
|
|
if [[ -f "$WORKFLOW_DIR/${name}.txt" ]]; then
|
|
wf_file="$WORKFLOW_DIR/${name}.txt"
|
|
elif [[ -f "$WORKFLOW_DIR/${name}-workflow.txt" ]]; then
|
|
wf_file="$WORKFLOW_DIR/${name}-workflow.txt"
|
|
else
|
|
# Fuzzy match: find workflow files containing the search term
|
|
if [[ -d "$WORKFLOW_DIR" ]]; then
|
|
wf_file=$(ls -1 "$WORKFLOW_DIR"/*.txt 2>/dev/null | grep -i "$name" | grep -v index | head -1)
|
|
fi
|
|
fi
|
|
|
|
if [[ -n "$wf_file" && -f "$wf_file" ]]; then
|
|
cat "$wf_file"
|
|
else
|
|
echo -e "${YELLOW}No workflow found matching '$name'${NC}"
|
|
echo ""
|
|
show_workflow # Show list
|
|
fi
|
|
}
|
|
|
|
show_coverage() {
|
|
echo -e "${CYAN}Help Coverage Statistics${NC}"
|
|
echo "========================"
|
|
echo ""
|
|
|
|
if [[ -f "$TOOLS_DB" ]]; then
|
|
local total=$(wc -l < "$TOOLS_DB" 2>/dev/null || echo 0)
|
|
local rich=$(grep -c '|rich$' "$TOOLS_DB" 2>/dev/null || echo 0)
|
|
local standard=$(grep -c '|standard$' "$TOOLS_DB" 2>/dev/null || echo 0)
|
|
local basic=$(grep -c '|basic$' "$TOOLS_DB" 2>/dev/null || echo 0)
|
|
|
|
echo -e " Tools in database: ${GREEN}$total${NC}"
|
|
echo -e " Rich help (FOR610): ${GREEN}$rich${NC}"
|
|
echo -e " Standard (docs): ${BLUE}$standard${NC}"
|
|
echo -e " Basic: ${YELLOW}$basic${NC}"
|
|
else
|
|
echo " Tools database not available"
|
|
fi
|
|
|
|
echo ""
|
|
|
|
if [[ -d "$CHEAT_DIR/personal" ]]; then
|
|
local cheats=$(ls -1 "$CHEAT_DIR/personal/"*.cheat 2>/dev/null | wc -l)
|
|
echo -e " Cheat sheets: ${GREEN}$cheats${NC}"
|
|
fi
|
|
|
|
if [[ -d "$WORKFLOW_DIR" ]]; then
|
|
local wfs=$(ls -1 "$WORKFLOW_DIR"/*.txt 2>/dev/null | grep -cv index 2>/dev/null || echo 0)
|
|
echo -e " Workflows: ${GREEN}$wfs${NC}"
|
|
fi
|
|
}
|
|
|
|
show_offline_status() {
|
|
echo -e "${CYAN}Offline Capability Check${NC}"
|
|
echo "==========================="
|
|
echo ""
|
|
echo "Documentation Tools:"
|
|
|
|
local tools=("find-tool" "cheat" "tldr")
|
|
for tool in "${tools[@]}"; do
|
|
if command -v "$tool" >/dev/null 2>&1; then
|
|
echo -e " ${GREEN}+ $tool - available${NC}"
|
|
else
|
|
echo -e " ${RED}- $tool - missing${NC}"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo "Documentation Files:"
|
|
|
|
if [[ -f "$TOOLS_DB" ]]; then
|
|
local db_count=$(wc -l < "$TOOLS_DB" 2>/dev/null || echo 0)
|
|
echo -e " ${GREEN}+ tools.db - $db_count tools${NC}"
|
|
else
|
|
echo -e " ${RED}- tools.db - missing${NC}"
|
|
fi
|
|
|
|
if [[ -d "$CHEAT_DIR/personal" ]]; then
|
|
local cheat_count=$(ls -1 "$CHEAT_DIR/personal/"*.cheat 2>/dev/null | wc -l)
|
|
echo -e " ${GREEN}+ cheatsheets - $cheat_count files${NC}"
|
|
else
|
|
echo -e " ${RED}- cheatsheets - missing${NC}"
|
|
fi
|
|
|
|
if [[ -d "$WORKFLOW_DIR" ]]; then
|
|
local wf_count=$(ls -1 "$WORKFLOW_DIR"/*.txt 2>/dev/null | grep -cv index 2>/dev/null || echo 0)
|
|
echo -e " ${GREEN}+ workflows - $wf_count workflows${NC}"
|
|
else
|
|
echo -e " ${RED}- workflows - missing${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${GREEN}Offline help system ready!${NC}"
|
|
}
|
|
|
|
show_all() {
|
|
echo -e "${CYAN}Complete Help System Overview${NC}"
|
|
echo "================================="
|
|
echo ""
|
|
|
|
show_coverage
|
|
echo ""
|
|
show_workflow
|
|
echo ""
|
|
show_offline_status
|
|
}
|
|
|
|
# Main command parsing
|
|
case "${1:-}" in
|
|
"start"|"quickstart"|"getting-started")
|
|
show_start
|
|
;;
|
|
"tools")
|
|
shift
|
|
show_tools "$@"
|
|
;;
|
|
"cheat")
|
|
shift
|
|
show_cheat "$@"
|
|
;;
|
|
"quick")
|
|
shift
|
|
show_quick "$@"
|
|
;;
|
|
"examples")
|
|
show_examples
|
|
;;
|
|
"workflow")
|
|
shift
|
|
show_workflow "$@"
|
|
;;
|
|
"pdf")
|
|
show_workflow "document-analysis"
|
|
;;
|
|
"malware")
|
|
show_workflow "static-analysis"
|
|
;;
|
|
"forensics")
|
|
show_workflow "behavioral-analysis"
|
|
;;
|
|
"coverage")
|
|
show_coverage
|
|
;;
|
|
"--offline")
|
|
show_offline_status
|
|
;;
|
|
"--all")
|
|
show_all
|
|
;;
|
|
"--help"|"-h"|"help"|"")
|
|
show_main_help
|
|
;;
|
|
*)
|
|
# Try as workflow name first, then show error
|
|
if [[ -d "$WORKFLOW_DIR" ]] && ls "$WORKFLOW_DIR"/*.txt 2>/dev/null | grep -qi "$1"; then
|
|
show_workflow "$1"
|
|
else
|
|
echo -e "${RED}Unknown option: $1${NC}"
|
|
echo ""
|
|
show_main_help
|
|
fi
|
|
;;
|
|
esac
|