• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IJHack / QtPass / 24125716387

08 Apr 2026 08:27AM UTC coverage: 21.044%. Remained the same
24125716387

Pull #930

github

web-flow
Merge 850974b8f into 4828f4ef1
Pull Request #930: chore: remove commented-out debug code

0 of 2 new or added lines in 2 files covered. (0.0%)

9 existing lines in 4 files now uncovered.

1109 of 5270 relevant lines covered (21.04%)

7.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

45.54
/src/pass.cpp
1
// SPDX-FileCopyrightText: 2016 Anne Jan Brouwer
2
// SPDX-License-Identifier: GPL-3.0-or-later
3
#include "pass.h"
4
#include "gpgkeystate.h"
5
#include "helpers.h"
6
#include "qtpasssettings.h"
7
#include "util.h"
8
#include <QDir>
9
#include <QFileInfo>
10
#include <QProcess>
11
#include <QRandomGenerator>
12
#include <QRegularExpression>
13
#include <utility>
14

15
#ifdef QT_DEBUG
16
#include "debughelper.h"
17
#endif
18

19
using Enums::GIT_INIT;
20
using Enums::GIT_PULL;
21
using Enums::GIT_PUSH;
22
using Enums::GPG_GENKEYS;
23
using Enums::PASS_COPY;
24
using Enums::PASS_INIT;
25
using Enums::PASS_INSERT;
26
using Enums::PASS_MOVE;
27
using Enums::PASS_OTP_GENERATE;
28
using Enums::PASS_REMOVE;
29
using Enums::PASS_SHOW;
30

31
/**
32
 * @brief Pass::Pass wrapper for using either pass or the pass imitation
33
 */
34
Pass::Pass() : wrapperRunning(false), env(QProcess::systemEnvironment()) {
11✔
35
  connect(&exec,
11✔
36
          static_cast<void (Executor::*)(int, int, const QString &,
37
                                         const QString &)>(&Executor::finished),
38
          this, &Pass::finished);
11✔
39

40
  // This was previously using direct QProcess signals.
41
  // The code now uses Executor instead of raw QProcess for better control.
42
  // connect(&process, SIGNAL(error(QProcess::ProcessError)), this,
43
  //        SIGNAL(error(QProcess::ProcessError)));
44

45
  connect(&exec, &Executor::starting, this, &Pass::startingExecuteWrapper);
11✔
46
  env.append("WSLENV=PASSWORD_STORE_DIR/p");
11✔
47
}
11✔
48

49
/**
50
 * @brief Executes a wrapper command.
51
 * @param id Process ID
52
 * @param app Application to execute
53
 * @param args Arguments
54
 * @param readStdout Whether to read stdout
55
 * @param readStderr Whether to read stderr
56
 */
57
void Pass::executeWrapper(PROCESS id, const QString &app,
×
58
                          const QStringList &args, bool readStdout,
59
                          bool readStderr) {
60
  executeWrapper(id, app, args, QString(), readStdout, readStderr);
×
61
}
×
62

63
void Pass::executeWrapper(PROCESS id, const QString &app,
×
64
                          const QStringList &args, QString input,
65
                          bool readStdout, bool readStderr) {
66
#ifdef QT_DEBUG
67
  dbg() << app << args;
68
#endif
69
  exec.execute(id, QtPassSettings::getPassStore(), app, args, std::move(input),
×
70
               readStdout, readStderr);
71
}
×
72

73
/**
74
 * @brief Initializes the pass wrapper environment.
75
 */
76
void Pass::init() {
1✔
77
#ifdef __APPLE__
78
  // If it exists, add the gpgtools to PATH
79
  if (QFile("/usr/local/MacGPG2/bin").exists())
80
    env.replaceInStrings("PATH=", "PATH=/usr/local/MacGPG2/bin:");
81
  // Add missing /usr/local/bin
82
  if (env.filter("/usr/local/bin").isEmpty())
83
    env.replaceInStrings("PATH=", "PATH=/usr/local/bin:");
84
#endif
85

86
  if (!QtPassSettings::getGpgHome().isEmpty()) {
2✔
87
    QDir absHome(QtPassSettings::getGpgHome());
×
88
    absHome.makeAbsolute();
×
89
    env << "GNUPGHOME=" + absHome.path();
×
90
  }
×
91
}
1✔
92

