Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

This is an implementation of HTTP Message Signatures per RFC 9421. The library provides functionality for signing and verifying HTTP requests and responses using various cryptographic algorithms.

**Module:** `github.com/remitly-oss/httpsig-go`
**Go Version:** 1.22+

## Development Commands

### Testing
Expand Down Expand Up @@ -34,6 +37,8 @@ This is an implementation of HTTP Message Signatures per RFC 9421. The library p
- `Signer` struct orchestrates request/response signing
- `SigningProfile` defines what fields and metadata to include
- `SigningKey` holds cryptographic material and metadata
- `SigningKeyOpts` supports custom `crypto.Signer` implementations (e.g., TPM, HSM)
- Supports multiple signatures on a single message
- Supports asymmetric (RSA, ECDSA, Ed25519) and symmetric (HMAC) algorithms

2. **Accept-Signature Support** (`accept.go`)
Expand Down
2 changes: 1 addition & 1 deletion base.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,5 +244,5 @@ func deriveTargetURI(req *http.Request) string {
scheme = "http"
}

return fmt.Sprintf("%s://%s%s%s", scheme, req.Host, req.URL.RawPath, req.URL.RawQuery)
return fmt.Sprintf("%s://%s%s", scheme, req.Host, req.URL.RequestURI())
}
53 changes: 53 additions & 0 deletions verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import (
"fmt"
"math/big"
"net/http"
"net/http/httptest"
"slices"
"strings"
"testing"
"time"

sfv "github.com/dunglas/httpsfv"
Expand Down Expand Up @@ -553,6 +555,57 @@ func (vp VerifyProfile) validateTiming(sig extractedSignature, currentTime time.
return nil
}

func TestDeriveTargetURI(t *testing.T) {
tests := []struct {
name string
url string
expected string
}{
{
name: "simple path no query",
url: "https://example.com/path",
expected: "https://example.com/path",
},
{
name: "path with query string",
url: "https://example.com/path?foo=bar",
expected: "https://example.com/path?foo=bar",
},
{
name: "path with multiple query params",
url: "https://example.com/data?name=value&other=123",
expected: "https://example.com/data?name=value&other=123",
},
{
name: "root path with query",
url: "https://example.com/?query=test",
expected: "https://example.com/?query=test",
},
{
name: "nested path no query",
url: "https://example.com/api/v1/users",
expected: "https://example.com/api/v1/users",
},
{
name: "empty query string preserved",
url: "https://example.com/path?",
expected: "https://example.com/path?",
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
// httptest.NewRequest sets req.TLS for https URLs
req := httptest.NewRequest("GET", tc.url, nil)

got := deriveTargetURI(req)
if got != tc.expected {
t.Errorf("deriveTargetURI() = %q, want %q", got, tc.expected)
}
})
}
}

type metadataProviderFromParams struct {
Params *sfv.Params
}
Expand Down
Loading