@sebastienrousseau/dotfiles 0.2.520 → 0.2.522

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.
Files changed (323) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/LICENSE-APACHE +190 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +43 -37
  5. package/install.sh +76 -10
  6. package/package.json +7 -7
  7. package/tools/README.md +49 -0
  8. package/tools/ci/install-chezmoi-verified.sh +68 -0
  9. package/docs/.vitepress/reports/localization-readability-audit.md +0 -73
  10. package/docs/AI.md +0 -179
  11. package/docs/ARCHITECTURE.md +0 -117
  12. package/docs/CNAME +0 -1
  13. package/docs/CONFIG_STRATEGY.md +0 -124
  14. package/docs/COPYRIGHT +0 -7
  15. package/docs/ECOSYSTEM.md +0 -220
  16. package/docs/GOLD-STANDARD-AUDIT.md +0 -352
  17. package/docs/GOVERNANCE.md +0 -98
  18. package/docs/MAINTAINERS.md +0 -41
  19. package/docs/MINIMUM-TOOLCHAIN.md +0 -100
  20. package/docs/NAMING_CONVENTIONS.md +0 -102
  21. package/docs/OPENCODE.md +0 -127
  22. package/docs/README.md +0 -84
  23. package/docs/STRUCTURE.md +0 -102
  24. package/docs/adr/ADR-001-ci-cd-pipeline.md +0 -118
  25. package/docs/adr/ADR-002-shell-performance.md +0 -130
  26. package/docs/adr/ADR-003-security-first.md +0 -158
  27. package/docs/adr/ADR-004-cli-architecture.md +0 -171
  28. package/docs/adr/ADR-005-chezmoi-choice.md +0 -99
  29. package/docs/adr/ADR-006-shell-selection.md +0 -124
  30. package/docs/adr/ADR-007-multi-shell-parity.md +0 -62
  31. package/docs/adr/ADR-008-alias-system-architecture.md +0 -95
  32. package/docs/adr/ADR-009-wallpaper-driven-theming.md +0 -131
  33. package/docs/adr/ADR-010-starship-transient-prompt.md +0 -144
  34. package/docs/adr/ADR-011-nushell-tier3-keep.md +0 -144
  35. package/docs/adr/ADR-012-ai-fleet-local-proxy.md +0 -79
  36. package/docs/adr/README.md +0 -40
  37. package/docs/architecture/AI_COST_OPTIMIZATION.md +0 -144
  38. package/docs/architecture/ARCHITECTURE.md +0 -20
  39. package/docs/architecture/INTEROP.md +0 -44
  40. package/docs/architecture/REPO_LAYOUT.md +0 -241
  41. package/docs/architecture/WALKTHROUGH.md +0 -86
  42. package/docs/architecture/fleet-deployment.md +0 -77
  43. package/docs/archive/EUXIS_2026_REVIEW.md +0 -127
  44. package/docs/archive/LEGACY_ROADMAP.md +0 -6
  45. package/docs/archive/MILESTONE_v0.2.493.md +0 -47
  46. package/docs/archive/PLAN.md +0 -199
  47. package/docs/archive/REPO_AUDIT.md +0 -31
  48. package/docs/articles/.pages +0 -6
  49. package/docs/articles/2026-07-05-custom-mkdocs-material-dark-theme.md +0 -216
  50. package/docs/articles/2026-07-05-fish-startup-abbr.md +0 -153
  51. package/docs/articles/2026-07-05-master-to-main-rename-runbook.md +0 -128
  52. package/docs/articles/index.md +0 -36
  53. package/docs/guides/INSTALL.md +0 -144
  54. package/docs/guides/MACOS_ICLOUD_SYMLINKS.md +0 -121
  55. package/docs/guides/NEOVIM_IDE_GUIDE.md +0 -61
  56. package/docs/guides/THEMING.md +0 -230
  57. package/docs/guides/TROUBLESHOOTING.md +0 -176
  58. package/docs/guides/WSL2_NIX_TROUBLESHOOTING.md +0 -792
  59. package/docs/index.md +0 -132
  60. package/docs/interop/A2A.md +0 -39
  61. package/docs/interop/POWERSHELL.md +0 -102
  62. package/docs/manual/00-introduction.md +0 -89
  63. package/docs/manual/01-concepts/01-architecture.md +0 -138
  64. package/docs/manual/01-concepts/02-trust-model.md +0 -183
  65. package/docs/manual/01-concepts/03-theme-engine.md +0 -186
  66. package/docs/manual/01-concepts/04-fleet.md +0 -148
  67. package/docs/manual/01-concepts/05-self-healing.md +0 -204
  68. package/docs/manual/02-tutorials/01-first-install.md +0 -197
  69. package/docs/manual/02-tutorials/02-add-wallpaper.md +0 -216
  70. package/docs/manual/02-tutorials/03-create-profile.md +0 -244
  71. package/docs/manual/02-tutorials/04-encrypt-secret.md +0 -281
  72. package/docs/manual/02-tutorials/05-deploy-fleet.md +0 -283
  73. package/docs/manual/03-reference/01-dot-cli.md +0 -475
  74. package/docs/manual/03-reference/02-config-files.md +0 -265
  75. package/docs/manual/03-reference/03-environment.md +0 -124
  76. package/docs/manual/03-reference/04-templates.md +0 -190
  77. package/docs/manual/03-reference/05-feature-flags.md +0 -187
  78. package/docs/manual/04-cookbook/01-recipes.md +0 -285
  79. package/docs/manual/04-cookbook/02-troubleshooting.md +0 -351
  80. package/docs/manual/04-cookbook/03-faq.md +0 -175
  81. package/docs/manual/05-appendices/A-platform-matrix.md +0 -101
  82. package/docs/manual/05-appendices/B-security-checklist.md +0 -85
  83. package/docs/manual/05-appendices/C-glossary.md +0 -40
  84. package/docs/manual/05-appendices/D-bibliography.md +0 -58
  85. package/docs/manual/05-appendices/E-license.md +0 -38
  86. package/docs/manual/_toc.yml +0 -58
  87. package/docs/manual/command-index.md +0 -175
  88. package/docs/manual/concept-index.md +0 -170
  89. package/docs/manual/index.md +0 -66
  90. package/docs/migration/README.md +0 -81
  91. package/docs/migration/from-bare-git-repo.md +0 -156
  92. package/docs/migration/from-gnu-stow.md +0 -165
  93. package/docs/migration/from-plain-chezmoi.md +0 -148
  94. package/docs/migration/from-yadm.md +0 -187
  95. package/docs/operations/ARCHITECTURE_ROADMAP.md +0 -7
  96. package/docs/operations/ATTESTATION.md +0 -44
  97. package/docs/operations/CI_CADENCE.md +0 -107
  98. package/docs/operations/CI_COMPOSITES.md +0 -156
  99. package/docs/operations/COMPLETIONS.md +0 -123
  100. package/docs/operations/COVERAGE.md +0 -204
  101. package/docs/operations/DRIFT.md +0 -107
  102. package/docs/operations/HARD_AUDIT_2026.md +0 -631
  103. package/docs/operations/MAINTENANCE.md +0 -63
  104. package/docs/operations/MANIFEST.md +0 -127
  105. package/docs/operations/MIGRATION.md +0 -109
  106. package/docs/operations/OPERATIONS.md +0 -188
  107. package/docs/operations/PERFORMANCE.md +0 -133
  108. package/docs/operations/PERFORMANCE_BUDGETS.md +0 -196
  109. package/docs/operations/REGISTRY.md +0 -90
  110. package/docs/operations/RELEASE_PIPELINE.md +0 -128
  111. package/docs/operations/RELIABILITY.md +0 -122
  112. package/docs/operations/RFC_v0_2_503_reorganization.md +0 -280
  113. package/docs/operations/ROADMAP.md +0 -10
  114. package/docs/operations/ROADMAP_2026.md +0 -7
  115. package/docs/operations/ROADMAP_V0_2_503.md +0 -10
  116. package/docs/operations/TESTING.md +0 -216
  117. package/docs/operations/TRACEABILITY.md +0 -44
  118. package/docs/operations/TRUSTED_AGENT_WORKSTATION.md +0 -65
  119. package/docs/operations/VERSION_SYNC.md +0 -393
  120. package/docs/packaging.md +0 -222
  121. package/docs/reference/ALIASES.md +0 -131
  122. package/docs/reference/ALIASES_CHEATSHEET.md +0 -32
  123. package/docs/reference/ALIASES_DEPRECATIONS.md +0 -13
  124. package/docs/reference/FEATURE-MATRIX.md +0 -646
  125. package/docs/reference/FEATURES.md +0 -66
  126. package/docs/reference/FONTS.md +0 -112
  127. package/docs/reference/POWERSHELL_PARITY.md +0 -82
  128. package/docs/reference/PROFILES.md +0 -69
  129. package/docs/reference/SCREENSHOTS.md +0 -121
  130. package/docs/reference/SCRIPTS.md +0 -71
  131. package/docs/reference/SUPPORT_MATRIX.md +0 -80
  132. package/docs/reference/THEMES.md +0 -117
  133. package/docs/reference/TOOLS.md +0 -110
  134. package/docs/reference/UTILS.md +0 -243
  135. package/docs/registry.json +0 -6
  136. package/docs/schema/dot-env-v1.json +0 -110
  137. package/docs/schema/dot-registry-v1.json +0 -33
  138. package/docs/security/AI_ACT_COMPLIANCE.md +0 -94
  139. package/docs/security/AUDIT_BYPASS.md +0 -103
  140. package/docs/security/AUTOMATION_SECRETS.md +0 -26
  141. package/docs/security/CI_EGRESS_ALLOWLIST.md +0 -127
  142. package/docs/security/CI_PINNING.md +0 -129
  143. package/docs/security/COMMIT_SIGNING.md +0 -138
  144. package/docs/security/COMPLIANCE.md +0 -458
  145. package/docs/security/DEPS_DEV_EXCEPTIONS.md +0 -86
  146. package/docs/security/DISCLOSURE.md +0 -130
  147. package/docs/security/ENCRYPTION.md +0 -57
  148. package/docs/security/FMEA.md +0 -159
  149. package/docs/security/FUZZING.md +0 -209
  150. package/docs/security/HISTORY_FILTERING.md +0 -132
  151. package/docs/security/INCIDENT_RESPONSE.md +0 -579
  152. package/docs/security/INSTALL_VERIFICATION.md +0 -122
  153. package/docs/security/KEYS.md +0 -49
  154. package/docs/security/KEY_ROTATION.md +0 -303
  155. package/docs/security/MCP_POLICY.md +0 -78
  156. package/docs/security/POLICY_RELEASES.md +0 -37
  157. package/docs/security/README.md +0 -28
  158. package/docs/security/SCORECARD.md +0 -195
  159. package/docs/security/SECRETS.md +0 -158
  160. package/docs/security/SECURITY.md +0 -45
  161. package/docs/security/SECURITY_CHECKLIST.md +0 -55
  162. package/docs/security/SHELL_EXEMPTIONS.md +0 -145
  163. package/docs/security/SOUP_REGISTER.md +0 -36
  164. package/docs/security/THREAT_MODEL.md +0 -130
  165. package/docs/security/VERIFICATION_VALIDATION.md +0 -228
  166. package/docs/security/VERIFY_RELEASE.md +0 -201
  167. package/docs/security/security-pubkey.asc +0 -15
  168. package/docs/stylesheets/extra.css +0 -444
  169. package/docs/themes/README.md +0 -10
  170. package/docs/themes/VISUAL_INTEGRITY_REPORT.md +0 -30
  171. package/docs/themes/hero-shot.svg +0 -78
  172. package/scripts/README.md +0 -123
  173. package/scripts/ci/check-copyright-headers.sh +0 -8
  174. package/scripts/ci/check-shell-preamble.sh +0 -8
  175. package/scripts/ci/guard-gitleaks-checkout.sh +0 -8
  176. package/scripts/demo/record.sh +0 -43
  177. package/scripts/diagnostics/a2a-conformance.sh +0 -163
  178. package/scripts/diagnostics/alias-governance.sh +0 -165
  179. package/scripts/diagnostics/aliases-cheatsheet.sh +0 -74
  180. package/scripts/diagnostics/aliases-manifest.sh +0 -77
  181. package/scripts/diagnostics/attest-verify.sh +0 -147
  182. package/scripts/diagnostics/benchmark.sh +0 -408
  183. package/scripts/diagnostics/conflicts.sh +0 -73
  184. package/scripts/diagnostics/doctor-unified.sh +0 -43
  185. package/scripts/diagnostics/doctor.sh +0 -797
  186. package/scripts/diagnostics/drift-dashboard.sh +0 -203
  187. package/scripts/diagnostics/health.sh +0 -656
  188. package/scripts/diagnostics/history-analysis.sh +0 -86
  189. package/scripts/diagnostics/mcp-doctor.sh +0 -582
  190. package/scripts/diagnostics/perf.sh +0 -453
  191. package/scripts/diagnostics/scorecard.sh +0 -120
  192. package/scripts/diagnostics/secret-governance.sh +0 -65
  193. package/scripts/diagnostics/security-score.sh +0 -467
  194. package/scripts/diagnostics/smoke-test.sh +0 -88
  195. package/scripts/diagnostics/snapshot.sh +0 -90
  196. package/scripts/diagnostics/verify.sh +0 -108
  197. package/scripts/diagnostics/verify_state.sh +0 -73
  198. package/scripts/diagnostics/version-locks.sh +0 -94
  199. package/scripts/diagnostics/workstation-attestation.sh +0 -212
  200. package/scripts/dot/commands/agent.sh +0 -535
  201. package/scripts/dot/commands/agents.sh +0 -352
  202. package/scripts/dot/commands/ai.sh +0 -600
  203. package/scripts/dot/commands/aliases.sh +0 -277
  204. package/scripts/dot/commands/appearance.sh +0 -110
  205. package/scripts/dot/commands/completion.sh +0 -171
  206. package/scripts/dot/commands/core.sh +0 -217
  207. package/scripts/dot/commands/diagnostics.sh +0 -265
  208. package/scripts/dot/commands/env-emit.sh +0 -203
  209. package/scripts/dot/commands/fleet.sh +0 -711
  210. package/scripts/dot/commands/init.sh +0 -185
  211. package/scripts/dot/commands/lint.sh +0 -208
  212. package/scripts/dot/commands/manual.sh +0 -169
  213. package/scripts/dot/commands/meta.sh +0 -438
  214. package/scripts/dot/commands/patterns.sh +0 -55
  215. package/scripts/dot/commands/registry.sh +0 -455
  216. package/scripts/dot/commands/restore.sh +0 -232
  217. package/scripts/dot/commands/secrets.sh +0 -296
  218. package/scripts/dot/commands/security.sh +0 -102
  219. package/scripts/dot/commands/tools.sh +0 -570
  220. package/scripts/dot/data/alias-deprecations.tsv +0 -2
  221. package/scripts/dot/powershell/Dot.psm1 +0 -319
  222. package/scripts/fonts/install-nerd-fonts.sh +0 -75
  223. package/scripts/fonts/patch-fonts.sh +0 -36
  224. package/scripts/git-hooks/install.sh +0 -12
  225. package/scripts/git-hooks/pre-commit +0 -12
  226. package/scripts/git-hooks/pre-commit-audit.sh +0 -146
  227. package/scripts/git-hooks/pre-push +0 -105
  228. package/scripts/git-hooks/prepare-commit-msg +0 -29
  229. package/scripts/lib/secrets_provider.sh +0 -200
  230. package/scripts/nvim/headless-upgrade.lua +0 -81
  231. package/scripts/ops/ai-setup.sh +0 -71
  232. package/scripts/ops/bundle.sh +0 -104
  233. package/scripts/ops/chaos.sh +0 -50
  234. package/scripts/ops/chezmoi-apply.sh +0 -333
  235. package/scripts/ops/chezmoi-diff.sh +0 -16
  236. package/scripts/ops/chezmoi-remove.sh +0 -46
  237. package/scripts/ops/chezmoi-update.sh +0 -67
  238. package/scripts/ops/heal-chezmoi.sh +0 -87
  239. package/scripts/ops/heal-system.sh +0 -129
  240. package/scripts/ops/heal-tools.sh +0 -297
  241. package/scripts/ops/heal.sh +0 -223
  242. package/scripts/ops/post-apply-repair.sh +0 -107
  243. package/scripts/ops/prewarm.sh +0 -128
  244. package/scripts/ops/release.sh +0 -262
  245. package/scripts/ops/rollback.sh +0 -613
  246. package/scripts/ops/setup.sh +0 -138
  247. package/scripts/ops/teleport.sh +0 -34
  248. package/scripts/qa/check-feature-matrix.sh +0 -296
  249. package/scripts/qa/check-version-consistency.sh +0 -12
  250. package/scripts/qa/coverage-baseline.sh +0 -61
  251. package/scripts/qa/docs-coverage.sh +0 -118
  252. package/scripts/qa/examples-coverage.sh +0 -94
  253. package/scripts/qa/powershell-contract.ps1 +0 -95
  254. package/scripts/qa/reliability-audit.sh +0 -139
  255. package/scripts/qa/scorecard-snapshot.sh +0 -128
  256. package/scripts/qa/traceability-coverage.sh +0 -124
  257. package/scripts/qa/validate-examples.sh +0 -90
  258. package/scripts/qa/wsl-contract.sh +0 -12
  259. package/scripts/secrets/age-init.sh +0 -82
  260. package/scripts/secrets/create-secrets-file.sh +0 -46
  261. package/scripts/secrets/encrypt-ssh-key.sh +0 -44
  262. package/scripts/security/backup.sh +0 -58
  263. package/scripts/security/check-disclosure-key-expiry.sh +0 -111
  264. package/scripts/security/dns-doh.sh +0 -52
  265. package/scripts/security/encryption-check.sh +0 -55
  266. package/scripts/security/enforce-policies.sh +0 -552
  267. package/scripts/security/firewall.sh +0 -91
  268. package/scripts/security/lock-configs.sh +0 -67
  269. package/scripts/security/lock-screen.sh +0 -56
  270. package/scripts/security/manage-secrets.sh +0 -429
  271. package/scripts/security/ssh-cert.sh +0 -204
  272. package/scripts/security/telemetry-kill.sh +0 -51
  273. package/scripts/security/usb-safety.sh +0 -52
  274. package/scripts/theme/apply-gnome-theme.sh +0 -333
  275. package/scripts/theme/extract-heic-frames.sh +0 -115
  276. package/scripts/theme/extract-theme.py +0 -1020
  277. package/scripts/theme/install-boot-logo.sh +0 -63
  278. package/scripts/theme/install-catppuccin-themes.sh +0 -371
  279. package/scripts/theme/install-cursors.sh +0 -26
  280. package/scripts/theme/install-file-icons.sh +0 -27
  281. package/scripts/theme/install-grub-theme.sh +0 -62
  282. package/scripts/theme/install-lock-icon.sh +0 -31
  283. package/scripts/theme/merge-wallpaper.sh +0 -146
  284. package/scripts/theme/rebuild-themes.sh +0 -603
  285. package/scripts/theme/switch.sh +0 -476
  286. package/scripts/theme/wallpaper-rotate.sh +0 -137
  287. package/scripts/theme/wallpaper-sync.sh +0 -690
  288. package/scripts/tools/cmatrix.sh +0 -22
  289. package/scripts/tools/detect-collisions.py +0 -103
  290. package/scripts/tools/emoji-picker.sh +0 -49
  291. package/scripts/tools/figlet-banner.sh +0 -19
  292. package/scripts/tools/log-rotate.sh +0 -31
  293. package/scripts/tools/lolcat-wrap.sh +0 -20
  294. package/scripts/tools/pipes.sh +0 -49
  295. package/scripts/tuning/linux.sh +0 -186
  296. package/scripts/tuning/macos.sh +0 -56
  297. package/scripts/uninstall.sh +0 -86
  298. package/scripts/verify-release-versions +0 -156
  299. package/scripts/version-sync.sh +0 -714
  300. package/templates/chezmoi-data/geekom-a9.toml.example +0 -21
  301. package/templates/chezmoi-data/mac-m1.toml.example +0 -16
  302. package/templates/chezmoi-data/mac-t2-linux.toml.example +0 -21
  303. package/templates/chezmoi-data/surface-pro-7p.toml.example +0 -21
  304. package/templates/projects/go/.github/workflows/ci.yml +0 -31
  305. package/templates/projects/go/README.md +0 -7
  306. package/templates/projects/go/cmd/__PROJECT_NAME__/main.go +0 -8
  307. package/templates/projects/go/go.mod +0 -3
  308. package/templates/projects/go/go.sum +0 -0
  309. package/templates/projects/molecule/README.md +0 -7
  310. package/templates/projects/molecule/converge.yml +0 -7
  311. package/templates/projects/molecule/molecule.yml +0 -16
  312. package/templates/projects/node/.github/workflows/ci.yml +0 -30
  313. package/templates/projects/node/README.md +0 -7
  314. package/templates/projects/node/package-lock.json +0 -12
  315. package/templates/projects/node/package.json +0 -10
  316. package/templates/projects/node/src/index.js +0 -3
  317. package/templates/projects/packer/README.md +0 -15
  318. package/templates/projects/packer/main.pkr.hcl +0 -15
  319. package/templates/projects/python/.github/workflows/ci.yml +0 -34
  320. package/templates/projects/python/README.md +0 -7
  321. package/templates/projects/python/pyproject.toml +0 -25
  322. package/templates/projects/python/src/__PROJECT_NAME__/__init__.py +0 -2
  323. 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