93
/**
94
 * @brief Pass::Generate use either pwgen or internal password
95
 * generator
96
 * @param length of the desired password
97
 * @param charset to use for generation
98
 * @return the password
99
 */
100
auto Pass::generatePassword(unsigned int length, const QString &charset)
1,004✔
101
    -> QString {
102
  QString passwd;
1,004✔
103
  if (QtPassSettings::isUsePwgen()) {
1,004✔
104
    // --secure goes first as it overrides --no-* otherwise
105
    QStringList args;
×
106
    args.append("-1");
×
107
    if (!QtPassSettings::isLessRandom()) {
×
108
      args.append("--secure");
×
109
    }
110
    args.append(QtPassSettings::isAvoidCapitals() ? "--no-capitalize"
×
111
                                                  : "--capitalize");
112
    args.append(QtPassSettings::isAvoidNumbers() ? "--no-numerals"
×
113
                                                 : "--numerals");
114
    if (QtPassSettings::isUseSymbols()) {
×
115
      args.append("--symbols");
×
116
    }
117
    args.append(QString::number(length));
×
118
    // executeBlocking returns 0 on success, non-zero on failure
119
    if (Executor::executeBlocking(QtPassSettings::getPwgenExecutable(), args,
×
120
                                  &passwd) == 0) {
121
      static const QRegularExpression literalNewLines{"[\\n\\r]"};
×
122
      passwd.remove(literalNewLines);
×
123
    } else {
124
      passwd.clear();
×
125
#ifdef QT_DEBUG
126
      qDebug() << __FILE__ << ":" << __LINE__ << "\t"
127
               << "pwgen fail";
128
#endif
129
      // Error is already handled by clearing passwd; no need for critical
130
      // signal here
131
    }
132
  } else {
133
    // Validate charset - if CUSTOM is selected but chars are empty,
134
    // fall back to ALLCHARS to prevent weak passwords (issue #780)
135
    QString effectiveCharset = charset;
136
    if (effectiveCharset.isEmpty()) {
1,004✔
137
      effectiveCharset = QtPassSettings::getPasswordConfiguration()
2✔
138
                             .Characters[PasswordConfiguration::ALLCHARS];
139
    }
140
    if (effectiveCharset.length() > 0) {
1,004✔
141
      passwd = generateRandomPassword(effectiveCharset, length);
2,008✔
142
    } else {
143
      emit critical(
×
144
          tr("No characters chosen"),
×
145
          tr("Can't generate password, there are no characters to choose from "
×
146
             "set in the configuration!"));
147
    }
148
  }
149
  return passwd;
1,004✔
150
}
151

152
/**
153
 * @brief Pass::gpgSupportsEd25519 check if GPG supports ed25519 (ECC)
154
 * GPG 2.1+ supports ed25519 which is much faster for key generation
155
 * @return true if ed25519 is supported
156
 */
157
bool Pass::gpgSupportsEd25519() {
3✔
158
  QString out, err;
3✔
159
  if (Executor::executeBlocking(QtPassSettings::getGpgExecutable(),
12✔
160
                                {"--version"}, &out, &err) != 0) {
161
    return false;
162
  }
163
  QRegularExpression versionRegex(R"(gpg \(GnuPG\) (\d+)\.(\d+))");
×
164
  QRegularExpressionMatch match = versionRegex.match(out);
×
165
  if (!match.hasMatch()) {
×
166
    return false;
167
  }
168
  int major = match.captured(1).toInt();
×
169
  int minor = match.captured(2).toInt();
×
170
  return major > 2 || (major == 2 && minor >= 1);
×
171
}
3✔
172

173
/**
174
 * @brief Pass::getDefaultKeyTemplate return default key generation template
175
 * Uses ed25519 if supported, otherwise falls back to RSA
176
 * @return GPG batch template string
177
 */
178
QString Pass::getDefaultKeyTemplate() {
1✔
179
  if (gpgSupportsEd25519()) {
1✔
180
    return QStringLiteral("%echo Generating a default key\n"
×
181
                          "Key-Type: EdDSA\n"
182
                          "Key-Curve: Ed25519\n"
183
                          "Subkey-Type: ECDH\n"
184
                          "Subkey-Curve: Curve25519\n"
185
                          "Name-Real: \n"
186
                          "Name-Comment: QtPass\n"
187
                          "Name-Email: \n"
188
                          "Expire-Date: 0\n"
189
                          "%no-protection\n"
190
                          "%commit\n"
191
                          "%echo done");
192
  }
193
  return QStringLiteral("%echo Generating a default key\n"
1✔
194
                        "Key-Type: RSA\n"
195
                        "Subkey-Type: RSA\n"
196
                        "Name-Real: \n"
197
                        "Name-Comment: QtPass\n"
198
                        "Name-Email: \n"
199
                        "Expire-Date: 0\n"
200
                        "%no-protection\n"
201
                        "%commit\n"
202
                        "%echo done");
203
}
204

