playwriter 0.2.0 → 0.3.1
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/dist/bippy.js +1 -1
- package/dist/cdp-log.d.ts +4 -1
- package/dist/cdp-log.d.ts.map +1 -1
- package/dist/cdp-log.js +39 -2
- package/dist/cdp-log.js.map +1 -1
- package/dist/cdp-log.test.d.ts +2 -0
- package/dist/cdp-log.test.d.ts.map +1 -0
- package/dist/cdp-log.test.js +109 -0
- package/dist/cdp-log.test.js.map +1 -0
- package/dist/cdp-relay.d.ts.map +1 -1
- package/dist/cdp-relay.js +99 -6
- package/dist/cdp-relay.js.map +1 -1
- package/dist/cli.js +14 -12
- package/dist/cli.js.map +1 -1
- package/dist/executor.d.ts +3 -0
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +106 -36
- package/dist/executor.js.map +1 -1
- package/dist/extension/background.js +23 -12
- package/dist/extension/manifest.json +1 -1
- package/dist/prompt.md +32 -13
- package/dist/readability.js +1 -1
- package/dist/relay-client.d.ts +11 -0
- package/dist/relay-client.d.ts.map +1 -1
- package/dist/relay-client.js +46 -1
- package/dist/relay-client.js.map +1 -1
- package/dist/relay-core.test.js +10 -6
- package/dist/relay-core.test.js.map +1 -1
- package/dist/relay-session.test.js +9 -1
- package/dist/relay-session.test.js.map +1 -1
- package/dist/relay-state.test.js +57 -1
- package/dist/relay-state.test.js.map +1 -1
- package/dist/selector-generator.js +1 -1
- package/dist/start-relay-server.d.ts +1 -1
- package/dist/start-relay-server.d.ts.map +1 -1
- package/dist/start-relay-server.js +23 -1
- package/dist/start-relay-server.js.map +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +3 -0
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/cdp-log.test.ts +131 -0
- package/src/cdp-log.ts +44 -2
- package/src/cdp-relay.ts +104 -6
- package/src/cli.ts +14 -13
- package/src/executor.ts +122 -39
- package/src/relay-client.ts +62 -5
- package/src/relay-core.test.ts +10 -6
- package/src/relay-session.test.ts +9 -1
- package/src/relay-state.test.ts +67 -1
- package/src/skill.md +32 -13
- package/src/start-relay-server.ts +22 -1
- package/src/utils.ts +4 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start-relay-server.js","sourceRoot":"","sources":["../src/start-relay-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,gBAAgB,CAAA;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAE9C,OAAO,CAAC,KAAK,GAAG,sBAAsB,CAAA;AAEtC,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAA;AAEjC,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;IAC5C,MAAM,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAA;IAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA;AAEF,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;IAChD,MAAM,MAAM,CAAC,KAAK,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAA;IAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA;AAEF,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;IAChC,MAAM,MAAM,CAAC,GAAG,CAAC,8BAA8B,IAAI,EAAE,CAAC,CAAA;AACxD,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAChC,IAAI,GAAG,KAAK,EACZ,IAAI,GAAG,WAAW,EAClB,KAAK,MAC+C,EAAE;IACtD,MAAM,MAAM,GAAG,MAAM,6BAA6B,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"start-relay-server.js","sourceRoot":"","sources":["../src/start-relay-server.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare function getCdpUrl({ port, host, token, extensionId, }?: {
|
|
|
17
17
|
token?: string;
|
|
18
18
|
extensionId?: string | null;
|
|
19
19
|
}): string;
|
|
20
|
+
export declare function shouldAutoEnablePlaywriter(): boolean;
|
|
20
21
|
export declare const LOG_FILE_PATH: string;
|
|
21
22
|
export declare const LOG_CDP_FILE_PATH: string;
|
|
22
23
|
export declare const VERSION: string;
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,aAAa,UAGzB,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAc,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAY7G;AAED,wBAAgB,SAAS,CAAC,EACxB,IAAY,EACZ,IAAkB,EAClB,KAAK,EACL,WAAW,GACZ,GAAE;IACD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB,UAaL;AAID,eAAO,MAAM,aAAa,QAAsF,CAAA;AAChH,eAAO,MAAM,iBAAiB,QACmE,CAAA;AAGjG,eAAO,MAAM,OAAO,EAAoE,MAAM,CAAA;AAE9F,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,aAAa,UAGzB,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAc,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAY7G;AAED,wBAAgB,SAAS,CAAC,EACxB,IAAY,EACZ,IAAkB,EAClB,KAAK,EACL,WAAW,GACZ,GAAE;IACD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB,UAaL;AAED,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAID,eAAO,MAAM,aAAa,QAAsF,CAAA;AAChH,eAAO,MAAM,iBAAiB,QACmE,CAAA;AAGjG,eAAO,MAAM,OAAO,EAAoE,MAAM,CAAA;AAE9F,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C"}
|
package/dist/utils.js
CHANGED
|
@@ -42,6 +42,9 @@ export function getCdpUrl({ port = 19988, host = '127.0.0.1', token, extensionId
|
|
|
42
42
|
const { wsBaseUrl } = parseRelayHost(host, port);
|
|
43
43
|
return `${wsBaseUrl}/cdp/${id}${suffix}`;
|
|
44
44
|
}
|
|
45
|
+
export function shouldAutoEnablePlaywriter() {
|
|
46
|
+
return process.env.PLAYWRITER_AUTO_ENABLE?.toLowerCase() !== 'false';
|
|
47
|
+
}
|
|
45
48
|
// Use ~/.playwriter for logs so each OS user gets their own dir (avoids permission errors on shared machines, see #44)
|
|
46
49
|
const LOG_BASE_DIR = path.join(os.homedir(), '.playwriter');
|
|
47
50
|
export const LOG_FILE_PATH = process.env.PLAYWRITER_LOG_FILE_PATH || path.join(LOG_BASE_DIR, 'relay-server.log');
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import os from 'node:os'
|
|
5
|
+
import { createCdpLogger, type CdpLogEntry } from './cdp-log.js'
|
|
6
|
+
|
|
7
|
+
function makeTmpDir() {
|
|
8
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), 'cdp-log-test-'))
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function makeEntry(i: number): CdpLogEntry {
|
|
12
|
+
return {
|
|
13
|
+
timestamp: new Date().toISOString(),
|
|
14
|
+
direction: 'from-extension',
|
|
15
|
+
message: { method: `Test.method${i}`, id: i },
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function readIds(logFile: string): number[] {
|
|
20
|
+
return fs
|
|
21
|
+
.readFileSync(logFile, 'utf-8')
|
|
22
|
+
.trim()
|
|
23
|
+
.split('\n')
|
|
24
|
+
.filter((l) => {
|
|
25
|
+
return l.length > 0
|
|
26
|
+
})
|
|
27
|
+
.map((l) => {
|
|
28
|
+
return JSON.parse(l).message.id as number
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe('CDP log rotation', () => {
|
|
33
|
+
it('rotates when lineCount exceeds maxEntries, keeping last half', async () => {
|
|
34
|
+
const tmpDir = makeTmpDir()
|
|
35
|
+
const logFile = path.join(tmpDir, 'cdp.jsonl')
|
|
36
|
+
const logger = createCdpLogger({ logFilePath: logFile, maxEntries: 20 })
|
|
37
|
+
|
|
38
|
+
// Write 25 entries to trigger rotation (threshold is 20)
|
|
39
|
+
for (let i = 0; i < 25; i++) {
|
|
40
|
+
logger.log(makeEntry(i))
|
|
41
|
+
}
|
|
42
|
+
await logger.flush()
|
|
43
|
+
|
|
44
|
+
const ids = readIds(logFile)
|
|
45
|
+
|
|
46
|
+
// Rotation triggers after entry 20 is written (lineCount becomes 21 > 20).
|
|
47
|
+
// It keeps last 10 (entries 11-20), then entries 21-24 are appended.
|
|
48
|
+
expect(ids).toMatchInlineSnapshot(`
|
|
49
|
+
[
|
|
50
|
+
11,
|
|
51
|
+
12,
|
|
52
|
+
13,
|
|
53
|
+
14,
|
|
54
|
+
15,
|
|
55
|
+
16,
|
|
56
|
+
17,
|
|
57
|
+
18,
|
|
58
|
+
19,
|
|
59
|
+
20,
|
|
60
|
+
21,
|
|
61
|
+
22,
|
|
62
|
+
23,
|
|
63
|
+
24,
|
|
64
|
+
]
|
|
65
|
+
`)
|
|
66
|
+
|
|
67
|
+
fs.rmSync(tmpDir, { recursive: true })
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('does not rotate when under maxEntries', async () => {
|
|
71
|
+
const tmpDir = makeTmpDir()
|
|
72
|
+
const logFile = path.join(tmpDir, 'cdp.jsonl')
|
|
73
|
+
const logger = createCdpLogger({ logFilePath: logFile, maxEntries: 50 })
|
|
74
|
+
|
|
75
|
+
for (let i = 0; i < 30; i++) {
|
|
76
|
+
logger.log(makeEntry(i))
|
|
77
|
+
}
|
|
78
|
+
await logger.flush()
|
|
79
|
+
|
|
80
|
+
const ids = readIds(logFile)
|
|
81
|
+
expect(ids.length).toBe(30)
|
|
82
|
+
expect(ids[0]).toBe(0)
|
|
83
|
+
expect(ids[29]).toBe(29)
|
|
84
|
+
|
|
85
|
+
fs.rmSync(tmpDir, { recursive: true })
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('handles multiple rotations', async () => {
|
|
89
|
+
const tmpDir = makeTmpDir()
|
|
90
|
+
const logFile = path.join(tmpDir, 'cdp.jsonl')
|
|
91
|
+
const logger = createCdpLogger({ logFilePath: logFile, maxEntries: 10 })
|
|
92
|
+
|
|
93
|
+
// Write 35 entries, should trigger multiple rotations
|
|
94
|
+
for (let i = 0; i < 35; i++) {
|
|
95
|
+
logger.log(makeEntry(i))
|
|
96
|
+
}
|
|
97
|
+
await logger.flush()
|
|
98
|
+
|
|
99
|
+
const ids = readIds(logFile)
|
|
100
|
+
|
|
101
|
+
// File should never exceed maxEntries
|
|
102
|
+
expect(ids.length).toBeLessThanOrEqual(15)
|
|
103
|
+
expect(ids.length).toBeGreaterThanOrEqual(5)
|
|
104
|
+
|
|
105
|
+
// Last entry should always be the most recent
|
|
106
|
+
expect(ids[ids.length - 1]).toBe(34)
|
|
107
|
+
// No entries from the very beginning should survive multiple rotations
|
|
108
|
+
expect(ids[0]).toBeGreaterThan(10)
|
|
109
|
+
|
|
110
|
+
fs.rmSync(tmpDir, { recursive: true })
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('uses atomic rename for rotation', async () => {
|
|
114
|
+
const tmpDir = makeTmpDir()
|
|
115
|
+
const logFile = path.join(tmpDir, 'cdp.jsonl')
|
|
116
|
+
const logger = createCdpLogger({ logFilePath: logFile, maxEntries: 10 })
|
|
117
|
+
|
|
118
|
+
for (let i = 0; i < 15; i++) {
|
|
119
|
+
logger.log(makeEntry(i))
|
|
120
|
+
}
|
|
121
|
+
await logger.flush()
|
|
122
|
+
|
|
123
|
+
// Temp file should not remain after successful rotation
|
|
124
|
+
expect(fs.existsSync(`${logFile}.tmp`)).toBe(false)
|
|
125
|
+
|
|
126
|
+
const ids = readIds(logFile)
|
|
127
|
+
expect(ids[ids.length - 1]).toBe(14)
|
|
128
|
+
|
|
129
|
+
fs.rmSync(tmpDir, { recursive: true })
|
|
130
|
+
})
|
|
131
|
+
})
|
package/src/cdp-log.ts
CHANGED
|
@@ -12,6 +12,8 @@ export type CdpLogEntry = {
|
|
|
12
12
|
|
|
13
13
|
export type CdpLogger = {
|
|
14
14
|
log(entry: CdpLogEntry): void
|
|
15
|
+
/** Wait for all pending writes (and any in-flight rotation) to complete */
|
|
16
|
+
flush(): Promise<void>
|
|
15
17
|
logFilePath: string
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -41,10 +43,20 @@ function createTruncatingReplacer({ maxStringLength }: { maxStringLength: number
|
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
const DEFAULT_MAX_ENTRIES = 10_000
|
|
47
|
+
|
|
48
|
+
function resolvePositiveInt(value: number | undefined, fallback: number): number {
|
|
49
|
+
if (value == null || !Number.isFinite(value) || value < 2) {
|
|
50
|
+
return fallback
|
|
51
|
+
}
|
|
52
|
+
return Math.floor(value)
|
|
53
|
+
}
|
|
54
|
+
|
|
44
55
|
export function createCdpLogger({
|
|
45
56
|
logFilePath,
|
|
46
57
|
maxStringLength,
|
|
47
|
-
|
|
58
|
+
maxEntries,
|
|
59
|
+
}: { logFilePath?: string; maxStringLength?: number; maxEntries?: number } = {}): CdpLogger {
|
|
48
60
|
const resolvedLogFilePath = logFilePath || LOG_CDP_FILE_PATH
|
|
49
61
|
const logDir = path.dirname(resolvedLogFilePath)
|
|
50
62
|
if (!fs.existsSync(logDir)) {
|
|
@@ -53,16 +65,46 @@ export function createCdpLogger({
|
|
|
53
65
|
fs.writeFileSync(resolvedLogFilePath, '')
|
|
54
66
|
|
|
55
67
|
let queue: Promise<void> = Promise.resolve()
|
|
68
|
+
let lineCount = 0
|
|
56
69
|
const maxLength = maxStringLength ?? DEFAULT_MAX_STRING_LENGTH
|
|
70
|
+
const envMaxEntries = Number(process.env.PLAYWRITER_CDP_LOG_MAX_ENTRIES)
|
|
71
|
+
const resolvedMaxEntries = resolvePositiveInt(maxEntries, resolvePositiveInt(envMaxEntries, DEFAULT_MAX_ENTRIES))
|
|
72
|
+
// Keep half the entries after rotation so we don't rotate on every write
|
|
73
|
+
const keepAfterRotation = Math.floor(resolvedMaxEntries / 2)
|
|
74
|
+
|
|
75
|
+
// Atomic rotation: write to temp file then rename to avoid corruption on crash
|
|
76
|
+
const rotate = async (): Promise<void> => {
|
|
77
|
+
try {
|
|
78
|
+
const content = await fs.promises.readFile(resolvedLogFilePath, 'utf-8')
|
|
79
|
+
const lines = content.split('\n').filter((l) => {
|
|
80
|
+
return l.length > 0
|
|
81
|
+
})
|
|
82
|
+
const kept = lines.slice(-keepAfterRotation)
|
|
83
|
+
const tmpPath = `${resolvedLogFilePath}.tmp`
|
|
84
|
+
await fs.promises.writeFile(tmpPath, kept.join('\n') + '\n')
|
|
85
|
+
await fs.promises.rename(tmpPath, resolvedLogFilePath)
|
|
86
|
+
lineCount = kept.length
|
|
87
|
+
} catch {
|
|
88
|
+
// If rotation fails (disk error, permissions), keep logging without rotation.
|
|
89
|
+
// lineCount stays high so rotation will be retried on next write.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
57
92
|
|
|
58
93
|
const log = (entry: CdpLogEntry): void => {
|
|
59
94
|
const replacer = createTruncatingReplacer({ maxStringLength: maxLength })
|
|
60
95
|
const line = JSON.stringify(entry, replacer)
|
|
61
|
-
queue = queue.then(() =>
|
|
96
|
+
queue = queue.then(async () => {
|
|
97
|
+
await fs.promises.appendFile(resolvedLogFilePath, `${line}\n`)
|
|
98
|
+
lineCount++
|
|
99
|
+
if (lineCount > resolvedMaxEntries) {
|
|
100
|
+
await rotate()
|
|
101
|
+
}
|
|
102
|
+
})
|
|
62
103
|
}
|
|
63
104
|
|
|
64
105
|
return {
|
|
65
106
|
log,
|
|
107
|
+
flush: () => queue,
|
|
66
108
|
logFilePath: resolvedLogFilePath,
|
|
67
109
|
}
|
|
68
110
|
}
|
package/src/cdp-relay.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Hono } from 'hono'
|
|
2
2
|
import { cors } from 'hono/cors'
|
|
3
|
-
import {
|
|
3
|
+
import { createAdaptorServer } from '@hono/node-server'
|
|
4
4
|
import { getConnInfo } from '@hono/node-server/conninfo'
|
|
5
5
|
import { createNodeWebSocket } from '@hono/node-ws'
|
|
6
6
|
import type { WSContext } from 'hono/ws'
|
|
@@ -25,7 +25,7 @@ Buffer.prototype[util.inspect.custom] = function () {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
import { EventEmitter } from 'node:events'
|
|
28
|
-
import { VERSION, EXTENSION_IDS } from './utils.js'
|
|
28
|
+
import { VERSION, EXTENSION_IDS, shouldAutoEnablePlaywriter } from './utils.js'
|
|
29
29
|
import { createCdpLogger, type CdpLogEntry, type CdpLogger } from './cdp-log.js'
|
|
30
30
|
import { RecordingRelay } from './recording-relay.js'
|
|
31
31
|
import { appendSessionToWsUrl } from './chrome-discovery.js'
|
|
@@ -521,10 +521,10 @@ export async function startPlayWriterCDPRelayServer({
|
|
|
521
521
|
return recordingRelays.get(connId) || null
|
|
522
522
|
}
|
|
523
523
|
|
|
524
|
-
// Auto-create initial tab when
|
|
525
|
-
//
|
|
524
|
+
// Auto-create an initial blank tab when no targets exist. Set
|
|
525
|
+
// PLAYWRITER_AUTO_ENABLE=false to require manually enabled tabs instead.
|
|
526
526
|
async function maybeAutoCreateInitialTab(extensionId: string): Promise<void> {
|
|
527
|
-
if (!
|
|
527
|
+
if (!shouldAutoEnablePlaywriter()) {
|
|
528
528
|
return
|
|
529
529
|
}
|
|
530
530
|
const conn = getExtensionConnection(extensionId)
|
|
@@ -856,6 +856,87 @@ export async function startPlayWriterCDPRelayServer({
|
|
|
856
856
|
allowMethods: ['GET', 'POST', 'HEAD', 'OPTIONS'],
|
|
857
857
|
}),
|
|
858
858
|
)
|
|
859
|
+
// Host header validation to prevent DNS rebinding attacks.
|
|
860
|
+
// DNS rebinding is worse than a simple cross-origin request: the attacker
|
|
861
|
+
// serves a page from http://evil.com:19988, then rebinds the DNS to
|
|
862
|
+
// 127.0.0.1. The browser now considers requests to our relay as same-origin,
|
|
863
|
+
// so Sec-Fetch-Site is "same-origin", CORS doesn't apply, and JSON POSTs
|
|
864
|
+
// don't need preflight. This bypasses all our other defenses.
|
|
865
|
+
// By rejecting any Host that isn't a known localhost value we kill DNS
|
|
866
|
+
// rebinding at the root. When a valid token is provided (remote access), we
|
|
867
|
+
// allow through regardless of Host since remote clients use real hostnames.
|
|
868
|
+
const ALLOWED_HOSTS = new Set([
|
|
869
|
+
'localhost',
|
|
870
|
+
'127.0.0.1',
|
|
871
|
+
'[::1]',
|
|
872
|
+
'::1',
|
|
873
|
+
])
|
|
874
|
+
|
|
875
|
+
// Parse the Host header into just the hostname, handling IPv6 brackets and
|
|
876
|
+
// port suffixes. Returns null for missing or malformed values.
|
|
877
|
+
function parseHostname(hostHeader: string | undefined): string | null {
|
|
878
|
+
const value = hostHeader?.trim().toLowerCase()
|
|
879
|
+
if (!value) {
|
|
880
|
+
return null
|
|
881
|
+
}
|
|
882
|
+
// IPv6 in brackets: [::1] or [::1]:19988
|
|
883
|
+
if (value.startsWith('[')) {
|
|
884
|
+
const closingBracket = value.indexOf(']')
|
|
885
|
+
if (closingBracket === -1) {
|
|
886
|
+
return null
|
|
887
|
+
}
|
|
888
|
+
const host = value.slice(0, closingBracket + 1)
|
|
889
|
+
const rest = value.slice(closingBracket + 1)
|
|
890
|
+
if (rest && !/^:\d+$/.test(rest)) {
|
|
891
|
+
return null
|
|
892
|
+
}
|
|
893
|
+
return host
|
|
894
|
+
}
|
|
895
|
+
// Bare ::1 without brackets (uncommon but possible)
|
|
896
|
+
if (value === '::1') {
|
|
897
|
+
return '::1'
|
|
898
|
+
}
|
|
899
|
+
// hostname or hostname:port
|
|
900
|
+
const colonIndex = value.indexOf(':')
|
|
901
|
+
if (colonIndex === -1) {
|
|
902
|
+
return value
|
|
903
|
+
}
|
|
904
|
+
const host = value.slice(0, colonIndex)
|
|
905
|
+
const portPart = value.slice(colonIndex + 1)
|
|
906
|
+
if (!/^\d+$/.test(portPart)) {
|
|
907
|
+
return null
|
|
908
|
+
}
|
|
909
|
+
return host || null
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
function hasValidToken(c: { req: { header: (name: string) => string | undefined; url: string } }): boolean {
|
|
913
|
+
if (!token) {
|
|
914
|
+
return false
|
|
915
|
+
}
|
|
916
|
+
const authHeader = c.req.header('authorization') || ''
|
|
917
|
+
const bearerToken = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : null
|
|
918
|
+
const queryToken = new URL(c.req.url, 'http://localhost').searchParams.get('token')
|
|
919
|
+
return bearerToken === token || queryToken === token
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
app.use('*', async (c, next) => {
|
|
923
|
+
const hostname = parseHostname(c.req.header('host'))
|
|
924
|
+
if (hostname && ALLOWED_HOSTS.has(hostname)) {
|
|
925
|
+
return next()
|
|
926
|
+
}
|
|
927
|
+
// Remote clients with a valid token are allowed regardless of Host
|
|
928
|
+
if (hasValidToken(c)) {
|
|
929
|
+
return next()
|
|
930
|
+
}
|
|
931
|
+
// Missing Host header from non-browser clients (curl without Host) is fine
|
|
932
|
+
// in local mode since they're not browser-based DNS rebinding attacks
|
|
933
|
+
if (!hostname && !token) {
|
|
934
|
+
return next()
|
|
935
|
+
}
|
|
936
|
+
logger?.log(pc.red(`Rejecting request with unexpected Host header: ${c.req.header('host')} (DNS rebinding protection)`))
|
|
937
|
+
return c.text('Forbidden - Invalid Host header', 403)
|
|
938
|
+
})
|
|
939
|
+
|
|
859
940
|
const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app })
|
|
860
941
|
|
|
861
942
|
const getCdpWsUrl = (c: { req: { header: (name: string) => string | undefined } }) => {
|
|
@@ -2065,9 +2146,26 @@ export async function startPlayWriterCDPRelayServer({
|
|
|
2065
2146
|
return c.json(result)
|
|
2066
2147
|
})
|
|
2067
2148
|
|
|
2068
|
-
|
|
2149
|
+
// Use createAdaptorServer instead of serve() so we control the listen()
|
|
2150
|
+
// timing. This lets us inject WebSocket upgrade handlers before binding and
|
|
2151
|
+
// await the bind to surface EADDRINUSE as a catchable error (issue #75).
|
|
2152
|
+
const server = createAdaptorServer({ fetch: app.fetch, hostname: host })
|
|
2069
2153
|
injectWebSocket(server)
|
|
2070
2154
|
|
|
2155
|
+
await new Promise<void>((resolve, reject) => {
|
|
2156
|
+
const onListening = () => {
|
|
2157
|
+
server.off('error', onError)
|
|
2158
|
+
resolve()
|
|
2159
|
+
}
|
|
2160
|
+
const onError = (error: Error) => {
|
|
2161
|
+
server.off('listening', onListening)
|
|
2162
|
+
reject(error)
|
|
2163
|
+
}
|
|
2164
|
+
server.once('listening', onListening)
|
|
2165
|
+
server.once('error', onError)
|
|
2166
|
+
server.listen(port, host)
|
|
2167
|
+
})
|
|
2168
|
+
|
|
2071
2169
|
const wsHost = `ws://${host}:${port}`
|
|
2072
2170
|
const cdpEndpoint = `${wsHost}/cdp`
|
|
2073
2171
|
const extensionEndpoint = `${wsHost}/extension`
|
package/src/cli.ts
CHANGED
|
@@ -27,8 +27,6 @@ import { discoverChromeInstances, resolveDirectInput, type DiscoveredInstance }
|
|
|
27
27
|
|
|
28
28
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
29
29
|
|
|
30
|
-
const cliRelayEnv = { PLAYWRITER_AUTO_ENABLE: '1' }
|
|
31
|
-
|
|
32
30
|
const cli = goke('playwriter')
|
|
33
31
|
|
|
34
32
|
cli
|
|
@@ -53,7 +51,7 @@ cli
|
|
|
53
51
|
import('./package-paths.js'),
|
|
54
52
|
])
|
|
55
53
|
|
|
56
|
-
await ensureRelayServer({ logger: console
|
|
54
|
+
await ensureRelayServer({ logger: console })
|
|
57
55
|
|
|
58
56
|
const browserPath = resolveBrowserExecutablePath({ browserPath: binaryPath })
|
|
59
57
|
const extensionPath = getBundledExtensionPath()
|
|
@@ -169,15 +167,18 @@ function buildAuthHeaders({ token, json }: { token?: string; json?: boolean }):
|
|
|
169
167
|
return headers
|
|
170
168
|
}
|
|
171
169
|
|
|
172
|
-
async function fetchExtensionsStatus(host?: string): Promise<ExtensionStatus[]> {
|
|
170
|
+
async function fetchExtensionsStatus({ host, token }: { host?: string; token?: string } = {}): Promise<ExtensionStatus[]> {
|
|
173
171
|
try {
|
|
174
172
|
const serverUrl = await getServerUrl(host)
|
|
173
|
+
const headers = buildAuthHeaders({ token })
|
|
175
174
|
const response = await fetch(`${serverUrl}/extensions/status`, {
|
|
176
175
|
signal: AbortSignal.timeout(2000),
|
|
176
|
+
headers,
|
|
177
177
|
})
|
|
178
178
|
if (!response.ok) {
|
|
179
179
|
const fallback = await fetch(`${serverUrl}/extension/status`, {
|
|
180
180
|
signal: AbortSignal.timeout(2000),
|
|
181
|
+
headers,
|
|
181
182
|
})
|
|
182
183
|
if (!fallback.ok) {
|
|
183
184
|
return []
|
|
@@ -234,7 +235,7 @@ async function executeCode(options: {
|
|
|
234
235
|
|
|
235
236
|
// Ensure relay server is running (only for local)
|
|
236
237
|
if (!host && !process.env.PLAYWRITER_HOST) {
|
|
237
|
-
const restarted = await ensureRelayServer({ logger: console
|
|
238
|
+
const restarted = await ensureRelayServer({ logger: console })
|
|
238
239
|
if (restarted) {
|
|
239
240
|
const connectedExtensions = await waitForConnectedExtensions({
|
|
240
241
|
logger: console,
|
|
@@ -442,7 +443,7 @@ cli
|
|
|
442
443
|
let extensions: ExtensionStatus[] = []
|
|
443
444
|
|
|
444
445
|
if (isLocal) {
|
|
445
|
-
await ensureRelayServer({ logger: console
|
|
446
|
+
await ensureRelayServer({ logger: console })
|
|
446
447
|
extensions = await waitForConnectedExtensions({
|
|
447
448
|
timeoutMs: 12000,
|
|
448
449
|
pollIntervalMs: 250,
|
|
@@ -458,7 +459,7 @@ cli
|
|
|
458
459
|
})
|
|
459
460
|
}
|
|
460
461
|
} else {
|
|
461
|
-
extensions = await fetchExtensionsStatus(options.host)
|
|
462
|
+
extensions = await fetchExtensionsStatus({ host: options.host, token: options.token })
|
|
462
463
|
}
|
|
463
464
|
|
|
464
465
|
if (extensions.length === 0) {
|
|
@@ -574,7 +575,7 @@ cli
|
|
|
574
575
|
|
|
575
576
|
async function ensureRelayForSessionCreation(isLocal: boolean): Promise<void> {
|
|
576
577
|
if (isLocal) {
|
|
577
|
-
await ensureRelayServer({ logger: console
|
|
578
|
+
await ensureRelayServer({ logger: console })
|
|
578
579
|
}
|
|
579
580
|
}
|
|
580
581
|
|
|
@@ -658,7 +659,7 @@ cli
|
|
|
658
659
|
.option('--token <token>', 'Authentication token (or use PLAYWRITER_TOKEN env var)')
|
|
659
660
|
.action(async (options) => {
|
|
660
661
|
if (!options.host && !process.env.PLAYWRITER_HOST) {
|
|
661
|
-
await ensureRelayServer({ logger: console
|
|
662
|
+
await ensureRelayServer({ logger: console })
|
|
662
663
|
}
|
|
663
664
|
|
|
664
665
|
const serverUrl = await getServerUrl(options.host)
|
|
@@ -751,7 +752,7 @@ cli
|
|
|
751
752
|
const serverUrl = await getServerUrl(options.host)
|
|
752
753
|
|
|
753
754
|
if (!options.host && !process.env.PLAYWRITER_HOST) {
|
|
754
|
-
await ensureRelayServer({ logger: console
|
|
755
|
+
await ensureRelayServer({ logger: console })
|
|
755
756
|
}
|
|
756
757
|
|
|
757
758
|
try {
|
|
@@ -783,7 +784,7 @@ cli
|
|
|
783
784
|
const serverUrl = await getServerUrl(options.host)
|
|
784
785
|
|
|
785
786
|
if (!options.host && !process.env.PLAYWRITER_HOST) {
|
|
786
|
-
await ensureRelayServer({ logger: console
|
|
787
|
+
await ensureRelayServer({ logger: console })
|
|
787
788
|
}
|
|
788
789
|
|
|
789
790
|
try {
|
|
@@ -923,13 +924,13 @@ cli
|
|
|
923
924
|
|
|
924
925
|
// Start relay if local so the extension can connect, then fetch in parallel
|
|
925
926
|
if (isLocal) {
|
|
926
|
-
await ensureRelayServer({ logger: console
|
|
927
|
+
await ensureRelayServer({ logger: console })
|
|
927
928
|
}
|
|
928
929
|
|
|
929
930
|
const [extensions, directInstances] = await Promise.all([
|
|
930
931
|
isLocal
|
|
931
932
|
? waitForConnectedExtensions({ timeoutMs: 2000, pollIntervalMs: 200, logger: console })
|
|
932
|
-
: fetchExtensionsStatus(options.host),
|
|
933
|
+
: fetchExtensionsStatus({ host: options.host, token: options.token }),
|
|
933
934
|
isLocal ? discoverChromeInstances() : Promise.resolve([] as DiscoveredInstance[]),
|
|
934
935
|
])
|
|
935
936
|
|