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

playwright 1.65.0-alpha-2026-10-21

Source
https://github.com/microsoft/playwright
Commit scanned
66070a1eaa0b (recorded by npm)
Directory
packages/playwright (only this part of the repository was scanned)
Compared with
27219bd2cc94 (0 signals introduced since)
Files
132 scanned, 13 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 12 info
Scan time
8.5 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 10 of 10 warn and critical signals; 10 demoted to info.
Disclude version
4.1.0
Scanned (UTC)
2026-10-22 20:57

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.95 Base64 decoding of test stdio output when Buffer is unavailable.
    Reasoning

    The atob call is a fallback for decoding base64 stdio chunks from a test runner. It only decodes data into a string or buffer and never evaluates or fetches anything, so the flagged pattern is not present.

  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.93 Loads the package's own package.json via a computed path.
    Reasoning

    The require path is built from __dirname and a fixed 'package.json' name, which is a standard way for a package to read its own metadata. The specifier is not attacker-controlled.

  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.92 Resolves and requires istanbul coverage libraries from the config directory.
    Reasoning

    The module names are hardcoded istanbul packages resolved relative to the user's config directory. This is ordinary plugin/dependency resolution for a coverage reporter.

  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.92 Requires istanbul-lib-report via resolved path.
    Reasoning

    Same as the adjacent finding: fixed istanbul package names resolved via require.resolve from the configured directory, which is expected behavior for a coverage reporter.

  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.92 Requires istanbul-reports via resolved path.
    Reasoning

    Hardcoded istanbul-reports package loaded through the same resolution helper. No untrusted input reaches the specifier.

  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.88 Requires Babel plugins named in the project's configuration.
    Reasoning

    The plugin names come from the user's own Babel configuration and are loaded with require, which is how Babel plugins are normally loaded. The code is not hiding the specifier or decoding anything.

  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.92 Loads the internal babelBundle module.
    Reasoning

    The path is a fixed internal module (babelBundle) resolved through libPath. This is an internal bundled-module load, not a remote or obfuscated payload.

  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.80 Uses eval to preserve a native dynamic import() of a local test file URL.
    Reasoning

    The eval wraps a dynamic import() of a file URL built with JSON.stringify. The eval is a known workaround so that TypeScript compilation does not rewrite import() into require() in the loader, so the critical severity overstates it. The input is a local test file path, not external data.

  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.80 Eval-wrapped native import() to preflight an ESM test file.
    Reasoning

    This is the same eval-wrapped native import() pattern, used to preflight an ESM loader file. The argument is a local file name, and the catch handler only logs a debug message, so there is no sign of malicious intent.

  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.93 Requires the test file currently being loaded by the runner.
    Reasoning

    This require loads the test file being run by the test runner. Requiring the user's own test sources is the expected behavior of a test runner.

  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.1.0
git clone https://github.com/microsoft/playwright playwright && cd playwright
git checkout 66070a1eaa0b869379607a9e0e1a652ddf2a77da
disclude scan --severity info --diff 27219bd2cc94e1ebceec58f188760a6bf458c192 packages/playwright