-
released this
2026-05-02 16:05:22 -06:00 | 0 commits to main since this releaseAudit-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=fieldssyntax removed. The flag was a string with
NoOptDefVal=" "sentinel —--jsonalone meant "everything",
--json=fieldsprojected. That produced--json string[=" "]in
--helpand required a literal=because--json fieldswas parsed
as the bare flag plus a positional. Migration:--json=fields→
--json-fields fields. Bare--jsonstill means "all fields as JSON".
--jsonand--json-fieldsare mutually exclusive;--jqcomposes
with either.
Added
fj api --json/--json-fields/--jq— projection and jq filtering
for raw API responses. Routes through the sameaddJSONFlagshelpers
as the other list commands. Closes the inconsistency wherefj api
was the only command returning raw JSON without these knobs.fj api --paginate— follows RFC 5988Link: 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— genericpaginateGitea[T any]helper. Applied to
repo list,pr list,issue list. Previously onlyrelease list
walked pages; the others passedPageSize: limitdirectly to the
gitea SDK, which silently caps PageSize at 50, so--limit > 50was
truncated without warning.CLAUDE.md— guide for Claude Code sessions: layout, codex review
pattern, release process, homebrew tap update steps.
Changed
--jsonflag rebuilt as a plainBool.--json-fieldskeeps
comma-separated projection. Both registered viaaddJSONFlagsand
markedMutuallyExclusive.cmd/actions.go—runandworkflowsubtrees converted from
package-levelvars to factory functions (newRunCmd,
newWorkflowCmd, ...).cmd/aliases.goshrank from 142 → 17 lines
and now calls those same factories with aparentLabelparameter that
disambiguates the alias variant. Result:diffoffj run list --helpflags vsfj actions run list --helpflags is now empty.
Drift between the two paths is structurally impossible.fj apinow usesinternal/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 apiresponse body bounded byio.LimitReaderat 64 MB to prevent
OOM-on-self.cmd/auth.goremoved redundant local--hostnamedeclarations on
three subcommands. The persistent flag on rootCmd is now the only
declaration; previously local declarations shadowed it, so
fj --hostname=X auth loginandfj auth login --hostname=Xwent
through different code paths.--tokenonauth loginemits a stderr warning when used (visible
inps auxeand shell history). Flag not removed; just discoverable.- Error handling:
Hintis now a structured field onCLIError.
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 structuredCLIErrorwith codeErrNetworkErrorand 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. Sofj --config other.yaml auth loginwrites to other.yaml now.fj run list --json,fj workflow list --json,fj wiki view --json
now produce JSON.cmd/aliases.goregistered--jsonasBoolbut
handlers calledwantJSON()which doesGetString("json")— pflag
returned a type-error thatwantJSONsilently swallowed.
cmd/wiki.gohad the inverse bug (GetBoolagainst an
addJSONFlags-registered string flag). Both routed through
addJSONFlags/wantJSON/outputJSONconsistently now.migrateConfigDiropens dst withO_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 intocopyOneConfigFile.
Security
fj apiendpoint path traversal closed.fj api '/../admin/users'
previously normalized throughhttp.NewRequestto
https://host/admin/users— silently sending authenticated traffic
to non-API paths. Endpoint is now parsed viaurl.Parse,..
segments rejected, thenJoinPathonto the/api/v1base.
URL-encoded%2E%2Eis also caught because Go decodes before our
split.fj api --paginatevalidates same-origin before forwarding the
bearer token to aLink: rel="next"URL. Refuses to reattach
Authorizationif the next URL's scheme isn'thttpsor its host
doesn't match the configured one.initConfigwarns on stderr if the resolved config file is world or
group readable (mode & 0o077 != 0).
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
1 download