205
namespace {
206
auto resolveWslGpgconfPath(const QString &lastPart) -> QString {
3✔
207
  int lastSep = lastPart.lastIndexOf('/');
3✔
208
  if (lastSep < 0) {
3✔
209
    lastSep = lastPart.lastIndexOf('\\');
2✔
210
  }
211
  if (lastSep >= 0) {
2✔
212
    return lastPart.left(lastSep + 1) + "gpgconf";
2✔
213
  }
214
  return QStringLiteral("gpgconf");
2✔
215
}
216

217
/**
218
 * @brief Finds the path to the gpgconf executable in the same directory as the
219
 * given GPG path.
220
 * @example
221
 * QString result = findGpgconfInGpgDir(gpgPath);
222
 * std::cout << result.toStdString() << std::endl; // Expected output: path to
223
 * gpgconf or empty string
224
 *
225
 * @param gpgPath - Absolute path to a GPG executable or related file used to
226
 * locate gpgconf.
227
 * @return QString - The full path to gpgconf if found and executable; otherwise
228
 * an empty QString.
229
 */
230
QString findGpgconfInGpgDir(const QString &gpgPath) {
1✔
231
  QFileInfo gpgInfo(gpgPath);
1✔
232
  if (!gpgInfo.isAbsolute()) {
1✔
233
    return QString();
234
  }
235

236
  QDir dir(gpgInfo.absolutePath());
1✔
237

238
#ifdef Q_OS_WIN
239
  QFileInfo candidateExe(dir.filePath("gpgconf.exe"));
240
  if (candidateExe.isExecutable()) {
241
    return candidateExe.filePath();
242
  }
243
#endif
244

245
  QFileInfo candidate(dir.filePath("gpgconf"));
1✔
246
  if (candidate.isExecutable()) {
1✔
247
    return candidate.filePath();
1✔
248
  }
249
  return QString();
250
}
1✔
251

252
/**
253
 * @brief Splits a command string into arguments while respecting quotes and
254
 * escape characters.
255
 * @example
256
 * QStringList result = splitCommandCompat("cmd \"arg one\" 'arg two'
257
 * escaped\\ space");
258
 * // Expected output: ["cmd", "arg one", "arg two", "escaped space"]
259
 *
260
 * @param command - The input command string to split into individual arguments.
261
 * @return QStringList - A list of parsed command arguments.
262
 */
263
QStringList splitCommandCompat(const QString &command) {
264
  QStringList result;
265
  QString current;
266
  bool inSingleQuote = false;
267
  bool inDoubleQuote = false;
268
  bool escaping = false;
269
  for (QChar ch : command) {
270
    if (escaping) {
271
      current.append(ch);
272
      escaping = false;
273
      continue;
274
    }
275
    if (ch == '\\') {
276
      escaping = true;
277
      continue;
278
    }
279
    if (ch == '\'' && !inDoubleQuote) {
280
      inSingleQuote = !inSingleQuote;
281
      continue;
282
    }
283
    if (ch == '"' && !inSingleQuote) {
284
      inDoubleQuote = !inDoubleQuote;
285
      continue;
286
    }
287
    if (ch.isSpace() && !inSingleQuote && !inDoubleQuote) {
288
      if (!current.isEmpty()) {
289
        result.append(current);
290
        current.clear();
291
      }
292
      continue;
293
    }
294
    current.append(ch);
295
  }
296
  if (escaping) {
297
    current.append('\\');
298
  }
299
  if (!current.isEmpty()) {
300
    result.append(current);
301
  }
302
  return result;
303
}
304
} // namespace
305

306
/**
307
 * @brief Resolves the appropriate gpgconf command from a given GPG executable
308
 * path or command string.
309
 * @example
310
 * ResolvedGpgconfCommand result = Pass::resolveGpgconfCommand("wsl.exe
311
 * /usr/bin/gpg"); std::cout << result.first.toStdString() << std::endl; //
312
 * Expected output sample
313
 *
314
 * @param const QString &gpgPath - Path or command string pointing to the GPG
315
 * executable.
316
 * @return ResolvedGpgconfCommand - A pair containing the resolved gpgconf
317
 * command and its arguments.
318
 */
