• v0.1.1.0 7c3a76e8c1

    brian released this 2026-05-09 14:54:58 -07:00 | 104 commits to main since this release

    Added

    • Optional git remote URL in .secrets-store. Add a second whitespace-separated token after the store name to give teammates a copy-paste-ready clone command:
      work git@github.com:acme/work-secrets.git
      
      When a teammate clones a project bound to a store they don't have on their machine yet, the directed error now fills in the actual git clone <url> <path> line — they no longer have to ask the original setter for the URL. The URL is optional; existing single-token .secrets-store files continue to work and show the <their-store-remote> placeholder as before. (EGB-282)

    Security

    • Hardened .secrets-store URL parser against copy-paste shell injection. The URL is rendered into a git clone line that a teammate is likely to copy-paste from the directed error. Without sanitization, a malicious .secrets-store containing work evil.git;rm -rf ~ would render verbatim and execute rm -rf ~ on paste. The parser now rejects URLs containing shell metacharacters (;&|<>$\(){}*?!"'\`), control characters (including ANSI escape sequences that could spoof terminal output), and embedded whitespace. Rejected URLs are dropped with a stderr warning; the directed error falls back to the safe placeholder. Found by adversarial review during /ship; verified with regression tests for every named attack vector.
    • Switched URL parsing from set -- $line to read -r spec rest. The previous form word-split and glob-expanded — work * from a populated directory would have leaked filenames into the URL field. The new form preserves the rest of the line verbatim into a single variable, so glob characters and internal whitespace are noticed by the sanitizer instead of silently expanded.

    Tests

    • 72 → 80 (+8). New coverage: backward-compat single-token form, two-token URL form (SSH, HTTPS, ~/-prefixed), comment-and-URL form, copy-paste injection (rm -rf payload), backtick injection, $() injection, ANSI escape injection, multi-token URL, glob-character URL, and a positive test asserting standard git URL chars (-, +, _, :, /, @, .) round-trip unchanged.
    Downloads
  • v0.1.0.1 1bb729f73b

    brian released this 2026-05-09 14:33:41 -07:00 | 105 commits to main since this release

    Fixed

    • secrets which now prints the full path of the .secrets-store file that won resolution. Before this fix, _find_secrets_store_file set _LAST_FOUND_AT inside a $(...) subshell, so the parent shell never saw it; the source line read .secrets-store file () with empty parens. The function now returns a tab-separated <dir>\t<source-file-path> tuple that resolve_store splits in the parent shell. Caught by the /land-and-deploy post-merge fresh-clone check; the existing test was too lenient and matched the truncated form. Test tightened to assert the full path appears in the parenthetical.
    Downloads
  • v0.1.0.0 7e6ddf3a12

    brian released this 2026-05-09 14:30:50 -07:00 | 107 commits to main since this release

    Added

    • Multiple stores per user. Run secrets push and secrets pull against any encrypted store directory you choose, not just ~/.secrets/. Use cases: keep work secrets isolated from personal, run a separate store per client, or onboard a teammate to one project without giving them every other project's keys.
    • .secrets-store file for per-project bindings. Drop a one-line file at the project root (e.g. echo work > .secrets-store && git add .secrets-store && git commit) and every machine that clones the project automatically uses ~/.secrets-work/ for that repo. No env var to remember, no per-machine setup.
    • --store <dir> flag for one-shot overrides on any subcommand. secrets --store ~/.secrets-clientA pull myapp works without touching files. Bare names like --store work expand to $HOME/.secrets-work. --store default is sugar for ~/.secrets.
    • secrets which prints the active store path and which rule chose it (flag, .secrets-store file, env var, or default). Aliases: secrets where, secrets status.
    • Directed errors for teammate onboarding. When .secrets-store resolves to an uninitialized store or one missing key.txt, the error message names both recovery paths: git clone <remote> to join an existing store, or secrets --store <name> init to start fresh.
    • Active-store echo. secrets push and secrets pull print ==> Store: <path> (from <source>) whenever a non-default store is active, so wrong-store mistakes surface immediately.

    Changed

    • secrets list now hints at secrets which when a non-default store is active.
    • cmd_help documents the four-rule resolution order (--store > .secrets-store file > SECRETS_DIR > default).
    • Error messages for missing init / missing key file are now context-aware: they distinguish between "default store on a fresh machine" and "non-default store referenced by .secrets-store."

    Security

    • Path expansion in .secrets-store is literal-only. No eval, no $VAR interpolation, no $(...) execution. A committed .secrets-store containing $(rm -rf ~) reads as plain text, not as a command.
    • Walk-up bounded by $HOME. secrets never reads $HOME/.secrets-store, never walks past $HOME to /, and never follows symlinked .secrets-store files. Symlinks (potential supply-chain attack via committed link to ~/.aws/credentials or similar) are ignored.
    • KEY_FILE re-derives after --store switches stores. Previously, calling secrets pull --store other would have decrypted ciphertext from the new store using the default store's key. Now secrets updates both SECRETS_DIR and KEY_FILE together inside resolve_store().
    • secrets run cleanup survives paths with apostrophes. The EXIT trap is now a named function rather than a string-interpolated command, so projects at e.g. /Users/you/Mom's Mac/code still get plaintext cleared after the wrapped command exits.
    • Test isolation: the bats suite now sets HOME=$TEST_TMPDIR so .secrets-store walk-up cannot wander into the developer's real home directory.
    • --store flag value validation. Empty values (--store=) and flag-shaped values (--store --workspaces) are rejected with directed errors instead of silently mapping to ~/.secrets--something.
    • Unset HOME is detected with a directed error before the script tries to expand it. Helps cron, sudo without -H, and minimal CI runners.

    Tests

    • 37 → 66 tests. New coverage: store resolution rules and precedence, walk-up boundaries, command-injection prevention, key-file re-derivation across stores, teammate-onboarding error path, monorepo workspace binding, F1–F5 adversarial regressions.
    Downloads