* chore: remove cargo-dist, use native fetch for npm installer
* fix: harden npm scripts — spawnSync, signal handling, binary-exists check
* fix: address PR review comments
- Add null body guard for fetch response
- Fix PowerShell Expand-Archive path quoting vulnerability
- Sanitize error output to prevent ANSI escape injection
- Add proxy support limitation note
- Fix upgrade bug: use .version marker so npm update downloads new binary
- Downgrade changeset from minor to patch (chore, not feature)
- Update AGENTS.md: remove stale cargo-dist reference from labels
* fix: use flat archives for consistent tar/zip extraction
Both tar.gz and zip archives now contain files at root (no nested
directory). Removes --strip-components 1 from install.js since it is
no longer needed. This makes extraction consistent across platforms.
---------
Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
Replace machine-local chrono::Local and UTC epoch math with the
authenticated user's Google account timezone (Calendar Settings API).
- Add chrono-tz dependency for IANA timezone parsing
- New src/timezone.rs: resolve timezone with priority:
--timezone flag > 24h cache > Calendar API > local fallback
- calendar.rs: add --timezone/--tz flag to +agenda
- workflows.rs: fix +standup-report, +weekly-digest, +meeting-prep
- auth_commands.rs: invalidate timezone cache on logout
- Update README.md and AGENTS.md with timezone docs
Supersedes #369 and #462.
Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
* feat(credential_store): add GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND env var
Add gogcli-style backend selection for encryption key storage:
- keyring (default): OS keyring with file fallback
- file: .encryption_key file only (Docker/CI/headless)
Never delete .encryption_key — it always serves as a durable fallback
for environments where the keyring is ephemeral. When generating new
keys with backend=keyring, save to both keyring and file.
Extracts KeyringProvider trait + resolve_key() for testability.
25 tests covering both backends and all edge cases.
Fixes#344
* chore: regenerate skills [skip ci]
* fix(credential_store): use O_EXCL for race-safe key generation
Use create_new(true) (O_EXCL on Unix, CREATE_NEW on Windows) when
generating a new encryption key file. If another process wins the
race, read their key instead. Platform-independent.
* fix(credential_store): sync winner's key into keyring after file race
When two processes race to create the encryption key file, the loser
now syncs the winner's key back into the keyring. Without this, the
keyring and file could permanently diverge.
* test(credential_store): add 9 tests covering file exclusion, env parsing, and race paths
- save_key_file_exclusive: creates new file, rejects existing
- save_key_file: overwrites existing
- ensure_key_dir: creates nested dirs
- KeyringBackend: file/FILE/invalid parsing
- Race loser: syncs winner key to keyring
- Race loser: corrupt file gets overwritten
* feat(credential_store): security and robustness hardening
1. Warn on unrecognized KEYRING_BACKEND values instead of silent default
2. fsync after key file writes for crash durability
3. Zeroize decoded key material from heap after copy
4. Warn if key file has overly permissive Unix permissions (mode & 077)
5. Log which keyring backend was selected to stderr
6. Expose keyring_backend in 'gws auth status' JSON output
---------
Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* feat: multi-account support with --account flag, per-account credential storage
- Add --account global flag and GOOGLE_WORKSPACE_CLI_ACCOUNT env var
- Per-account encrypted credential files (credentials.<b64-email>.enc)
- Per-account token cache (token_cache.<b64-email>.json)
- accounts.json registry with default account tracking
- New auth subcommands: list, default, per-account logout
- login_hint in OAuth URL for account pre-selection
- Email validation via Google userinfo after OAuth flow
- 12 new unit tests (380 total)
BREAKING CHANGE: Existing users must run 'gws auth login' again.
Credential storage changed from single credentials.enc to per-account files.
* refactor: Improve error handling for file system operations, rename `GWS_ACCOUNT` to `GOOGLE_WORKSPACE_CLI_ACCOUNT`, and refine service account token cache path generation.
* fix: clean up per-account token caches on logout
---------
Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
* feat: add `gws version` bare subcommand
* refactor: extract help and version flag checks into dedicated functions and add test coverage guidance to AGENTS.md
---------
Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
* refactor: replace manual urlencoded() with reqwest .query() builder
Remove duplicate hand-rolled urlencoded() functions from workflows.rs
and calendar.rs. All query parameters are now passed via reqwest's
.query() API, which handles percent-encoding correctly and completely.
* fix: percent-encode path parameters to prevent path traversal
Use percent_encoding::utf8_percent_encode for calendar_id, cal.id,
message_id, and file_id before interpolating into URL path segments.
Addresses code review feedback on security regression.
* fix: add shared URL safety helpers for path params
Add encode_path_segment() for single-segment IDs and
validate_resource_name() for multi-segment resource names.
encode_path_segment: percent-encodes all non-alphanumeric chars,
used for calendar IDs, file IDs, and message IDs.
validate_resource_name: rejects path traversal (..) and control
chars while preserving intentional / structure, used for Chat
space names, task list IDs, and subscription names. Returns clear
error messages for LLM callers.
* test: add AI edge case tests for URL safety helpers
Cover query/fragment injection, double-encoding, unicode, spaces,
path traversal via encoding, control chars (CR/tab), and clear
error message assertions for LLM callers.
* fix: warn on stderr when API calls fail silently
- Daily briefing calendar events fetch
- Daily briefing tasks fetch
- Daily summary calendar events fetch
- Daily summary unread email count fetch
Addresses PR review feedback about confusing silent failures,
especially for LLM callers that cannot see visual cues.
* fix: harden input validation for AI/LLM callers
- Add src/validate.rs with validate_safe_output_dir, validate_msg_format,
and validate_safe_dir_path helpers
- Validate --output-dir against path traversal in gmail +watch and
events +subscribe
- Validate --msg-format against allowlist in gmail +watch
- Validate --dir against path traversal in script +push
- Add clap value_parser constraint for --msg-format
- Document input validation patterns in AGENTS.md
Closes#23
* chore: add changesets for PR #21 commits
* test: add comprehensive test coverage for input validation handlers
* docs: document input validation and URL safety patterns in AGENTS.md and CONTRIBUTING.md
* fix: address PR review comments — reject ?/# in resource names, validate subscription arg, remove redundant validate_msg_format
* fix: store validated PathBuf, remove dead code, delete duplicate SubscribeConfig
Addresses review comments:
- Store validated PathBuf from validate_safe_output_dir instead of
discarding it (output_dir is now Option<PathBuf>)
- Remove duplicate SubscribeConfig from events/mod.rs
- Delete unused validate_msg_format (clap value_parser handles this)
- Remove all #[allow(dead_code)] annotations
* fix: per-segment traversal check in validate_resource_name, fix docs
* fix: harden security validation and deduplicate logic
---------
Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>