summaryrefslogtreecommitdiff
path: root/src/shared/messages.js
blob: 95a257e51743a2df39c722cf925f742edfa85354 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
 * Message validation helpers (background side).
 *
 * All messages received from content scripts must pass through these
 * validators before they are used. The page origin is untrusted
 * (Security.md §7): every field is type-checked and length-limited.
 */

import { CONTENT_TO_BG, MAX_SELECTION_LENGTH } from './constants.js';

/**
 * Generate a random request id. Uses crypto.randomUUID when available and
 * falls back to a random-hex string otherwise.
 * @returns {string}
 */
export function createRequestId() {
  if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
    return crypto.randomUUID();
  }
  const bytes = new Uint8Array(16);
  if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {
    crypto.getRandomValues(bytes);
  } else {
    for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);
  }
  return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
}

/**
 * @param {*} message
 * @returns {boolean} true when the message is a valid translate request.
 */
export function isValidTranslateRequest(message) {
  if (message === null || typeof message !== 'object' || Array.isArray(message)) {
    return false;
  }
  if (message.type !== CONTENT_TO_BG.TRANSLATE) return false;
  if (typeof message.requestId !== 'string' || message.requestId.length === 0 || message.requestId.length > 128) {
    return false;
  }
  if (typeof message.text !== 'string') return false;
  if (message.text.length === 0) return false;
  if (message.text.length > MAX_SELECTION_LENGTH) return false;
  return true;
}

/**
 * @param {*} message
 * @returns {boolean} true when the message is a valid cancel request.
 */
export function isValidCancelRequest(message) {
  if (message === null || typeof message !== 'object' || Array.isArray(message)) {
    return false;
  }
  if (message.type !== CONTENT_TO_BG.CANCEL) return false;
  return typeof message.requestId === 'string' && message.requestId.length > 0 && message.requestId.length <= 128;
}

/**
 * Check whether the sender is a background-trusted extension context
 * (i.e. a message coming from our own content script, popup or options).
 * @param {unknown} sender WebExtensions MessageSender
 * @returns {boolean}
 */
export function isTrustedSender(sender) {
  // Only extension-internal senders carry an id; external senders are not
  // accepted anyway because onMessageExternal is not used.
  return Boolean(sender && sender.id);
}