diff --git a/extensions/markdown-language-features/src/preview/documentRenderer.ts b/extensions/markdown-language-features/src/preview/documentRenderer.ts index c7bc75b9849..61182a24436 100644 --- a/extensions/markdown-language-features/src/preview/documentRenderer.ts +++ b/extensions/markdown-language-features/src/preview/documentRenderer.ts @@ -8,8 +8,9 @@ import * as uri from 'vscode-uri'; import { ILogger } from '../logging'; import { MarkdownItEngine } from '../markdownEngine'; import { MarkdownContributionProvider } from '../markdownExtensions'; -import { escapeAttribute, getNonce } from '../util/dom'; +import { escapeAttribute } from '../util/dom'; import { WebviewResourceProvider } from '../util/resources'; +import { generateUuid } from '../util/uuid'; import { MarkdownPreviewConfiguration, MarkdownPreviewConfigurationManager } from './previewConfig'; import { ContentSecurityPolicyArbiter, MarkdownPreviewSecurityLevel } from './security'; @@ -82,7 +83,7 @@ export class MdDocumentRenderer { this._logger.trace('DocumentRenderer', `provideTextDocumentContent - ${markdownDocument.uri}`, initialData); // Content Security Policy - const nonce = getNonce(); + const nonce = generateUuid(); const csp = this._getCsp(resourceProvider, sourceUri, nonce); const body = await this.renderBody(markdownDocument, resourceProvider); diff --git a/extensions/markdown-language-features/src/util/dom.ts b/extensions/markdown-language-features/src/util/dom.ts index 8bbce79c303..16c825c68ff 100644 --- a/extensions/markdown-language-features/src/util/dom.ts +++ b/extensions/markdown-language-features/src/util/dom.ts @@ -11,11 +11,3 @@ export function escapeAttribute(value: string | vscode.Uri): string { .replace(/'/g, '''); } -export function getNonce() { - let text = ''; - const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let i = 0; i < 64; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; -} diff --git a/extensions/markdown-language-features/src/util/uuid.ts b/extensions/markdown-language-features/src/util/uuid.ts new file mode 100644 index 00000000000..ca420b3b6af --- /dev/null +++ b/extensions/markdown-language-features/src/util/uuid.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Copied from src/vs/base/common/uuid.ts + */ +export function generateUuid(): string { + // use `randomUUID` if possible + if (typeof crypto.randomUUID === 'function') { + // see https://developer.mozilla.org/en-US/docs/Web/API/Window/crypto + // > Although crypto is available on all windows, the returned Crypto object only has one + // > usable feature in insecure contexts: the getRandomValues() method. + // > In general, you should use this API only in secure contexts. + + return crypto.randomUUID.bind(crypto)(); + } + + // prep-work + const _data = new Uint8Array(16); + const _hex: string[] = []; + for (let i = 0; i < 256; i++) { + _hex.push(i.toString(16).padStart(2, '0')); + } + + // get data + crypto.getRandomValues(_data); + + // set version bits + _data[6] = (_data[6] & 0x0f) | 0x40; + _data[8] = (_data[8] & 0x3f) | 0x80; + + // print as string + let i = 0; + let result = ''; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + return result; +} diff --git a/extensions/media-preview/package-lock.json b/extensions/media-preview/package-lock.json index d26855f3ad2..fcd827cb0c3 100644 --- a/extensions/media-preview/package-lock.json +++ b/extensions/media-preview/package-lock.json @@ -12,6 +12,9 @@ "@vscode/extension-telemetry": "^0.9.8", "vscode-uri": "^3.0.6" }, + "devDependencies": { + "@types/node": "22.x" + }, "engines": { "vscode": "^1.70.0" } @@ -140,6 +143,16 @@ "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", "license": "MIT" }, + "node_modules/@types/node": { + "version": "22.18.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.13.tgz", + "integrity": "sha512-Bo45YKIjnmFtv6I1TuC8AaHBbqXtIo+Om5fE4QiU1Tj8QR/qt+8O3BAtOimG5IFmwaWiPmB3Mv3jtYzBA4Us2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, "node_modules/@vscode/extension-telemetry": { "version": "0.9.8", "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", @@ -154,6 +167,13 @@ "vscode": "^1.75.0" } }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "node_modules/vscode-uri": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.6.tgz", diff --git a/extensions/media-preview/package.json b/extensions/media-preview/package.json index 02b0134e4cf..18cc50bfb3d 100644 --- a/extensions/media-preview/package.json +++ b/extensions/media-preview/package.json @@ -163,6 +163,9 @@ "@vscode/extension-telemetry": "^0.9.8", "vscode-uri": "^3.0.6" }, + "devDependencies": { + "@types/node": "22.x" + }, "repository": { "type": "git", "url": "https://github.com/microsoft/vscode.git" diff --git a/extensions/media-preview/src/audioPreview.ts b/extensions/media-preview/src/audioPreview.ts index 5058f7e978e..282d579b380 100644 --- a/extensions/media-preview/src/audioPreview.ts +++ b/extensions/media-preview/src/audioPreview.ts @@ -6,7 +6,8 @@ import * as vscode from 'vscode'; import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry'; import { MediaPreview, reopenAsText } from './mediaPreview'; -import { escapeAttribute, getNonce } from './util/dom'; +import { escapeAttribute } from './util/dom'; +import { generateUuid } from './util/uuid'; class AudioPreviewProvider implements vscode.CustomReadonlyEditorProvider { @@ -57,7 +58,7 @@ class AudioPreview extends MediaPreview { src: await this.getResourcePath(this._webviewEditor, this._resource, version), }; - const nonce = getNonce(); + const nonce = generateUuid(); const cspSource = this._webviewEditor.webview.cspSource; return /* html */` diff --git a/extensions/media-preview/src/imagePreview/index.ts b/extensions/media-preview/src/imagePreview/index.ts index b405cd652c4..6c2c8a73f66 100644 --- a/extensions/media-preview/src/imagePreview/index.ts +++ b/extensions/media-preview/src/imagePreview/index.ts @@ -6,7 +6,8 @@ import * as vscode from 'vscode'; import { BinarySizeStatusBarEntry } from '../binarySizeStatusBarEntry'; import { MediaPreview, PreviewState, reopenAsText } from '../mediaPreview'; -import { escapeAttribute, getNonce } from '../util/dom'; +import { escapeAttribute } from '../util/dom'; +import { generateUuid } from '../util/uuid'; import { SizeStatusBarEntry } from './sizeStatusBarEntry'; import { Scale, ZoomStatusBarEntry } from './zoomStatusBarEntry'; @@ -184,7 +185,7 @@ class ImagePreview extends MediaPreview { src: await this.getResourcePath(this._webviewEditor, this._resource, version), }; - const nonce = getNonce(); + const nonce = generateUuid(); const cspSource = this._webviewEditor.webview.cspSource; return /* html */` diff --git a/extensions/media-preview/src/util/dom.ts b/extensions/media-preview/src/util/dom.ts index 0f6c00da9da..f89d668c74d 100644 --- a/extensions/media-preview/src/util/dom.ts +++ b/extensions/media-preview/src/util/dom.ts @@ -7,12 +7,3 @@ import * as vscode from 'vscode'; export function escapeAttribute(value: string | vscode.Uri): string { return value.toString().replace(/"/g, '"'); } - -export function getNonce() { - let text = ''; - const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let i = 0; i < 64; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; -} diff --git a/extensions/media-preview/src/util/uuid.ts b/extensions/media-preview/src/util/uuid.ts new file mode 100644 index 00000000000..ca420b3b6af --- /dev/null +++ b/extensions/media-preview/src/util/uuid.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Copied from src/vs/base/common/uuid.ts + */ +export function generateUuid(): string { + // use `randomUUID` if possible + if (typeof crypto.randomUUID === 'function') { + // see https://developer.mozilla.org/en-US/docs/Web/API/Window/crypto + // > Although crypto is available on all windows, the returned Crypto object only has one + // > usable feature in insecure contexts: the getRandomValues() method. + // > In general, you should use this API only in secure contexts. + + return crypto.randomUUID.bind(crypto)(); + } + + // prep-work + const _data = new Uint8Array(16); + const _hex: string[] = []; + for (let i = 0; i < 256; i++) { + _hex.push(i.toString(16).padStart(2, '0')); + } + + // get data + crypto.getRandomValues(_data); + + // set version bits + _data[6] = (_data[6] & 0x0f) | 0x40; + _data[8] = (_data[8] & 0x3f) | 0x80; + + // print as string + let i = 0; + let result = ''; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + return result; +} diff --git a/extensions/media-preview/src/videoPreview.ts b/extensions/media-preview/src/videoPreview.ts index 67012128cf7..1cb74c58426 100644 --- a/extensions/media-preview/src/videoPreview.ts +++ b/extensions/media-preview/src/videoPreview.ts @@ -6,7 +6,8 @@ import * as vscode from 'vscode'; import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry'; import { MediaPreview, reopenAsText } from './mediaPreview'; -import { escapeAttribute, getNonce } from './util/dom'; +import { escapeAttribute } from './util/dom'; +import { generateUuid } from './util/uuid'; class VideoPreviewProvider implements vscode.CustomReadonlyEditorProvider { @@ -61,7 +62,7 @@ class VideoPreview extends MediaPreview { loop: configurations.get('loop'), }; - const nonce = getNonce(); + const nonce = generateUuid(); const cspSource = this._webviewEditor.webview.cspSource; return /* html */` diff --git a/extensions/mermaid-chat-features/src/extension.ts b/extensions/mermaid-chat-features/src/extension.ts index d66518a8db3..51294649f4f 100644 --- a/extensions/mermaid-chat-features/src/extension.ts +++ b/extensions/mermaid-chat-features/src/extension.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import { generateUuid } from './uuid'; /** * View type that uniquely identifies the Mermaid chat output renderer. @@ -42,7 +43,7 @@ export function activate(context: vscode.ExtensionContext) { }; // Set the HTML content for the webview - const nonce = getNonce(); + const nonce = generateUuid(); const mermaidScript = vscode.Uri.joinPath(mediaRoot, 'index.js'); webview.html = ` @@ -96,11 +97,4 @@ function escapeHtmlText(str: string): string { .replace(/'/g, '''); } -function getNonce() { - let text = ''; - const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let i = 0; i < 64; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; -} + diff --git a/extensions/mermaid-chat-features/src/uuid.ts b/extensions/mermaid-chat-features/src/uuid.ts new file mode 100644 index 00000000000..ca420b3b6af --- /dev/null +++ b/extensions/mermaid-chat-features/src/uuid.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Copied from src/vs/base/common/uuid.ts + */ +export function generateUuid(): string { + // use `randomUUID` if possible + if (typeof crypto.randomUUID === 'function') { + // see https://developer.mozilla.org/en-US/docs/Web/API/Window/crypto + // > Although crypto is available on all windows, the returned Crypto object only has one + // > usable feature in insecure contexts: the getRandomValues() method. + // > In general, you should use this API only in secure contexts. + + return crypto.randomUUID.bind(crypto)(); + } + + // prep-work + const _data = new Uint8Array(16); + const _hex: string[] = []; + for (let i = 0; i < 256; i++) { + _hex.push(i.toString(16).padStart(2, '0')); + } + + // get data + crypto.getRandomValues(_data); + + // set version bits + _data[6] = (_data[6] & 0x0f) | 0x40; + _data[8] = (_data[8] & 0x3f) | 0x80; + + // print as string + let i = 0; + let result = ''; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + return result; +} diff --git a/extensions/simple-browser/package-lock.json b/extensions/simple-browser/package-lock.json index c6d9b23636a..8aa3894ba1e 100644 --- a/extensions/simple-browser/package-lock.json +++ b/extensions/simple-browser/package-lock.json @@ -12,6 +12,7 @@ "@vscode/extension-telemetry": "^0.9.8" }, "devDependencies": { + "@types/node": "22.x", "@types/vscode-webview": "^1.57.0", "@vscode/codicons": "^0.0.36" }, @@ -143,6 +144,16 @@ "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", "license": "MIT" }, + "node_modules/@types/node": { + "version": "22.18.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.13.tgz", + "integrity": "sha512-Bo45YKIjnmFtv6I1TuC8AaHBbqXtIo+Om5fE4QiU1Tj8QR/qt+8O3BAtOimG5IFmwaWiPmB3Mv3jtYzBA4Us2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, "node_modules/@types/vscode-webview": { "version": "1.57.0", "resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.0.tgz", @@ -169,6 +180,13 @@ "engines": { "vscode": "^1.75.0" } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/simple-browser/package.json b/extensions/simple-browser/package.json index 5e081c4bbd2..6dd737b08f5 100644 --- a/extensions/simple-browser/package.json +++ b/extensions/simple-browser/package.json @@ -69,6 +69,7 @@ "@vscode/extension-telemetry": "^0.9.8" }, "devDependencies": { + "@types/node": "22.x", "@types/vscode-webview": "^1.57.0", "@vscode/codicons": "^0.0.36" }, diff --git a/extensions/simple-browser/src/simpleBrowserView.ts b/extensions/simple-browser/src/simpleBrowserView.ts index 5725dcf4f9b..56c5aff5c8a 100644 --- a/extensions/simple-browser/src/simpleBrowserView.ts +++ b/extensions/simple-browser/src/simpleBrowserView.ts @@ -5,6 +5,7 @@ import * as vscode from 'vscode'; import { Disposable } from './dispose'; +import { generateUuid } from './uuid'; export interface ShowOptions { @@ -112,7 +113,7 @@ export class SimpleBrowserView extends Disposable { private getHtml(url: string) { const configuration = vscode.workspace.getConfiguration('simpleBrowser'); - const nonce = getNonce(); + const nonce = generateUuid(); const mainJs = this.extensionResourceUrl('media', 'index.js'); const mainCss = this.extensionResourceUrl('media', 'main.css'); @@ -181,12 +182,3 @@ export class SimpleBrowserView extends Disposable { function escapeAttribute(value: string | vscode.Uri): string { return value.toString().replace(/"/g, '"'); } - -function getNonce() { - let text = ''; - const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let i = 0; i < 64; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; -} diff --git a/extensions/simple-browser/src/uuid.ts b/extensions/simple-browser/src/uuid.ts new file mode 100644 index 00000000000..ca420b3b6af --- /dev/null +++ b/extensions/simple-browser/src/uuid.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Copied from src/vs/base/common/uuid.ts + */ +export function generateUuid(): string { + // use `randomUUID` if possible + if (typeof crypto.randomUUID === 'function') { + // see https://developer.mozilla.org/en-US/docs/Web/API/Window/crypto + // > Although crypto is available on all windows, the returned Crypto object only has one + // > usable feature in insecure contexts: the getRandomValues() method. + // > In general, you should use this API only in secure contexts. + + return crypto.randomUUID.bind(crypto)(); + } + + // prep-work + const _data = new Uint8Array(16); + const _hex: string[] = []; + for (let i = 0; i < 256; i++) { + _hex.push(i.toString(16).padStart(2, '0')); + } + + // get data + crypto.getRandomValues(_data); + + // set version bits + _data[6] = (_data[6] & 0x0f) | 0x40; + _data[8] = (_data[8] & 0x3f) | 0x80; + + // print as string + let i = 0; + let result = ''; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + return result; +} diff --git a/extensions/simple-browser/tsconfig.json b/extensions/simple-browser/tsconfig.json index 60b13d71670..43ed762ce7d 100644 --- a/extensions/simple-browser/tsconfig.json +++ b/extensions/simple-browser/tsconfig.json @@ -2,7 +2,6 @@ "extends": "../tsconfig.base.json", "compilerOptions": { "outDir": "./out", - "types": [], "typeRoots": [ "./node_modules/@types" ]