@sebastienrousseau/dotfiles 0.2.520 → 0.2.521
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/LICENSE-APACHE +190 -0
- package/LICENSE-MIT +21 -0
- package/README.md +43 -37
- package/install.sh +76 -10
- package/package.json +7 -7
- package/tools/README.md +49 -0
- package/tools/ci/install-chezmoi-verified.sh +68 -0
- package/docs/.vitepress/reports/localization-readability-audit.md +0 -73
- package/docs/AI.md +0 -179
- package/docs/ARCHITECTURE.md +0 -117
- package/docs/CNAME +0 -1
- package/docs/CONFIG_STRATEGY.md +0 -124
- package/docs/COPYRIGHT +0 -7
- package/docs/ECOSYSTEM.md +0 -220
- package/docs/GOLD-STANDARD-AUDIT.md +0 -352
- package/docs/GOVERNANCE.md +0 -98
- package/docs/MAINTAINERS.md +0 -41
- package/docs/MINIMUM-TOOLCHAIN.md +0 -100
- package/docs/NAMING_CONVENTIONS.md +0 -102
- package/docs/OPENCODE.md +0 -127
- package/docs/README.md +0 -84
- package/docs/STRUCTURE.md +0 -102
- package/docs/adr/ADR-001-ci-cd-pipeline.md +0 -118
- package/docs/adr/ADR-002-shell-performance.md +0 -130
- package/docs/adr/ADR-003-security-first.md +0 -158
- package/docs/adr/ADR-004-cli-architecture.md +0 -171
- package/docs/adr/ADR-005-chezmoi-choice.md +0 -99
- package/docs/adr/ADR-006-shell-selection.md +0 -124
- package/docs/adr/ADR-007-multi-shell-parity.md +0 -62
- package/docs/adr/ADR-008-alias-system-architecture.md +0 -95
- package/docs/adr/ADR-009-wallpaper-driven-theming.md +0 -131
- package/docs/adr/ADR-010-starship-transient-prompt.md +0 -144
- package/docs/adr/ADR-011-nushell-tier3-keep.md +0 -144
- package/docs/adr/ADR-012-ai-fleet-local-proxy.md +0 -79
- package/docs/adr/README.md +0 -40
- package/docs/architecture/AI_COST_OPTIMIZATION.md +0 -144
- package/docs/architecture/ARCHITECTURE.md +0 -20
- package/docs/architecture/INTEROP.md +0 -44
- package/docs/architecture/REPO_LAYOUT.md +0 -241
- package/docs/architecture/WALKTHROUGH.md +0 -86
- package/docs/architecture/fleet-deployment.md +0 -77
- package/docs/archive/EUXIS_2026_REVIEW.md +0 -127
- package/docs/archive/LEGACY_ROADMAP.md +0 -6
- package/docs/archive/MILESTONE_v0.2.493.md +0 -47
- package/docs/archive/PLAN.md +0 -199
- package/docs/archive/REPO_AUDIT.md +0 -31
- package/docs/articles/.pages +0 -6
- package/docs/articles/2026-07-05-custom-mkdocs-material-dark-theme.md +0 -216
- package/docs/articles/2026-07-05-fish-startup-abbr.md +0 -153
- package/docs/articles/2026-07-05-master-to-main-rename-runbook.md +0 -128
- package/docs/articles/index.md +0 -36
- package/docs/guides/INSTALL.md +0 -144
- package/docs/guides/MACOS_ICLOUD_SYMLINKS.md +0 -121
- package/docs/guides/NEOVIM_IDE_GUIDE.md +0 -61
- package/docs/guides/THEMING.md +0 -230
- package/docs/guides/TROUBLESHOOTING.md +0 -176
- package/docs/guides/WSL2_NIX_TROUBLESHOOTING.md +0 -792
- package/docs/index.md +0 -132
- package/docs/interop/A2A.md +0 -39
- package/docs/interop/POWERSHELL.md +0 -102
- package/docs/manual/00-introduction.md +0 -89
- package/docs/manual/01-concepts/01-architecture.md +0 -138
- package/docs/manual/01-concepts/02-trust-model.md +0 -183
- package/docs/manual/01-concepts/03-theme-engine.md +0 -186
- package/docs/manual/01-concepts/04-fleet.md +0 -148
- package/docs/manual/01-concepts/05-self-healing.md +0 -204
- package/docs/manual/02-tutorials/01-first-install.md +0 -197
- package/docs/manual/02-tutorials/02-add-wallpaper.md +0 -216
- package/docs/manual/02-tutorials/03-create-profile.md +0 -244
- package/docs/manual/02-tutorials/04-encrypt-secret.md +0 -281
- package/docs/manual/02-tutorials/05-deploy-fleet.md +0 -283
- package/docs/manual/03-reference/01-dot-cli.md +0 -475
- package/docs/manual/03-reference/02-config-files.md +0 -265
- package/docs/manual/03-reference/03-environment.md +0 -124
- package/docs/manual/03-reference/04-templates.md +0 -190
- package/docs/manual/03-reference/05-feature-flags.md +0 -187
- package/docs/manual/04-cookbook/01-recipes.md +0 -285
- package/docs/manual/04-cookbook/02-troubleshooting.md +0 -351
- package/docs/manual/04-cookbook/03-faq.md +0 -175
- package/docs/manual/05-appendices/A-platform-matrix.md +0 -101
- package/docs/manual/05-appendices/B-security-checklist.md +0 -85
- package/docs/manual/05-appendices/C-glossary.md +0 -40
- package/docs/manual/05-appendices/D-bibliography.md +0 -58
- package/docs/manual/05-appendices/E-license.md +0 -38
- package/docs/manual/_toc.yml +0 -58
- package/docs/manual/command-index.md +0 -175
- package/docs/manual/concept-index.md +0 -170
- package/docs/manual/index.md +0 -66
- package/docs/migration/README.md +0 -81
- package/docs/migration/from-bare-git-repo.md +0 -156
- package/docs/migration/from-gnu-stow.md +0 -165
- package/docs/migration/from-plain-chezmoi.md +0 -148
- package/docs/migration/from-yadm.md +0 -187
- package/docs/operations/ARCHITECTURE_ROADMAP.md +0 -7
- package/docs/operations/ATTESTATION.md +0 -44
- package/docs/operations/CI_CADENCE.md +0 -107
- package/docs/operations/CI_COMPOSITES.md +0 -156
- package/docs/operations/COMPLETIONS.md +0 -123
- package/docs/operations/COVERAGE.md +0 -204
- package/docs/operations/DRIFT.md +0 -107
- package/docs/operations/HARD_AUDIT_2026.md +0 -631
- package/docs/operations/MAINTENANCE.md +0 -63
- package/docs/operations/MANIFEST.md +0 -127
- package/docs/operations/MIGRATION.md +0 -109
- package/docs/operations/OPERATIONS.md +0 -188
- package/docs/operations/PERFORMANCE.md +0 -133
- package/docs/operations/PERFORMANCE_BUDGETS.md +0 -196
- package/docs/operations/REGISTRY.md +0 -90
- package/docs/operations/RELEASE_PIPELINE.md +0 -128
- package/docs/operations/RELIABILITY.md +0 -122
- package/docs/operations/RFC_v0_2_503_reorganization.md +0 -280
- package/docs/operations/ROADMAP.md +0 -10
- package/docs/operations/ROADMAP_2026.md +0 -7
- package/docs/operations/ROADMAP_V0_2_503.md +0 -10
- package/docs/operations/TESTING.md +0 -216
- package/docs/operations/TRACEABILITY.md +0 -44
- package/docs/operations/TRUSTED_AGENT_WORKSTATION.md +0 -65
- package/docs/operations/VERSION_SYNC.md +0 -393
- package/docs/packaging.md +0 -222
- package/docs/reference/ALIASES.md +0 -131
- package/docs/reference/ALIASES_CHEATSHEET.md +0 -32
- package/docs/reference/ALIASES_DEPRECATIONS.md +0 -13
- package/docs/reference/FEATURE-MATRIX.md +0 -646
- package/docs/reference/FEATURES.md +0 -66
- package/docs/reference/FONTS.md +0 -112
- package/docs/reference/POWERSHELL_PARITY.md +0 -82
- package/docs/reference/PROFILES.md +0 -69
- package/docs/reference/SCREENSHOTS.md +0 -121
- package/docs/reference/SCRIPTS.md +0 -71
- package/docs/reference/SUPPORT_MATRIX.md +0 -80
- package/docs/reference/THEMES.md +0 -117
- package/docs/reference/TOOLS.md +0 -110
- package/docs/reference/UTILS.md +0 -243
- package/docs/registry.json +0 -6
- package/docs/schema/dot-env-v1.json +0 -110
- package/docs/schema/dot-registry-v1.json +0 -33
- package/docs/security/AI_ACT_COMPLIANCE.md +0 -94
- package/docs/security/AUDIT_BYPASS.md +0 -103
- package/docs/security/AUTOMATION_SECRETS.md +0 -26
- package/docs/security/CI_EGRESS_ALLOWLIST.md +0 -127
- package/docs/security/CI_PINNING.md +0 -129
- package/docs/security/COMMIT_SIGNING.md +0 -138
- package/docs/security/COMPLIANCE.md +0 -458
- package/docs/security/DEPS_DEV_EXCEPTIONS.md +0 -86
- package/docs/security/DISCLOSURE.md +0 -130
- package/docs/security/ENCRYPTION.md +0 -57
- package/docs/security/FMEA.md +0 -159
- package/docs/security/FUZZING.md +0 -209
- package/docs/security/HISTORY_FILTERING.md +0 -132
- package/docs/security/INCIDENT_RESPONSE.md +0 -579
- package/docs/security/INSTALL_VERIFICATION.md +0 -122
- package/docs/security/KEYS.md +0 -49
- package/docs/security/KEY_ROTATION.md +0 -303
- package/docs/security/MCP_POLICY.md +0 -78
- package/docs/security/POLICY_RELEASES.md +0 -37
- package/docs/security/README.md +0 -28
- package/docs/security/SCORECARD.md +0 -195
- package/docs/security/SECRETS.md +0 -158
- package/docs/security/SECURITY.md +0 -45
- package/docs/security/SECURITY_CHECKLIST.md +0 -55
- package/docs/security/SHELL_EXEMPTIONS.md +0 -145
- package/docs/security/SOUP_REGISTER.md +0 -36
- package/docs/security/THREAT_MODEL.md +0 -130
- package/docs/security/VERIFICATION_VALIDATION.md +0 -228
- package/docs/security/VERIFY_RELEASE.md +0 -201
- package/docs/security/security-pubkey.asc +0 -15
- package/docs/stylesheets/extra.css +0 -444
- package/docs/themes/README.md +0 -10
- package/docs/themes/VISUAL_INTEGRITY_REPORT.md +0 -30
- package/docs/themes/hero-shot.svg +0 -78
- package/scripts/README.md +0 -123
- package/scripts/ci/check-copyright-headers.sh +0 -8
- package/scripts/ci/check-shell-preamble.sh +0 -8
- package/scripts/ci/guard-gitleaks-checkout.sh +0 -8
- package/scripts/demo/record.sh +0 -43
- package/scripts/diagnostics/a2a-conformance.sh +0 -163
- package/scripts/diagnostics/alias-governance.sh +0 -165
- package/scripts/diagnostics/aliases-cheatsheet.sh +0 -74
- package/scripts/diagnostics/aliases-manifest.sh +0 -77
- package/scripts/diagnostics/attest-verify.sh +0 -147
- package/scripts/diagnostics/benchmark.sh +0 -408
- package/scripts/diagnostics/conflicts.sh +0 -73
- package/scripts/diagnostics/doctor-unified.sh +0 -43
- package/scripts/diagnostics/doctor.sh +0 -797
- package/scripts/diagnostics/drift-dashboard.sh +0 -203
- package/scripts/diagnostics/health.sh +0 -656
- package/scripts/diagnostics/history-analysis.sh +0 -86
- package/scripts/diagnostics/mcp-doctor.sh +0 -582
- package/scripts/diagnostics/perf.sh +0 -453
- package/scripts/diagnostics/scorecard.sh +0 -120
- package/scripts/diagnostics/secret-governance.sh +0 -65
- package/scripts/diagnostics/security-score.sh +0 -467
- package/scripts/diagnostics/smoke-test.sh +0 -88
- package/scripts/diagnostics/snapshot.sh +0 -90
- package/scripts/diagnostics/verify.sh +0 -108
- package/scripts/diagnostics/verify_state.sh +0 -73
- package/scripts/diagnostics/version-locks.sh +0 -94
- package/scripts/diagnostics/workstation-attestation.sh +0 -212
- package/scripts/dot/commands/agent.sh +0 -535
- package/scripts/dot/commands/agents.sh +0 -352
- package/scripts/dot/commands/ai.sh +0 -600
- package/scripts/dot/commands/aliases.sh +0 -277
- package/scripts/dot/commands/appearance.sh +0 -110
- package/scripts/dot/commands/completion.sh +0 -171
- package/scripts/dot/commands/core.sh +0 -217
- package/scripts/dot/commands/diagnostics.sh +0 -265
- package/scripts/dot/commands/env-emit.sh +0 -203
- package/scripts/dot/commands/fleet.sh +0 -711
- package/scripts/dot/commands/init.sh +0 -185
- package/scripts/dot/commands/lint.sh +0 -208
- package/scripts/dot/commands/manual.sh +0 -169
- package/scripts/dot/commands/meta.sh +0 -438
- package/scripts/dot/commands/patterns.sh +0 -55
- package/scripts/dot/commands/registry.sh +0 -455
- package/scripts/dot/commands/restore.sh +0 -232
- package/scripts/dot/commands/secrets.sh +0 -296
- package/scripts/dot/commands/security.sh +0 -102
- package/scripts/dot/commands/tools.sh +0 -570
- package/scripts/dot/data/alias-deprecations.tsv +0 -2
- package/scripts/dot/powershell/Dot.psm1 +0 -319
- package/scripts/fonts/install-nerd-fonts.sh +0 -75
- package/scripts/fonts/patch-fonts.sh +0 -36
- package/scripts/git-hooks/install.sh +0 -12
- package/scripts/git-hooks/pre-commit +0 -12
- package/scripts/git-hooks/pre-commit-audit.sh +0 -146
- package/scripts/git-hooks/pre-push +0 -105
- package/scripts/git-hooks/prepare-commit-msg +0 -29
- package/scripts/lib/secrets_provider.sh +0 -200
- package/scripts/nvim/headless-upgrade.lua +0 -81
- package/scripts/ops/ai-setup.sh +0 -71
- package/scripts/ops/bundle.sh +0 -104
- package/scripts/ops/chaos.sh +0 -50
- package/scripts/ops/chezmoi-apply.sh +0 -333
- package/scripts/ops/chezmoi-diff.sh +0 -16
- package/scripts/ops/chezmoi-remove.sh +0 -46
- package/scripts/ops/chezmoi-update.sh +0 -67
- package/scripts/ops/heal-chezmoi.sh +0 -87
- package/scripts/ops/heal-system.sh +0 -129
- package/scripts/ops/heal-tools.sh +0 -297
- package/scripts/ops/heal.sh +0 -223
- package/scripts/ops/post-apply-repair.sh +0 -107
- package/scripts/ops/prewarm.sh +0 -128
- package/scripts/ops/release.sh +0 -262
- package/scripts/ops/rollback.sh +0 -613
- package/scripts/ops/setup.sh +0 -138
- package/scripts/ops/teleport.sh +0 -34
- package/scripts/qa/check-feature-matrix.sh +0 -296
- package/scripts/qa/check-version-consistency.sh +0 -12
- package/scripts/qa/coverage-baseline.sh +0 -61
- package/scripts/qa/docs-coverage.sh +0 -118
- package/scripts/qa/examples-coverage.sh +0 -94
- package/scripts/qa/powershell-contract.ps1 +0 -95
- package/scripts/qa/reliability-audit.sh +0 -139
- package/scripts/qa/scorecard-snapshot.sh +0 -128
- package/scripts/qa/traceability-coverage.sh +0 -124
- package/scripts/qa/validate-examples.sh +0 -90
- package/scripts/qa/wsl-contract.sh +0 -12
- package/scripts/secrets/age-init.sh +0 -82
- package/scripts/secrets/create-secrets-file.sh +0 -46
- package/scripts/secrets/encrypt-ssh-key.sh +0 -44
- package/scripts/security/backup.sh +0 -58
- package/scripts/security/check-disclosure-key-expiry.sh +0 -111
- package/scripts/security/dns-doh.sh +0 -52
- package/scripts/security/encryption-check.sh +0 -55
- package/scripts/security/enforce-policies.sh +0 -552
- package/scripts/security/firewall.sh +0 -91
- package/scripts/security/lock-configs.sh +0 -67
- package/scripts/security/lock-screen.sh +0 -56
- package/scripts/security/manage-secrets.sh +0 -429
- package/scripts/security/ssh-cert.sh +0 -204
- package/scripts/security/telemetry-kill.sh +0 -51
- package/scripts/security/usb-safety.sh +0 -52
- package/scripts/theme/apply-gnome-theme.sh +0 -333
- package/scripts/theme/extract-heic-frames.sh +0 -115
- package/scripts/theme/extract-theme.py +0 -1020
- package/scripts/theme/install-boot-logo.sh +0 -63
- package/scripts/theme/install-catppuccin-themes.sh +0 -371
- package/scripts/theme/install-cursors.sh +0 -26
- package/scripts/theme/install-file-icons.sh +0 -27
- package/scripts/theme/install-grub-theme.sh +0 -62
- package/scripts/theme/install-lock-icon.sh +0 -31
- package/scripts/theme/merge-wallpaper.sh +0 -146
- package/scripts/theme/rebuild-themes.sh +0 -603
- package/scripts/theme/switch.sh +0 -476
- package/scripts/theme/wallpaper-rotate.sh +0 -137
- package/scripts/theme/wallpaper-sync.sh +0 -690
- package/scripts/tools/cmatrix.sh +0 -22
- package/scripts/tools/detect-collisions.py +0 -103
- package/scripts/tools/emoji-picker.sh +0 -49
- package/scripts/tools/figlet-banner.sh +0 -19
- package/scripts/tools/log-rotate.sh +0 -31
- package/scripts/tools/lolcat-wrap.sh +0 -20
- package/scripts/tools/pipes.sh +0 -49
- package/scripts/tuning/linux.sh +0 -186
- package/scripts/tuning/macos.sh +0 -56
- package/scripts/uninstall.sh +0 -86
- package/scripts/verify-release-versions +0 -156
- package/scripts/version-sync.sh +0 -714
- package/templates/chezmoi-data/geekom-a9.toml.example +0 -21
- package/templates/chezmoi-data/mac-m1.toml.example +0 -16
- package/templates/chezmoi-data/mac-t2-linux.toml.example +0 -21
- package/templates/chezmoi-data/surface-pro-7p.toml.example +0 -21
- package/templates/projects/go/.github/workflows/ci.yml +0 -31
- package/templates/projects/go/README.md +0 -7
- package/templates/projects/go/cmd/__PROJECT_NAME__/main.go +0 -8
- package/templates/projects/go/go.mod +0 -3
- package/templates/projects/go/go.sum +0 -0
- package/templates/projects/molecule/README.md +0 -7
- package/templates/projects/molecule/converge.yml +0 -7
- package/templates/projects/molecule/molecule.yml +0 -16
- package/templates/projects/node/.github/workflows/ci.yml +0 -30
- package/templates/projects/node/README.md +0 -7
- package/templates/projects/node/package-lock.json +0 -12
- package/templates/projects/node/package.json +0 -10
- package/templates/projects/node/src/index.js +0 -3
- package/templates/projects/packer/README.md +0 -15
- package/templates/projects/packer/main.pkr.hcl +0 -15
- package/templates/projects/python/.github/workflows/ci.yml +0 -34
- package/templates/projects/python/README.md +0 -7
- package/templates/projects/python/pyproject.toml +0 -25
- package/templates/projects/python/src/__PROJECT_NAME__/__init__.py +0 -2
- package/templates/projects/python/tests/test_basic.py +0 -3
|
@@ -1,552 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
3
|
-
# Copyright (c) 2015-2026 Sebastien Rousseau
|
|
4
|
-
# Security Policy Enforcement Script
|
|
5
|
-
# This script enforces security policies across the dotfiles repository
|
|
6
|
-
|
|
7
|
-
set -euo pipefail
|
|
8
|
-
|
|
9
|
-
# Script configuration
|
|
10
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
-
readonly SCRIPT_DIR
|
|
12
|
-
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
|
13
|
-
readonly REPO_ROOT
|
|
14
|
-
readonly POLICIES_DIR="${REPO_ROOT}/.github/security-policies"
|
|
15
|
-
readonly LOG_FILE="${REPO_ROOT}/.security-audit.log"
|
|
16
|
-
|
|
17
|
-
# shellcheck source=../../lib/dot/ui.sh
|
|
18
|
-
# shellcheck disable=SC1091
|
|
19
|
-
source "${SCRIPT_DIR}/../../lib/dot/ui.sh"
|
|
20
|
-
ui_init
|
|
21
|
-
|
|
22
|
-
# Logging function (also writes to log file)
|
|
23
|
-
log() {
|
|
24
|
-
local level="$1"
|
|
25
|
-
shift
|
|
26
|
-
local message="$*"
|
|
27
|
-
local timestamp
|
|
28
|
-
timestamp=$(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
|
29
|
-
echo "[${timestamp}] [${level}] ${message}" | tee -a "${LOG_FILE}"
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
# Per-check outcomes, recorded by main() and rendered by generate_report().
|
|
33
|
-
# Parallel arrays rather than an associative array: this runs under bash 3.2 on
|
|
34
|
-
# stock macOS, where `declare -A` does not exist.
|
|
35
|
-
CHECK_NAMES=()
|
|
36
|
-
CHECK_RESULTS=()
|
|
37
|
-
|
|
38
|
-
record_check() {
|
|
39
|
-
CHECK_NAMES+=("$1")
|
|
40
|
-
CHECK_RESULTS+=("$2")
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
# Error handling
|
|
44
|
-
error_exit() {
|
|
45
|
-
log "ERROR" "$1"
|
|
46
|
-
exit 1
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
# Check if required tools are installed.
|
|
50
|
-
#
|
|
51
|
-
# Only the tools every check needs are fatal. gitleaks, shellcheck and opa each
|
|
52
|
-
# back exactly one check, and treating them as fatal meant a machine without
|
|
53
|
-
# opa ran NO checks at all — as a pre-commit hook, that is a gate that silently
|
|
54
|
-
# does nothing on most contributors' machines. A missing optional tool now skips
|
|
55
|
-
# its own check and nothing else.
|
|
56
|
-
#
|
|
57
|
-
# A skip is never counted as a pass: it is reported as SKIPPED in the output and
|
|
58
|
-
# in the report, and DOTFILES_POLICY_STRICT=1 turns any skip into a hard error
|
|
59
|
-
# so CI can demand the full set.
|
|
60
|
-
MISSING_OPTIONAL=()
|
|
61
|
-
|
|
62
|
-
have_tool() {
|
|
63
|
-
command -v "$1" >/dev/null 2>&1
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
check_dependencies() {
|
|
67
|
-
log "INFO" "Checking dependencies..."
|
|
68
|
-
|
|
69
|
-
local missing_required=()
|
|
70
|
-
local tool
|
|
71
|
-
|
|
72
|
-
for tool in grep find git; do
|
|
73
|
-
have_tool "$tool" || missing_required+=("$tool")
|
|
74
|
-
done
|
|
75
|
-
|
|
76
|
-
if [[ ${#missing_required[@]} -gt 0 ]]; then
|
|
77
|
-
error_exit "Missing required tools: ${missing_required[*]}"
|
|
78
|
-
fi
|
|
79
|
-
|
|
80
|
-
for tool in gitleaks shellcheck opa; do
|
|
81
|
-
have_tool "$tool" || MISSING_OPTIONAL+=("$tool")
|
|
82
|
-
done
|
|
83
|
-
|
|
84
|
-
if [[ ${#MISSING_OPTIONAL[@]} -gt 0 ]]; then
|
|
85
|
-
if [[ "${DOTFILES_POLICY_STRICT:-0}" == "1" ]]; then
|
|
86
|
-
error_exit "Missing tools (strict mode): ${MISSING_OPTIONAL[*]}"
|
|
87
|
-
fi
|
|
88
|
-
log "WARN" "Not installed, related checks will be SKIPPED: ${MISSING_OPTIONAL[*]}"
|
|
89
|
-
fi
|
|
90
|
-
|
|
91
|
-
log "INFO" "Dependency check complete"
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
# Scan for hardcoded secrets
|
|
95
|
-
# repo_files [pattern] — NUL-separated list of TRACKED files.
|
|
96
|
-
#
|
|
97
|
-
# Every check below used to walk the working tree with `find`, which pulls in
|
|
98
|
-
# whatever happens to be sitting there: coverage/ alone is 165 MB of xtrace
|
|
99
|
-
# output and produced 50 of the 52 gitleaks findings on a clean checkout, and
|
|
100
|
-
# it is gitignored at .gitignore:80 with zero tracked files. A policy gate
|
|
101
|
-
# about the repository should examine the repository, so this asks git.
|
|
102
|
-
#
|
|
103
|
-
# Falls back to find when run outside a work tree (a tarball, say), with the
|
|
104
|
-
# same exclusions spelled out rather than assumed.
|
|
105
|
-
repo_files() {
|
|
106
|
-
local pattern="${1:-}"
|
|
107
|
-
if git -C "${REPO_ROOT}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
108
|
-
if [[ -n "$pattern" ]]; then
|
|
109
|
-
git -C "${REPO_ROOT}" ls-files -z -- "$pattern"
|
|
110
|
-
else
|
|
111
|
-
git -C "${REPO_ROOT}" ls-files -z
|
|
112
|
-
fi
|
|
113
|
-
else
|
|
114
|
-
if [[ -n "$pattern" ]]; then
|
|
115
|
-
find "${REPO_ROOT}" -name "$pattern" -type f \
|
|
116
|
-
-not -path "*/.git/*" -not -path "*/coverage/*" -not -path "*/node_modules/*" -print0
|
|
117
|
-
else
|
|
118
|
-
find "${REPO_ROOT}" -type f \
|
|
119
|
-
-not -path "*/.git/*" -not -path "*/coverage/*" -not -path "*/node_modules/*" -print0
|
|
120
|
-
fi
|
|
121
|
-
fi
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
scan_secrets() {
|
|
125
|
-
log "INFO" "Scanning for hardcoded secrets..."
|
|
126
|
-
|
|
127
|
-
local violations=0
|
|
128
|
-
|
|
129
|
-
# gitleaks in GIT mode, not --no-git.
|
|
130
|
-
#
|
|
131
|
-
# --no-git scanned the working tree, which meant gitignored build output
|
|
132
|
-
# (coverage/, 165 MB) and, worse, it cannot honour .gitleaksignore: those
|
|
133
|
-
# entries are fingerprinted `<commit>:<file>:<rule>:<line>`, and with no
|
|
134
|
-
# commit to key on, every allowlisted false positive came back. That is how
|
|
135
|
-
# this reported "potential secrets" while `gitleaks detect` over all 2278
|
|
136
|
-
# commits reported none.
|
|
137
|
-
#
|
|
138
|
-
# Git mode is also ~18x faster here: 10s against 3m0s.
|
|
139
|
-
local gl_out
|
|
140
|
-
if ! have_tool gitleaks; then
|
|
141
|
-
log "WARN" "⏭ gitleaks SKIPPED (not installed) — pattern scan still runs"
|
|
142
|
-
elif gl_out="$(gitleaks detect --source="${REPO_ROOT}" \
|
|
143
|
-
--config="${REPO_ROOT}/config/gitleaks.toml" \
|
|
144
|
-
--redact --no-banner 2>&1)"; then
|
|
145
|
-
log "INFO" "✅ No secrets detected by gitleaks"
|
|
146
|
-
else
|
|
147
|
-
log "WARN" "❌ Potential secrets detected by gitleaks"
|
|
148
|
-
printf '%s\n' "$gl_out" | grep -E '^(Finding|File|RuleID|Commit):' | head -40 >>"${LOG_FILE}" 2>/dev/null || true
|
|
149
|
-
violations=$((violations + 1))
|
|
150
|
-
fi
|
|
151
|
-
|
|
152
|
-
# A short list of patterns that are unambiguous on sight, scanned over
|
|
153
|
-
# TRACKED files only.
|
|
154
|
-
#
|
|
155
|
-
# The previous list could not pass on any real repository. `[0-9a-f]{32,}`
|
|
156
|
-
# matches every git SHA, checksum and fingerprint in the tree, so it fired
|
|
157
|
-
# unconditionally; and `key\s*=\s*"..."` matches documentation, test
|
|
158
|
-
# fixtures and any config key whose name ends in "key". Four of the six
|
|
159
|
-
# patterns reported a violation on a clean checkout, which trains a reader
|
|
160
|
-
# to ignore the output — the opposite of what a gate is for.
|
|
161
|
-
#
|
|
162
|
-
# What survives is the shapes that do not occur by accident. Everything
|
|
163
|
-
# else is gitleaks' job, and it does it with entropy analysis and an
|
|
164
|
-
# allowlist rather than a bare grep.
|
|
165
|
-
local secret_patterns=(
|
|
166
|
-
'AKIA[0-9A-Z]{16}' # AWS access key id
|
|
167
|
-
'ASIA[0-9A-Z]{16}' # AWS temporary access key id
|
|
168
|
-
'-----BEGIN [A-Z ]*PRIVATE KEY-----' # any PEM private key block
|
|
169
|
-
'xox[baprs]-[0-9A-Za-z-]{10,}' # Slack token
|
|
170
|
-
'ghp_[0-9A-Za-z]{36}' # GitHub personal access token
|
|
171
|
-
'AIza[0-9A-Za-z_-]{35}' # Google API key
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
# Two kinds of file contain these shapes on purpose, and both already
|
|
175
|
-
# declare it using gitleaks' own conventions rather than a list kept here:
|
|
176
|
-
#
|
|
177
|
-
# * the gitleaks config itself — it allowlists a fake AWS key by value,
|
|
178
|
-
# so a rule-definition file necessarily holds the shape its rules
|
|
179
|
-
# match. (Deliberately not quoted here: writing the literal out made
|
|
180
|
-
# THIS file match its own pattern, which is the same self-match the
|
|
181
|
-
# original guarded against with --exclude=enforce-policies.sh);
|
|
182
|
-
# * fixtures that test secret detection — the atuin history filter's test
|
|
183
|
-
# carries `export AWS_SECRET_ACCESS_KEY=ASIA... # gitleaks:allow`,
|
|
184
|
-
# because the point of the test is that such a line gets filtered.
|
|
185
|
-
#
|
|
186
|
-
# Honouring `gitleaks:allow` means one convention, already understood by
|
|
187
|
-
# contributors and by gitleaks, instead of a second exclusion list here
|
|
188
|
-
# that would drift out of step with the first.
|
|
189
|
-
local pattern hits f line
|
|
190
|
-
for pattern in "${secret_patterns[@]}"; do
|
|
191
|
-
hits=""
|
|
192
|
-
while IFS= read -r -d '' f; do
|
|
193
|
-
case "$f" in
|
|
194
|
-
config/gitleaks.toml | .gitleaks.toml) continue ;;
|
|
195
|
-
esac
|
|
196
|
-
[[ -f "${REPO_ROOT}/$f" ]] || continue
|
|
197
|
-
while IFS= read -r line; do
|
|
198
|
-
[[ "$line" == *"gitleaks:allow"* ]] && continue
|
|
199
|
-
hits="${hits}${f}\n"
|
|
200
|
-
break
|
|
201
|
-
done < <(LC_ALL=C grep -E "${pattern}" "${REPO_ROOT}/$f" 2>/dev/null || true)
|
|
202
|
-
done < <(repo_files)
|
|
203
|
-
if [[ -n "$hits" ]]; then
|
|
204
|
-
log "WARN" "❌ Credential pattern ${pattern} found in:"
|
|
205
|
-
printf '%b' "$hits" | head -10 | while IFS= read -r h; do
|
|
206
|
-
[[ -n "$h" ]] && log "WARN" " ${h}"
|
|
207
|
-
done
|
|
208
|
-
violations=$((violations + 1))
|
|
209
|
-
fi
|
|
210
|
-
done
|
|
211
|
-
|
|
212
|
-
if [[ $violations -eq 0 ]]; then
|
|
213
|
-
log "INFO" "✅ No hardcoded secrets detected"
|
|
214
|
-
fi
|
|
215
|
-
|
|
216
|
-
return $violations
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
# Check file permissions
|
|
220
|
-
check_file_permissions() {
|
|
221
|
-
log "INFO" "Checking file permissions..."
|
|
222
|
-
|
|
223
|
-
local violations=0
|
|
224
|
-
|
|
225
|
-
# World-writable is the property worth catching. The old test was
|
|
226
|
-
# `[[ $perms -gt 755 ]]`, comparing an octal mode as a decimal number,
|
|
227
|
-
# which lets 764 (group-writable) through while flagging 775.
|
|
228
|
-
# Paths are reported repository-relative, the way every other check reports
|
|
229
|
-
# them, so the output is diffable between machines.
|
|
230
|
-
#
|
|
231
|
-
# Symlinks are skipped. A symlink carries its own mode bits, but the kernel
|
|
232
|
-
# ignores them: access is governed entirely by the target, which is checked
|
|
233
|
-
# on its own if it is tracked. Those bits are also not portable — Linux
|
|
234
|
-
# creates symlinks 777 and macOS 755 — so including them made this pass on a
|
|
235
|
-
# Mac and fail on a runner for the same eight tracked symlinks
|
|
236
|
-
# (.gitleaks.toml, .pre-commit-config.yaml and friends, all pointing into
|
|
237
|
-
# config/). `[[ -f ]]` follows the link, so it cannot filter them; -L must be
|
|
238
|
-
# tested first.
|
|
239
|
-
local file perms abs
|
|
240
|
-
while IFS= read -r -d '' file; do
|
|
241
|
-
abs="${REPO_ROOT}/${file}"
|
|
242
|
-
[[ -L "$abs" ]] && continue
|
|
243
|
-
[[ -f "$abs" ]] || continue
|
|
244
|
-
perms=$(stat -c %a "$abs" 2>/dev/null || stat -f %OLp "$abs" 2>/dev/null) || continue
|
|
245
|
-
# Other-writable is the bit that matters; group-writable in a repo is
|
|
246
|
-
# normal on shared checkouts.
|
|
247
|
-
if ((8#${perms} & 8#0002)); then
|
|
248
|
-
log "WARN" "❌ World-writable file: ${file} (${perms})"
|
|
249
|
-
violations=$((violations + 1))
|
|
250
|
-
fi
|
|
251
|
-
done < <(repo_files)
|
|
252
|
-
|
|
253
|
-
# Executable text files. `find -executable` is GNU-only: on macOS, where
|
|
254
|
-
# this hook actually runs for most contributors, that predicate is an
|
|
255
|
-
# error and the whole loop found nothing. Test the file instead.
|
|
256
|
-
# Symlinks skipped here for the same reason: `-x` follows the link, so a
|
|
257
|
-
# tracked symlink pointing at any executable would be reported as an
|
|
258
|
-
# executable text file.
|
|
259
|
-
local ext
|
|
260
|
-
for ext in md txt json yaml yml toml; do
|
|
261
|
-
while IFS= read -r -d '' file; do
|
|
262
|
-
[[ -L "${REPO_ROOT}/${file}" ]] && continue
|
|
263
|
-
if [[ -x "${REPO_ROOT}/${file}" ]]; then
|
|
264
|
-
log "WARN" "❌ Executable text file: ${file}"
|
|
265
|
-
violations=$((violations + 1))
|
|
266
|
-
fi
|
|
267
|
-
done < <(repo_files "*.${ext}")
|
|
268
|
-
done
|
|
269
|
-
|
|
270
|
-
if [[ $violations -eq 0 ]]; then
|
|
271
|
-
log "INFO" "✅ File permissions are appropriate"
|
|
272
|
-
fi
|
|
273
|
-
|
|
274
|
-
return $violations
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
# Validate shell scripts
|
|
278
|
-
validate_shell_scripts() {
|
|
279
|
-
log "INFO" "Validating shell scripts..."
|
|
280
|
-
|
|
281
|
-
if ! have_tool shellcheck; then
|
|
282
|
-
log "WARN" "⏭ Shell script validation SKIPPED (shellcheck not installed)"
|
|
283
|
-
return 2
|
|
284
|
-
fi
|
|
285
|
-
|
|
286
|
-
local violations=0
|
|
287
|
-
|
|
288
|
-
# Tracked scripts only, at the severity CI gates on. Running plain
|
|
289
|
-
# `shellcheck` here contradicted tools/ci and the Shell Lint workflow,
|
|
290
|
-
# which use `-S error` / `-S warning`, so this reported "violations" for
|
|
291
|
-
# style notes that the repository has deliberately decided not to gate.
|
|
292
|
-
local script
|
|
293
|
-
while IFS= read -r -d '' script; do
|
|
294
|
-
[[ -f "${REPO_ROOT}/${script}" ]] || continue
|
|
295
|
-
if ! shellcheck -S error -x "${REPO_ROOT}/${script}" >/dev/null 2>&1; then
|
|
296
|
-
log "WARN" "❌ ShellCheck errors in: ${script}"
|
|
297
|
-
violations=$((violations + 1))
|
|
298
|
-
fi
|
|
299
|
-
done < <(repo_files "*.sh")
|
|
300
|
-
|
|
301
|
-
if [[ $violations -eq 0 ]]; then
|
|
302
|
-
log "INFO" "✅ All shell scripts pass validation"
|
|
303
|
-
fi
|
|
304
|
-
|
|
305
|
-
return $violations
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
# check_environment_variables() was removed here.
|
|
309
|
-
#
|
|
310
|
-
# It warned when a file contained `^[A-Z_]+=...` but did not also contain
|
|
311
|
-
# `${VAR:-default}` ANYWHERE in the same file. That is not a security
|
|
312
|
-
# property: one defaulted variable anywhere exempted every assignment in the
|
|
313
|
-
# file, and a file with no defaults at all — a constants file, a .env
|
|
314
|
-
# template, a generated manifest — was flagged regardless of whether any
|
|
315
|
-
# value was sensitive. It ran `file` on every path in the working tree to
|
|
316
|
-
# decide what to read.
|
|
317
|
-
#
|
|
318
|
-
# There is no threshold that makes the condition meaningful, so it is gone
|
|
319
|
-
# rather than tuned. Hardcoded credentials are what gitleaks and the pattern
|
|
320
|
-
# list in scan_secrets() are for, and they judge the value rather than
|
|
321
|
-
# whether a neighbouring line happens to use parameter expansion.
|
|
322
|
-
|
|
323
|
-
# Validate OPA policies
|
|
324
|
-
validate_policies() {
|
|
325
|
-
log "INFO" "Validating OPA policies..."
|
|
326
|
-
|
|
327
|
-
if [[ ! -f "${POLICIES_DIR}/security.rego" ]]; then
|
|
328
|
-
log "WARN" "❌ Security policy file not found"
|
|
329
|
-
return 1
|
|
330
|
-
fi
|
|
331
|
-
|
|
332
|
-
if ! have_tool opa; then
|
|
333
|
-
log "WARN" "⏭ OPA policy validation SKIPPED (opa not installed)"
|
|
334
|
-
return 2
|
|
335
|
-
fi
|
|
336
|
-
|
|
337
|
-
if opa test "${POLICIES_DIR}" >/dev/null 2>&1; then
|
|
338
|
-
log "INFO" "✅ OPA policies are valid"
|
|
339
|
-
return 0
|
|
340
|
-
else
|
|
341
|
-
log "WARN" "❌ OPA policy validation failed"
|
|
342
|
-
return 1
|
|
343
|
-
fi
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
# Check for sensitive files
|
|
347
|
-
check_sensitive_files() {
|
|
348
|
-
log "INFO" "Checking for sensitive files..."
|
|
349
|
-
|
|
350
|
-
local violations=0
|
|
351
|
-
local sensitive_patterns=(
|
|
352
|
-
"*.key"
|
|
353
|
-
"*.pem"
|
|
354
|
-
"*.p12"
|
|
355
|
-
"*.pfx"
|
|
356
|
-
"*.jks"
|
|
357
|
-
"*.keystore"
|
|
358
|
-
".env"
|
|
359
|
-
".env.*"
|
|
360
|
-
"credentials.json"
|
|
361
|
-
"service-account.json"
|
|
362
|
-
"id_rsa"
|
|
363
|
-
"id_dsa"
|
|
364
|
-
"id_ecdsa"
|
|
365
|
-
"id_ed25519"
|
|
366
|
-
)
|
|
367
|
-
|
|
368
|
-
# Tracked files only. A `.env` a contributor keeps locally, or a key in an
|
|
369
|
-
# ignored scratch directory, is not something this repository ships — and
|
|
370
|
-
# flagging it trains people to ignore the check. What matters is whether
|
|
371
|
-
# such a file is committed.
|
|
372
|
-
#
|
|
373
|
-
# Each name is queried at the root AND at any depth. A git pathspec with a
|
|
374
|
-
# wildcard (`*.key`) already matches at any depth, because `*` spans `/` in
|
|
375
|
-
# pathspecs; a bare name (`id_rsa`, `.env`) matches ONLY at the repository
|
|
376
|
-
# root. Without the `*/` form, a committed `.ssh/id_rsa` — the likeliest
|
|
377
|
-
# place for a private key to appear — would not have been reported at all.
|
|
378
|
-
local pattern file
|
|
379
|
-
for pattern in "${sensitive_patterns[@]}"; do
|
|
380
|
-
while IFS= read -r -d '' file; do
|
|
381
|
-
if [[ ! "$file" =~ (environment-template\.env|\.pub$|KEYS\.asc$) ]]; then
|
|
382
|
-
log "WARN" "❌ Sensitive file committed: ${file}"
|
|
383
|
-
violations=$((violations + 1))
|
|
384
|
-
fi
|
|
385
|
-
done < <(
|
|
386
|
-
repo_files "${pattern}"
|
|
387
|
-
repo_files "*/${pattern}"
|
|
388
|
-
)
|
|
389
|
-
done
|
|
390
|
-
|
|
391
|
-
if [[ $violations -eq 0 ]]; then
|
|
392
|
-
log "INFO" "✅ No sensitive files detected"
|
|
393
|
-
fi
|
|
394
|
-
|
|
395
|
-
return $violations
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
# Render the checklist from what actually ran, so a failing check cannot show
|
|
399
|
-
# a tick. The previous version printed six fixed ✅ lines regardless of outcome,
|
|
400
|
-
# including one for a check that no longer exists.
|
|
401
|
-
_report_checklist() {
|
|
402
|
-
local i
|
|
403
|
-
# An empty array expansion is an unbound-variable error under `set -u` on
|
|
404
|
-
# bash 3.2, so the guard is load-bearing, not defensive noise.
|
|
405
|
-
[[ ${#CHECK_NAMES[@]} -eq 0 ]] && return 0
|
|
406
|
-
for i in "${!CHECK_NAMES[@]}"; do
|
|
407
|
-
if [[ "${CHECK_RESULTS[$i]}" == "SKIPPED" ]]; then
|
|
408
|
-
echo "- ⏭ ${CHECK_NAMES[$i]} — SKIPPED (required tool not installed)"
|
|
409
|
-
elif [[ "${CHECK_RESULTS[$i]}" == "0" ]]; then
|
|
410
|
-
echo "- ✅ ${CHECK_NAMES[$i]}"
|
|
411
|
-
else
|
|
412
|
-
echo "- ❌ ${CHECK_NAMES[$i]} (${CHECK_RESULTS[$i]} violation(s))"
|
|
413
|
-
fi
|
|
414
|
-
done
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
# Generate security report
|
|
418
|
-
generate_report() {
|
|
419
|
-
local total_violations="$1"
|
|
420
|
-
local report_file="${REPO_ROOT}/security-report.md"
|
|
421
|
-
|
|
422
|
-
log "INFO" "Generating security report..."
|
|
423
|
-
|
|
424
|
-
cat >"${report_file}" <<EOF
|
|
425
|
-
# Security Policy Enforcement Report
|
|
426
|
-
|
|
427
|
-
**Generated:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
|
428
|
-
**Repository:** $(basename "${REPO_ROOT}")
|
|
429
|
-
**Total Violations:** ${total_violations}
|
|
430
|
-
|
|
431
|
-
## Security Checks Performed
|
|
432
|
-
|
|
433
|
-
$(_report_checklist)
|
|
434
|
-
|
|
435
|
-
## Security Score
|
|
436
|
-
|
|
437
|
-
$(if [[ $total_violations -eq 0 ]]; then echo "🟢 **PASSED** - No security violations detected"; else echo "🔴 **FAILED** - ${total_violations} violations detected"; fi)
|
|
438
|
-
|
|
439
|
-
## Recommendations
|
|
440
|
-
|
|
441
|
-
1. Review any flagged violations in the audit log
|
|
442
|
-
2. Ensure proper file permissions (644 for files, 755 for executables)
|
|
443
|
-
3. Keep secrets out of tracked files; use the environment template for configuration
|
|
444
|
-
4. Run this script regularly as part of your development workflow
|
|
445
|
-
|
|
446
|
-
## Next Steps
|
|
447
|
-
|
|
448
|
-
- Fix any identified violations
|
|
449
|
-
- Run \`scripts/security/enforce-policies.sh\` again to verify fixes
|
|
450
|
-
- Consider setting up automated policy enforcement in CI/CD
|
|
451
|
-
|
|
452
|
-
---
|
|
453
|
-
*This report was generated by the automated security policy enforcement system.*
|
|
454
|
-
EOF
|
|
455
|
-
|
|
456
|
-
log "INFO" "Security report generated: ${report_file}"
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
# Main execution
|
|
460
|
-
main() {
|
|
461
|
-
log "INFO" "Starting security policy enforcement..."
|
|
462
|
-
|
|
463
|
-
# Initialize log file
|
|
464
|
-
echo "# Security Audit Log - $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >"${LOG_FILE}"
|
|
465
|
-
|
|
466
|
-
check_dependencies
|
|
467
|
-
|
|
468
|
-
# Report what is installed and stop, without scanning.
|
|
469
|
-
#
|
|
470
|
-
# A full pass walks every tracked file and runs shellcheck over each tracked
|
|
471
|
-
# script — ~85s here. That is the right cost for a gate and the wrong cost
|
|
472
|
-
# for a caller that only wants to know whether the tooling is present, or a
|
|
473
|
-
# smoke test checking the command is wired up.
|
|
474
|
-
#
|
|
475
|
-
# The feature-matrix test got this for free from a bug: gitleaks and opa were
|
|
476
|
-
# fatal dependencies, so on a machine without them the script exited before
|
|
477
|
-
# doing any work. Fixing that turned a sub-second test into a full scan
|
|
478
|
-
# inside a 120s budget, and it began failing on the macOS runners. This is
|
|
479
|
-
# the same fast path, made deliberate and documented rather than accidental.
|
|
480
|
-
if [[ "${DOTFILES_POLICY_DEPS_ONLY:-0}" == "1" ]]; then
|
|
481
|
-
log "INFO" "Dependency check only (DOTFILES_POLICY_DEPS_ONLY=1); no scan performed"
|
|
482
|
-
ui_ok "Dependencies checked"
|
|
483
|
-
exit 0
|
|
484
|
-
fi
|
|
485
|
-
|
|
486
|
-
local total_violations=0
|
|
487
|
-
|
|
488
|
-
# Run all checks, recording each outcome for the report.
|
|
489
|
-
local rc
|
|
490
|
-
|
|
491
|
-
local label
|
|
492
|
-
|
|
493
|
-
rc=0
|
|
494
|
-
scan_secrets || rc=$?
|
|
495
|
-
label="Secrets scanning (gitleaks + high-signal patterns)"
|
|
496
|
-
have_tool gitleaks || label="Secrets scanning (pattern scan only, gitleaks absent)"
|
|
497
|
-
record_check "$label" "$rc"
|
|
498
|
-
total_violations=$((total_violations + rc))
|
|
499
|
-
|
|
500
|
-
rc=0
|
|
501
|
-
check_file_permissions || rc=$?
|
|
502
|
-
record_check "File permissions validation" "$rc"
|
|
503
|
-
total_violations=$((total_violations + rc))
|
|
504
|
-
|
|
505
|
-
# rc 2 is the skip sentinel: the tool that backs this check is absent. A skip
|
|
506
|
-
# adds nothing to the violation count and is rendered as SKIPPED, never as a
|
|
507
|
-
# tick — use DOTFILES_POLICY_STRICT=1 to make it an error instead.
|
|
508
|
-
rc=0
|
|
509
|
-
validate_shell_scripts || rc=$?
|
|
510
|
-
if have_tool shellcheck; then
|
|
511
|
-
record_check "Shell script analysis (shellcheck)" "$rc"
|
|
512
|
-
total_violations=$((total_violations + rc))
|
|
513
|
-
else
|
|
514
|
-
record_check "Shell script analysis (shellcheck)" "SKIPPED"
|
|
515
|
-
fi
|
|
516
|
-
|
|
517
|
-
rc=0
|
|
518
|
-
validate_policies || rc=$?
|
|
519
|
-
if have_tool opa; then
|
|
520
|
-
[[ $rc -ne 0 ]] && rc=1
|
|
521
|
-
record_check "OPA policy validation" "$rc"
|
|
522
|
-
total_violations=$((total_violations + rc))
|
|
523
|
-
else
|
|
524
|
-
record_check "OPA policy validation" "SKIPPED"
|
|
525
|
-
fi
|
|
526
|
-
|
|
527
|
-
rc=0
|
|
528
|
-
check_sensitive_files || rc=$?
|
|
529
|
-
record_check "Sensitive files detection" "$rc"
|
|
530
|
-
total_violations=$((total_violations + rc))
|
|
531
|
-
|
|
532
|
-
# Generate report
|
|
533
|
-
generate_report "$total_violations"
|
|
534
|
-
|
|
535
|
-
# Final result
|
|
536
|
-
if [[ $total_violations -eq 0 ]]; then
|
|
537
|
-
ui_ok "All security checks passed"
|
|
538
|
-
log "INFO" "Security policy enforcement completed successfully"
|
|
539
|
-
exit 0
|
|
540
|
-
else
|
|
541
|
-
ui_err "$total_violations violations" "Security policy enforcement failed"
|
|
542
|
-
ui_warn "Details" "Check ${LOG_FILE}"
|
|
543
|
-
ui_warn "Report" "Review security-report.md"
|
|
544
|
-
log "ERROR" "Security policy enforcement failed with ${total_violations} violations"
|
|
545
|
-
exit 1
|
|
546
|
-
fi
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
# Script entry point
|
|
550
|
-
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
551
|
-
main "$@"
|
|
552
|
-
fi
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
3
|
-
# Copyright (c) 2015-2026 Sebastien Rousseau
|
|
4
|
-
## Configure system firewall with secure defaults.
|
|
5
|
-
##
|
|
6
|
-
## Enables firewall with deny-by-default policy and minimal exceptions.
|
|
7
|
-
## Requires explicit opt-in via DOTFILES_FIREWALL=1 to prevent accidental
|
|
8
|
-
## lockouts.
|
|
9
|
-
##
|
|
10
|
-
## # Usage
|
|
11
|
-
## DOTFILES_FIREWALL=1 dot firewall
|
|
12
|
-
##
|
|
13
|
-
## # Dependencies
|
|
14
|
-
## - macOS: socketfilterfw (built-in)
|
|
15
|
-
## - Linux: ufw (Uncomplicated Firewall)
|
|
16
|
-
##
|
|
17
|
-
## # Platform Notes
|
|
18
|
-
## ### Platform: macOS
|
|
19
|
-
## - Enables Application Firewall via socketfilterfw
|
|
20
|
-
## - Enables stealth mode (no ICMP responses)
|
|
21
|
-
## - Allows signed applications
|
|
22
|
-
##
|
|
23
|
-
## ### Platform: Linux
|
|
24
|
-
## - Configures UFW with deny incoming / allow outgoing
|
|
25
|
-
## - Allows OpenSSH to prevent lockout
|
|
26
|
-
## - Requires ufw package installed
|
|
27
|
-
##
|
|
28
|
-
## # Security
|
|
29
|
-
## **Requires sudo.** All changes logged to dotfiles.log.
|
|
30
|
-
##
|
|
31
|
-
## # Side Effects
|
|
32
|
-
## - Modifies system firewall rules
|
|
33
|
-
## - May block existing connections
|
|
34
|
-
##
|
|
35
|
-
## # Idempotency
|
|
36
|
-
## Safe to run repeatedly. Applies same rules each time.
|
|
37
|
-
|
|
38
|
-
set -euo pipefail
|
|
39
|
-
|
|
40
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
41
|
-
# shellcheck source=../../lib/dot/ui.sh
|
|
42
|
-
# shellcheck disable=SC1091
|
|
43
|
-
source "$SCRIPT_DIR/../../lib/dot/ui.sh"
|
|
44
|
-
# shellcheck source=../../lib/dot/platform.sh
|
|
45
|
-
# shellcheck disable=SC1091
|
|
46
|
-
source "$SCRIPT_DIR/../../lib/dot/platform.sh"
|
|
47
|
-
|
|
48
|
-
DRY_RUN=0
|
|
49
|
-
for arg in "$@"; do
|
|
50
|
-
case "$arg" in --dry-run | -n) DRY_RUN=1 ;; esac
|
|
51
|
-
done
|
|
52
|
-
_run_cmd() {
|
|
53
|
-
if [[ "$DRY_RUN" -eq 1 ]]; then ui_info "[dry-run]" "$*"; else "$@"; fi
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
ui_init
|
|
57
|
-
ui_header "Firewall"
|
|
58
|
-
|
|
59
|
-
if [[ "$DRY_RUN" -eq 1 ]]; then
|
|
60
|
-
ui_info "Mode" "dry-run (no changes will be made)"
|
|
61
|
-
elif [ "${DOTFILES_FIREWALL:-}" != "1" ]; then
|
|
62
|
-
ui_warn "Firewall" "disabled by default"
|
|
63
|
-
ui_info "Re-run" "DOTFILES_FIREWALL=1"
|
|
64
|
-
exit 1
|
|
65
|
-
fi
|
|
66
|
-
|
|
67
|
-
case "$(dot_platform_id)" in
|
|
68
|
-
macos)
|
|
69
|
-
ui_info "Enabling" "macOS firewall"
|
|
70
|
-
_run_cmd sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate on
|
|
71
|
-
_run_cmd sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setallowsigned on
|
|
72
|
-
_run_cmd sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setallowsignedapp on
|
|
73
|
-
_run_cmd sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setstealthmode on
|
|
74
|
-
;;
|
|
75
|
-
linux | wsl)
|
|
76
|
-
if command -v ufw >/dev/null; then
|
|
77
|
-
ui_info "Enabling" "UFW"
|
|
78
|
-
_run_cmd sudo ufw default deny incoming
|
|
79
|
-
_run_cmd sudo ufw default allow outgoing
|
|
80
|
-
_run_cmd sudo ufw allow OpenSSH
|
|
81
|
-
_run_cmd sudo ufw --force enable
|
|
82
|
-
else
|
|
83
|
-
ui_err "ufw" "not installed; re-run after install"
|
|
84
|
-
exit 1
|
|
85
|
-
fi
|
|
86
|
-
;;
|
|
87
|
-
*)
|
|
88
|
-
ui_err "Unsupported OS" "firewall setup"
|
|
89
|
-
exit 1
|
|
90
|
-
;;
|
|
91
|
-
esac
|