/
← Voltar ao feed
low

hate_crack maintainer's git hook mishap pushes private notes to public repo; project splits config and hardens its release process

1 fonte(s) · 1 atualização(ões) · publicado 15/10/2026 01:00 · coletado há 4 dias

TrustedSec's Justin Bollinger describes how a prek pre-push hook corrupted his git index on July 30. The resulting commit deleted 85,280 lines across about 165 files and staged gitignored, local-only paths (CLAUDE.md, .claude/, docs/plans/, docs/superpowers/). The commit reached the public trustedsec/hate_crack remote before it was force-pushed away. The maintainer says the exposed content turned out to be clean. He also found that an earlier history purge had not unpublished the files, because GitHub's immutable refs/pull/N/head refs kept them fetchable from PRs #78 through #137. The post is a hardening retrospective covering new commit-time guards, a breaking config split into config.json and .env, an offsite-LLM egress control, and a rebuilt release-tagging scheme.

Detalhes técnicos

  • Root cause: the pre-push hook's stash-and-restore cycle returned a corrupted index. The only secret-scanning gate, detect-private-key, did not cover these paths, CI had no checks, and git add -f bypasses .gitignore.
  • New guards (pre-commit): check-publication-boundary.sh reads the staged changeset via git diff --cached and refuses forbidden paths with no override. check-mass-deletion.sh aborts commits that delete more than 50 tracked files; HATE_CRACK_ALLOW_MASS_DELETE=1 is the escape hatch. Tests run both scripts as real hooks against throwaway repos.
  • Publication policy change (2.33.0): CLAUDE.md and .claude/ are now published deliberately. .claude/plans/, .claude/specs/, docs/plans/, docs/superpowers/ and .claude/settings.local.json remain refused. tests/test_agent_instruction_integrity.py pins hooks, scripts, subagents and skills under .claude/ to an approved set. It refuses a permissions key in shared settings and any network verb in .claude/ scripts, because a PR could otherwise alter files a maintainer's coding agent reads as instructions or executes as code.
  • Config split (breaking): config.json keeps operational settings (41 keys). A new .env holds 16 integration keys (Hashview, Hashmob, Pushover, Ollama/LLM). Each key has one home file; the process environment is an ephemeral override. A one-shot migration creates .env with 0600 permissions and backs up the original to config.json.pre-split.bak. Dangling config symlinks are now fatal. LLM_API_KEY is not marked as a secret and is not redacted from error messages.
  • LLM egress control: OLLAMA_NO_CLOUD=1 refuses -cloud models and, through llm.is_offsite_url(), any offsite destination for the Ollama, vLLM and OpenAI-compatible backends. It fails closed on unresolvable hosts, treats mixed private/global DNS results as offsite, treats CGNAT (100.64.0.0/10) as local, and treats 6to4 as offsite. Prompts carry recovered plaintexts and client details. OLLAMA_HOST previously never worked from .env because dotenv_values() does not export to the environment, so runs could silently talk to localhost.
  • CLI and env hygiene: eight flags became schema-backed defaults using BooleanOptionalAction. DEBUG and UPDATE_CHANNEL were renamed HATE_CRACK_DEBUG and HATE_CRACK_UPDATE_CHANNEL, since an ambient DEBUG=1 would write cracked plaintexts to disk.
  • Release engineering: automatic Conventional-Commit tagging produced eight releases in one day (July 28). It was replaced with PEP 440 release candidates aimed one version forward (e.g. v2.33.1rc1) on nightly-dev, promoted to final on merge to main. The policy lives in tools/next_version.py. Majors are never bumped automatically. workflow_run must pin head_sha, and the nightly workflow must be byte-identical on both branches. CI runs ruff, ty, pytest, bandit (baseline) and pip-audit, with 3,246 tests across 136 files.

Remediação

  • Enforce publication boundaries at commit time with pre-commit hooks rather than .gitignore or pre-push checks, and never commit sensitive files to a repository that accepts PRs, since PR refs retain content after a history rewrite.
  • Gate release tagging on CI passing and pin workflow_run checkouts to head_sha.
  • Set OLLAMA_NO_CLOUD=1 when engagement terms forbid data leaving the host.
  • Namespace environment variables (e.g. HATE_CRACK_*).
  • Move integration keys from config.json to .env after upgrading.
  • Recover from the index corruption with git config core.bare false, git reset HEAD, then git status --short.

