chore: bump version to 0.4.0
CHANGELOG.md updated with the audit-driven hardening pass spanning 13 findings across cmd/ and internal/. Adds CLAUDE.md documenting dev workflow, codex review pattern, release process, and homebrew tap update steps.
This commit is contained in:
parent
373c769d2c
commit
0069198ca6
3 changed files with 268 additions and 1 deletions
101
CHANGELOG.md
101
CHANGELOG.md
|
|
@ -5,6 +5,107 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.4.0] - 2026-05-02
|
||||
|
||||
Audit-driven hardening pass. Three reviewers (Codex + two Claude agents
|
||||
with non-overlapping focuses) found 13 issues across cmd/ and internal/;
|
||||
this release ships fixes for all 13.
|
||||
|
||||
### BREAKING
|
||||
|
||||
- `--json=fields` syntax removed. The flag was a string with
|
||||
`NoOptDefVal=" "` sentinel — `--json` alone meant "everything",
|
||||
`--json=fields` projected. That produced `--json string[=" "]` in
|
||||
`--help` and required a literal `=` because `--json fields` was parsed
|
||||
as the bare flag plus a positional. **Migration**: `--json=fields` →
|
||||
`--json-fields fields`. Bare `--json` still means "all fields as JSON".
|
||||
`--json` and `--json-fields` are mutually exclusive; `--jq` composes
|
||||
with either.
|
||||
|
||||
### Added
|
||||
|
||||
- `fj api --json` / `--json-fields` / `--jq` — projection and jq filtering
|
||||
for raw API responses. Routes through the same `addJSONFlags` helpers
|
||||
as the other list commands. Closes the inconsistency where `fj api`
|
||||
was the only command returning raw JSON without these knobs.
|
||||
- `fj api --paginate` — follows RFC 5988 `Link: rel="next"` headers and
|
||||
concatenates JSON array pages, gh-compatible. Validates same-origin
|
||||
before forwarding the bearer token to the next URL.
|
||||
- `cmd/paginate.go` — generic `paginateGitea[T any]` helper. Applied to
|
||||
`repo list`, `pr list`, `issue list`. Previously only `release list`
|
||||
walked pages; the others passed `PageSize: limit` directly to the
|
||||
gitea SDK, which silently caps PageSize at 50, so `--limit > 50` was
|
||||
truncated without warning.
|
||||
- `CLAUDE.md` — guide for Claude Code sessions: layout, codex review
|
||||
pattern, release process, homebrew tap update steps.
|
||||
|
||||
### Changed
|
||||
|
||||
- `--json` flag rebuilt as a plain `Bool`. `--json-fields` keeps
|
||||
comma-separated projection. Both registered via `addJSONFlags` and
|
||||
marked `MutuallyExclusive`.
|
||||
- `cmd/actions.go` — `run` and `workflow` subtrees converted from
|
||||
package-level `var`s to factory functions (`newRunCmd`,
|
||||
`newWorkflowCmd`, ...). `cmd/aliases.go` shrank from 142 → 17 lines
|
||||
and now calls those same factories with a `parentLabel` parameter that
|
||||
disambiguates the alias variant. Result: `diff` of `fj run list
|
||||
--help` flags vs `fj actions run list --help` flags is now empty.
|
||||
Drift between the two paths is structurally impossible.
|
||||
- `fj api` now uses `internal/api.SharedHTTPClient` (30s timeout, pooled
|
||||
connections) instead of a zero-value `&http.Client{}` with no timeout.
|
||||
A hung Forgejo no longer pins the CLI indefinitely.
|
||||
- `fj api` response body bounded by `io.LimitReader` at 64 MB to prevent
|
||||
OOM-on-self.
|
||||
- `cmd/auth.go` removed redundant local `--hostname` declarations on
|
||||
three subcommands. The persistent flag on rootCmd is now the only
|
||||
declaration; previously local declarations shadowed it, so
|
||||
`fj --hostname=X auth login` and `fj auth login --hostname=X` went
|
||||
through different code paths.
|
||||
- `--token` on `auth login` emits a stderr warning when used (visible
|
||||
in `ps auxe` and shell history). Flag not removed; just discoverable.
|
||||
- Error handling: `Hint` is now a structured field on `CLIError`.
|
||||
JSON-error consumers get clean structure; the human renderer still
|
||||
appends `\nHint: ...`. Dropped substring matching of `"401"`/`"403"`
|
||||
against rendered error strings (would match issue #403); now relies
|
||||
exclusively on typed `*api.APIError`.
|
||||
- Network errors (`no such host`, `connection refused`, `i/o timeout`)
|
||||
return a structured `CLIError` with code `ErrNetworkError` and a hint.
|
||||
- Config dir created with mode 0700 instead of 0755.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `--config <path>` now actually honored. Previously fed only into
|
||||
Viper; every command that touched config went through
|
||||
`internal/config.Load()` / `Save()` which always read the default
|
||||
path. So `fj --config other.yaml auth login` writes to other.yaml now.
|
||||
- `fj run list --json`, `fj workflow list --json`, `fj wiki view --json`
|
||||
now produce JSON. `cmd/aliases.go` registered `--json` as `Bool` but
|
||||
handlers called `wantJSON()` which does `GetString("json")` — pflag
|
||||
returned a type-error that `wantJSON` silently swallowed.
|
||||
`cmd/wiki.go` had the inverse bug (`GetBool` against an
|
||||
`addJSONFlags`-registered string flag). Both routed through
|
||||
`addJSONFlags`/`wantJSON`/`outputJSON` consistently now.
|
||||
- `migrateConfigDir` opens dst with `O_TRUNC`. Previously a partially-
|
||||
pre-existing dst file would have legacy contents overwrite a prefix
|
||||
and leave stale tail bytes — silent YAML/token corruption. Refactored
|
||||
close handling into `copyOneConfigFile`.
|
||||
|
||||
### Security
|
||||
|
||||
- `fj api` endpoint path traversal closed. `fj api '/../admin/users'`
|
||||
previously normalized through `http.NewRequest` to
|
||||
`https://host/admin/users` — silently sending authenticated traffic
|
||||
to non-API paths. Endpoint is now parsed via `url.Parse`, `..`
|
||||
segments rejected, then `JoinPath` onto the `/api/v1` base.
|
||||
URL-encoded `%2E%2E` is also caught because Go decodes before our
|
||||
split.
|
||||
- `fj api --paginate` validates same-origin before forwarding the
|
||||
bearer token to a `Link: rel="next"` URL. Refuses to reattach
|
||||
`Authorization` if the next URL's scheme isn't `https` or its host
|
||||
doesn't match the configured one.
|
||||
- `initConfig` warns on stderr if the resolved config file is world or
|
||||
group readable (`mode & 0o077 != 0`).
|
||||
|
||||
## [0.3.0c] - 2026-03-21
|
||||
|
||||
### Added
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue