Skip to content

refactor: extract clipboard MIME helper for testability#971

Merged
annejan merged 13 commits intomainfrom
refactor/clipboard-mime-helper
Apr 11, 2026
Merged

refactor: extract clipboard MIME helper for testability#971
annejan merged 13 commits intomainfrom
refactor/clipboard-mime-helper

Conversation

@nogeenharrie
Copy link
Copy Markdown
Contributor

@nogeenharrie nogeenharrie commented Apr 11, 2026

User description

Summary

  • Extract buildClipboardMimeData() and dwordBytes() helpers from copyTextToClipboard()
  • Enables unit testing of platform-specific clipboard security hints
  • Centralizes Linux/macOS/Windows MIME data construction

Changes

  • Add static dwordBytes() helper for Windows DWORD conversion
  • Add buildClipboardMimeData() that returns populated QMimeData*
  • Simplify copyTextToClipboard() to use helper

Testing

All existing tests pass.

Summary by CodeRabbit

  • Refactor

    • Centralized clipboard/MIME payload construction for more consistent, secure, and correctly applied cross-platform copy behavior; clipboard vs selection is resolved once at application.
  • Tests

    • Added automated tests verifying copied text, platform-specific security/hint MIME entries, and clipboard payload integrity across Linux, Windows, and macOS.

CodeAnt-AI Description

Copying text to the clipboard now applies the right privacy hints on Linux, macOS, and Windows

What Changed

  • Clipboard copies now include the platform-specific security hints needed to keep copied passwords out of clipboard history and monitoring tools.
  • Linux copies now set the password manager hint correctly.
  • Windows copies now write the clipboard protection values in the expected format.
  • The clipboard setup is now handled in one place, keeping copy behavior consistent across platforms.

Impact

✅ Fewer passwords saved in clipboard history
✅ Better clipboard privacy on Linux, macOS, and Windows
✅ More reliable password copying across platforms

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@codeant-ai
Copy link
Copy Markdown
Contributor

codeant-ai Bot commented Apr 11, 2026

CodeAnt AI is reviewing your PR.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 11, 2026

Warning

Rate limit exceeded

@nogeenharrie has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 5 minutes and 10 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 5 minutes and 10 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 153f92a6-a3d2-43d5-b344-3814a79bb2c1

📥 Commits

Reviewing files that changed from the base of the PR and between ddb9812 and 6266f09.

📒 Files selected for processing (1)
  • tests/auto/util/tst_util.cpp
📝 Walkthrough

Walkthrough

Extracted clipboard MIME construction into a new helper buildClipboardMimeData(const QString&) that centralizes platform-specific MIME/security entries (Linux KDE hint, macOS concealed-type, Windows clipboard flags). QtPass::copyTextToClipboard now selects clipboard mode and calls QClipboard::setMimeData(mimeData, mode) once.

Changes

Cohort / File(s) Summary
Clipboard core
src/qtpass.cpp
Removed inline per-platform MIME construction from copyTextToClipboard; now computes clipboard mode, calls new buildClipboardMimeData, and applies QClipboard::setMimeData(mimeData, mode) once.
Header & helpers
src/qtpass.h
Added #include <QMimeData>; declared auto buildClipboardMimeData(const QString &text) -> QMimeData *;; added static inline auto dwordBytes(quint32) -> QByteArray helper for Windows DWORD byte representation.
Tests
tests/auto/util/tst_util.cpp
Included src/qtpass.h; added OS-guarded tests: buildClipboardMimeDataLinux, buildClipboardMimeDataWindows, buildClipboardMimeDataMac, and buildClipboardMimeDataDword; validate MIME keys, text payload, and dwordBytes output; each test deletes returned QMimeData*.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

size:S

Suggested reviewers

  • annejan

Poem

🐇 I hopped through bytes and stitched the paste,
Linux hints tucked in, macOS cloak in place.
Windows dword whispers tucked neat and tight,
A helper nibbled code to make clipboard right.
🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main refactoring objective: extracting clipboard MIME helpers for improved testability.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/clipboard-mime-helper

Comment @coderabbitai help to get the list of available commands and usage tips.

@codeant-ai codeant-ai Bot added the size:M This PR changes 30-99 lines, ignoring generated files label Apr 11, 2026
@codeant-ai
Copy link
Copy Markdown
Contributor

codeant-ai Bot commented Apr 11, 2026

CodeAnt AI finished reviewing your PR.

