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

playwright-core 1.65.0-alpha-2026-10-21

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

Signals

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

  1. 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 the Chrome Beta binary inside /Applications using escaped spaces in the path.
    Reasoning

    The backslash escapes are just the standard way to write a path with spaces in the Chrome Beta app bundle. The command runs a version check on the installed browser, which is ordinary install-script behaviour.

  2. 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.85 Removes a temporary Edge Beta installer package, then runs the escaped app binary with --version.
    Reasoning

    The escaped spaces only encode the Edge Beta app path. The rm -rf targets a fixed temp .pkg file in /tmp, which is routine cleanup in a reinstall script.

  3. 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 Removes a temporary Edge Dev installer package, then runs the escaped app binary with --version.
    Reasoning

    Same pattern as the other reinstall scripts: escaped spaces in a fixed app path and a fixed temp-file cleanup. Nothing obfuscated about it.

  4. 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.90 Loads a browser reference directory's package.json to read its version.
    Reasoning

    The specifier is path.join of a browser reference directory with 'package.json'. This is a plain read of a version file, not code injection.

  5. 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
  6. 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
  7. 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.92 Loads the package's own package.json via a path joined from the package root.
    Reasoning

    The path is built from __dirname and the fixed filename package.json. It is a standard way to read the package's own metadata.

  8. 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*)
  9. 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*)
  10. 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*)
  11. 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" )
  12. 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" )
  13. 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.80 Gradle wrapper eval that splits DEFAULT_JVM_OPTS, JAVA_OPTS and GRADLE_OPTS into arguments.
    Reasoning

    The eval of a set -- expansion over JVM options is the well-known boilerplate in the official Gradle wrapper script. Gradle wrapper scripts are typically flagged by scanners like this, and the eval only processes option strings.

  14. 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.90 Final exec of the resolved Java binary from the Gradle wrapper.
    Reasoning

    exec of $JAVACMD with the forwarded arguments is the standard final line of the Gradle wrapper, launching the resolved java binary.

  15. 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
  16. 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
  17. 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 to build File objects for a synthetic drop event.
    Reasoning

    atob decodes base64 payloads that are turned into File objects and added to a DataTransfer for simulating drag and drop. The decoded bytes become file data, not executed code.

  18. 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-validity check on an expression string while serializing a function argument.
    Reasoning

    new Function('(' + expression + ')') is only used to test whether an expression parses, as part of serializing a function-shorthand. It creates a function without calling it, which is common in tooling.

  19. 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 Second syntax-validity fallback for expression serialization using new Function.
    Reasoning

    Same parse-check pattern as the earlier Function call in the same serializer fallback. It validates syntax rather than running attacker-controlled logic.

  20. 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.75 Loads browsers.json from a symlink target path in the browser cache.
    Reasoning

    The path is derived from a symlink target in the browsers directory and loads a browsers.json. That is a data-file load, but the link target is read from disk, so it is worth a note. Nothing suggests malice.

  21. 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 build the browser registry.
    Reasoning

    The path is packageRoot joined with browsers.json, a fixed package data file loaded to construct the registry.

  22. 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 caller-supplied JavaScript expression in the page for an evaluate tool.
    Reasoning

    eval of a user-supplied expression inside the page's evaluate callback is the intended behaviour of a browser automation evaluate tool. It runs in the page context, not as hidden obfuscation, though it is powerful and deserves review.

  23. 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 caller-supplied JavaScript expression on the page for the tab-level evaluate tool.
    Reasoning

    Same as the locator-based evaluate: eval of the supplied expression runs in page context as the tool's designed function. It is by design rather than covert.

  24. info warn → info by review dynamic-import packages/playwright-core/src/tools/backend/tab.ts:199:35

    `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.80 Dynamically requires a configured init-page module and calls its default export with the page object.
    Reasoning

    The require specifier is `initPage`, which appears to be a user-configured init-page script path that the tool runs once per page. Loading a user-supplied script is a deliberate feature, not obfuscation, and the snippet shows no encoding or hidden payload. The residual risk is that it executes whatever path is configured, so it is a code-execution surface rather than a concealed one.

  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.93 Loads a bundled help.json file via a path helper with literal segments.
    Reasoning

    The specifier is `libPath('tools', 'cli-client', 'help.json')`, built from literal path segments and pointing to a bundled JSON file. The flag is only a non-literal-specifier heuristic. Loading a static help file this way is ordinary code.

  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.72 Requires a Playwright library module named by a descriptor field, then uses it to connect to a browser.
    Reasoning

    The specifier comes from `descriptor.playwrightLib`, so the module loaded depends on the descriptor's contents. That is consistent with a cross-version Playwright connector that picks a library by version. The snippet shows no concealment, but whether the descriptor can be influenced by untrusted input is outside what it shows, which keeps confidence moderate.

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 66070a1eaa0b869379607a9e0e1a652ddf2a77da
disclude scan --severity info --diff 27219bd2cc94e1ebceec58f188760a6bf458c192 packages/playwright-core