appium-adb 16.1.2 → 16.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41,6 +41,7 @@ export async function verifyLockCredential(this: ADB, credential: string | null
41
41
  try {
42
42
  const {stdout, stderr} = await this.shell(buildCommand('verify', credential), {
43
43
  outputFormat: this.EXEC_OUTPUT_FORMAT.FULL,
44
+ quoteArgs: true,
44
45
  });
45
46
  if (stdout?.includes('verified successfully')) {
46
47
  return true;
@@ -78,6 +79,7 @@ export async function clearLockCredential(this: ADB, credential: string | null =
78
79
  try {
79
80
  const {stdout, stderr} = await this.shell(buildCommand('clear', credential), {
80
81
  outputFormat: this.EXEC_OUTPUT_FORMAT.FULL,
82
+ quoteArgs: true,
81
83
  });
82
84
  if (!['user has no password', 'Lock credential cleared'].some((x) => (stderr || stdout)?.includes(x))) {
83
85
  throw new Error(stderr || stdout);
@@ -106,6 +108,7 @@ export async function isLockEnabled(this: ADB): Promise<boolean> {
106
108
  try {
107
109
  const {stdout, stderr} = await this.shell(buildCommand('get-disabled'), {
108
110
  outputFormat: this.EXEC_OUTPUT_FORMAT.FULL,
111
+ quoteArgs: true,
109
112
  });
110
113
  if (
111
114
  /\bfalse\b/.test(stdout) ||
@@ -160,6 +163,7 @@ export async function setLockCredential(
160
163
  try {
161
164
  const {stdout, stderr} = await this.shell(cmd, {
162
165
  outputFormat: this.EXEC_OUTPUT_FORMAT.FULL,
166
+ quoteArgs: true,
163
167
  });
164
168
  if (!stdout?.includes('set to')) {
165
169
  throw new Error(stderr || stdout);
@@ -318,10 +322,10 @@ export function isScreenOnFully(dumpsys: string): boolean {
318
322
  function buildCommand(verb: string, oldCredential: string | null = null, ...args: string[]): string[] {
319
323
  const cmd = ['locksettings', verb];
320
324
  if (oldCredential && !util.isEmpty(oldCredential)) {
321
- cmd.push('--old', util.quote(`${oldCredential}`));
325
+ cmd.push('--old', `${oldCredential}`);
322
326
  }
323
327
  if (args.length > 0) {
324
- cmd.push(...args.map((arg) => util.quote(`${arg}`)));
328
+ cmd.push(...args.map((arg) => `${arg}`));
325
329
  }
326
330
  return cmd;
327
331
  }
@@ -465,7 +465,12 @@ export const EXEC_OUTPUT_FORMAT = {
465
465
  } as const;
466
466
 
467
467
  /**
468
- * Execute the given adb command.
468
+ * Execute the given adb command without automatically quoting its arguments.
469
+ *
470
+ * When invoking `adb shell`, the device shell interprets the command.
471
+ * Unquoted values may be expanded or interpreted as shell syntax.
472
+ * For literal shell arguments, prefer `shell(args, {quoteArgs: true})`
473
+ * and pass unquoted argument values. `adbExec` itself does not apply `quoteArgs`.
469
474
  *
470
475
  * @param cmd - Command string or array of command arguments
471
476
  * @param opts - Execution options with `outputFormat: 'full'`
@@ -478,7 +483,12 @@ export async function adbExec(
478
483
  opts: AdbExecOptions & {outputFormat: 'full'},
479
484
  ): Promise<ExecResult>;
480
485
  /**
481
- * Execute the given adb command.
486
+ * Execute the given adb command without automatically quoting its arguments.
487
+ *
488
+ * When invoking `adb shell`, the device shell interprets the command.
489
+ * Unquoted values may be expanded or interpreted as shell syntax.
490
+ * For literal shell arguments, prefer `shell(args, {quoteArgs: true})`
491
+ * and pass unquoted argument values. `adbExec` itself does not apply `quoteArgs`.
482
492
  *
483
493
  * @param cmd - Command string or array of command arguments
484
494
  * @param opts - Execution options
@@ -593,9 +603,10 @@ export async function shell(
593
603
  */
594
604
  export async function shell(this: ADB, cmd: string | string[], opts?: ShellExecOptions): Promise<string>;
595
605
  export async function shell(this: ADB, cmd: string | string[], opts?: ShellExecOptions): Promise<string | ExecResult> {
596
- const {privileged} = opts ?? {};
597
-
598
- const cmdArr = Array.isArray(cmd) ? cmd : [cmd];
606
+ const {quoteArgs = false, ...execOpts} = opts ?? {};
607
+ const {privileged} = execOpts;
608
+ const args = Array.isArray(cmd) ? cmd : [cmd];
609
+ const cmdArr = quoteArgs ? args.map((arg) => util.quote(arg)) : args;
599
610
  const fullCmd: string[] = ['shell'];
600
611
  if (privileged) {
601
612
  log.info(`'adb shell ${util.quote(redactSecrets(cmdArr))}' requires root access`);
@@ -608,7 +619,7 @@ export async function shell(this: ADB, cmd: string | string[], opts?: ShellExecO
608
619
  } else {
609
620
  fullCmd.push(...cmdArr);
610
621
  }
611
- return await this.adbExec(fullCmd, opts);
622
+ return await this.adbExec(fullCmd, opts === undefined ? undefined : execOpts);
612
623
  }
613
624
 
614
625
  /**
@@ -1368,16 +1379,25 @@ export async function isMitmCertificateInstalled(this: ADB, cert: Buffer | strin
1368
1379
  *
1369
1380
  * @param argTransformer - Function to transform each argument into command array
1370
1381
  * @param args - Array of arguments to process
1382
+ * @param opts - Set quoteArgs to true for unquoted literal command arguments (default false)
1371
1383
  * @throws {Error} If argument transformer returns invalid result or command execution fails
1372
1384
  */
1373
- export async function shellChunks(this: ADB, argTransformer: (x: string) => string[], args: string[]): Promise<void> {
1385
+ export async function shellChunks(
1386
+ this: ADB,
1387
+ argTransformer: (x: string) => string[],
1388
+ args: string[],
1389
+ opts: Pick<ShellExecOptions, 'quoteArgs'> = {},
1390
+ ): Promise<void> {
1374
1391
  const commands: string[][] = [];
1375
1392
  let cmdChunk: string[] = [];
1376
1393
  for (const arg of args) {
1377
- const nextCmd = argTransformer(arg);
1378
- if (!Array.isArray(nextCmd)) {
1394
+ const command = argTransformer(arg);
1395
+ if (!Array.isArray(command)) {
1379
1396
  throw new Error('Argument transformer must result in an array');
1380
1397
  }
1398
+ // Quote before measuring chunks and adding separators. A caller-supplied
1399
+ // semicolon is literal data when quoting is enabled.
1400
+ const nextCmd = opts.quoteArgs ? command.map((value) => util.quote(value)) : command;
1381
1401
  if (nextCmd.at(-1) !== ';') {
1382
1402
  nextCmd.push(';');
1383
1403
  }
@@ -637,6 +637,14 @@ export interface SpecialAdbExecOptions {
637
637
  }
638
638
 
639
639
  export interface ShellExecOptions {
640
+ /**
641
+ * Quote each command argument as literal data for the device shell.
642
+ * Pass unquoted arguments when enabled. A string command is treated as one
643
+ * literal argument; use an array for an executable and its arguments.
644
+ * Defaults to false for compatibility with existing shell expressions.
645
+ * TODO: Enable by default in the next breaking release.
646
+ */
647
+ quoteArgs?: boolean;
640
648
  /**
641
649
  * The name of the corresponding Appium's timeout capability
642
650
  * (used in the error messages).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium-adb",
3
- "version": "16.1.2",
3
+ "version": "16.2.0",
4
4
  "description": "Android Debug Bridge interface",
5
5
  "homepage": "https://appium.io",
6
6
  "bugs": {
@@ -63,6 +63,5 @@
63
63
  "engines": {
64
64
  "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
65
65
  "npm": ">=10"
66
- },
67
- "gitHead": "dd7fac6576be570d54de8ff2874b88426b4446dd"
66
+ }
68
67
  }