npm / playwright / 1.65.0-alpha-2026-10-16

playwright 1.65.0-alpha-2026-10-16

Source
https://github.com/microsoft/playwright
Commit scanned
034f230cc68b (recorded by npm)
Directory
packages/playwright (only this part of the repository was scanned)
Compared with
a038fe7e0eb9 (0 signals introduced since)
Files
132 scanned, 13 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 12 info
Scan time
8.2 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 10 of 10 warn and critical signals; 10 demoted to info.
Disclude version
4.0.0
Scanned (UTC)
2026-10-16 21:59

Signals

Ordered by severity, then signals introduced in this release. Hidden characters are shown as ⟨U+…⟩.

  1. info warn → info by review dynamic-execution packages/playwright/src/isomorphic/teleReceiver.ts:462:90

    `atob` decodes a base64 value at runtime — common step before dynamic fetch or eval (ast pass, confidence 0.75)

      private _onStdIO(type: JsonStdIOType, testId: string | undefined, resultId: string | undefined, data: string, isBase64: boolean) {
        const chunk = isBase64 ? ((globalThis as any).Buffer ? Buffer.from(data, 'base64') : atob(data)) : data;
        const test = testId ? this._tests.get(testId) : undefined;
    likely benign LLM, confidence 0.90 Base64 decoding of test stdio output with a Buffer/atob fallback.
    Reasoning

    atob is only the fallback when globalThis.Buffer is absent (browser runtime). It decodes stdio chunk data for display, not to fetch or eval anything. The base64 decode is a routine, legitimate step.

  2. info warn → info by review dynamic-import packages/playwright/src/package.ts:22:28

    `require` called with a non-literal specifier (ast pass, confidence 0.60)

    export const packageRoot = path.join(__dirname, '..');
    export const packageJSON = require(path.join(packageRoot, 'package.json'));
    
    export function libPath(...parts: string[]): string {
    likely benign LLM, confidence 0.90 Loads the package's own package.json via a computed path.
    Reasoning

    The require specifier is path.join of the package root and a fixed 'package.json'. It loads the package's own manifest, so the non-literal argument is harmless.

  3. info warn → info by review dynamic-import packages/playwright/src/reporters/coverage.ts:120:27

    `require` called with a non-literal specifier (ast pass, confidence 0.60)

          const resolveFrom = (name: string) => require.resolve(name, { paths: [this._options.configDir] });
          const libCoverage = require(resolveFrom('istanbul-lib-coverage'));
          const libReport = require(resolveFrom('istanbul-lib-report'));
          const reports = require(resolveFrom('istanbul-reports'));
    likely benign LLM, confidence 0.90 Loads the istanbul coverage library, resolved from a fixed name.
    Reasoning

    The specifier comes from require.resolve of fixed module names ('istanbul-lib-coverage') anchored to the config directory. This is standard optional-dependency loading for coverage reporting.

  4. info warn → info by review dynamic-import packages/playwright/src/reporters/coverage.ts:121:25

    `require` called with a non-literal specifier (ast pass, confidence 0.60)

          const libCoverage = require(resolveFrom('istanbul-lib-coverage'));
          const libReport = require(resolveFrom('istanbul-lib-report'));
          const reports = require(resolveFrom('istanbul-reports'));
          const context = libReport.createContext({
    likely benign LLM, confidence 0.90 Loads istanbul-lib-report through a resolved fixed name.
    Reasoning

    Same pattern as the adjacent line: a fixed module name resolved via require.resolve from the config directory. No attacker-controlled input is involved.

  5. info warn → info by review dynamic-import packages/playwright/src/reporters/coverage.ts:122:23

    `require` called with a non-literal specifier (ast pass, confidence 0.60)

          const libReport = require(resolveFrom('istanbul-lib-report'));
          const reports = require(resolveFrom('istanbul-reports'));
          const context = libReport.createContext({
            dir: outputDir,
    likely benign LLM, confidence 0.90 Loads istanbul-reports through a resolved fixed name.
    Reasoning

    Identical resolution pattern to the other coverage requires, with a fixed module name. It is a legitimate dependency load.

  6. info warn → info by review dynamic-import packages/playwright/src/transform/babelBundle.ts:169:52

    `require` called with a non-literal specifier (ast pass, confidence 0.60)

          ...plugins,
          ...pluginsEpilogue.map(([name, options]) => [require(name), options]),
        ],
        compact: false,
    likely benign LLM, confidence 0.85 Requires Babel plugins named in the internal plugin configuration.
    Reasoning

    Plugins come from the bundle's own configuration list (pluginsEpilogue) and are loaded by name. This is config-driven plugin loading, typical of Babel setups. Dynamic loading is expected here, not hidden payload execution.

  7. info warn → info by review dynamic-import packages/playwright/src/transform/transform.ts:259:74

    `require` called with a non-literal specifier (ast pass, confidence 0.60)

    
      const { babelTransform }: { babelTransform: BabelTransformFunction } = require(libPath('transform', 'babelBundle'));
      const babelResult = babelTransform(originalCode, filename, !!moduleUrl, pluginsEpilogue, useDefineForClassFields, jsx);
      if (!babelResult?.code)
    likely benign LLM, confidence 0.90 Loads the internal babelBundle module via a computed library path.
    Reasoning

    The path is libPath('transform','babelBundle'), a fixed internal module within the package. The computed path only locates its own bundled code.

  8. info critical → info by review dynamic-execution packages/playwright/src/transform/transform.ts:286:29

    `eval` called with a non-literal expression (ast pass, confidence 0.90)

        const fileName = url.pathToFileURL(file);
        const esmImport = () => eval(`import(${JSON.stringify(fileName)})`);
    
        // For ESM imports handled by the asynchronous loader, issue a preflight to populate
    likely benign LLM, confidence 0.85 Dynamic ESM import of a test file via eval, to bypass CJS transpilation.
    Reasoning

    eval is used to build a dynamic import() of a file URL, with the path passed through JSON.stringify. This is a known technique to keep the import from being rewritten into require when loading ESM from CJS. The input is the test file path, not untrusted remote data, so it is not a dropper.

  9. info critical → info by review dynamic-execution packages/playwright/src/transform/transform.ts:293:13

    `eval` called with a non-literal expression (ast pass, confidence 0.90)

        if (loaderChannel) {
          await eval(`import(${JSON.stringify(fileName + '.esm.preflight')})`)
              .catch((error: any) => debugTest('Failed to load preflight for ' + file + ', source maps may be missing for errors thrown during loading.', error))
              .finally(nextTask);
    likely benign LLM, confidence 0.85 Eval-based dynamic import of an ESM preflight marker for a test file.
    Reasoning

    Same eval-based import() pattern used to issue an ESM preflight for source-map support. The argument is the local file name with a '.esm.preflight' suffix and errors are caught. No external input or decoding is involved.

  10. info warn → info by review dynamic-import packages/playwright/src/transform/transform.ts:305:18

    `require` called with a non-literal specifier (ast pass, confidence 0.60)

      }
      const result = require(file);
      const depsCollector = cc.currentFileDepsCollector();
      if (depsCollector) {
    likely benign LLM, confidence 0.90 Requires the test file under the runner's loader and dependency collection.
    Reasoning

    require(file) loads the test file being run by the test runner, with a dependency collector wrapped around it. That is the core purpose of the loader, not malicious execution.

  11. file mixes tab and space indentation (9 tab-indented and 112 space-indented lines) (raw pass, confidence 0.40)

    interface TsConfig {
      extends?: string;
      compilerOptions?: {
        baseUrl?: string;
  12. line length 560 characters (raw pass, confidence 0.50)

      video: VideoMode | /** deprecated */ 'retry-with-video' | { mode: VideoMode, size?: ViewportSize, fps?: number, show?: { actions?: { duration?: number, position?: 'top-left' | 'top' | 'top-right' | 'bottom-left' | 'bottom' | 'bottom-right', /** @deprecated Use `style.title` instead. */ fontSize?: number, cursor?: 'no

Reproduce This Scan

Run disclude locally to verify. Add --format sarif for SARIF output. Add --llm (with an API key in the environment) to include the LLM review.

cargo install disclude --version 4.0.0
git clone https://github.com/microsoft/playwright playwright && cd playwright
git checkout 034f230cc68b286055186f4d9783188536f2f3cf
disclude scan --severity info --diff a038fe7e0eb955ef41db53d40a859ce487889b6d packages/playwright