@coveralls
Copy link
Copy Markdown

coveralls commented Apr 11, 2026

Coverage Status

coverage: 20.923% (+0.08%) from 20.84% — refactor/clipboard-mime-helper into main

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/auto/util/tst_util.cpp`:
- Around line 81-83: Add a macOS test slot declaration consistent with the
existing ones: declare void buildClipboardMimeDataMac(); alongside
buildClipboardMimeDataLinux(), buildClipboardMimeDataWindows(), and
buildClipboardMimeDataDword() so the macOS-specific MIME type set by
buildClipboardMimeData is covered by a corresponding test slot.
- Around line 1131-1145: The test calls the internally-linked helper
dwordBytes(), which is defined as static/inline in the implementation and thus
not visible to the test; either make dwordBytes a public non-static function by
moving its full definition into the implementation file without static/inline
and adding a matching declaration to the header, or change the test to exercise
the behavior via the public API (e.g., call buildClipboardMimeData() and inspect
the produced QByteArray) so you no longer rely on the internal dwordBytes()
symbol.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: c0e3fd34-e375-4bbf-a400-864fff9d5ec0

📥 Commits

Reviewing files that changed from the base of the PR and between b3afcbb and 7efe3e8.

📒 Files selected for processing (2)
  • src/qtpass.h
  • tests/auto/util/tst_util.cpp

Comment thread tests/auto/util/tst_util.cpp
Comment thread tests/auto/util/tst_util.cpp
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/qtpass.h`:
- Around line 120-125: The public helper buildClipboardMimeData(const QString
&text) currently returns a raw QMimeData* without documenting ownership; update
the function comment to explicitly state that ownership is transferred to the
caller (caller is responsible for deleting the pointer or transferring it to a
QObject owner such as QClipboard::setMimeData which takes ownership), or
alternatively change the API to return a smart pointer (e.g.,
std::unique_ptr<QMimeData>) to express ownership — reference
buildClipboardMimeData in your change so callers know who must free or accept
ownership.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 57e3d4fb-15a2-4102-900e-b2391ca1608c

📥 Commits

Reviewing files that changed from the base of the PR and between 7efe3e8 and f70af5d.

📒 Files selected for processing (2)
  • src/qtpass.cpp
  • src/qtpass.h

Comment thread src/qtpass.h
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/auto/util/tst_util.cpp`:
- Around line 1117-1126: The test tst_util::buildClipboardMimeDataWindows only
asserts ExcludeClipboardContentFromMonitorProcessing; update it to also verify
the two other Windows hardening flags set by buildClipboardMimeData: check that
mime->hasFormat("CanIncludeInClipboardHistory") and
mime->data("CanIncludeInClipboardHistory") equals dwordBytes(0), and similarly
assert mime->hasFormat("CanUploadToCloudClipboard") and
mime->data("CanUploadToCloudClipboard") equals dwordBytes(0); ensure these
assertions use QVERIFY2 with clear messages and keep the existing cleanup
(delete mime).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: aa370501-e46a-463e-b4f4-bb87a46e40f1

📥 Commits

Reviewing files that changed from the base of the PR and between f70af5d and 611d38b.

📒 Files selected for processing (1)
  • tests/auto/util/tst_util.cpp

Comment thread tests/auto/util/tst_util.cpp
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 11, 2026

Codecov Report

❌ Patch coverage is 28.57143% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 26.99%. Comparing base (74c15d9) to head (6266f09).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/qtpass.cpp 28.57% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #971      +/-   ##
==========================================
+ Coverage   26.86%   26.99%   +0.12%     
==========================================
  Files          39       39              
  Lines        3283     3286       +3     
==========================================
+ Hits          882      887       +5     
+ Misses       2401     2399       -2     
Flag Coverage Δ
qtpass 26.99% <28.57%> (+0.12%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/auto/util/tst_util.cpp`:
- Around line 1117-1136: The function tst_util::buildClipboardMimeDataWindows
contains a duplicated preprocessor tail: remove the orphaned second
`#else`/`#endif` and extra closing brace that repeat the non-Windows skip block
so there is only one `#ifdef Q_OS_WIN ... `#else` QSKIP("Windows-only test");
`#endif`` and a single closing `}` for the function; ensure the remaining block
keeps the Windows assertions (calls to buildClipboardMimeData, QVERIFY/QCOMPARE
checks) and the non-Windows QSKIP branch intact so the file compiles without
`#else without `#if`` errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8acc0d88-b9f8-415e-8e5f-3896708a774c