Linha do tempo

  1. trustedsec.com ↗ · pub. 15/10/2026 01:00 · coletado 19/10 17:57 primeiro registro

    hate_crack maintainer's git hook mishap pushes private notes to public repo; project splits config and hardens its release process

    ler a análise desta fonte

    TrustedSec's Justin Bollinger describes how a prek pre-push hook corrupted his git index on July 30. The resulting commit deleted 85,280 lines across about 165 files and staged gitignored, local-only paths (CLAUDE.md, .claude/, docs/plans/, docs/superpowers/). The commit reached the public trustedsec/hate_crack remote before it was force-pushed away. The maintainer says the exposed content turned out to be clean. He also found that an earlier history purge had not unpublished the files, because GitHub's immutable refs/pull/N/head refs kept them fetchable from PRs #78 through #137. The post is a hardening retrospective covering new commit-time guards, a breaking config split into config.json and .env, an offsite-LLM egress control, and a rebuilt release-tagging scheme.

    Technical Details

    • Root cause: the pre-push hook's stash-and-restore cycle returned a corrupted index. The only secret-scanning gate, detect-private-key, did not cover these paths, CI had no checks, and git add -f bypasses .gitignore.
    • New guards (pre-commit): check-publication-boundary.sh reads the staged changeset via git diff --cached and refuses forbidden paths with no override. check-mass-deletion.sh aborts commits that delete more than 50 tracked files; HATE_CRACK_ALLOW_MASS_DELETE=1 is the escape hatch. Tests run both scripts as real hooks against throwaway repos.
    • Publication policy change (2.33.0): CLAUDE.md and .claude/ are now published deliberately. .claude/plans/, .claude/specs/, docs/plans/, docs/superpowers/ and .claude/settings.local.json remain refused. tests/test_agent_instruction_integrity.py pins hooks, scripts, subagents and skills under .claude/ to an approved set. It refuses a permissions key in shared settings and any network verb in .claude/ scripts, because a PR could otherwise alter files a maintainer's coding agent reads as instructions or executes as code.
    • Config split (breaking): config.json keeps operational settings (41 keys). A new .env holds 16 integration keys (Hashview, Hashmob, Pushover, Ollama/LLM). Each key has one home file; the process environment is an ephemeral override. A one-shot migration creates .env with 0600 permissions and backs up the original to config.json.pre-split.bak. Dangling config symlinks are now fatal. LLM_API_KEY is not marked as a secret and is not redacted from error messages.
    • LLM egress control: OLLAMA_NO_CLOUD=1 refuses -cloud models and, through llm.is_offsite_url(), any offsite destination for the Ollama, vLLM and OpenAI-compatible backends. It fails closed on unresolvable hosts, treats mixed private/global DNS results as offsite, treats CGNAT (100.64.0.0/10) as local, and treats 6to4 as offsite. Prompts carry recovered plaintexts and client details. OLLAMA_HOST previously never worked from .env because dotenv_values() does not export to the environment, so runs could silently talk to localhost.
    • CLI and env hygiene: eight flags became schema-backed defaults using BooleanOptionalAction. DEBUG and UPDATE_CHANNEL were renamed HATE_CRACK_DEBUG and HATE_CRACK_UPDATE_CHANNEL, since an ambient DEBUG=1 would write cracked plaintexts to disk.
    • Release engineering: automatic Conventional-Commit tagging produced eight releases in one day (July 28). It was replaced with PEP 440 release candidates aimed one version forward (e.g. v2.33.1rc1) on nightly-dev, promoted to final on merge to main. The policy lives in tools/next_version.py. Majors are never bumped automatically. workflow_run must pin head_sha, and the nightly workflow must be byte-identical on both branches. CI runs ruff, ty, pytest, bandit (baseline) and pip-audit, with 3,246 tests across 136 files.

    Remediation

    • Enforce publication boundaries at commit time with pre-commit hooks rather than .gitignore or pre-push checks, and never commit sensitive files to a repository that accepts PRs, since PR refs retain content after a history rewrite.
    • Gate release tagging on CI passing and pin workflow_run checkouts to head_sha.
    • Set OLLAMA_NO_CLOUD=1 when engagement terms forbid data leaving the host.
    • Namespace environment variables (e.g. HATE_CRACK_*).
    • Move integration keys from config.json to .env after upgrading.
    • Recover from the index corruption with git config core.bare false, git reset HEAD, then git status --short.