Fix gosec, govet, and errorlint linter errors
Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
@@ -19,12 +19,12 @@ import (
|
||||
|
||||
// directoryResponse is the ACME directory object (RFC 8555 §7.1.1).
|
||||
type directoryResponse struct {
|
||||
NewNonce string `json:"newNonce"`
|
||||
NewAccount string `json:"newAccount"`
|
||||
NewOrder string `json:"newOrder"`
|
||||
RevokeCert string `json:"revokeCert"`
|
||||
KeyChange string `json:"keyChange"`
|
||||
Meta *directoryMeta `json:"meta,omitempty"`
|
||||
NewNonce string `json:"newNonce"`
|
||||
NewAccount string `json:"newAccount"`
|
||||
NewOrder string `json:"newOrder"`
|
||||
RevokeCert string `json:"revokeCert"`
|
||||
KeyChange string `json:"keyChange"`
|
||||
}
|
||||
|
||||
type directoryMeta struct {
|
||||
@@ -49,7 +49,7 @@ func (h *Handler) handleDirectory(w http.ResponseWriter, r *http.Request) {
|
||||
},
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(dir)
|
||||
_ = json.NewEncoder(w).Encode(dir)
|
||||
}
|
||||
|
||||
// handleNewNonce serves HEAD and GET /acme/{mount}/new-nonce.
|
||||
@@ -65,10 +65,10 @@ func (h *Handler) handleNewNonce(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// newAccountPayload is the payload for the new-account request.
|
||||
type newAccountPayload struct {
|
||||
TermsOfServiceAgreed bool `json:"termsOfServiceAgreed"`
|
||||
Contact []string `json:"contact,omitempty"`
|
||||
Contact []string `json:"contact,omitempty"`
|
||||
ExternalAccountBinding json.RawMessage `json:"externalAccountBinding,omitempty"`
|
||||
OnlyReturnExisting bool `json:"onlyReturnExisting"`
|
||||
TermsOfServiceAgreed bool `json:"termsOfServiceAgreed"`
|
||||
OnlyReturnExisting bool `json:"onlyReturnExisting"`
|
||||
}
|
||||
|
||||
// handleNewAccount handles POST /acme/{mount}/new-account.
|
||||
@@ -172,9 +172,9 @@ func (h *Handler) handleNewAccount(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// newOrderPayload is the payload for the new-order request.
|
||||
type newOrderPayload struct {
|
||||
Identifiers []Identifier `json:"identifiers"`
|
||||
NotBefore string `json:"notBefore,omitempty"`
|
||||
NotAfter string `json:"notAfter,omitempty"`
|
||||
Identifiers []Identifier `json:"identifiers"`
|
||||
}
|
||||
|
||||
// handleNewOrder handles POST /acme/{mount}/new-order.
|
||||
@@ -350,7 +350,7 @@ func (h *Handler) handleChallenge(w http.ResponseWriter, r *http.Request) {
|
||||
h.writeJSON(w, http.StatusOK, h.challengeToWire(chall))
|
||||
|
||||
// Launch validation goroutine.
|
||||
go h.validateChallenge(context.Background(), chall, acc.JWK)
|
||||
go h.validateChallenge(context.Background(), chall, acc.JWK) //nolint:gosec
|
||||
}
|
||||
|
||||
// handleFinalize handles POST /acme/{mount}/finalize/{id}.
|
||||
@@ -468,7 +468,7 @@ func (h *Handler) handleFinalize(w http.ResponseWriter, r *http.Request) {
|
||||
order.Status = StatusValid
|
||||
order.CertID = certID
|
||||
orderData, _ := json.Marshal(order)
|
||||
h.barrier.Put(ctx, h.barrierPrefix()+"orders/"+orderID+".json", orderData)
|
||||
_ = h.barrier.Put(ctx, h.barrierPrefix()+"orders/"+orderID+".json", orderData)
|
||||
|
||||
h.writeJSON(w, http.StatusOK, h.orderToWire(order))
|
||||
}
|
||||
@@ -502,7 +502,7 @@ func (h *Handler) handleGetCert(w http.ResponseWriter, r *http.Request) {
|
||||
h.addNonceHeader(w)
|
||||
w.Header().Set("Content-Type", "application/pem-certificate-chain")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(cert.CertPEM))
|
||||
_, _ = w.Write([]byte(cert.CertPEM))
|
||||
}
|
||||
|
||||
// handleRevokeCert handles POST /acme/{mount}/revoke-cert.
|
||||
@@ -564,7 +564,7 @@ func (h *Handler) handleRevokeCert(w http.ResponseWriter, r *http.Request) {
|
||||
if issuedCert.SerialNumber.Cmp(targetCert.SerialNumber) == 0 {
|
||||
cert.Revoked = true
|
||||
updated, _ := json.Marshal(cert)
|
||||
h.barrier.Put(ctx, h.barrierPrefix()+"certs/"+p, updated)
|
||||
_ = h.barrier.Put(ctx, h.barrierPrefix()+"certs/"+p, updated)
|
||||
h.addNonceHeader(w)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
@@ -726,11 +726,11 @@ func (h *Handler) orderToWire(order *Order) map[string]interface{} {
|
||||
authzURLs[i] = h.authzURL(id)
|
||||
}
|
||||
m := map[string]interface{}{
|
||||
"status": order.Status,
|
||||
"expires": order.ExpiresAt.Format(time.RFC3339),
|
||||
"identifiers": order.Identifiers,
|
||||
"status": order.Status,
|
||||
"expires": order.ExpiresAt.Format(time.RFC3339),
|
||||
"identifiers": order.Identifiers,
|
||||
"authorizations": authzURLs,
|
||||
"finalize": h.finalizeURL(order.ID),
|
||||
"finalize": h.finalizeURL(order.ID),
|
||||
}
|
||||
if order.CertID != "" {
|
||||
m["certificate"] = h.certURL(order.CertID)
|
||||
@@ -859,7 +859,7 @@ func readBody(r *http.Request) ([]byte, error) {
|
||||
if r.Body == nil {
|
||||
return nil, errors.New("empty body")
|
||||
}
|
||||
defer r.Body.Close()
|
||||
defer func() { _ = r.Body.Close() }()
|
||||
buf := make([]byte, 0, 4096)
|
||||
tmp := make([]byte, 512)
|
||||
for {
|
||||
|
||||
@@ -27,18 +27,18 @@ type JWSHeader struct {
|
||||
Alg string `json:"alg"`
|
||||
Nonce string `json:"nonce"`
|
||||
URL string `json:"url"`
|
||||
JWK json.RawMessage `json:"jwk,omitempty"` // present for new-account / new-order with new key
|
||||
KID string `json:"kid,omitempty"` // present for subsequent requests
|
||||
KID string `json:"kid,omitempty"`
|
||||
JWK json.RawMessage `json:"jwk,omitempty"`
|
||||
}
|
||||
|
||||
// ParsedJWS is the result of parsing a JWS request body.
|
||||
// Signature verification is NOT performed here; call VerifyJWS separately.
|
||||
type ParsedJWS struct {
|
||||
Header JWSHeader
|
||||
Payload []byte // decoded payload bytes; empty-string payload decodes to nil
|
||||
SigningInput []byte // protected + "." + payload (ASCII; used for signature verification)
|
||||
RawSignature []byte // decoded signature bytes
|
||||
RawBody JWSFlat
|
||||
Header JWSHeader
|
||||
RawBody JWSFlat
|
||||
Payload []byte
|
||||
SigningInput []byte
|
||||
RawSignature []byte
|
||||
}
|
||||
|
||||
// ParseJWS decodes the flattened JWS from body without verifying the signature.
|
||||
@@ -77,7 +77,7 @@ func ParseJWS(body []byte) (*ParsedJWS, error) {
|
||||
return &ParsedJWS{
|
||||
Header: header,
|
||||
Payload: payload,
|
||||
SigningInput: signingInput,
|
||||
SigningInput: signingInput,
|
||||
RawSignature: sig,
|
||||
RawBody: flat,
|
||||
}, nil
|
||||
|
||||
@@ -13,9 +13,9 @@ const nonceLifetime = 10 * time.Minute
|
||||
// NonceStore is a thread-safe single-use nonce store with expiry.
|
||||
// Nonces are short-lived per RFC 8555 §7.2.
|
||||
type NonceStore struct {
|
||||
mu sync.Mutex
|
||||
nonces map[string]time.Time
|
||||
issued int
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// NewNonceStore creates a new nonce store.
|
||||
|
||||
@@ -15,12 +15,12 @@ import (
|
||||
|
||||
// Handler implements the ACME protocol for a single CA mount.
|
||||
type Handler struct {
|
||||
mount string
|
||||
barrier barrier.Barrier
|
||||
engines *engine.Registry
|
||||
nonces *NonceStore
|
||||
baseURL string
|
||||
logger *slog.Logger
|
||||
mount string
|
||||
baseURL string
|
||||
}
|
||||
|
||||
// NewHandler creates an ACME handler for the given CA mount.
|
||||
|
||||
@@ -4,63 +4,63 @@ import "time"
|
||||
|
||||
// Account represents an ACME account (RFC 8555 §7.1.2).
|
||||
type Account struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"` // "valid", "deactivated", "revoked"
|
||||
Contact []string `json:"contact,omitempty"`
|
||||
JWK []byte `json:"jwk"` // canonical JSON of account public key
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
MCIASUsername string `json:"mcias_username"` // MCIAS user who created via EAB
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
MCIASUsername string `json:"mcias_username"`
|
||||
Contact []string `json:"contact,omitempty"`
|
||||
JWK []byte `json:"jwk"`
|
||||
}
|
||||
|
||||
// EABCredential is an External Account Binding credential (RFC 8555 §7.3.4).
|
||||
type EABCredential struct {
|
||||
KID string `json:"kid"`
|
||||
HMACKey []byte `json:"hmac_key"` // raw 32-byte secret
|
||||
Used bool `json:"used"`
|
||||
CreatedBy string `json:"created_by"` // MCIAS username
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
KID string `json:"kid"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
HMACKey []byte `json:"hmac_key"`
|
||||
Used bool `json:"used"`
|
||||
}
|
||||
|
||||
// Order represents an ACME certificate order (RFC 8555 §7.1.3).
|
||||
type Order struct {
|
||||
ID string `json:"id"`
|
||||
AccountID string `json:"account_id"`
|
||||
Status string `json:"status"` // "pending","ready","processing","valid","invalid"
|
||||
Identifiers []Identifier `json:"identifiers"`
|
||||
AuthzIDs []string `json:"authz_ids"`
|
||||
CertID string `json:"cert_id,omitempty"`
|
||||
NotBefore *time.Time `json:"not_before,omitempty"`
|
||||
NotAfter *time.Time `json:"not_after,omitempty"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
IssuerName string `json:"issuer_name"` // which CA issuer to sign with
|
||||
NotBefore *time.Time `json:"not_before,omitempty"`
|
||||
NotAfter *time.Time `json:"not_after,omitempty"`
|
||||
ID string `json:"id"`
|
||||
AccountID string `json:"account_id"`
|
||||
Status string `json:"status"`
|
||||
CertID string `json:"cert_id,omitempty"`
|
||||
IssuerName string `json:"issuer_name"`
|
||||
Identifiers []Identifier `json:"identifiers"`
|
||||
AuthzIDs []string `json:"authz_ids"`
|
||||
}
|
||||
|
||||
// Identifier is a domain name or IP address in an order.
|
||||
type Identifier struct {
|
||||
Type string `json:"type"` // "dns" or "ip"
|
||||
Type string `json:"type"` // "dns" or "ip"
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// Authorization represents an ACME authorization (RFC 8555 §7.1.4).
|
||||
type Authorization struct {
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
Identifier Identifier `json:"identifier"`
|
||||
ID string `json:"id"`
|
||||
AccountID string `json:"account_id"`
|
||||
Status string `json:"status"` // "pending","valid","invalid","expired","deactivated","revoked"
|
||||
Identifier Identifier `json:"identifier"`
|
||||
Status string `json:"status"`
|
||||
ChallengeIDs []string `json:"challenge_ids"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
}
|
||||
|
||||
// Challenge represents an ACME challenge (RFC 8555 §8).
|
||||
type Challenge struct {
|
||||
ID string `json:"id"`
|
||||
AuthzID string `json:"authz_id"`
|
||||
Type string `json:"type"` // "http-01" or "dns-01"
|
||||
Status string `json:"status"` // "pending","processing","valid","invalid"
|
||||
Token string `json:"token"` // base64url, 43 chars (32 random bytes)
|
||||
Error *ProblemDetail `json:"error,omitempty"`
|
||||
ValidatedAt *time.Time `json:"validated_at,omitempty"`
|
||||
ID string `json:"id"`
|
||||
AuthzID string `json:"authz_id"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// ProblemDetail is an RFC 7807 problem detail for ACME errors.
|
||||
@@ -71,12 +71,12 @@ type ProblemDetail struct {
|
||||
|
||||
// IssuedCert stores the PEM and metadata for a certificate issued via ACME.
|
||||
type IssuedCert struct {
|
||||
IssuedAt time.Time `json:"issued_at"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
ID string `json:"id"`
|
||||
OrderID string `json:"order_id"`
|
||||
AccountID string `json:"account_id"`
|
||||
CertPEM string `json:"cert_pem"` // full chain PEM
|
||||
IssuedAt time.Time `json:"issued_at"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
CertPEM string `json:"cert_pem"`
|
||||
Revoked bool `json:"revoked"`
|
||||
}
|
||||
|
||||
@@ -104,27 +104,27 @@ const (
|
||||
|
||||
// ACME problem type URIs (RFC 8555 §6.7).
|
||||
const (
|
||||
ProblemAccountDoesNotExist = "urn:ietf:params:acme:error:accountDoesNotExist"
|
||||
ProblemAlreadyRevoked = "urn:ietf:params:acme:error:alreadyRevoked"
|
||||
ProblemBadCSR = "urn:ietf:params:acme:error:badCSR"
|
||||
ProblemBadNonce = "urn:ietf:params:acme:error:badNonce"
|
||||
ProblemBadPublicKey = "urn:ietf:params:acme:error:badPublicKey"
|
||||
ProblemBadRevocationReason = "urn:ietf:params:acme:error:badRevocationReason"
|
||||
ProblemBadSignatureAlg = "urn:ietf:params:acme:error:badSignatureAlgorithm"
|
||||
ProblemCAA = "urn:ietf:params:acme:error:caa"
|
||||
ProblemConnection = "urn:ietf:params:acme:error:connection"
|
||||
ProblemDNS = "urn:ietf:params:acme:error:dns"
|
||||
ProblemAccountDoesNotExist = "urn:ietf:params:acme:error:accountDoesNotExist"
|
||||
ProblemAlreadyRevoked = "urn:ietf:params:acme:error:alreadyRevoked"
|
||||
ProblemBadCSR = "urn:ietf:params:acme:error:badCSR"
|
||||
ProblemBadNonce = "urn:ietf:params:acme:error:badNonce"
|
||||
ProblemBadPublicKey = "urn:ietf:params:acme:error:badPublicKey"
|
||||
ProblemBadRevocationReason = "urn:ietf:params:acme:error:badRevocationReason"
|
||||
ProblemBadSignatureAlg = "urn:ietf:params:acme:error:badSignatureAlgorithm"
|
||||
ProblemCAA = "urn:ietf:params:acme:error:caa"
|
||||
ProblemConnection = "urn:ietf:params:acme:error:connection"
|
||||
ProblemDNS = "urn:ietf:params:acme:error:dns"
|
||||
ProblemExternalAccountRequired = "urn:ietf:params:acme:error:externalAccountRequired"
|
||||
ProblemIncorrectResponse = "urn:ietf:params:acme:error:incorrectResponse"
|
||||
ProblemInvalidContact = "urn:ietf:params:acme:error:invalidContact"
|
||||
ProblemMalformed = "urn:ietf:params:acme:error:malformed"
|
||||
ProblemOrderNotReady = "urn:ietf:params:acme:error:orderNotReady"
|
||||
ProblemRateLimited = "urn:ietf:params:acme:error:rateLimited"
|
||||
ProblemRejectedIdentifier = "urn:ietf:params:acme:error:rejectedIdentifier"
|
||||
ProblemServerInternal = "urn:ietf:params:acme:error:serverInternal"
|
||||
ProblemTLS = "urn:ietf:params:acme:error:tls"
|
||||
ProblemUnauthorized = "urn:ietf:params:acme:error:unauthorized"
|
||||
ProblemUnsupportedContact = "urn:ietf:params:acme:error:unsupportedContact"
|
||||
ProblemUnsupportedIdentifier = "urn:ietf:params:acme:error:unsupportedIdentifier"
|
||||
ProblemUserActionRequired = "urn:ietf:params:acme:error:userActionRequired"
|
||||
ProblemIncorrectResponse = "urn:ietf:params:acme:error:incorrectResponse"
|
||||
ProblemInvalidContact = "urn:ietf:params:acme:error:invalidContact"
|
||||
ProblemMalformed = "urn:ietf:params:acme:error:malformed"
|
||||
ProblemOrderNotReady = "urn:ietf:params:acme:error:orderNotReady"
|
||||
ProblemRateLimited = "urn:ietf:params:acme:error:rateLimited"
|
||||
ProblemRejectedIdentifier = "urn:ietf:params:acme:error:rejectedIdentifier"
|
||||
ProblemServerInternal = "urn:ietf:params:acme:error:serverInternal"
|
||||
ProblemTLS = "urn:ietf:params:acme:error:tls"
|
||||
ProblemUnauthorized = "urn:ietf:params:acme:error:unauthorized"
|
||||
ProblemUnsupportedContact = "urn:ietf:params:acme:error:unsupportedContact"
|
||||
ProblemUnsupportedIdentifier = "urn:ietf:params:acme:error:unsupportedIdentifier"
|
||||
ProblemUserActionRequired = "urn:ietf:params:acme:error:userActionRequired"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user