📥 Commits

Reviewing files that changed from the base of the PR and between 611d38b and 8414ab7.

📒 Files selected for processing (2)
  • src/qtpass.h
  • tests/auto/util/tst_util.cpp

Comment thread tests/auto/util/tst_util.cpp
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/auto/util/tst_util.cpp`:
- Around line 1123-1125: The test currently only checks excl.size() and
excl.at(0); instead, assert the entire 4-byte DWORD payload matches the expected
bytes by comparing excl to dwordBytes(1). Replace the single-byte check on
excl.at(0) with a full-buffer equality check (e.g., QVERIFY2(excl ==
dwordBytes(1), ...)) referencing the excl variable and the dwordBytes(1) helper
so malformed DWORD encodings are caught.
- Around line 1160-1162: Add an explicit format existence check before asserting
the payload: call mime->hasFormat("application/x-nspasteboard-concealed-type")
and assert it is true (using QVERIFY or QVERIFY2) to ensure the format key is
present, then keep the existing QVERIFY2 comparing
mime->data("application/x-nspasteboard-concealed-type") to QByteArray() to
verify the value; update the assertion messages accordingly to reflect the two
checks (format exists, then value is empty).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0ce5d49b-3fca-480b-93c0-38e3fc7f1def

📥 Commits

Reviewing files that changed from the base of the PR and between 611d38b and 12147b4.

📒 Files selected for processing (2)
  • src/qtpass.h
  • tests/auto/util/tst_util.cpp

Comment thread tests/auto/util/tst_util.cpp Outdated
Comment thread tests/auto/util/tst_util.cpp Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
tests/auto/util/tst_util.cpp (1)

1127-1128: ⚠️ Potential issue | 🟠 Major

Assert Windows flag payload values, not only format presence.

Line 1127 and Line 1128 only check hasFormat() for CanIncludeInClipboardHistory and CanUploadToCloudClipboard. A wrong DWORD payload would still pass this test, weakening the security regression coverage added by this PR.

🔧 Suggested test strengthening
   QVERIFY(mime->hasFormat("ExcludeClipboardContentFromMonitorProcessing"));
   QVERIFY(mime->hasFormat("CanIncludeInClipboardHistory"));
   QVERIFY(mime->hasFormat("CanUploadToCloudClipboard"));
+  QCOMPARE(mime->data("CanIncludeInClipboardHistory"), dwordBytes(0));
+  QCOMPARE(mime->data("CanUploadToCloudClipboard"), dwordBytes(0));
   delete mime;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/auto/util/tst_util.cpp` around lines 1127 - 1128, The test currently
only checks presence of formats; instead read the payload bytes for
"CanIncludeInClipboardHistory" and "CanUploadToCloudClipboard" from the mime
object (e.g., call mime->data("CanIncludeInClipboardHistory") /
mime->data("CanUploadToCloudClipboard")), assert the QByteArray size is 4
(DWORD) and decode it as a little-endian 32-bit unsigned integer (using
QDataStream or manual byte unpacking) and QCOMPARE that the decoded value equals
the expected DWORD flag (e.g., 1 for enabled or the correct constant your
product uses) for both formats to ensure the actual flag values are validated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@tests/auto/util/tst_util.cpp`:
- Around line 1127-1128: The test currently only checks presence of formats;
instead read the payload bytes for "CanIncludeInClipboardHistory" and
"CanUploadToCloudClipboard" from the mime object (e.g., call
mime->data("CanIncludeInClipboardHistory") /
mime->data("CanUploadToCloudClipboard")), assert the QByteArray size is 4
(DWORD) and decode it as a little-endian 32-bit unsigned integer (using
QDataStream or manual byte unpacking) and QCOMPARE that the decoded value equals
the expected DWORD flag (e.g., 1 for enabled or the correct constant your
product uses) for both formats to ensure the actual flag values are validated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4a48eac5-6b70-4eba-9e5b-7f85f26e3d94

📥 Commits

Reviewing files that changed from the base of the PR and between 12147b4 and ddb9812.

📒 Files selected for processing (1)
  • tests/auto/util/tst_util.cpp

@annejan annejan merged commit 318ddf9 into main Apr 11, 2026
26 checks passed
@annejan annejan deleted the refactor/clipboard-mime-helper branch April 11, 2026 19:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M This PR changes 30-99 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants