2e764746e7
5-phase hardening to prevent silent file-layer collisions (the D-Bus regression class): Phase 1: lint-config-paths.sh + make lint-config in depends.mk Phase 2: CollisionTracker in installer (content-hash comparison) Phase 3: installs manifests in recipe.toml + validate-file-ownership.sh Phase 4: validate-init-services.sh + make validate in disk.mk Phase 5: documentation (AGENTS.md, BUILD-SYSTEM-HARDENING-PLAN.md) Both redbear-mini and redbear-full build and validate clean. 66 declared install paths in base, zero conflicts.
129 lines
4.2 KiB
Bash
Executable File
129 lines
4.2 KiB
Bash
Executable File
#!/bin/bash
|
|
# validate-file-ownership.sh — Check recipe file ownership conflicts
|
|
#
|
|
# Reads the optional 'installs' field from recipe.toml [package] sections
|
|
# and detects conflicts where multiple recipes claim the same path.
|
|
#
|
|
# Also cross-references config [[files]] paths against recipe installs
|
|
# to detect config-layer / package-layer collisions.
|
|
#
|
|
# Usage:
|
|
# scripts/validate-file-ownership.sh
|
|
#
|
|
# Exit codes:
|
|
# 0 — No conflicts or violations found
|
|
# 1 — Conflicts detected
|
|
|
|
set -euo pipefail
|
|
|
|
failures=0
|
|
|
|
# Registry: path -> recipe_name
|
|
declare -A PATH_REGISTRY
|
|
|
|
echo "=== Scanning recipes for installs declarations ==="
|
|
|
|
recipe_count=0
|
|
declared_count=0
|
|
|
|
for recipe_toml in recipes/*/recipe.toml recipes/*/*/recipe.toml; do
|
|
[ -f "$recipe_toml" ] || continue
|
|
recipe_count=$((recipe_count + 1))
|
|
|
|
recipe_dir=$(dirname "$recipe_toml")
|
|
recipe_name=$(basename "$recipe_dir")
|
|
|
|
# Parse installs field from [package] section
|
|
# Format: installs = ["/usr/bin/foo", "/usr/lib/init.d/10_bar.service"]
|
|
in_package=false
|
|
in_installs=false
|
|
while IFS= read -r line; do
|
|
if [[ "$line" =~ ^\[package\] ]]; then
|
|
in_package=true
|
|
in_installs=false
|
|
continue
|
|
elif [[ "$line" =~ ^\[ ]]; then
|
|
in_package=false
|
|
in_installs=false
|
|
continue
|
|
fi
|
|
|
|
if $in_package; then
|
|
if [[ "$line" =~ ^installs ]]; then
|
|
in_installs=true
|
|
fi
|
|
if $in_installs; then
|
|
paths=$(echo "$line" | grep -oP '"[^"]+"' | tr -d '"' || true)
|
|
for path in $paths; do
|
|
declared_count=$((declared_count + 1))
|
|
if [ -n "${PATH_REGISTRY[$path]+x}" ]; then
|
|
existing="${PATH_REGISTRY[$path]}"
|
|
echo "CONFLICT: '$path' claimed by both '$existing' and '$recipe_name'"
|
|
if [[ "$path" == *"/init.d/"* ]]; then
|
|
echo " SEVERITY: init service conflict (critical)"
|
|
failures=$((failures + 1))
|
|
else
|
|
echo " SEVERITY: non-critical path overlap"
|
|
fi
|
|
else
|
|
PATH_REGISTRY["$path"]="$recipe_name"
|
|
fi
|
|
done
|
|
if [[ "$line" =~ \] ]]; then
|
|
in_installs=false
|
|
fi
|
|
fi
|
|
fi
|
|
done < "$recipe_toml"
|
|
done
|
|
|
|
echo " Scanned $recipe_count recipes, found $declared_count declared install paths"
|
|
|
|
echo ""
|
|
echo "=== Cross-referencing config [[files]] against recipe installs ==="
|
|
|
|
config_conflicts=0
|
|
for config_file in config/redbear-*.toml; do
|
|
[ -f "$config_file" ] || continue
|
|
config_name=$(basename "$config_file")
|
|
|
|
in_files=false
|
|
while IFS= read -r line; do
|
|
if [[ "$line" =~ ^\[\[files\]\] ]]; then
|
|
in_files=true
|
|
continue
|
|
elif [[ "$line" =~ ^\[ ]]; then
|
|
in_files=false
|
|
continue
|
|
fi
|
|
|
|
if $in_files && [[ "$line" =~ path[[:space:]]*=[[:space:]]*\"([^\"]+)\" ]]; then
|
|
config_path="${BASH_REMATCH[1]}"
|
|
for registered_path in "${!PATH_REGISTRY[@]}"; do
|
|
if [ "$config_path" = "$registered_path" ]; then
|
|
echo "COLLISION: config '$config_name' creates '$config_path' but recipe '${PATH_REGISTRY[$registered_path]}' also installs it"
|
|
config_conflicts=$((config_conflicts + 1))
|
|
if [[ "$config_path" == *"/usr/lib/init.d/"* ]]; then
|
|
echo " SEVERITY: init service in /usr/lib/init.d/ (will be overwritten by package)"
|
|
failures=$((failures + 1))
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
done < "$config_file"
|
|
done
|
|
|
|
echo " Found $config_conflicts config/recipe path collision(s)"
|
|
|
|
echo ""
|
|
echo "=== Validation complete ==="
|
|
if [ $declared_count -eq 0 ]; then
|
|
echo " NOTE: No recipes declare 'installs' yet. Add installs = [...] to [package] sections for full validation."
|
|
fi
|
|
if [ $failures -gt 0 ]; then
|
|
echo "FAILED: $failures conflict(s) found" >&2
|
|
exit 1
|
|
else
|
|
echo "PASSED: No conflicts found"
|
|
fi
|