319
auto Pass::resolveGpgconfCommand(const QString &gpgPath)
8✔
320
    -> ResolvedGpgconfCommand {
321
  if (gpgPath.trimmed().isEmpty()) {
8✔
322
    return {"gpgconf", {}};
323
  }
324

325
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
326
  QStringList parts = QProcess::splitCommand(gpgPath);
7✔
327
#else
328
  QStringList parts = splitCommandCompat(gpgPath);
329
#endif
330

331
  if (parts.isEmpty()) {
7✔
332
    return {"gpgconf", {}};
333
  }
334

335
  const QString first = parts.first();
336
  if (first == "wsl" || first == "wsl.exe") {
9✔
337
    if (parts.size() >= 2 && parts.at(1).startsWith("sh")) {
9✔
338
      return {"gpgconf", {}};
339
    }
340
    if (parts.size() >= 2 &&
4✔
341
        QFileInfo(parts.last()).fileName().startsWith("gpg")) {
10✔
342
      QString wslGpgconf = resolveWslGpgconfPath(parts.last());
3✔
343
      parts.removeLast();
3✔
344
      parts.append(wslGpgconf);
345
      return {parts.first(), parts.mid(1)};
346
    }
347
    return {"gpgconf", {}};
348
  }
349

350
  if (!first.contains('/') && !first.contains('\\')) {
2✔
351
    return {"gpgconf", {}};
352
  }
353

354
  QString gpgconfPath = findGpgconfInGpgDir(gpgPath);
1✔
355
  if (!gpgconfPath.isEmpty()) {
1✔
356
    return {gpgconfPath, {}};
1✔
357
  }
358

359
  return {"gpgconf", {}};
360
}
8✔
361

362
/**
363
 * @brief Pass::GenerateGPGKeys internal gpg keypair generator . .
364
 * @param batch GnuPG style configuration string
365
 */
366
void Pass::GenerateGPGKeys(QString batch) {
×
367
  // Kill any stale GPG agents that might be holding locks on the key database
368
  // This helps avoid "database locked" timeouts during key generation
369
  QString gpgPath = QtPassSettings::getGpgExecutable();
×
370
  if (!gpgPath.isEmpty()) {
×
371
    ResolvedGpgconfCommand gpgconf = resolveGpgconfCommand(gpgPath);
×
372
    QStringList killArgs = gpgconf.arguments;
373
    killArgs << "--kill";
×
374
    killArgs << "gpg-agent";
×
375
    // Use same environment as key generation to target correct gpg-agent
376
    Executor::executeBlocking(env, gpgconf.program, killArgs);
×
377
  }
378

379
  executeWrapper(GPG_GENKEYS, gpgPath, {"--gen-key", "--no-tty", "--batch"},
×
380
                 std::move(batch));
381
}
×
382

383
/**
384
 * @brief Pass::listKeys list users
385
 * @param keystrings
386
 * @param secret list private keys
387
 * @return QList<UserInfo> users
388
 */
389
auto Pass::listKeys(QStringList keystrings, bool secret) -> QList<UserInfo> {
×
390
  QStringList args = {"--no-tty", "--with-colons", "--with-fingerprint"};
×
391
  args.append(secret ? "--list-secret-keys" : "--list-keys");
×
392

393
  for (const QString &keystring : AS_CONST(keystrings)) {
×
394
    if (!keystring.isEmpty()) {
×
395
      args.append(keystring);
396
    }
397
  }
398
  QString p_out;
×
399
  if (Executor::executeBlocking(QtPassSettings::getGpgExecutable(), args,
×
400
                                &p_out) != 0) {
401
    return QList<UserInfo>();
×
402
  }
403
  return parseGpgColonOutput(p_out, secret);
×
404
}
×
405

406
/**
407
 * @brief Pass::listKeys list users
408
 * @param keystring
409
 * @param secret list private keys
410
 * @return QList<UserInfo> users
411
 */
412
auto Pass::listKeys(const QString &keystring, bool secret) -> QList<UserInfo> {
×
413
  return listKeys(QStringList(keystring), secret);
×
414
}
415

