npm / playwright-core / 1.64.1

playwright-core 1.64.1

Source
https://github.com/microsoft/playwright
Tag scanned
v1.64.1
Directory
packages/playwright-core (only this part of the repository was scanned)
Compared with
v1.64.0 (0 signals introduced since)
Files
370 scanned, 49 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 1 warn · 25 info
Scan time
18.5 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:08

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.65 Dynamically requires an init module by path and calls its default export with the current page.
    Reasoning

    The require specifier is a variable (initPage) rather than a literal, and the module's default export is invoked with the page. This looks like loading a project-internal init script. The risk is that initPage comes from untrusted input, but the snippet does not show that, so the pattern is most likely a legitimate plugin or init hook.

  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.95 Invokes Google Chrome Beta's binary by its spaced .app path to check its version.
    Reasoning

    The backslash escapes are the standard way to write macOS application paths that contain spaces. The command runs the app's own binary with --version, so this is a normal shell idiom, not hidden behavior.

  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.95 Runs Microsoft Edge Beta's binary by its escaped .app path to print its version.
    Reasoning

    Escaped spaces in the Edge Beta .app path are required for the shell to resolve the name. The command only queries the version of the installed browser, which fits a reinstall helper script.

  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.95 Runs Microsoft Edge Dev's binary by its escaped .app path to print its version.
    Reasoning

    Same pattern as the Edge Beta script: escaped spaces in a macOS app path followed by a --version check. Nothing here 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.85 Reads a browser reference directory's package.json to record its version.
    Reasoning

    The require target is path.join of a browser's referenceDir with 'package.json', a local package metadata file read to get a version string. The non-literal specifier is a normal dynamic load of a known filename.

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

    This loads the package's own package.json from the package root computed from __dirname. It is a standard self-metadata read with no external input.

  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 boilerplate that evals a quoted-argument splitting of JVM options.
    Reasoning

    The eval of a set -- $(...) construction over DEFAULT_JVM_OPTS, JAVA_OPTS and GRADLE_OPTS is the standard Gradle wrapper idiom for splitting quoted option strings into arguments. It is a well-known, unmodified-looking boilerplate pattern.

  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's final exec of the resolved Java command with forwarded arguments.
    Reasoning

    exec "$JAVACMD" "$@" is the standard final line of the Gradle wrapper, launching the java binary resolved earlier in the script. It is expected behavior for this file.

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

    atob decodes base64 payloads into bytes that are wrapped in a File and added to a DataTransfer for drag-and-drop simulation. The decoded data is not executed or fetched, so the flagged step is data handling rather than a dropper.

  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-checks an expression string by constructing a Function, without calling it.
    Reasoning

    new Function('(' + expression + ')') is used only to test whether a serialized expression parses, as a syntax probe before falling back to function shorthand handling. It compiles but does not invoke the code.

  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 parse-only Function probe in the expression serializer fallback.
    Reasoning

    This is the same parse-only Function construction as the earlier branch, used as a fallback probe in the serializer. The code is compiled to test validity and is not executed here.

  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 a browsers.json through a path read from a registry link file.
    Reasoning

    The specifier is joined from a linkTarget read from a link file, then loaded as browsers.json. This is a dynamic path, but it only parses JSON data for a registry link and does not run code. Some doubt remains because the path originates from a file on disk.

  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.90 Loads the bundled browsers.json to construct the browser registry.
    Reasoning

    This loads browsers.json from the package root for the registry constructor. It is a fixed, expected configuration load.

  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 caller-provided JavaScript expression inside a page locator's evaluate call.
    Reasoning

    eval of a template-wrapped expression runs caller-supplied JavaScript in the page, which is the explicit purpose of this evaluate tool. It is a dangerous capability by design rather than concealed obfuscation, so it is not treated as malicious here.

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

    Same design as the locator variant: the tool evaluates a user-supplied expression in the page context as its intended function. The eval is an unsanitized execution sink, but its presence matches the tool's stated behavior.

  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 for CLI help output.
    Reasoning

    The require target is built from libPath with fixed segments ('tools', 'cli-client', 'help.json') and loads a JSON help file. The AST rule fires on the non-literal call form, but the specifier is effectively constant and the target is data, not 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.70 Requires a Playwright library from a descriptor-specified path and connects to a browser via its BrowserType.
    Reasoning

    The module is loaded from descriptor.playwrightLib, a configurable library path, to support connecting across Playwright versions. This is a plausible compatibility mechanism. The dynamic path is worth noting, but nothing in the snippet shows attacker-controlled input or payload handling.

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 v1.64.1
disclude scan --severity info --diff v1.64.0 packages/playwright-core