- Configure Firefox with privacy settings and extensions (Bitwarden, Plasma Integration, MetaMask, Kagi Search, uBlock Origin) - Set Kagi as default/only search engine - Add MOZ_USE_XINPUT2=1 for smooth scrolling - Create context/ directory for concise unit documentation - Create tests/ directory for test scripts - Move test-firefox-config.sh to tests/ - Update agents.md with documentation workflow guidelines - Fix syntax errors in desktop.nix and dev.nix
477 lines
14 KiB
Bash
Executable file
477 lines
14 KiB
Bash
Executable file
#!/usr/bin/env bash
|
||
# Firefox Configuration Test Script
|
||
# Tests the Firefox Home Manager configuration for user jsutter
|
||
|
||
set -e
|
||
|
||
# Colors for output
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# Test counters
|
||
PASSED=0
|
||
FAILED=0
|
||
SKIPPED=0
|
||
TOTAL=0
|
||
|
||
# Helper functions
|
||
print_header() {
|
||
echo ""
|
||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo -e "${BLUE} $1${NC}"
|
||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
}
|
||
|
||
print_test() {
|
||
echo -e "\n${YELLOW}[TEST]${NC} $1"
|
||
((TOTAL++))
|
||
}
|
||
|
||
print_pass() {
|
||
echo -e "${GREEN} ✓ PASS${NC}: $1"
|
||
((PASSED++))
|
||
}
|
||
|
||
print_fail() {
|
||
echo -e "${RED} ✗ FAIL${NC}: $1"
|
||
((FAILED++))
|
||
}
|
||
|
||
print_skip() {
|
||
echo -e "${YELLOW} ⊘ SKIP${NC}: $1"
|
||
((SKIPPED++))
|
||
}
|
||
|
||
print_info() {
|
||
echo -e " ℹ ${NC}$1"
|
||
}
|
||
|
||
print_report() {
|
||
local description="$1"
|
||
local pass_count="$2"
|
||
local fail_count="$3"
|
||
|
||
if [[ $fail_count -eq 0 ]]; then
|
||
print_pass "$description (passed $pass_count/$pass_count)"
|
||
else
|
||
local total=$((pass_count + fail_count))
|
||
print_fail "$description (passed $pass_count/$total)"
|
||
fi
|
||
}
|
||
|
||
# Check if a command exists
|
||
command_exists() {
|
||
command -v "$1" &> /dev/null
|
||
}
|
||
|
||
# Check if a preference matches expected value
|
||
check_pref() {
|
||
local profile="$1"
|
||
local pref="$2"
|
||
local expected="$3"
|
||
local actual=$(grep "\"${pref}\"" "$profile/prefs.js" 2>/dev/null | head -1 | sed 's/.*user_pref[^,]*,\s*//;s/);.*$//' 2>/dev/null || echo "")
|
||
|
||
if [[ "$actual" == *"$expected"* ]]; then
|
||
print_pass "$pref = $expected"
|
||
return 0
|
||
else
|
||
print_fail "$pref = $expected (got: $actual)"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# Get Firefox profile directory
|
||
get_firefox_profile() {
|
||
local firefox_dir="$HOME/.mozilla/firefox"
|
||
|
||
if [[ -d "$firefox_dir" ]]; then
|
||
local profile=$(grep -E "Default=1" "$firefox_dir/profiles.ini" 2>/dev/null | head -1 | cut -d= -f2 -s)
|
||
if [[ -n "$profile" ]] && [[ -d "$firefox_dir/$profile" ]]; then
|
||
echo "$firefox_dir/$profile"
|
||
return 0
|
||
fi
|
||
|
||
# Try to find jsutter profile
|
||
profile=$(find "$firefox_dir" -maxdepth 1 -type d -name "*jsutter*" 2>/dev/null | head -1)
|
||
if [[ -n "$profile" ]]; then
|
||
echo "$profile"
|
||
return 0
|
||
fi
|
||
|
||
# Get first available profile
|
||
profile=$(ls -1 "$firefox_dir"/*.default* 2>/dev/null | head -1)
|
||
if [[ -n "$profile" ]]; then
|
||
echo "$profile"
|
||
return 0
|
||
fi
|
||
fi
|
||
|
||
echo ""
|
||
return 1
|
||
}
|
||
|
||
# Get Firefox executable
|
||
get_firefox_exec() {
|
||
if command_exists firefox-esr &>/dev/null; then
|
||
echo firefox-esr
|
||
elif command_exists firefox &>/dev/null; then
|
||
echo firefox
|
||
elif [[ -f "/run/current-system/sw/bin/firefox" ]]; then
|
||
echo /run/current-system/sw/bin/firefox
|
||
else
|
||
echo ""
|
||
fi
|
||
}
|
||
|
||
print_header "Firefox Configuration Test Script"
|
||
echo "Testing configuration for user: $(whoami)"
|
||
echo "Date: $(date '+%Y-%m-%d %H:%M:%S')"
|
||
|
||
# ============================================
|
||
# Environment Variables
|
||
# ============================================
|
||
print_header "1. Environment Variables"
|
||
|
||
print_test "Check MOZ_USE_XINPUT2 environment variable"
|
||
if [[ -z "$MOZ_USE_XINPUT2" ]]; then
|
||
print_fail "MOZ_USE_XINPUT2 not set"
|
||
print_info "This should be set in Home Manager sessionVariables"
|
||
else
|
||
if [[ "$MOZ_USE_XINPUT2" == "1" ]]; then
|
||
print_pass "MOZ_USE_XINPUT2 = 1"
|
||
else
|
||
print_fail "MOZ_USE_XINPUT2 = $MOZ_USE_XINPUT2 (expected: 1)"
|
||
fi
|
||
fi
|
||
|
||
print_test "Check session variables in shell config"
|
||
if [[ -f "$HOME/.zshrc" ]] || [[ -f "$HOME/.bashrc" ]]; then
|
||
print_info "Shell config files found"
|
||
else
|
||
print_skip "No shell config file found"
|
||
fi
|
||
|
||
# ============================================
|
||
# Firefox Installation
|
||
# ============================================
|
||
print_header "2. Firefox Installation"
|
||
|
||
FIREFOX=$(get_firefox_exec)
|
||
if [[ -n "$FIREFOX" ]]; then
|
||
print_test "Firefox executable found"
|
||
print_pass "Firefox at: $FIREFOX"
|
||
|
||
print_test "Firefox version"
|
||
VERSION=$($FIREFOX --version 2>/dev/null || echo "unknown")
|
||
print_pass "Firefox version: $VERSION"
|
||
else
|
||
print_fail "Firefox executable not found"
|
||
print_info "Check if Firefox is installed via nixos-rebuild switch"
|
||
fi
|
||
|
||
# ============================================
|
||
# Policy Configuration
|
||
# ============================================
|
||
print_header "3. Enterprise Policies"
|
||
|
||
POLICY_FILE="/run/current-system/sw/lib/firefox/distribution/policies.json"
|
||
print_test "Check policy file exists"
|
||
if [[ -f "$POLICY_FILE" ]]; then
|
||
print_pass "Policy file found: $POLICY_FILE"
|
||
else
|
||
print_fail "Policy file not found"
|
||
print_info "Activate the configuration with: nixos-rebuild switch"
|
||
fi
|
||
|
||
if [[ -f "$POLICY_FILE" ]]; then
|
||
print_test "Check policy file is valid JSON"
|
||
if jq empty "$POLICY_FILE" 2>/dev/null; then
|
||
print_pass "Policy file is valid JSON"
|
||
else
|
||
print_fail "Policy file is not valid JSON"
|
||
fi
|
||
|
||
print_test "Check extension policies"
|
||
EXTENSIONS=$(jq -r '.policies.ExtensionSettings | keys[]' "$POLICY_FILE" 2>/dev/null || echo "")
|
||
EXPECTED_EXTENSIONS=(
|
||
"uBlock0@raymondhill.net"
|
||
"{446900e4-71c2-419f-a6a7-df9c2b2dc922}"
|
||
"plasma-browser-integration@kde.org"
|
||
"webextension@metamask.io"
|
||
"kagi-search@kagi.com"
|
||
)
|
||
|
||
all_found=true
|
||
for ext in "${EXPECTED_EXTENSIONS[@]}"; do
|
||
if echo "$EXTENSIONS" | grep -q "$ext"; then
|
||
print_info "Extension policy found: $ext"
|
||
else
|
||
print_fail "Extension policy missing: $ext"
|
||
all_found=false
|
||
fi
|
||
done
|
||
if $all_found; then
|
||
print_pass "All extension policies configured"
|
||
fi
|
||
|
||
print_test "Check privacy settings policies"
|
||
PRIVACY_OK=true
|
||
|
||
if jq -e '.policies.OfferToSaveLogins == false' "$POLICY_FILE" &>/dev/null; then
|
||
print_info "Password saving disabled"
|
||
else
|
||
print_fail "Password saving not disabled"
|
||
PRIVACY_OK=false
|
||
fi
|
||
|
||
if jq -e '.policies.PasswordManagerEnabled == false' "$POLICY_FILE" &>/dev/null; then
|
||
print_info "Password manager disabled"
|
||
else
|
||
print_fail "Password manager not disabled"
|
||
PRIVACY_OK=false
|
||
fi
|
||
|
||
if jq -e '.policies.DisableTelemetry == true' "$POLICY_FILE" &>/dev/null; then
|
||
print_info "Telemetry disabled"
|
||
else
|
||
print_fail "Telemetry not disabled"
|
||
PRIVACY_OK=false
|
||
fi
|
||
|
||
if jq -e '.policies.DisableFirefoxStudies == true' "$POLICY_FILE" &>/dev/null; then
|
||
print_info "Firefox Studies disabled"
|
||
else
|
||
print_fail "Firefox Studies not disabled"
|
||
PRIVACY_OK=false
|
||
fi
|
||
|
||
if $PRIVACY_OK; then
|
||
print_pass "Privacy settings policies configured correctly"
|
||
fi
|
||
|
||
print_test "Check homepage/new tab policies"
|
||
HOME_OK=true
|
||
|
||
if jq -e '.policies.FirefoxHome.Search == false' "$POLICY_FILE" &>/dev/null; then
|
||
print_info "Search on home page disabled"
|
||
else
|
||
print_fail "Search on home page not disabled"
|
||
HOME_OK=false
|
||
fi
|
||
|
||
if jq -e '.policies.FirefoxHome.TopSites == false' "$POLICY_FILE" &>/dev/null; then
|
||
print_info "Top Sites disabled"
|
||
else
|
||
print_fail "Top Sites not disabled"
|
||
HOME_OK=false
|
||
fi
|
||
|
||
if jq -e '.policies.FirefoxHome.Pocket == false' "$POLICY_FILE" &>/dev/null; then
|
||
print_info "Pocket disabled"
|
||
else
|
||
print_fail "Pocket not disabled"
|
||
HOME_OK=false
|
||
fi
|
||
|
||
if $HOME_OK; then
|
||
print_pass "Homepage/new tab policies configured correctly"
|
||
fi
|
||
fi
|
||
|
||
# ============================================
|
||
# User Profile Configuration
|
||
# ============================================
|
||
print_header "4. Profile Configuration"
|
||
|
||
PROFILE=$(get_firefox_profile)
|
||
if [[ -n "$PROFILE" ]]; then
|
||
print_test "Firefox profile directory found"
|
||
print_pass "Profile: $PROFILE"
|
||
|
||
# Check for user.js
|
||
USER_JS="$PROFILE/user.js"
|
||
if [[ -f "$USER_JS" ]]; then
|
||
print_test "user.js file exists"
|
||
print_pass "user.js: $USER_JS"
|
||
|
||
# Check key settings
|
||
print_test "Check key user.js preferences"
|
||
|
||
cat > /tmp/firefox_check.sh << 'EOF'
|
||
#!/bin/bash
|
||
PROFILE="$1"
|
||
check_pref() {
|
||
local pref="$1"
|
||
local expected="$2"
|
||
local actual=$(grep "\"${pref}\"" "$PROFILE/user.js" 2>/dev/null | head -1 | sed 's/.*user_pref[^,]*,\s*//;s/);.*$//' 2>/dev/null || echo "")
|
||
|
||
if [[ "$actual" == *"$expected"* ]]; then
|
||
echo "PASS: $pref"
|
||
return 0
|
||
else
|
||
echo "FAIL: $pref (got: $actual)"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
check_pref "browser.ctrlTab.recentlyUsedOrder" "true"
|
||
check_pref "browser.tabs.hoverPreview.enabled" "false"
|
||
check_pref "browser.link.preview.enabled" "false"
|
||
check_pref "browser.newtabpage.enabled" "false"
|
||
check_pref "browser.startup.homepage" "about:blank"
|
||
check_pref "browser.search.suggest.enabled" "false"
|
||
check_pref "signon.rememberSignons" "false"
|
||
check_pref "media.videocontrols.picture-in-picture.allow-multiple" "false"
|
||
check_pref "browser.contentblocking.category" "strict"
|
||
check_pref "extensions.pocket.enabled" "false"
|
||
check_pref "privacy.sanitize.sanitizeOnShutdown" "true"
|
||
check_pref "toolkit.legacyUserProfileCustomizations.stylesheets" "true"
|
||
check_pref "privacy.userContext.enabled" "true"
|
||
EOF
|
||
chmod +x /tmp/firefox_check.sh
|
||
output=$(/tmp/firefox_check.sh "$PROFILE")
|
||
|
||
pass_count=$(echo "$output" | grep -c "PASS" || true)
|
||
fail_count=$(echo "$output" | grep -c "FAIL" || true)
|
||
|
||
echo "$output" | while read line; do
|
||
if [[ "$line" == "PASS:"* ]]; then
|
||
print_pass "${line#PASS: }"
|
||
else
|
||
print_fail "${line#FAIL: }"
|
||
fi
|
||
done
|
||
|
||
print_report "user.js key settings" "$pass_count" "$fail_count"
|
||
else
|
||
print_fail "user.js not found (Firefox needs to be run once to generate)"
|
||
fi
|
||
|
||
# Check for extensions
|
||
print_test "Check installed extensions"
|
||
INSTALLED_EXTENSIONS=$(ls -1 "$PROFILE/extensions" 2>/dev/null || echo "")
|
||
|
||
if [[ -n "$INSTALLED_EXTENSIONS" ]]; then
|
||
print_info "Found $(echo "$INSTALLED_EXTENSIONS" | wc -l) extension(s):"
|
||
echo "$INSTALLED_EXTENSIONS" | while read ext; do
|
||
print_info " - $ext"
|
||
done
|
||
print_pass "Extensions directory exists"
|
||
else
|
||
print_skip "No extensions installed yet (start Firefox to install via policy)"
|
||
fi
|
||
else
|
||
print_fail "Firefox profile not found"
|
||
print_info "Start Firefox once to create a profile"
|
||
print_info "Profile location is typically: ~/.mozilla/firefox/*.default*"
|
||
fi
|
||
|
||
# ============================================
|
||
# Manual Verification Checklist
|
||
# ============================================
|
||
print_header "5. Manual Verification Checklist"
|
||
|
||
cat << 'MANUAL'
|
||
|
||
The following items require manual verification in Firefox:
|
||
|
||
[ ] 1. Open Firefox and check all 4 extensions are installed:
|
||
- uBlock Origin
|
||
- Privacy Badger
|
||
- Facebook Container
|
||
- Multi-account Containers
|
||
|
||
[ ] 2. Open about:preferences#privacy and verify:
|
||
- Content blocking is set to "Strict"
|
||
- "Clear history when Firefox closes" is checked
|
||
- "Remember passwords" is unchecked
|
||
- "Autoplay" is set to "Block audio and video"
|
||
|
||
[ ] 3. Open about:policies and verify:
|
||
- All policies are "Active"
|
||
- Extensions are listed as "Force Installed"
|
||
|
||
[ ] 4. Test tab behavior:
|
||
- Press Ctrl+Tab and verify it cycles through recent tabs
|
||
- Hover over a tab and verify no image preview appears
|
||
|
||
[ ] 5. Test new tab/homepage:
|
||
- Open a new tab (Ctrl+T) - should be blank
|
||
- Open a new window (Ctrl+N) - should be blank
|
||
- No search bar, shortcuts, or widgets should appear
|
||
|
||
[ ] 6. Test search:
|
||
- Type in the address bar and verify no suggestions appear
|
||
- Search results should come from Kagi
|
||
- No other search engines should be available
|
||
- Sign in to Kagi account via Kagi extension for full features
|
||
|
||
[ ] 7. Test smooth scrolling:
|
||
- Scroll on a long page - should feel smooth and responsive
|
||
|
||
[ ] 8. Test permissions:
|
||
- Visit a site that requests location - should auto-block
|
||
- Visit a site that requests notifications - should auto-block
|
||
- Play a video with audio - should auto-block
|
||
|
||
MANUAL
|
||
|
||
print_info "Manual verification above requires user interaction"
|
||
|
||
# ============================================
|
||
# Troubleshooting Information
|
||
# ============================================
|
||
print_header "6. Troubleshooting Information"
|
||
|
||
echo "Useful Firefox URLs:"
|
||
print_info "about:policies - View active policies"
|
||
print_info "about:preferences - Firefox settings"
|
||
print_info "about:config - Advanced preferences"
|
||
print_info "about:support - Troubleshooting information"
|
||
print_info "about:addons - View/manage extensions"
|
||
print_info "about:preferences#privacy - Privacy settings"
|
||
|
||
echo ""
|
||
print_info "Configuration files:"
|
||
print_info " Policies: $POLICY_FILE"
|
||
print_info " Profile: $PROFILE"
|
||
print_info " user.js: $USER_JS"
|
||
if [[ -n "$PROFILE" ]]; then
|
||
print_info " search.json.mozlz4: $PROFILE/search.json.mozlz4"
|
||
fi
|
||
|
||
echo ""
|
||
print_info "To rebuild the configuration:"
|
||
print_info " nixos-rebuild switch"
|
||
|
||
echo ""
|
||
print_info "To restart Firefox:"
|
||
print_info " pkill firefox"
|
||
|
||
# ============================================
|
||
# Summary
|
||
# ============================================
|
||
print_header "Test Summary"
|
||
echo "Total tests: $TOTAL"
|
||
echo -e "${GREEN}Passed: $PASSED${NC}"
|
||
echo -e "${RED}Failed: $FAILED${NC}"
|
||
echo -e "${YELLOW}Skipped: $SKIPPED${NC}"
|
||
|
||
if [[ $FAILED -eq 0 ]]; then
|
||
echo ""
|
||
echo -e "${GREEN}✓ All automated tests passed!${NC}"
|
||
echo ""
|
||
echo "Please run Firefox once to install extensions via policy,"
|
||
echo "then complete the manual verification checklist above."
|
||
echo ""
|
||
echo "Note: Kagi search is the default and only search engine."
|
||
echo " Sign in to your Kagi account via the Kagi extension."
|
||
exit 0
|
||
else
|
||
echo ""
|
||
echo -e "${RED}✗ Some tests failed. Review the output above for details.${NC}"
|
||
exit 1
|
||
fi
|