416
/**
417
 * @brief Pass::processFinished reemits specific signal based on what process
418
 * has finished
419
 * @param id    id of Pass process that was scheduled and finished
420
 * @param exitCode  return code of a process
421
 * @param out   output generated by process(if capturing was requested, empty
422
 *              otherwise)
423
 * @param err   error output generated by process(if capturing was requested,
424
 *              or error occurred)
425
 */
426
void Pass::finished(int id, int exitCode, const QString &out,
×
427
                    const QString &err) {
428
  auto pid = static_cast<PROCESS>(id);
429
  if (exitCode != 0) {
×
430
    emit processErrorExit(exitCode, err);
×
431
    return;
×
432
  }
433
  switch (pid) {
×
434
  case GIT_INIT:
×
435
    emit finishedGitInit(out, err);
×
436
    break;
×
437
  case GIT_PULL:
×
438
    emit finishedGitPull(out, err);
×
439
    break;
×
440
  case GIT_PUSH:
×
441
    emit finishedGitPush(out, err);
×
442
    break;
×
443
  case PASS_SHOW:
×
444
    emit finishedShow(out);
×
445
    break;
×
446
  case PASS_OTP_GENERATE:
×
447
    emit finishedOtpGenerate(out);
×
448
    break;
×
449
  case PASS_INSERT:
×
450
    emit finishedInsert(out, err);
×
451
    break;
×
452
  case PASS_REMOVE:
×
453
    emit finishedRemove(out, err);
×
454
    break;
×
455
  case PASS_INIT:
×
456
    emit finishedInit(out, err);
×
457
    break;
×
458
  case PASS_MOVE:
×
459
    emit finishedMove(out, err);
×
460
    break;
×
461
  case PASS_COPY:
×
462
    emit finishedCopy(out, err);
×
463
    break;
×
464
  case GPG_GENKEYS:
×
465
    emit finishedGenerateGPGKeys(out, err);
×
466
    break;
×
467
  default:
468
#ifdef QT_DEBUG
469
    dbg() << "Unhandled process type" << pid;
470
#endif
471
    break;
472
  }
473
}
474

475
/**
476
 * @brief Pass::updateEnv update the execution environment (used when
477
 * switching profiles)
478
 */
479
void Pass::updateEnv() {
×
480
  // put PASSWORD_STORE_SIGNING_KEY in env
481
  QStringList envSigningKey = env.filter("PASSWORD_STORE_SIGNING_KEY=");
×
482
  QString currentSigningKey = QtPassSettings::getPassSigningKey();
×
483
  if (envSigningKey.isEmpty()) {
×
484
    if (!currentSigningKey.isEmpty()) {
×
485
      // dbg()<< "Added
486
      // PASSWORD_STORE_SIGNING_KEY with" + currentSigningKey;
487
      env.append("PASSWORD_STORE_SIGNING_KEY=" + currentSigningKey);
×
488
    }
489
  } else {
490
    if (currentSigningKey.isEmpty()) {
×
491
      env.removeAll(envSigningKey.first());
492
    } else {
493
      // dbg()<< "Update
494
      // PASSWORD_STORE_SIGNING_KEY with " + currentSigningKey;
495
      env.replaceInStrings(envSigningKey.first(),
×
496
                           "PASSWORD_STORE_SIGNING_KEY=" + currentSigningKey);
×
497
    }
498
  }
499
  // put PASSWORD_STORE_DIR in env
500
  QStringList store = env.filter("PASSWORD_STORE_DIR=");
×
501
  if (store.isEmpty()) {
×
UNCOV
502
    env.append("PASSWORD_STORE_DIR=" + QtPassSettings::getPassStore());
×
503
  } else {
504
    // dbg()<< "Update
505
    // PASSWORD_STORE_DIR with " + passStore;
506
    env.replaceInStrings(store.first(), "PASSWORD_STORE_DIR=" +
×
507
                                            QtPassSettings::getPassStore());
×
508
  }
509
  exec.setEnvironment(env);
×
510
}
×
511

512
/**
513
 * @brief Pass::getGpgIdPath return gpgid file path for some file (folder).
514
 * @param for_file which file (folder) would you like the gpgid file path for.
515
 * @return path to the gpgid file.
516
 */
