diff options
Diffstat (limited to 'src/shared/messages.js')
| -rw-r--r-- | src/shared/messages.js | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/src/shared/messages.js b/src/shared/messages.js new file mode 100644 index 0000000..95a257e --- /dev/null +++ b/src/shared/messages.js @@ -0,0 +1,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); +}
\ No newline at end of file |
