Security hardening, robustness fixes, and Windows support - #6
Open
abinashstack wants to merge 8 commits into
Open
abinashstack wants to merge 8 commits into
abinashstack wants to merge 8 commits into
Conversation
Security: - menubar: escape asset_type (was printed raw before SwiftBar attributes, allowing an upstream value to inject a clickable bash= action); sbEscape now also strips newlines/control chars so upstream text can't add lines. - oauth: never echo token/registration response bodies in errors (a malformed 200 leaked access/refresh tokens into agent.log); cap reads. - oauth: callbacks without our state are ignored instead of aborting the login, so any web page can't cancel an in-progress login; constant-time state compare; ReadHeaderTimeout on the callback server. - menubar install: validate refresh_interval (used in the plugin filename) and shell-quote the binary path in the generated script. - invoke security/osascript/launchctl/open/defaults/tail by absolute path. - CLI output strips control characters from server-supplied names. - CI: least-privilege permissions, SHA-pinned actions, no persisted credentials, pinned govulncheck, and run go test. Robustness: - serialise token refreshes across processes with a lock file and re-read Keychain after acquiring it, so the poller and menu bar can't burn a rotated refresh token; keep refreshed tokens in memory if saving fails. - keychain save uses -U only (no delete-then-add window). - MCP client refreshes and retries once on HTTP 401. - due-date alerts compare local calendar dates (were off by one: "due tomorrow" showed as 0d and due-today never alerted). - launchd schedule converts weekday as well as time to local zone and uses the current DST offset; cadence now matches the documented 10 minutes. - watchlist dedupe no longer collapses ticker-only entries; prune stale debounce keys; rune-safe truncation/padding. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TT1VrXR9CkmWFfRQN1H87J
Extract the OAuth redirect handler into callbackHandler so it can be exercised directly, and cover: forged ?error=/?state= requests being ignored without ending the login, HTML-escaping of the error param, and exclusivity of the refresh lock file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TT1VrXR9CkmWFfRQN1H87J
Each macOS-only piece gets a Windows counterpart behind build tags: - Tokens: DPAPI-encrypted file (CryptProtectData, per-user, with app entropy) in %AppData%\indmoney-watch instead of the Keychain. Credential Manager's 2.5 KB blob limit is too small for JWTs. - Notifications: Windows toast via PowerShell/WinRT, text passed through env vars and XML-escaped, script passed with -EncodedCommand. - Background poller: `indw start`/`stop` register a Task Scheduler task from an XML definition (local-time weekdays like launchd, runs hidden under conhost --headless, on battery, no overlap, 5 min limit). - Refresh lock: LockFileEx instead of flock. - Browser: rundll32 url.dll instead of open(1). - Config dir: %AppData%\indmoney-watch. Cross-platform changes: - `indw logs [-f]` is implemented in Go instead of shelling out to tail. - `run-once --log` appends to agent.log with simple 5 MB rotation (Task Scheduler can't redirect output the way launchd does). - `menubar install` errors clearly off macOS (SwiftBar is macOS-only). - notify.MacBanner renamed to notify.Banner. CI now runs build/vet/test on windows-latest as well as macos-latest; the Windows tests cover the DPAPI round trip, toast script parsing and escaping, and registering, reading back and deleting a real scheduled task. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TT1VrXR9CkmWFfRQN1H87J
On a fresh Windows runner, Windows PowerShell writes "Preparing modules for first use" progress records to stderr as CLIXML when output is redirected. The toast tests compared combined output to "ok" and failed even though the checks passed. Set $ProgressPreference in the toast script (keeps real error messages clean too) and have the tests read stdout only, reporting stderr on failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TT1VrXR9CkmWFfRQN1H87J
govulncheck in CI flags six reachable std-lib issues in go1.26.4 (GO-2026-6218 net/url, GO-2026-6090 and GO-2026-5856 crypto/tls, GO-2026-6089 and GO-2026-5026 net/http, GO-2026-5972 encoding/asn1), all fixed in go1.26.6. Same remedy as 42a3508. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TT1VrXR9CkmWFfRQN1H87J
Owner
Author
|
CI on
Generated by Claude Code |
govulncheck failures appear without any code change when new Go vulnerabilities are published (as happened with go1.26.4), but CI only ran on push/PR, so main could sit red unnoticed. A weekly schedule surfaces that automatically; workflow_dispatch allows on-demand runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TT1VrXR9CkmWFfRQN1H87J
Lets a scan be started on demand (Actions tab or API), e.g. after the workflow is re-enabled following GitHub's inactivity auto-disable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TT1VrXR9CkmWFfRQN1H87J
CodeQL (go/unhandled-writable-file-close) flagged the lock file handle as writable and closed without error handling. Nothing is ever written to it and flock/LockFileEx only need read access, so open it O_RDONLY. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TT1VrXR9CkmWFfRQN1H87J
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three parts: fixes from a security and correctness audit of the repo, tests for those fixes, and Windows support.
1. Security
asset_typefrom INDmoney unescaped, before the| color=…attributes. An upstream value containing|or a newline could add a clickablebash=action. It's escaped now, andsbEscapealso strips newlines and control characters.200token response would writeaccess_tokenandrefresh_tokentoagent.log. Errors now report only the OAuth error code, and response reads are capped in size.indw loginby sending the browser to127.0.0.1:47823/callback?error=…. Callbacks without the correctstateare now ignored.stateis compared in constant time, and the callback server has aReadHeaderTimeout.menubar.refresh_intervalis used in the plugin filename, so it's now validated (blocks path traversal). The binary path is shell-quoted in the generated script.$PATH.go.modis raised from 1.26.4 to 1.26.6, which fixes six standard-library vulnerabilities thatgovulncheckreports as reachable (GO-2026-6218, -6090, -6089, -5972, -5856, -5026).permissions: contents: read, actions pinned to commit SHAs,persist-credentials: false,govulncheckpinned, andgo testadded.2. Robustness
-Uonly, so there's no delete-then-add window.state.json, and truncation and padding count characters rather than bytes.3. Windows support
Each macOS-only piece gets a Windows counterpart, selected by build tags:
%AppData%\indmoney-watch(Credential Manager's 2.5 KB limit is too small for JWTs)osascript-EncodedCommandindw start/stopconhost --headless, on battery, never overlapping, 5 min limitflockLockFileExopenrundll32 url.dllmenubar installexplains whyCross-platform changes:
indw logs [-f]is implemented in Go instead of callingtail.run-once --logwrites toagent.logwith 5 MB rotation. Task Scheduler can't redirect output the way launchd does, so the Windows task uses this flag.notify.MacBanneris renamed tonotify.Banner.Testing
macos-latestandwindows-latest: build, vet, tests, andgovulncheck.go vetandstaticcheckare clean forwindows,darwinandlinux, andgo test -race ./...passes.windows-latest:HOME,run-once --logwrites toagent.logandindw logsreads it back. The installer rejects a traversal interval. A binary path containing',$(…)and backticks doesn't inject anything.After merging, re-run
indw start(andindw menubar installon macOS) to pick up the new schedule and plugin script.🤖 Generated with Claude Code
https://claude.ai/code/session_01TT1VrXR9CkmWFfRQN1H87J