npm / playwright-core / 1.65.0-alpha-2026-10-17

playwright-core 1.65.0-alpha-2026-10-17

Source
https://github.com/microsoft/playwright
Commit scanned
6bbe2475ba79 (recorded by npm)
Directory
packages/playwright-core (only this part of the repository was scanned)
Compared with
034f230cc68b (0 signals introduced since)
Files
370 scanned, 49 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 1 warn · 25 info
Scan time
17.0 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 17 of 17 warn and critical signals; 16 demoted to info.
Disclude version
4.0.0
Scanned (UTC)
2026-10-17 23:56

Signals

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

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

          try {
            const { default: func } = require(initPage);
            await func({ page: this.page });
          } catch (e) {
    likely benign LLM, confidence 0.60 Dynamically requires a user-configured init-page module and calls its default export with the page.
    Reasoning

    The require target is a variable, initPage, inside a try block that calls the default export with the page. This looks like a config-driven init-script hook. The specifier is not shown as attacker-controlled, but it is loaded from a path the tool user supplies, so a doubt remains.

  2. info warn → info by review obfuscated-command-name packages/playwright-core/bin/reinstall_chrome_beta_mac.sh:13:1

    command name contains 4 backslash-escape sequences — hiding the true command (ast pass, confidence 0.85)

    
    /Applications/Google\ Chrome\ Beta.app/Contents/MacOS/Google\ Chrome\ Beta --version
    likely benign LLM, confidence 0.90 Invokes Chrome Beta's binary by its escaped .app path to print its version.
    Reasoning

    The backslash-escaped spaces in the macOS application bundle path (Google Chrome Beta.app) are the ordinary shell way to quote a path with spaces. The command is a plain version check on a known app bundle.

  3. info warn → info by review obfuscated-command-name packages/playwright-core/bin/reinstall_msedge_beta_mac.sh:11:1

    command name contains 4 backslash-escape sequences — hiding the true command (ast pass, confidence 0.85)

    rm -rf /tmp/msedge_beta.pkg
    /Applications/Microsoft\ Edge\ Beta.app/Contents/MacOS/Microsoft\ Edge\ Beta --version
    likely benign LLM, confidence 0.90 Runs Edge Beta's binary by its escaped .app path after clearing a temp installer package.
    Reasoning

    The escaped spaces in the Microsoft Edge Beta.app path are standard shell quoting. The script removes a temporary package and runs a version check on the installed app, which is typical installer-script behaviour.

  4. info warn → info by review obfuscated-command-name packages/playwright-core/bin/reinstall_msedge_dev_mac.sh:11:1

    command name contains 4 backslash-escape sequences — hiding the true command (ast pass, confidence 0.85)

    rm -rf /tmp/msedge_dev.pkg
    /Applications/Microsoft\ Edge\ Dev.app/Contents/MacOS/Microsoft\ Edge\ Dev --version
    likely benign LLM, confidence 0.90 Runs Edge Dev's binary by its escaped .app path to print its version.
    Reasoning

    Same pattern as the Edge Beta script: escaped spaces in the app bundle path, followed by a version check. Nothing in the snippet hides a different command.

  5. info warn → info by review dynamic-import packages/playwright-core/src/cli/installActions.ts:49:25

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

          continue;
        const packageJSON = require(path.join(browser.referenceDir, 'package.json'));
        const version = packageJSON.version;
        dirToVersion.set(browser.referenceDir, version);
    likely benign LLM, confidence 0.92 Reads the package.json version from a browser reference directory.
    Reasoning

    The require target is path.join of a browser reference directory with 'package.json', a fixed filename loaded from a local install directory. This is a normal way to read a version and not a hidden payload.

  6. line length 614 characters (raw pass, confidence 0.50)

    import type { Binary, Channel, AndroidWebView, AndroidSelector, AndroidElementInfo, APIResponse, Metadata, ClientSideCallMetadata, SDKLanguage, WaitInfo, HttpCredentials, SetNetworkCookie, NetworkCookie, ResourceTiming, SecurityDetails, RequestSizes, RemoteAddr, ExpectedTextValue, SelectorEngine, FormField, LifecycleEv
  7. line length 614 characters (raw pass, confidence 0.50)

    export type { Binary, Channel, AndroidWebView, AndroidSelector, AndroidElementInfo, APIResponse, Metadata, ClientSideCallMetadata, SDKLanguage, WaitInfo, HttpCredentials, SetNetworkCookie, NetworkCookie, ResourceTiming, SecurityDetails, RequestSizes, RemoteAddr, ExpectedTextValue, SelectorEngine, FormField, LifecycleEv
  8. info warn → info by review dynamic-import packages/playwright-core/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 const binPath = path.join(packageRoot, 'bin');
    likely benign LLM, confidence 0.93 Loads the package's own package.json from its computed root directory.
    Reasoning

    The file requires package.json from the package root computed from __dirname. This is the standard way a package reads its own metadata.

  9. command name is a variable (`$cygwin`) that this file sets only to plain text (ast pass, confidence 0.40)

    # Increase the maximum file descriptors if we can.
    if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
        case $MAX_FD in #(
          max*)
  10. command name is a variable (`$darwin`) that this file sets only to plain text (ast pass, confidence 0.40)

    # Increase the maximum file descriptors if we can.
    if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
        case $MAX_FD in #(
          max*)
  11. command name is a variable (`$nonstop`) that this file sets only to plain text (ast pass, confidence 0.40)

    # Increase the maximum file descriptors if we can.
    if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
        case $MAX_FD in #(
          max*)
  12. command name is a variable (`$cygwin`) that this file sets only to plain text (ast pass, confidence 0.40)

    # For Cygwin or MSYS, switch paths to Windows format before running java
    if "$cygwin" || "$msys" ; then
        APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
        CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
  13. command name is a variable (`$msys`) that this file sets only to plain text (ast pass, confidence 0.40)

    # For Cygwin or MSYS, switch paths to Windows format before running java
    if "$cygwin" || "$msys" ; then
        APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
        CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
  14. info critical → info by review dynamic-execution packages/playwright-core/src/server/android/driver/gradlew:233:1

    `eval` called on a dynamic (variable/substitution) expression (ast pass, confidence 0.90)

    
    eval "set -- $(
            printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
            xargs -n1 |
    likely benign LLM, confidence 0.90 Gradle wrapper line that evals JVM option variables to rebuild the argument list.
    Reasoning

    This is the standard Gradle wrapper idiom: eval over a shell-split argument list built from DEFAULT_JVM_OPTS, JAVA_OPTS and GRADLE_OPTS. It is a well-known, widely shipped script, and the expression is local configuration rather than remote input.

  15. info critical → info by review dynamic-execution packages/playwright-core/src/server/android/driver/gradlew:240:1

    `exec` called with a dynamic (variable/substitution) path (ast pass, confidence 0.85)

    
    exec "$JAVACMD" "$@"
    likely benign LLM, confidence 0.88 Final exec of the resolved Java binary with the wrapper's arguments.
    Reasoning

    exec of $JAVACMD with the forwarded arguments is the normal final step of the stock Gradle wrapper. JAVACMD is resolved from JAVA_HOME or the PATH earlier in the script.

  16. line length 614 characters (raw pass, confidence 0.50)

    import type { Binary, Channel, AndroidWebView, AndroidSelector, AndroidElementInfo, APIResponse, Metadata, ClientSideCallMetadata, SDKLanguage, WaitInfo, HttpCredentials, SetNetworkCookie, NetworkCookie, ResourceTiming, SecurityDetails, RequestSizes, RemoteAddr, ExpectedTextValue, SelectorEngine, FormField, LifecycleEv
  17. line length 614 characters (raw pass, confidence 0.50)

    export type { Binary, Channel, AndroidWebView, AndroidSelector, AndroidElementInfo, APIResponse, Metadata, ClientSideCallMetadata, SDKLanguage, WaitInfo, HttpCredentials, SetNetworkCookie, NetworkCookie, ResourceTiming, SecurityDetails, RequestSizes, RemoteAddr, ExpectedTextValue, SelectorEngine, FormField, LifecycleEv
  18. info warn → info by review dynamic-execution packages/playwright-core/src/server/dom.ts:714:43

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

              for (const p of payloads) {
                const bytes = Uint8Array.from(atob(p.buffer), c => c.charCodeAt(0));
                const file = new File([bytes], p.name, { type: p.mimeType, lastModified: p.lastModifiedMs });
                dt.items.add(file);
    likely benign LLM, confidence 0.85 Decodes base64 file payloads into a File for a synthetic drop event.
    Reasoning

    atob decodes base64 file contents into bytes that are wrapped in a File and added to a DataTransfer. This is how drag-and-drop file payloads are built in a browser automation library. It does not execute the decoded data.

  19. info critical → info by review dynamic-execution packages/playwright-core/src/server/javascript.ts:315:7

    `Function(...)` called with a non-literal body (ast pass, confidence 0.90)

        try {
          new Function('(' + expression + ')');
        } catch (e1) {
          // This means we might have a function shorthand. Try another
    likely benign LLM, confidence 0.80 Syntax-check of a serialized expression by constructing a Function, with a fallback for shorthand functions.
    Reasoning

    new Function is used here only to test whether an expression parses, as part of serializing a user-supplied evaluate argument. It is a syntax probe inside a Playwright-style evaluate helper, not a covert execution path.

  20. info critical → info by review dynamic-execution packages/playwright-core/src/server/javascript.ts:324:9

    `Function(...)` called with a non-literal body (ast pass, confidence 0.90)

          try {
            new Function('(' + expression  + ')');
          } catch (e2) {
            // We tried hard to serialize, but there's a weird beast here.
    likely benign LLM, confidence 0.80 Fallback syntax probe for a non-trivial serialized expression.
    Reasoning

    The second new Function call is a parse-only fallback in the same serialization logic. The code comments explain the purpose, and the expression comes from the caller's evaluate request.

  21. info warn → info by review dynamic-import packages/playwright-core/src/server/registry/index.ts:1201:30

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

            linkTarget = (await fs.promises.readFile(linkPath)).toString();
            const browsersJSON = require(path.join(linkTarget, 'browsers.json'));
            const descriptors = readDescriptors(browsersJSON);
            for (const browserName of allDownloadableDirectoriesThatEverExisted) {
    likely benign LLM, confidence 0.90 Loads browsers.json from a registry link target to read browser descriptors.
    Reasoning

    The require path is browsers.json inside a directory resolved from a symlink target that the registry itself manages. It is read as JSON config for browser descriptors, which is expected for this module.

  22. info warn → info by review dynamic-import packages/playwright-core/src/server/registry/index.ts:1396:38

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

    
    export const registry = new Registry(require(path.join(packageRoot, 'browsers.json')));
    
    export { runOopDownloadBrowserMain } from './oopDownloadBrowserMain';
    likely benign LLM, confidence 0.92 Loads the packaged browsers.json to construct the browser registry.
    Reasoning

    The module loads browsers.json from the package root to build the registry at startup. The path is a fixed filename under the package's own directory.

  23. info critical → info by review dynamic-execution packages/playwright-core/src/tools/backend/evaluate.ts:50:25

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

            evalResult = await locator.locator.evaluate(async (element, expr) => {
              const value = eval(`(${expr})`);
              const isFunction = typeof value === 'function';
              const result = await (isFunction ? value(element) : value);
    likely benign LLM, confidence 0.70 Evaluates a user-supplied JavaScript expression in the element's page context.
    Reasoning

    The eval runs a caller-supplied expression inside the page context, which is the purpose of this evaluate tool. It is not hidden or obfuscated, but eval of external input is a real risk surface, so this is not fully clean.

  24. info critical → info by review dynamic-execution packages/playwright-core/src/tools/backend/evaluate.ts:57:25

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

            evalResult = await tab.page.evaluate(async expr => {
              const value = eval(`(${expr})`);
              const isFunction = typeof value === 'function';
              const result = await (isFunction ? value() : value);
    likely benign LLM, confidence 0.70 Evaluates a user-supplied JavaScript expression in the page context.
    Reasoning

    Same design as the element evaluate: eval of the caller's expression within the page, used as the tool's intended function. Intent is visible in the code and not concealed.

  25. info warn → info by review dynamic-import packages/playwright-core/src/tools/cli-client/program.ts:78:16

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

      const clientInfo = createClientInfo();
      const help = require(libPath('tools', 'cli-client', 'help.json'));
    
      const argv = process.argv.slice(2);
    dismissed LLM, confidence 0.92 Loads a bundled help.json resource through a path helper built from fixed string segments.
    Reasoning

    The specifier is libPath applied to fixed string literals pointing to a bundled help.json file. This is a static resource load with a non-literal form only because of a helper call, and it is a false positive.

  26. info warn → info by review dynamic-import packages/playwright-core/src/tools/utils/connect.ts:21:14

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

    export async function connectToBrowserAcrossVersions(descriptor: BrowserDescriptor): Promise<playwright.Browser> {
      const pw = require(descriptor.playwrightLib);
      const browserType = pw[descriptor.browser.browserName] as playwright.BrowserType;
      return await browserType.connect(descriptorEndpoint(descriptor));
    likely benign LLM, confidence 0.70 Dynamically loads a Playwright library chosen by a browser descriptor to connect across versions.
    Reasoning

    The module path comes from descriptor.playwrightLib, which lets the tool load a Playwright build matching a given browser version. This is a known pattern for cross-version compatibility. The risk depends on whether the descriptor can be attacker-controlled, which the snippet does not show.

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 6bbe2475ba79174c3df5f28a435958ef0351d515
disclude scan --severity info --diff 034f230cc68b286055186f4d9783188536f2f3cf packages/playwright-core