mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
Merge pull request #163 from dayi/fix/recording-http-random-uuid
fix(extension): support UUID generation on insecure origins
This commit is contained in:
@@ -45,10 +45,32 @@ function portHarness() {
|
|||||||
|
|
||||||
describe("RecordFrameAgent", () => {
|
describe("RecordFrameAgent", () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
vi.unstubAllGlobals();
|
||||||
document.documentElement.removeAttribute(RECORD_DOCUMENT_ATTRIBUTE);
|
document.documentElement.removeAttribute(RECORD_DOCUMENT_ATTRIBUTE);
|
||||||
document.body.replaceChildren();
|
document.body.replaceChildren();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("starts when crypto.randomUUID is unavailable", async () => {
|
||||||
|
const harness = portHarness();
|
||||||
|
const getRandomValues = vi.fn((bytes: Uint8Array) => {
|
||||||
|
bytes.fill(0x11);
|
||||||
|
return bytes;
|
||||||
|
});
|
||||||
|
vi.stubGlobal("crypto", { getRandomValues });
|
||||||
|
vi.stubGlobal("chrome", {
|
||||||
|
runtime: {
|
||||||
|
connect: vi.fn(() => harness.port),
|
||||||
|
sendMessage: vi.fn(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const agent = new RecordFrameAgent();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
agent.start({ type: RECORD_FRAME_START, requestId: "rec-http", startedAtMs: 10 }),
|
||||||
|
).resolves.toEqual({ ok: true });
|
||||||
|
expect(getRandomValues).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps a failed stop retryable and flushes the final dirty fill", async () => {
|
it("keeps a failed stop retryable and flushes the final dirty fill", async () => {
|
||||||
const harness = portHarness();
|
const harness = portHarness();
|
||||||
const sendMessage = vi
|
const sendMessage = vi
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { createRandomUuid } from "@/lib/random-uuid";
|
||||||
import {
|
import {
|
||||||
isAcceptedRecordStepAck,
|
isAcceptedRecordStepAck,
|
||||||
RECORD_STEP,
|
RECORD_STEP,
|
||||||
@@ -18,7 +19,7 @@ export class RecordStepDelivery {
|
|||||||
constructor(
|
constructor(
|
||||||
requestId: string,
|
requestId: string,
|
||||||
send: SendRecordStepMessage = (message) => chrome.runtime.sendMessage(message),
|
send: SendRecordStepMessage = (message) => chrome.runtime.sendMessage(message),
|
||||||
producerId: string = crypto.randomUUID(),
|
producerId: string = createRandomUuid(),
|
||||||
) {
|
) {
|
||||||
this.#requestId = requestId;
|
this.#requestId = requestId;
|
||||||
this.#producerId = producerId;
|
this.#producerId = producerId;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { createRandomUuid } from "@/lib/random-uuid";
|
||||||
import {
|
import {
|
||||||
markRecordingDocument,
|
markRecordingDocument,
|
||||||
type RecordingDocumentMarker,
|
type RecordingDocumentMarker,
|
||||||
@@ -67,7 +68,7 @@ export class RecordFrameAgent {
|
|||||||
async #start(
|
async #start(
|
||||||
message: RecordFrameStartMessage,
|
message: RecordFrameStartMessage,
|
||||||
): Promise<{ ok: true } | { ok: false; error: string }> {
|
): Promise<{ ok: true } | { ok: false; error: string }> {
|
||||||
const producerId = crypto.randomUUID();
|
const producerId = createRandomUuid();
|
||||||
const root = await waitForDocumentElement();
|
const root = await waitForDocumentElement();
|
||||||
if (this.#disposed) return { ok: false, error: "recording document is disposed" };
|
if (this.#disposed) return { ok: false, error: "recording document is disposed" };
|
||||||
const marker = markRecordingDocument(producerId, root);
|
const marker = markRecordingDocument(producerId, root);
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import { createRandomUuid } from "../random-uuid";
|
||||||
|
|
||||||
|
describe("createRandomUuid", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
vi.unstubAllGlobals();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses crypto.randomUUID when it is available", () => {
|
||||||
|
const randomUUID = vi.fn(() => "00000000-0000-4000-8000-000000000000");
|
||||||
|
vi.stubGlobal("crypto", { randomUUID, getRandomValues: vi.fn() });
|
||||||
|
|
||||||
|
expect(createRandomUuid()).toBe("00000000-0000-4000-8000-000000000000");
|
||||||
|
expect(randomUUID).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("falls back to getRandomValues and sets UUID v4 bits", () => {
|
||||||
|
const bytes = new Uint8Array(16).fill(0xff);
|
||||||
|
const getRandomValues = vi.fn((target: Uint8Array) => {
|
||||||
|
target.set(bytes);
|
||||||
|
return target;
|
||||||
|
});
|
||||||
|
vi.stubGlobal("crypto", { getRandomValues });
|
||||||
|
|
||||||
|
const uuid = createRandomUuid();
|
||||||
|
|
||||||
|
expect(uuid).toBe("ffffffff-ffff-4fff-bfff-ffffffffffff");
|
||||||
|
expect(getRandomValues).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Generate a UUID v4 in browser contexts where randomUUID() is unavailable.
|
||||||
|
*
|
||||||
|
* Crypto.randomUUID() is restricted to secure contexts, while
|
||||||
|
* crypto.getRandomValues() is available to content scripts on ordinary HTTP
|
||||||
|
* origins. BrowserSkill records many intranet pages served over HTTP, so the
|
||||||
|
* fallback is required for recording to work on those pages.
|
||||||
|
*/
|
||||||
|
export function createRandomUuid(): string {
|
||||||
|
const cryptoApi = globalThis.crypto;
|
||||||
|
if (typeof cryptoApi.randomUUID === "function") return cryptoApi.randomUUID();
|
||||||
|
|
||||||
|
const bytes = new Uint8Array(16);
|
||||||
|
cryptoApi.getRandomValues(bytes);
|
||||||
|
bytes[6] = (bytes[6]! & 0x0f) | 0x40;
|
||||||
|
bytes[8] = (bytes[8]! & 0x3f) | 0x80;
|
||||||
|
|
||||||
|
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
||||||
|
return [
|
||||||
|
hex.slice(0, 8),
|
||||||
|
hex.slice(8, 12),
|
||||||
|
hex.slice(12, 16),
|
||||||
|
hex.slice(16, 20),
|
||||||
|
hex.slice(20),
|
||||||
|
].join("-");
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { createRandomUuid } from "../random-uuid";
|
||||||
import {
|
import {
|
||||||
isRecordFrameQueryMessage,
|
isRecordFrameQueryMessage,
|
||||||
RECORD_FRAME_PORT,
|
RECORD_FRAME_PORT,
|
||||||
@@ -188,7 +189,7 @@ export class RecordFrameCoordinator {
|
|||||||
const recording = this.#recordings.get(requestId);
|
const recording = this.#recordings.get(requestId);
|
||||||
if (!recording) return true;
|
if (!recording) return true;
|
||||||
recording.finishing = true;
|
recording.finishing = true;
|
||||||
const commandId = crypto.randomUUID();
|
const commandId = createRandomUuid();
|
||||||
const results = await Promise.all(
|
const results = await Promise.all(
|
||||||
[...recording.agents.values()].map((agent) => this.#stopAgent(agent, requestId, commandId)),
|
[...recording.agents.values()].map((agent) => this.#stopAgent(agent, requestId, commandId)),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user