Skip to content

Release Go SDK v1.9.0 - #71

Open
stas-schaller wants to merge 9 commits into
masterfrom
release/sdk/golang/core/v1.9.0
Open

Release Go SDK v1.9.0#71
stas-schaller wants to merge 9 commits into
masterfrom
release/sdk/golang/core/v1.9.0

Conversation

@stas-schaller

Copy link
Copy Markdown
Contributor

Summary

Release branch for v1.9.0 of the Go SDK. Bundles a GetFolders binding fix, throttle retry with exponential backoff, a new PAM field, and CI improvements.

Changes

Bug Fixes

  • GetFolders binding fix (KSM-1048): GetFolders() failed with "app key is missing from the storage" when called as the first method on a freshly bound application. It now processes encryptedAppKey from the server binding response the same way GetSecrets() does.
  • Throttle jitter (KSM-881): Throttle retry jitter was two-sided, which could reduce a retry delay below the computed floor. Jitter is now one-sided (0 to +25%). The SDK also caps a server-supplied retry_after at 176s and gates retry on HTTP 403 status.

New Features

  • Throttle retry (KSM-881): On HTTP 403 {"error":"throttled"}, PostQuery now retries up to 5 times with exponentially increasing delays (11s, 22s, 44s, 88s, 176s) plus one-sided jitter, honoring retry_after from the response. Returns ErrThrottled (checkable via errors.Is) once retries are exhausted.
  • dbConnectionMethod (KSM-1076): Added dbConnectionMethod to PamSettingsConnection.

Maintenance

  • CI now builds and tests against Go 1.25 and 1.26 in addition to existing matrix.
  • Added govulncheck scan with SARIF output to GitHub code scanning.

Breaking Changes

None.

Security Impact

KSM-1048 touches the initial key derivation path: fetchAndDecryptFolders now decrypts encryptedAppKey using the client key and stores the result as the app master key, matching the behavior already in fetchAndDecryptSecrets. No new cryptographic operations introduced.

Testing

go test ./...

Related Issues

  • Jira: KSM-1048, KSM-881, KSM-1076

stas-schaller and others added 8 commits July 2, 2026 16:16
Detect HTTP 403 {"error":"throttled"} inside PostQuery and retry up to
maxThrottleRetries (5) with exponential backoff (baseThrottleDelaySec = 11s,
doubling: 11/22/44/88/176s) plus +/-25% jitter, honoring retry_after from the
response body when present. Returns the new sentinel ErrThrottled (checkable
via errors.Is) once retries are exhausted.

The throttle check sits before HandleHttpError in the PostQuery loop, so the
existing key-rotation retry path is unaffected. No new public config; the sleep
is injectable in tests via a new optional Context.Sleep field (mirrors the
existing Context.Transport seam).

No version bump; CHANGELOG entry added under Unreleased. Adds core/throttle_test.go
(unit) and test/throttle_test.go (end-to-end); TestHTTPErrorErrorsAs switched to a
non-throttle code since "throttled" is now retried. go test ./... green in both modules.
Mirrors the Python SDK fix (PR #1033, per @stas-schaller review): parseThrottle
only inspected the response body, so a non-403 response (e.g. 500/502) carrying
{"error":"throttled"} would be retried 5x before failing. Gate the throttle
parse/retry on ksmRs.StatusCode == 403 so non-403 responses fall straight through
to HandleHttpError. Adds a regression test (TestThrottleBodyNon403NotRetried).
Symmetric ±25% jitter could fire a retry before the backend's 10s
memcached window expires (floor - 25% = 8.25s). Switch to 0 to +25%
so the delay is always >= the floor. Also add maxThrottleDelaySec = 176
constant and apply it as a cap in throttleDelay so a server-supplied
retry_after above 176s is clamped rather than honored blindly.
The test and publish matrices topped out at Go 1.22, which upstream no longer
supports. Only 1.25 and 1.26 receive security fixes, so nothing in CI exercised
a toolchain a consumer would actually compile with.

govulncheck against core with go1.22.12 reports 24 reachable standard library
vulnerabilities, including request smuggling in net/http/internal reached from
KeeperFile.GetFileData and proxy bypass reached from SecretsManager.PostQuery.
The same scan on go1.25.12 reports none. The SDK ships source rather than a
binary, so a consumer on a current toolchain was never exposed; the gap was
that CI could not have told us either way.

1.25.x and 1.26.x appended to all three matrices. The 1.16 floor in core/go.mod
is deliberately unchanged, since raising it would break consumers on older Go.
The v1.7.0 changelog already claimed testing against 1.25.0, which was not true
of any matrix until now.

Verified locally: core unit tests and the test module both pass on go1.25.12
and go1.26.5. actionlint and zizmor clean.
govulncheck ran in no workflow in any KSM Go repo, despite being the only Go
scanner that filters by reachability. It reports a vulnerability solely when the
code can actually reach the affected symbol, so its output arrives triaged
rather than needing a manual pass like a Dependabot manifest diff.

Runs on pull requests touching core or test, plus weekly on a schedule. The
schedule matters more than the PR trigger: advisories are published against
unchanged code, so a scan wired only to PRs goes quiet exactly when a new CVE
lands.

Scans on the newest supported Go rather than the 1.16 floor. Standard library
findings depend on the toolchain doing the building, so a scan on an
end-of-life toolchain reports every stdlib defect fixed since and says nothing
about this SDK's own code.

Results upload as SARIF to code scanning instead of failing the job. Dismissing
an alert there records who accepted a risk and why, which is the audit trail a
release review needs and which a red build does not produce. Making it blocking
is a branch protection setting rather than a change to this file.

Verified: govulncheck -format sarif on core reports 0 error-level results, and
the jq summary expressions were checked against that output and against the
vault plugin's, which has 2. actionlint, zizmor and act -l clean.
… by key size (KSM-1064)

Drive (NSF) folders require AES-GCM for the folder key wrap and folder
data in CreateFolder/UpdateFolder; the previous CBC encryption caused
"invalid sharedFolderKey" errors against NSF-enabled endpoints.

fetchAndDecryptFolders now dispatches decrypt mode by subfolder key
byte length (60 = GCM, 64 = CBC legacy) - mirrors the read-path fix
already on the other KSM SDKs. KeeperFolder gains a UseGcm field;
NewKeeperFolder dispatches its own internal folder-data decrypt on it
and takes UseGcm as a new required parameter (updated its one existing
caller in record_test.go, KSM-917's test, with useGcm=false since that
test explicitly encrypts with CBC - assertion and behavior unchanged).

Also fixes keeperSecretsManagerClientId, which was hardcoded to a stale
"mg17.1.0" unrelated to any real SDK version, to "mg18.0.0" so the NSF
gate on the backend recognizes this client. The Go module's own package
version intentionally stays in the v1.x line (v1.9.0) rather than
following the v18 convention literally - Go modules require an
import-path change for any major version 2+, a real migration cost the
other six ecosystems don't have. See secrets-manager-go/main/CLAUDE.md
for the full reasoning.

Verified end-to-end on dev CA: all folders decrypt via GetFolders
(previously GCM-created folders failed with padding errors);
CreateFolder, read-back, UpdateFolder, and DeleteFolder all round-trip
correctly for a fresh GCM subfolder.
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.

2 participants