@unavatar/core 3.24.1 → 3.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -52,6 +52,7 @@
52
52
  - [X/Twitter](#xtwitter)
53
53
  - [Xbox Gamertag](#xbox-gamertag)
54
54
  - [YouTube](#youtube)
55
+ - [Privacy](#privacy)
55
56
  - [Response Format](#response-format)
56
57
  - [Response Headers](#response-headers)
57
58
  - [Response Errors](#response-errors)
@@ -69,6 +70,7 @@ It's proudly powered by [microlink.io](https://microlink.io/), the headless brow
69
70
  The service is exposed in **unavatar.io** via provider endpoints:
70
71
 
71
72
  - an **email**: [unavatar.io/email/hello@microlink.io](https://unavatar.io/email/hello@microlink.io)
73
+ - an **email hash** (auto-detected as hashed email): [unavatar.io/email/295dee0d3dbb93aac67e72ae4af40f728f050e5fd18417ad60d1ed19186ae03b](https://unavatar.io/email/295dee0d3dbb93aac67e72ae4af40f728f050e5fd18417ad60d1ed19186ae03b)
72
74
  - a **domain**: [unavatar.io/domain/reddit.com](https://unavatar.io/domain/reddit.com)
73
75
  - an **email** via Gravatar: [unavatar.io/gravatar/hello@microlink.io](https://unavatar.io/gravatar/hello@microlink.io)
74
76
  - an **email** via GitHub: [unavatar.io/github/sindresorhus@gmail.com](https://unavatar.io/github/sindresorhus@gmail.com)
@@ -411,6 +413,10 @@ Get any user's avatar by their email address via Gravatar. The most widely used
411
413
  Available inputs:
412
414
 
413
415
  - Email address, e.g., [unavatar.io/gravatar/hello@microlink.io](https://unavatar.io/gravatar/hello@microlink.io)
416
+ - SHA256 hash, e.g., [unavatar.io/gravatar/295dee0d3dbb93aac67e72ae4af40f728f050e5fd18417ad60d1ed19186ae03b](https://unavatar.io/gravatar/295dee0d3dbb93aac67e72ae4af40f728f050e5fd18417ad60d1ed19186ae03b)
417
+ - MD5 hash (legacy), e.g., [unavatar.io/gravatar/0bc83cb571cd1c50ba6f3e8a78ef1346](https://unavatar.io/gravatar/0bc83cb571cd1c50ba6f3e8a78ef1346)
418
+
419
+ Passing a pre-computed hash instead of the raw email avoids exposing the email address in the URL. See [Privacy](#privacy) for details.
414
420
 
415
421
  ### Instagram
416
422
 
@@ -665,6 +671,27 @@ Available inputs:
665
671
  - `username`: [unavatar.io/youtube/casey](https://unavatar.io/youtube/casey)
666
672
  - `channel`: [unavatar.io/youtube/UC_x5XG1OV2P6uZZ5FSM9Ttw](https://unavatar.io/youtube/UC_x5XG1OV2P6uZZ5FSM9Ttw)
667
673
 
674
+ ## Privacy
675
+
676
+ When you pass an email address directly in the URL, it is visible in plain text — both in server logs and to any intermediary that inspects the request.
677
+
678
+ To avoid this, pass a pre-computed **MD5** or **SHA256** hash of the email instead. Unavatar detects the hash automatically and routes it to Gravatar, which natively supports both hash formats.
679
+
680
+ ```bash
681
+ # Instead of exposing the raw email:
682
+ https://unavatar.io/hello@example.com
683
+
684
+ # Pass the SHA256 hash:
685
+ https://unavatar.io/295dee0d3dbb93aac67e72ae4af40f728f050e5fd18417ad60d1ed19186ae03b
686
+
687
+ # Or the MD5 hash (legacy, still supported by Gravatar):
688
+ https://unavatar.io/0bc83cb571cd1c50ba6f3e8a78ef1346
689
+ ```
690
+
691
+ The hash must be exactly 32 hex characters (MD5) or 64 hex characters (SHA256), lowercase or uppercase — unavatar normalises it automatically.
692
+
693
+ Note: hash-based lookup is only supported for Gravatar. Other providers such as GitHub require the raw email address and cannot reverse a hash.
694
+
668
695
  ## Response Format
669
696
 
670
697
  A response is returning the user avatar by default.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@unavatar/core",
3
3
  "description": "Get unified user avatar from social networks, including Instagram, SoundCloud, Telegram, Twitter, YouTube & more.",
4
4
  "homepage": "https://unavatar.io",
5
- "version": "3.24.1",
5
+ "version": "3.25.0",
6
6
  "main": "src/index.js",
7
7
  "exports": {
8
8
  ".": "./src/index.js",
@@ -50,6 +50,10 @@
50
50
  "name": "Nicolas Hedger",
51
51
  "email": "649677+nhedger@users.noreply.github.com"
52
52
  },
53
+ {
54
+ "name": "Mariano Álvarez",
55
+ "email": "66380259+creativoma@users.noreply.github.com"
56
+ },
53
57
  {
54
58
  "name": "Angel M De Miguel",
55
59
  "email": "angel@bitnami.com"
@@ -15,8 +15,10 @@ const ExtendableError = require('../util/error')
15
15
  const DATA_URI_REGEX = dataUriRegex()
16
16
  const DOMAIN_REGEX = urlRegex({ strict: false })
17
17
 
18
+ const isHash = require('../util/is-hash')
19
+
18
20
  const getInputType = input => {
19
- if (isEmail(input)) return 'email'
21
+ if (isEmail(input) || isHash(input)) return 'email'
20
22
  if (stableRegex(DOMAIN_REGEX, input)) return 'domain'
21
23
  return 'username'
22
24
  }
@@ -84,7 +86,12 @@ const factory = ({ constants, providers, providersBy, reachableUrl }) => {
84
86
  }
85
87
 
86
88
  const resolveAutoByType = async (inputType, input, context) => {
87
- const collection = providerEntriesByType[inputType]
89
+ let collection = providerEntriesByType[inputType]
90
+
91
+ if (inputType === 'email' && isHash(input)) {
92
+ collection = collection.filter(([provider]) => provider === 'gravatar')
93
+ }
94
+
88
95
  const promises = new Array(collection.length)
89
96
 
90
97
  for (let index = 0; index < collection.length; index++) {
@@ -3,14 +3,16 @@
3
3
  const crypto = require('crypto')
4
4
 
5
5
  const stringify = require('../util/stringify')
6
+ const isHash = require('../util/is-hash')
6
7
 
7
- const md5 = str => crypto.createHash('md5').update(str).digest('hex')
8
+ const sha256 = str => crypto.createHash('sha256').update(str).digest('hex')
9
+
10
+ const toHash = input =>
11
+ isHash(input) ? input.toLowerCase() : sha256(input.trim().toLowerCase())
8
12
 
9
13
  module.exports = ({ constants }) =>
10
14
  function gravatar (input) {
11
- return `https://gravatar.com/avatar/${md5(
12
- input.trim().toLowerCase()
13
- )}?${stringify({
15
+ return `https://gravatar.com/avatar/${toHash(input)}?${stringify({
14
16
  size: constants.AVATAR_SIZE,
15
17
  d: '404'
16
18
  })}`
@@ -0,0 +1,5 @@
1
+ 'use strict'
2
+
3
+ const HASH_REGEX = /^[0-9a-f]{32}$|^[0-9a-f]{64}$/i
4
+
5
+ module.exports = input => HASH_REGEX.test(input)