-
Notifications
You must be signed in to change notification settings - Fork 0
Add "source" field to user-agent header #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |||||||||||||||||
| import java.io.InputStream; | ||||||||||||||||||
| import java.net.ConnectException; | ||||||||||||||||||
| import java.net.SocketTimeoutException; | ||||||||||||||||||
| import java.security.MessageDigest; | ||||||||||||||||||
| import java.time.Duration; | ||||||||||||||||||
| import java.util.HashMap; | ||||||||||||||||||
| import java.util.Map; | ||||||||||||||||||
|
|
@@ -26,6 +27,53 @@ public abstract class HttpClient { | |||||||||||||||||
| /** A value indicating whether the client should sleep between automatic request retries. */ | ||||||||||||||||||
| boolean networkRetriesSleep = true; | ||||||||||||||||||
|
|
||||||||||||||||||
| static String UNAME_HASH = computeUnameHash(); | ||||||||||||||||||
|
|
||||||||||||||||||
| private static String computeUnameHash() { | ||||||||||||||||||
| String uname = ""; | ||||||||||||||||||
| try { | ||||||||||||||||||
| uname = | ||||||||||||||||||
| (System.getProperty("os.name", "") | ||||||||||||||||||
| + " " | ||||||||||||||||||
| + System.getProperty("os.version", "") | ||||||||||||||||||
| + " " | ||||||||||||||||||
| + System.getProperty("os.arch", "") | ||||||||||||||||||
| + " " | ||||||||||||||||||
| + System.getProperty("java.version", "") | ||||||||||||||||||
| + " " | ||||||||||||||||||
| + System.getProperty("java.vendor", "") | ||||||||||||||||||
| + " " | ||||||||||||||||||
| + System.getProperty("java.vm.name", "") | ||||||||||||||||||
| + " " | ||||||||||||||||||
| + getHostname()) | ||||||||||||||||||
| .trim(); | ||||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||||
| // fall through with empty string | ||||||||||||||||||
| } | ||||||||||||||||||
| if (uname.isEmpty()) { | ||||||||||||||||||
| return ""; | ||||||||||||||||||
| } | ||||||||||||||||||
| try { | ||||||||||||||||||
| MessageDigest md = MessageDigest.getInstance("MD5"); | ||||||||||||||||||
| byte[] hashBytes = md.digest(uname.getBytes(java.nio.charset.StandardCharsets.UTF_8)); | ||||||||||||||||||
| StringBuilder sb = new StringBuilder(); | ||||||||||||||||||
| for (byte b : hashBytes) { | ||||||||||||||||||
| sb.append(String.format("%02x", b)); | ||||||||||||||||||
| } | ||||||||||||||||||
| return sb.toString(); | ||||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||||
| return ""; | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| private static String getHostname() { | ||||||||||||||||||
| try { | ||||||||||||||||||
| return java.net.InetAddress.getLocalHost().getHostName(); | ||||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||||
| return ""; | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** Initializes a new instance of the {@link HttpClient} class. */ | ||||||||||||||||||
| protected HttpClient() {} | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -228,6 +276,10 @@ static String buildXStripeClientUserAgentString(String aiAgent) { | |||||||||||||||||
| propertyMap.put("ai_agent", aiAgent); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (!UNAME_HASH.isEmpty()) { | ||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛑 Blocker · Security · The UNAME_HASH is included in the X-Stripe-Client-User-Agent header unconditionally, even when users set Stripe.enableTelemetry = false to opt out of system information collection. The platform field correctly respects this setting (lines 261-269), but the source field does not. This creates inconsistent privacy behavior where users who explicitly disable telemetry still have their system fingerprint (OS, Java version, hostname hash) transmitted. Fix: wrap the source field insertion with the same telemetry guard:
Suggested change
Was this helpful? React with 👍 or 👎 to help VibeOps learn. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛑 Blocker · Security · The
Suggested change
Was this helpful? React with 👍 or 👎 to help VibeOps learn. |
||||||||||||||||||
| propertyMap.put("source", UNAME_HASH); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| return ApiResource.INTERNAL_GSON.toJson(propertyMap); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💭 Heads-up · Bug ·
UNAME_HASHis declared as a non-final, non-volatilepackage-private static field (static String UNAME_HASH). This has two problems: (1) Withoutvolatile, reads/writes from multiple threads are not guaranteed to see a consistent value — a thread could observe a partially-initialized or stale value. (2) The field is mutable by any code in the same package (as demonstrated by the test directly assigningHttpClient.UNAME_HASH = ""), making it easy to accidentally corrupt shared state. Since the value is computed once at class-load time and should never change, it should be declaredstatic final. The test that mutates it should instead use a test-specific override mechanism (e.g., a package-private setter or reflection) rather than relying on the field being mutable.Was this helpful? React with 👍 or 👎 to help VibeOps learn.