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

playwright-core 1.65.0-alpha-2026-10-14

Source
https://github.com/microsoft/playwright
Commit scanned
647e485f2ba5 (recorded by npm)
Directory
packages/playwright-core (only this part of the repository was scanned)
Compared with
a3401cfe0dcf (0 signals introduced since)
Files
370 scanned, 49 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 26 info
Scan time
19.4 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-15 00:08

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 Chrome Beta's binary by its escaped, space-containing app bundle path to check its version.
    Reasoning

    The backslash escapes are ordinary shell escaping of spaces in the macOS app bundle path /Applications/Google Chrome Beta.app. The command just runs the browser with --version, so this is a normal coding pattern.

  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.90 Removes a temporary Edge Beta installer package and runs the escaped app bundle binary with --version.
    Reasoning

    The escaped spaces are required to reference the Microsoft Edge Beta.app bundle path. The script removes a temp .pkg file and checks the version, which is consistent with a reinstall helper.

  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 and runs the escaped app bundle binary with --version.
    Reasoning

    Same pattern as the Edge Beta script: escaped spaces in the app bundle path, plus cleanup of a temp installer package. No obfuscation of intent.

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

    The require target is path.join of a browser reference directory with 'package.json'. Reading a package manifest to get its version is routine and 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.90 Loads the package's own package.json via a computed path.
    Reasoning

    The require resolves package.json relative to the package's own root directory. This 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.85 Gradle wrapper shell idiom that evals parsed JVM options into positional parameters.
    Reasoning

    The eval of set -- with xargs-split JVM options is the standard idiom in the official Gradle wrapper script for parsing DEFAULT_JVM_OPTS and related variables. It is a known, legitimate pattern despite the critical severity.

  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.85 Gradle wrapper's final exec that launches the located Java binary with its arguments.
    Reasoning

    The exec of $JAVACMD with "$@" is the final launch step of the standard Gradle wrapper script. It launches the java binary the wrapper located, which is expected behavior.

  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.80 Decodes base64 file payloads to build File objects for simulated drag-and-drop.
    Reasoning

    The base64 payloads are decoded into bytes to construct a File for a DataTransfer, which is how drag-and-drop file emulation works. The decoded data is not executed or fetched.

  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.85 Parses a wrapped expression with new Function to test whether it is valid JavaScript.
    Reasoning

    new Function is used only to syntax-check a user expression wrapped in parentheses while serializing a value. It is a parse test, not execution of attacker-controlled logic, and matches the tool's JS evaluation purpose.

  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.85 Fallback syntax check using new Function during expression serialization.
    Reasoning

    This is the second fallback syntax check in the same serialization routine, parsing the expression with new Function. It validates syntax and does not run hidden payloads.

  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.80 Requires a browsers.json whose directory comes from a resolved link target.
    Reasoning

    The path is derived from a symlink target and points to browsers.json, a data file describing browsers. Loading it through require is a common but loose pattern, and the content is not attacker-controlled code in this context.

  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.90 Loads the bundled browsers.json from the package root to create the browser registry.
    Reasoning

    The require loads browsers.json from the package root to construct the registry. This is the package's own static configuration file.

  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-provided JavaScript expression in the page context for an evaluate tool.
    Reasoning

    eval runs a caller-supplied expression inside the page's evaluate callback. This is the advertised purpose of an evaluate tool and matches Playwright's page.evaluate semantics. It is risky by design but is not hidden malicious logic, though the eval of unsanitized input is a real concern.

  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-provided JavaScript expression at page level for an evaluate tool.
    Reasoning

    Same as the locator variant: eval of a caller-provided expression in the page for the page-level evaluate tool. It is the tool's intended function and not concealed malicious behavior.

  24. info warn → info by review dynamic-import packages/playwright-core/src/tools/backend/tab.ts:189: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.70 Dynamically requires a user-configured init-page module and calls its default export with the page.
    Reasoning

    The require specifier is an init-page path, which suggests a user- or config-supplied initialization module. Loading a configured plugin is a legitimate pattern, but it is arbitrary code loading controlled by configuration, so the risk depends on where initPage comes from. The snippet shows no obfuscation or encoded payload.

  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.90 Loads a bundled help.json file through a path helper.
    Reasoning

    The specifier is built by libPath from fixed segments ending in help.json, so it resolves to a bundled static data file. The non-literal argument triggers the rule, but there is no code execution or obfuscation here.

  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.75 Dynamically loads a Playwright library named by a descriptor to connect to a browser of a matching version.
    Reasoning

    The require specifier comes from a BrowserDescriptor's playwrightLib field. This fits a cross-version browser connection feature that selects a Playwright library by version. The source of descriptor is not shown, so a malicious descriptor could load arbitrary modules, but the snippet itself shows ordinary, purposeful code.

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 647e485f2ba563ef055e2368fcc67bde67789cf0
disclude scan --severity info --diff a3401cfe0dcf317101572d025fa46248d0779b42 packages/playwright-core