Skip to content

fix: canonical hardening (audit findings #1-12)#9

Merged
voska merged 1 commit into
mainfrom
fix/canonical-hardening
Jun 17, 2026
Merged

fix: canonical hardening (audit findings #1-12)#9
voska merged 1 commit into
mainfrom
fix/canonical-hardening

Conversation

@voska

@voska voska commented Jun 17, 2026

Copy link
Copy Markdown
Owner

Resolves findings #1–#12 from the codebase audit. Findings #13+ (dependabot, CONTRIBUTING, retry/backoff, godocs, dry-run double-header, go.mod patch pin) are intentionally out of scope.

Correctness & safety

  • batch was a silent no-opbatch.go parsed the file then posted nil. Now passes the items through under BatchItemRequest; accepts a JSON array and rejects empty/invalid input.
  • --no-input deleted without confirming — it was treated as a --force synonym. Now it refuses (exit 2, "refusing to delete without --force in --no-input mode"); only --force/--yes authorizes skipping the prompt. Matches the documented contract.
  • No request timeout / no cancellation — API client now has a 60s timeout; signal.NotifyContext (SIGINT/SIGTERM) is wired into the root context so in-flight requests cancel on Ctrl-C; local login is bounded by a 5m timeout (also makes the previously-dead "login timed out" branch reachable and frees port 8844 on abandonment).

Contract

  • Exit code 3 (empty) is now emitted by list/query on zero results, while stdout still gets a valid (empty) array — agents can finally branch on it.
  • auth login --redirect-uri now appears in qbo schema.

Auth robustness

  • GenerateState propagates crypto/rand errors (no more all-zeros/predictable CSRF state on failure).
  • Token-expiry check gains a 30s clock-skew leeway (avoids 401s on near-expiry tokens).

Docs

  • Add SECURITY.md (token/secret handling + private disclosure path).
  • Remove the dead demo.gif <img> from the README; fix llms.txt (--order-by) and COMMANDS (--select dot paths); document the batch file format.

Tests (#12)

internal/cmd had zero tests (the root cause that let #1/#2 ship). Added: delete unknown-entity / --no-input / dry-run-no-network guards, create unknown-entity, batch empty/invalid rejection, batch passthrough (httptest), and output.IsEmpty.

All local checks green: build, race tests, vet, golangci-lint.

Correctness & safety
- batch: pass the parsed file through instead of posting nil (was a silent
  no-op that ignored the input); accept a JSON array of items and reject
  empty/invalid input
- delete: --no-input now refuses with an error instead of deleting without
  confirmation — only --force/--yes authorizes skipping the prompt
- api client: set a 60s request timeout; wire signal.NotifyContext for
  SIGINT/SIGTERM into the root context so requests are cancellable; bound
  local browser login with a 5m timeout so the callback port is released

Contract
- list/query: emit exit code 3 (empty) on zero results while still writing a
  valid (empty) array to stdout
- schema: expose `auth login --redirect-uri`

Auth robustness
- GenerateState propagates crypto/rand errors instead of risking a predictable
  OAuth CSRF state
- token expiry check gains a 30s clock-skew leeway

Docs
- add SECURITY.md (token/secret handling + private disclosure path)
- remove dead demo.gif reference from README
- fix llms.txt (--order-by) and COMMANDS (--select dot paths); document the
  batch file format

Tests
- cover internal/cmd (delete/create/batch guards), batch passthrough, and
  output.IsEmpty
@voska voska merged commit 8dff12f into main Jun 17, 2026
2 checks passed
@voska voska deleted the fix/canonical-hardening branch June 17, 2026 21:37
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