517
auto Pass::getGpgIdPath(const QString &for_file) -> QString {
8✔
518
  QString passStore =
519
      QDir::fromNativeSeparators(QtPassSettings::getPassStore());
16✔
520
  QString normalizedFile = QDir::fromNativeSeparators(for_file);
8✔
521
  QString fullPath = normalizedFile.startsWith(passStore)
8✔
522
                         ? normalizedFile
8✔
523
                         : passStore + "/" + normalizedFile;
6✔
524
  QDir gpgIdDir(QFileInfo(fullPath).absoluteDir());
8✔
525
  bool found = false;
526
  while (gpgIdDir.exists() && gpgIdDir.absolutePath().startsWith(passStore)) {
10✔
527
    if (QFile(gpgIdDir.absoluteFilePath(".gpg-id")).exists()) {
2✔
528
      found = true;
529
      break;
530
    }
531
    if (!gpgIdDir.cdUp()) {
×
532
      break;
533
    }
534
  }
535
  QString gpgIdPath(
536
      found ? gpgIdDir.absoluteFilePath(".gpg-id")
8✔
537
            : QDir(QtPassSettings::getPassStore()).filePath(".gpg-id"));
29✔
538

539
  return gpgIdPath;
8✔
540
}
8✔
541

542
/**
543
 * @brief Pass::getRecipientList return list of gpg-id's to encrypt for
544
 * @param for_file which file (folder) would you like recipients for
545
 * @return recipients gpg-id contents
546
 */
547
auto Pass::getRecipientList(const QString &for_file) -> QStringList {
5✔
548
  QFile gpgId(getGpgIdPath(for_file));
5✔
549
  if (!gpgId.open(QIODevice::ReadOnly | QIODevice::Text)) {
5✔
550
    return {};
×
551
  }
552
  QStringList recipients;
5✔
553
  while (!gpgId.atEnd()) {
14✔
554
    QString recipient(gpgId.readLine());
18✔
555
    recipient = recipient.split("#")[0].trimmed();
18✔
556
    if (!recipient.isEmpty()) {
9✔
557
      recipients += recipient;
558
    }
559
  }
560
  return recipients;
561
}
5✔
562

563
/**
564
 * @brief Pass::getRecipientString formatted string for use with GPG
565
 * @param for_file which file (folder) would you like recipients for
566
 * @param separator formating separator eg: " -r "
567
 * @param count
568
 * @return recipient string
569
 */
570
auto Pass::getRecipientString(const QString &for_file, const QString &separator,
2✔
571
                              int *count) -> QStringList {
572
  Q_UNUSED(separator)
573
  QStringList recipients = Pass::getRecipientList(for_file);
2✔
574
  if (count) {
2✔
575
    *count = recipients.size();
1✔
576
  }
577
  return recipients;
2✔
578
}
579

580
/* Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
581
 */
582

583
/**
584
 * @brief Generates a random number bounded by the given value.
585
 * @param bound Upper bound (exclusive)
586
 * @return Random number in range [0, bound)
587
 */
588
auto Pass::boundedRandom(quint32 bound) -> quint32 {
1,160✔
589
  if (bound < 2) {
1,160✔
590
    return 0;
591
  }
592

593
  quint32 randval;
594
  // Rejection-sampling threshold to avoid modulo bias:
595
  // In quint32 arithmetic, (1 + ~bound) wraps to (2^32 - bound), so
596
  // (1 + ~bound) % bound == 2^32 % bound.
597
  // Values randval < max_mod_bound are rejected; accepted values produce a
598
  // uniform distribution when reduced with (randval % bound).
599
  const quint32 max_mod_bound = (1 + ~bound) % bound;
1,160✔
600

601
  do {
602
    randval = QRandomGenerator::system()->generate();
603
  } while (randval < max_mod_bound);
1,160✔
604

605
  return randval % bound;
1,160✔
606
}
607

608
/**
609
 * @brief Generates a random password from the given charset.
610
 * @param charset Characters to use in the password
611
 * @param length Desired password length
612
 * @return Generated password string
613
 */
614
auto Pass::generateRandomPassword(const QString &charset, unsigned int length)
1,004✔
615
    -> QString {
616
  if (charset.isEmpty() || length == 0U) {
1,004✔
617
    return {};
618
  }
619
  QString out;
1,003✔
620
  for (unsigned int i = 0; i < length; ++i) {
2,163✔
621
    out.append(charset.at(static_cast<int>(
1,160✔
622
        boundedRandom(static_cast<quint32>(charset.length())))));
1,160✔
623
  }
624
  return out;
625
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc