{"record":{"id":"4e3e12c2df7e2b08","repo":"larksuite/cli","slug":"invalid-timestamp-q-w-4e3e12","errorCode":null,"errorMessage":"invalid timestamp %q: %w","messagePattern":"invalid timestamp %q: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sidecar/hmac.go","lineNumber":72,"sourceCode":"\t\tc.Identity,\n\t\tc.AuthHeader,\n\t}, \"\\n\")\n}\n\n// Sign computes the HMAC-SHA256 signature over the canonical request string.\nfunc Sign(key []byte, req CanonicalRequest) string {\n\tmac := hmac.New(sha256.New, key)\n\tmac.Write([]byte(req.canonicalString()))\n\treturn hex.EncodeToString(mac.Sum(nil))\n}\n\n// Verify checks that signature matches the HMAC-SHA256 of the canonical\n// request and that the timestamp is within MaxTimestampDrift seconds of now.\n// Returns nil on success.\nfunc Verify(key []byte, req CanonicalRequest, signature string) error {\n\tts, err := strconv.ParseInt(req.Timestamp, 10, 64)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid timestamp %q: %w\", req.Timestamp, err)\n\t}\n\tdrift := math.Abs(float64(time.Now().Unix() - ts))\n\tif drift > MaxTimestampDrift {\n\t\treturn fmt.Errorf(\"timestamp drift %.0fs exceeds limit %ds\", drift, MaxTimestampDrift)\n\t}\n\texpected := Sign(key, req)\n\tif !hmac.Equal([]byte(expected), []byte(signature)) {\n\t\treturn fmt.Errorf(\"HMAC signature mismatch\")\n\t}\n\treturn nil\n}\n\n// Timestamp returns the current Unix epoch seconds as a string.\nfunc Timestamp() string {\n\treturn strconv.FormatInt(time.Now().Unix(), 10)\n}\n","sourceCodeStart":54,"sourceCodeEnd":89,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/sidecar/hmac.go#L54-L89","documentation":"sidecar.Verify authenticates HMAC-signed requests: it parses the CanonicalRequest.Timestamp as a Unix-seconds integer before checking drift and the signature. If ParseInt fails it returns 'invalid timestamp %q: %w'. Unlike the CLI helpers, this is a final error from a public API (used by ServeHTTP / verifyWithClientKeys), so callers receive it directly.","triggerScenarios":"Sending an HMAC-authenticated sidecar request whose X-Timestamp (or equivalent) header is empty, non-numeric ('2026-09-04T10:00:00Z' raw RFC3339, 'now', with quotes/whitespace), or in milliseconds ('1780000000000' overflows nothing but is not seconds — that fails drift instead; the parse failure is for non-integer text).","commonSituations":"Clients formatting the timestamp as RFC3339 instead of Unix seconds, including surrounding whitespace/quotes, empty header from a misconfigured HTTP client, or clock-sync scripts writing localized digits.","solutions":["Send the timestamp header as plain Unix seconds (base-10 integer), e.g. time.Now().Unix() formatted with strconv.Itoa.","Trim whitespace/quotes from the header value before signing/sending.","Ensure the header is actually populated — empty string fails ParseInt; check client wiring.","Recompute the signature over the canonical request with the corrected timestamp so HMAC still matches."],"exampleFix":"// before\nreq.Timestamp = time.Now().Format(time.RFC3339) // \"2026-09-04T10:00:00Z\"\n// after\nreq.Timestamp = strconv.FormatInt(time.Now().Unix(), 10) // \"1788228000\"","handlingStrategy":"validation","validationCode":"func validTimestampHeader(v string) bool {\n\tv = strings.TrimSpace(v)\n\tif v == \"\" { return false }\n\t_, err := strconv.ParseInt(v, 10, 64)\n\treturn err == nil\n}\n// client side: req.Timestamp = strconv.FormatInt(time.Now().Unix(), 10)","typeGuard":"func isUnixSecondsHeader(v string) bool {\n\t_, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)\n\treturn err == nil\n}","tryCatchPattern":"if err := sidecar.Verify(key, req, sig); err != nil {\n\tvar te *sidecar.TimestampError // or string match on 'invalid timestamp'\n\tif strings.Contains(err.Error(), \"invalid timestamp\") {\n\t\thttp.Error(w, \"timestamp must be Unix seconds\", http.StatusUnauthorized)\n\t\treturn\n\t}\n\thttp.Error(w, \"unauthorized\", http.StatusUnauthorized)\n}","preventionTips":["Always send the timestamp header as Unix seconds from time.Now().Unix().","Never format timestamps as RFC3339 or localized text in HMAC canonical requests.","Trim whitespace/quotes from headers before signing.","Include the timestamp in signature computation after normalization so Verify's parse and HMAC agree."],"tags":["hmac","sidecar","timestamp","authentication"],"backgroundTag":"invalid-timestamp-format","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}