Skip to content

fix(credentials): surface error responses from credential server endpoint#694

Merged
skevetter merged 8 commits intomainfrom
fix/ssh-signature-error-handling
Apr 5, 2026
Merged

fix(credentials): surface error responses from credential server endpoint#694
skevetter merged 8 commits intomainfrom
fix/ssh-signature-error-handling

Conversation

@skevetter
Copy link
Copy Markdown
Owner

@skevetter skevetter commented Apr 5, 2026

Summary

  • Surface actual server error messages in the SSH signature client instead of opaque JSON parse failures (invalid character 'g')
  • Return structured JSON error responses from the credentials server /git-ssh-signature endpoint
  • Gate SSH signature helper setup on the feature-enabled context option so it doesn't run when disabled

Fixes #645

Summary by CodeRabbit

Release Notes

  • New Features

    • Git SSH signature forwarding is now optional and controllable via configuration option, allowing users to disable when not needed
  • Bug Fixes

    • Enhanced error handling for SSH signing operations with clearer, more user-readable error messages
    • Added HTTP status code validation to prevent silent failures in signature server communication

Check HTTP status code before attempting JSON decode. When the
credentials server returns an error, include the actual error message
instead of an opaque JSON parse failure.

Fixes #645
Write structured JSON errors instead of plain text so the response
is always valid JSON regardless of success or failure.
Only run setupGitSSHSignature when both the signing key is configured
AND the git SSH signature forwarding context option is enabled.

Fixes #645
Add getSignatureURL() helper with a package-level override var so
tests can inject httptest server URLs without changing function
signatures.
Verify handleGitSSHSignatureRequest returns JSON error responses
(not plain text) when the tunnel gRPC call fails, and returns
valid JSON on success.
Test the full HTTP request/response cycle using httptest servers.
Covers plain-text 500 (customer bug scenario), JSON 500, valid
success, and invalid JSON responses.
Wire up the full signing chain with a test credentials server and
mock tunnel client. Verify that signing failures surface clear error
messages (not JSON parse errors) and that successful signing writes
the .sig file correctly.

Remove credentials package import from gitsshsigning to avoid an
import cycle in tests - inline the port resolution logic instead.

Fixes #645
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

📝 Walkthrough

Walkthrough

This PR adds comprehensive testing and error handling improvements for Git SSH signature forwarding. Changes include gating the signature setup in cmd/up.go by a context option, implementing an HTTP signature server with JSON error responses, adding test utilities and integration tests, and enhancing client-side error handling with status code validation.

Changes

Cohort / File(s) Summary
CLI Configuration
cmd/up.go
Gates git SSH signature setup execution with the config.ContextOptionGitSSHSignatureForwarding context option, enabling conditional feature control.
Signature Server
pkg/credentials/server.go, pkg/credentials/server_test.go
HTTP handler now returns JSON-formatted error responses (HTTP 500) when tunnel client calls fail, improving error visibility; unit tests validate both error and success response paths.
Signature Client
pkg/gitsshsigning/client.go, pkg/gitsshsigning/client_test.go
Adds environment variable-based port configuration, test URL override support, HTTP status code validation, response body error capture, and improved error wrapping in JSON parsing; unit tests verify error propagation and successful signature retrieval.
Test Infrastructure
pkg/credentials/mock_tunnel_client_test.go, pkg/credentials/integration_test.go
New mock tunnel client stub for testing with configurable signature function; integration tests validate signature handling via local HTTP server with success and failure scenarios.
E2E Tests
e2e/tests/ssh/ssh.go
Adds new Ginkgo specs for verifying signature setup is skipped when flag is absent and error output is human-readable on forwarded SSH agent failures; introduces osWindows constant to replace inline string comparisons.

Sequence Diagram(s)

sequenceDiagram
    participant Git as Git Process
    participant Handler as HTTP Handler<br/>(handleGitSSHSignatureRequest)
    participant TunnelClient as Tunnel Client
    participant Server as Signature Server

    Git->>Handler: POST /git-ssh-signature<br/>(buffer file path)
    Handler->>TunnelClient: GitSSHSignature(ctx, request)
    
    alt Tunnel Client Error
        TunnelClient-->>Handler: error (e.g., Permission denied)
        Handler->>Handler: Marshal error to JSON
        Handler-->>Git: HTTP 500<br/>{"error": "Permission denied"}
    else Tunnel Client Success
        TunnelClient->>Server: Forward signature request
        Server-->>TunnelClient: {"signature": "..."}
        TunnelClient-->>Handler: tunnel.Message (JSON response)
        Handler->>Handler: Parse signature from JSON
        Handler-->>Git: HTTP 200<br/>{"signature": "..."}
    end
    
    Git->>Git: Write signature to file
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

size/l

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR addresses all coding objectives from issue #645: gating SSH signature setup on context option [cmd/up.go], returning structured JSON errors [pkg/credentials/server.go], validating HTTP status before JSON parsing [pkg/gitsshsigning/client.go], and adding comprehensive tests [e2e, pkg/credentials, pkg/gitsshsigning].
Out of Scope Changes check ✅ Passed All changes are directly scoped to the objectives: error handling improvements, context option gating, test infrastructure additions, and test coverage for the SSH signature feature.
Title check ✅ Passed The title directly describes the main change: surfacing error responses from the credential server endpoint, which is the core objective across all file changes in this PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ssh-signature-error-handling

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added the size/l label Apr 5, 2026
Add two e2e tests to the SSH test suite:

1. Verify helper is not installed when --git-ssh-signing-key is omitted
2. Verify signing failure produces human-readable errors (not JSON parse artifacts)

Also extract repeated "windows" string to a constant to satisfy goconst linter.
@github-actions github-actions bot added size/xl and removed size/l labels Apr 5, 2026
@skevetter skevetter marked this pull request as ready for review April 5, 2026 18:24
@coderabbitai coderabbitai bot added the size/l label Apr 5, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
pkg/gitsshsigning/client.go (1)

90-96: Package-level mutable state may cause test flakiness with parallel execution.

The signatureServerURL variable can cause race conditions if tests run in parallel. Consider using sync.Mutex or sync.RWMutex to protect access, or restructure to pass the URL as a parameter.

🔧 Thread-safe alternative
+import "sync"
+
-// signatureServerURL overrides the server URL for testing. Empty means use credentials.GetPort().
-var signatureServerURL string
+var (
+	signatureServerURL   string
+	signatureServerURLMu sync.RWMutex
+)

 // SetSignatureServerURL sets the server URL override for testing.
 func SetSignatureServerURL(url string) {
+	signatureServerURLMu.Lock()
+	defer signatureServerURLMu.Unlock()
 	signatureServerURL = url
 }

 func getSignatureURL() (string, error) {
+	signatureServerURLMu.RLock()
+	override := signatureServerURL
+	signatureServerURLMu.RUnlock()
+	if override != "" {
+		return override, nil
-	if signatureServerURL != "" {
-		return signatureServerURL, nil
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/gitsshsigning/client.go` around lines 90 - 96, The package-level mutable
variable signatureServerURL and its setter SetSignatureServerURL are not safe
for parallel tests; protect reads/writes with a sync.RWMutex (declare e.g.
signatureURLMu) and lock in SetSignatureServerURL and any accessor, or remove
the global and instead pass the URL as an explicit parameter to functions that
need it (refactor callers of SetSignatureServerURL to accept the URL argument).
Ensure every place that reads signatureServerURL uses the guarded accessor or
the new parameter to avoid races in parallel test execution.
pkg/gitsshsigning/client_test.go (1)

32-49: Consider marking tests as non-parallel due to shared state.

These tests mutate the package-level signatureServerURL variable. If Go's test runner executes them in parallel with -parallel, race conditions could occur. Consider either adding t.Parallel() guards or documenting that these tests cannot run in parallel.

Note: Since go test doesn't parallelize tests by default within a package unless t.Parallel() is explicitly called, this is low risk but worth noting for future maintainability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/gitsshsigning/client_test.go` around lines 32 - 49, Tests mutate the
package-level signatureServerURL which can cause races if tests run
concurrently; protect that shared state by introducing a package-level
sync.Mutex (e.g., testMutex) and lock it at the start of each test that changes
signatureServerURL (including TestRequestContentSignature_ServerError_PlainText)
and release it in t.Cleanup so the original value restoration still runs;
alternatively, explicitly avoid parallel execution by not calling t.Parallel()
and documenting the tests are serialized — but the recommended change is to add
the mutex lock/unlock around modifications to signatureServerURL to ensure safe
concurrent test runs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/gitsshsigning/client_test.go`:
- Around line 32-49: Tests mutate the package-level signatureServerURL which can
cause races if tests run concurrently; protect that shared state by introducing
a package-level sync.Mutex (e.g., testMutex) and lock it at the start of each
test that changes signatureServerURL (including
TestRequestContentSignature_ServerError_PlainText) and release it in t.Cleanup
so the original value restoration still runs; alternatively, explicitly avoid
parallel execution by not calling t.Parallel() and documenting the tests are
serialized — but the recommended change is to add the mutex lock/unlock around
modifications to signatureServerURL to ensure safe concurrent test runs.

In `@pkg/gitsshsigning/client.go`:
- Around line 90-96: The package-level mutable variable signatureServerURL and
its setter SetSignatureServerURL are not safe for parallel tests; protect
reads/writes with a sync.RWMutex (declare e.g. signatureURLMu) and lock in
SetSignatureServerURL and any accessor, or remove the global and instead pass
the URL as an explicit parameter to functions that need it (refactor callers of
SetSignatureServerURL to accept the URL argument). Ensure every place that reads
signatureServerURL uses the guarded accessor or the new parameter to avoid races
in parallel test execution.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f0ff7178-c293-4210-a007-50b1b003ad79

📥 Commits

Reviewing files that changed from the base of the PR and between 4fb2d7b and 9af99f5.

📒 Files selected for processing (8)
  • cmd/up.go
  • e2e/tests/ssh/ssh.go
  • pkg/credentials/integration_test.go
  • pkg/credentials/mock_tunnel_client_test.go
  • pkg/credentials/server.go
  • pkg/credentials/server_test.go
  • pkg/gitsshsigning/client.go
  • pkg/gitsshsigning/client_test.go

@skevetter skevetter changed the title fix: improve SSH signature error handling and add tests fix(credentials): surface error responses from credential server endpoint Apr 5, 2026
@skevetter skevetter merged commit f2b481f into main Apr 5, 2026
41 checks passed
@skevetter skevetter deleted the fix/ssh-signature-error-handling branch April 5, 2026 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Git SSH signature forwarding doesn't work

1 participant