Skip to content

fix(fastfetch): harden the status cache and sign the terminal args script - #657

Merged
DevSecNinja merged 1 commit into
mainfrom
fastfetch-status-hardening
Jul 30, 2026
Merged

fix(fastfetch): harden the status cache and sign the terminal args script#657
DevSecNinja merged 1 commit into
mainfrom
fastfetch-status-hardening

Conversation

@DevSecNinja

Copy link
Copy Markdown
Owner

Follow-ups on #654 from a rubber-duck review, plus the .ps1.tmpl signing question you raised.

1. The terminal-args script is now a plain, signable .ps1

You were right to question the .tmpl. sign-powershell.yml signs **.ps1 but explicitly excludes **.ps1.tmpl (a rendered template no longer matches its signature), so as merged that script could never be signed.

Neither template input turned out to be load-bearing:

  • The {{ if eq .chezmoi.os "windows" }} guard is redundant — .chezmoiignore already drops .chezmoiscripts/windows/** on non-Windows, and the plain signed scripts already in that folder (run_once_setup-windows-terminal.ps1, run_once_install-wsl.ps1, …) carry no such guard.
  • {{ .chezmoi.sourceDir }} is replaceable with $env:CHEZMOI_SOURCE_DIR, which I confirmed chezmoi exports to every script it runs:
    CHEZMOI_SOURCE_DIR=[C:/Users/JEANPA~1/AppData/Local/Temp/cz-src-test]
    CHEZMOI=[1]
    Note it arrives with forward slashes and sometimes in 8.3 form, so the path is composed with Join-Path rather than concatenated, and falls back to the applied tree (~/.config/powershell/modules/DotfilesHelpers) when the script is run by hand.

I also verified signing survives execution: chezmoi copies the script to a temp file before running it ($PSCommandPath = …\Temp\2548640419.probe.ps1), and Authenticode status is unchanged by a copy to a different path/name.

The sibling templates deliberately keep their .tmplset-windows-terminal-copilot-profile.ps1.tmpl genuinely embeds .copilotSshHost. The default-profile and font ones could be converted in a focused follow-up if you want; I left them alone rather than churn unrelated files here.

2. Two cache bugs, both only visible with two shells starting at once

  • Orphan cleanup was too broad. It deleted every non-.src file lacking a matching source — including another writer's in-flight <name>.tmp, which would then break that process's Move-Item. It now skips .tmp files.
  • Both writers used a fixed <name>.tmp. Two shells rendering the same section raced for one path. Temp names are now unique (GetRandomFileName) and cleaned up in a finally.

3. fastfetch exiting ≠ fastfetch succeeding

WaitForExit() returning true only means the process ended. A non-zero exit (a bad config, say) paints nothing, but still suppressed the welcome lines — so a broken fastfetch looked like a hung shell. Now only ExitCode -eq 0 counts as a rendered banner.

Testing

  • Pester: 773 passed, 4 failed — the same pre-existing signing-cert tests that fail on main.
  • bats 20/20; shellcheck + shfmt clean; PSScriptAnalyzer clean; chezmoi apply --dry-run clean.
  • New regression tests cover all three fixes: in-flight .tmp survival, unique temp names, and the non-zero-exit case.
  • The converted script was executed exactly the way chezmoi invokes it (pwsh -NoLogo -NoProfile -File … with CHEZMOI_SOURCE_DIR set) — it applied pwsh.exe -NoLogo -NoProfileLoadTime to the real Windows Terminal profile and is idempotent on re-run.

Rebased onto current main (post-#654 and its signing commit); the stale signature blocks on the three edited files are left in place for the workflow to replace, per the repo convention.

…ript

Review follow-ups on #654.

The Windows Terminal args script was a .ps1.tmpl, which the signing
workflow deliberately skips (a rendered template no longer matches its
signature). Neither template input was load-bearing: the OS guard is
redundant because .chezmoiignore already drops .chezmoiscripts/windows
off non-Windows, and the module path can come from CHEZMOI_SOURCE_DIR,
which chezmoi exports to every script it runs. Make it a plain, signable
.ps1. That variable arrives with forward slashes and sometimes in 8.3
form, so the path is composed with Join-Path rather than concatenated,
and it falls back to the applied tree when run by hand. The sibling
templates keep their .tmpl: the copilot-profile one genuinely embeds
.copilotSshHost.

Two cache bugs, both only visible with two shells starting at once:

  * Orphan cleanup deleted every non-.src file without a matching
    source, which could remove another writer's in-flight temp file and
    break its Move-Item. It now skips .tmp files.
  * Both writers used a fixed '<name>.tmp', so simultaneous renders of
    the same section raced for one path. Temp names are now unique and
    cleaned up on failure.

Finally, fastfetch exiting quickly is not the same as fastfetch
succeeding: a non-zero exit (a bad config, say) paints nothing, so it no
longer counts as a rendered banner and the welcome lines stay as the one
sign of life.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6ff40b19-afbf-48c8-9b80-233b13652222
@DevSecNinja
DevSecNinja merged commit e9cc781 into main Jul 30, 2026
16 checks passed
@DevSecNinja
DevSecNinja deleted the fastfetch-status-hardening branch July 30, 2026 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant