npm / playwright-core / 1.64.1-beta-1791924977000

playwright-core 1.64.1-beta-1791924977000

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

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 an init-page module and invokes its default export with the page object.
    Reasoning

    The require call uses a variable (initPage) as its specifier, which is a dynamic-load pattern. The snippet is a try block that loads an init module and calls its default export with the page. This reads as a plugin or init-script hook. The snippet doesn't show where initPage comes from; if it came from untrusted input it would be a code-execution risk, so confidence is moderate.

  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 the Google Chrome Beta binary by its escaped path to print its version.
    Reasoning

    The backslash escapes are ordinary bash escaping of spaces in the Chrome Beta app bundle path. The command runs the app's --version flag, which fits a reinstall helper script.

  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 the Microsoft Edge Beta binary by its escaped path to check its version.
    Reasoning

    The escaped spaces in the Microsoft Edge Beta app path are standard shell quoting. The script removes a temp pkg and then checks the version, which is consistent with a legitimate reinstall helper.

  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 the Microsoft Edge Dev binary by its escaped path to check its version.
    Reasoning

    Same pattern as the Edge Beta script: escaped spaces in the app path with a version check. No download-and-execute chain is visible.

  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.90 Reads the version from a browser directory's package.json via a computed require path.
    Reasoning

    The require targets package.json inside a browser reference directory built with path.join. This is a plain metadata read, not code loading from an untrusted source.

  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.95 Loads the package's own package.json using a path derived from __dirname.
    Reasoning

    The require resolves package.json relative to the package root computed from __dirname. This is a standard way to load a project's 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.85 Gradle wrapper script word-splits JVM options through eval to build the argument list.
    Reasoning

    The eval over a set -- of xargs-split options is the well-known Gradle wrapper idiom for splitting JVM option strings. It operates on the script's own option variables, not on fetched data.

  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.90 Gradle wrapper execs the resolved Java command with the script arguments.
    Reasoning

    exec of $JAVACMD with the forwarded arguments is the standard final line of the Gradle wrapper. It launches the Java binary the script resolved.

  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:709: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 bytes to construct File objects for drag-and-drop simulation.
    Reasoning

    atob decodes base64 file contents supplied by the caller so a File object can be built for a drag-and-drop payload. The decoded bytes are data, not executed code.

  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 Uses new Function as a parse check to test whether an expression is valid JavaScript.
    Reasoning

    new Function on the wrapped expression is used as a syntax-validity probe while serializing user-supplied JS expressions. The code is parsed, not run, and the pattern is a common parse-check idiom.

  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 Second Function-based syntax probe in the expression serializer fallback path.
    Reasoning

    This is the second parse-check fallback in the same expression serializer. It matches the first Function call and serves the same purpose.

  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.70 Loads browsers.json from a path read from a cache symlink target.
    Reasoning

    The require loads browsers.json from a path derived from a symlink target in the browser cache. That is a dynamic path, but it is a data file in a tool-managed directory, and no hidden or encoded payload is visible.

  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.95 Loads the bundled browsers.json manifest from the package root.
    Reasoning

    The require loads browsers.json from the package root. It is a fixed, expected data file with no untrusted input.

  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 against a locator's element in the browser.
    Reasoning

    eval runs a caller-supplied expression inside the page. That is the stated purpose of an evaluate tool in browser automation, not hidden obfuscation, though it is a powerful capability to keep in view.

  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 on the page.
    Reasoning

    Same as the locator case: eval of a caller-provided expression inside the page is the intended behavior of the evaluate tool.

  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 via a path helper inside the CLI client.
    Reasoning

    The require specifier is built by libPath with literal segments pointing at a bundled help.json file. It is a non-literal expression to the scanner, but it is a fixed, local, data-file load with no user-controlled component visible in the snippet.

  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 Loads a Playwright library chosen by a descriptor to connect to a browser of a matching version.
    Reasoning

    The module is loaded from descriptor.playwrightLib, which allows connecting to a Playwright build matching a given browser version. Then it picks a browser type by name and connects. This is consistent with a deliberate cross-version compatibility feature. The risk depends on whether 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 866cbb864009f84d7e8e41c64fdbfceff43f280d
disclude scan --severity info --diff 606ecd8b34bac100db32561b6278ffd76e5eb3cd packages/playwright-core