Skip to main content

CopySec

Clipboard protection: only the active (foreground) application and its process tree can read the clipboard. Every unauthorized read attempt receives empty data and is logged as JSONL.

How it works

Windows does not allow ACLs on the clipboard; instead, CopySec takes ownership of the clipboard via delayed rendering. Whenever any process calls GetClipboardData, the system forwards the request to CopySec as a WM_RENDERFORMAT message; at that moment GetOpenClipboardWindow() resolves the requester's PID and a decision is made:

  • Allowed: ourselves, the foreground window's process, descendants of the foreground process (child/grandchild), allowlist
  • Denied: no format is rendered, the reader receives NULL, and the event is logged

The real content is kept in memory only while CopySec owns the clipboard; the clipboard always remains owned by CopySec. On exit or pause, the real data is written back to the clipboard and the in-memory copies are overwritten and released. Password manager auto-clear timers keep working: when the source app empties the clipboard, CopySec sees the change and drops its snapshot as well.

Installation and running

uv sync
uv run copysec            # with tray icon
uv run copysec --no-tray --verbose   # console mode + live log

To run as administrator, open PowerShell via "Run as administrator" and run the same command. Not required; all APIs used work with normal privileges. Running elevated is recommended when you want protection against other processes on the machine: Windows (UIPI) then blocks foreign processes from posting control messages to the guard window, so a same-user process cannot pause or close the guard silently.

Configuration

%USERPROFILE%\AppData\Local\CopySec\config.json (the standard %LOCALAPPDATA% location) is created on first run:

Field Default Meaning
allowlist ["C:\\Windows\\System32\\svchost.exe"] Always-allowed programs (case-insensitive). A full image path pins the entry to that exact file; a bare name like "foo.exe" matches any process with that name regardless of where it loads from (legacy behavior, an insecure_allowlist record is logged at startup). The default covers the Win+V clipboard history service (cbdhsvc inside the system svchost.exe)
allow_uwp_frame_host false Legacy escape hatch: allows every request from ApplicationFrameHost.exe itself. Regular UWP paste works out of the box because CopySec resolves the frame-hosted app's real process; enable only if some packaged app still fails
deny_unknown_requester true Deny when it cannot be determined which window opened the clipboard (spyware can pass NULL)
log_allows false Also log allowed accesses
max_format_bytes 33554432 Skip clipboard formats larger than this many bytes on adopt (logged as oversized_format); guards the guard's memory against hostile oversized payloads

Logs: %USERPROFILE%\AppData\Local\CopySec\logs\audit-YYYYMMDD.jsonl. When a file reaches 1 MB it is rotated to audit-YYYYMMDD.1.jsonl, .2.jsonl, and so on for the rest of the day. On every startup CopySec also checks the whole logs folder: if it exceeds 10 MB, the oldest files are deleted until 5 MB or less remains. A logs_pruned record notes how many files were removed. Note: with --config <path> the log directory becomes <path parent>\logs instead.

Rearm benchmarking: every rearmed record carries dur_ms (the re-arm operation itself), avg_ms (rolling mean over the last 64 re-arms), samples, and, when the clipboard was busy before the success, settle_ms (time from first failed attempt to success). Watch them live with --verbose.

Testing

Everything (unit + integration) in one run:

uv run pytest

The two integration tests (matrix_a, matrix_b) drive the real guard end to end through the PowerShell scripts in scripts\ and need an interactive desktop session; each adds roughly 15-20 seconds. Useful selections:

uv run pytest -m "not integration" -q   # unit tests only
uv run pytest -m integration -q         # matrices only
powershell -File scripts\matrix_a.ps1   # denied reader to empty data, restore on exit
powershell -File scripts\matrix_b.ps1   # allowlisted reader to real data
uv run python scripts\spy_sim.py        # background "spy" simulator (live monitoring)

Notes:

  • The matrices take over the clipboard and briefly move window focus; do not run pytest sessions in parallel.
  • Without an interactive desktop (SSH, services) they skip automatically.

Manual scenario:

  1. Start CopySec and run spy_sim in the background (it sees empty data).
  2. Write something in Notepad, press Ctrl+A and Ctrl+C, then Ctrl+V in Notepad (works because it is foreground).
  3. spy_sim output must stay empty; deny lines accumulate in the audit log.
  4. Tray > Pause: spy_sim now sees the content. Resume closes it again.

Known limitations

  • Between the user copying and CopySec taking ownership there is a millisecond-scale window in which a very fast reader can see the real data once.
  • After an allowed read, the content stays as real clipboard data for a short time; CopySec returns it to delayed mode within ~50 ms (re-arm). Other readers racing during that window can see the data. Allowlisted components that read continuously, such as clipboard history (cbdhsvc), keep triggering this cycle; that is normal.
  • Owner-tied formats (CF_OWNERDISPLAY and CF_DSP*) cannot be carried across ownership changes; they are skipped.
  • Brief exposure window: after an allowed app reads the clipboard, Windows keeps the real data available until CopySec re-arms delayed rendering (normally well under a second, retried aggressively). A process reading inside that window may see the data without being checked.
  • UWP paste works automatically: when the foreground window belongs to ApplicationFrameHost.exe, CopySec resolves the hosted app's process through its CoreWindow child window and applies the normal rules to it.
  • Rare UIPI quirks are possible with elevated (high IL) readers plus a non-admin guard; if you hit issues, run both at the same integrity level.

Troubleshooting

  • Pasted content came out empty: The reading app is not foreground or was denied. Check the deny lines printed with --verbose (the rule field explains why: no-match, unknown-requester, ...). If needed add an entry to allowlist (prefer a full image path so only that exact binary matches), or for exotic packaged apps that still fail enable allow_uwp_frame_host.
  • Leave the clipboard cleanly: Close CopySec with Ctrl+C or tray > Exit (the real data is written back to the clipboard). If you force-kill it from Task Manager the delayed-render data goes away too and the clipboard ends up empty; that is Windows' delayed rendering behavior.
  • Running elevated (admin PowerShell): Supported and verified. Windows delivers clipboard render messages across integrity levels, so non-elevated apps still go through the normal decision path; elevation of the guard itself grants nothing to any reader. Verify anytime with scripts\xil_check.ps1 (spawns a real Medium-IL reader via a scheduled task and checks both the deny and allow paths).
  • Do not run two copies: Only one guard can take ownership at a time; the second one waits pointlessly.
  • rearm_failed / rearmed pairs in the log: Normal when an allowlisted background reader (typically the clipboard history service, cbdhsvc inside svchost.exe) keeps the clipboard open right after reading. CopySec re-arms its delayed rendering as soon as the clipboard frees up (retried every 500 ms); during that gap the real data is briefly readable by anyone (see Known limitations). A consecutive count above a few would signal something is holding the clipboard open for a long time.

Architecture

src/copysec/
    winapi.py     ctypes Win32 bindings (clipboard, global memory, DIB to HBITMAP)
    store.py      real content store + adopt/flush/render
    policy.py     decision engine (foreground, process tree, allowlist)
    proctree.py   psutil-based PID/exe/ancestor-chain cache
    guard.py      hidden window, WndProc, message loop
    audit.py      JSONL audit log + rate limit
    tray.py       pystray tray icon
    cli.py        entry point

Metadata

Release files for copysec 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for copysec 1.0.0
File Size Uploaded
copysec-1.0.0.tar.gz 17.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for copysec 1.0.0
File Interpreter ABI Platform
copysec-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 39.6 kB

Release files / copysec-1.0.0.tar.gz

Download URL copysec-1.0.0.tar.gz
Size 17.8 kB
Tags Source
SHA-256 checksum
How to use checksums
2313d50c55bf615b5cfb627fc5cbbdb794fa33aa1dbb59102828bccfe9b9eee8
BLAKE2b-256 checksum
How to use checksums
d5904512b56f2223db838be57e5dbc4a8fcc708be0a4206401503ed05e0b7e47
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 23, 2026.

Transparency log

Release files / copysec-1.0.0-py3-none-any.whl

Download URL copysec-1.0.0-py3-none-any.whl
Size 21.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b5af789531a65f555a58883bc37dc67c9ddf1399186a6a0a05f44ae7501bde1c
BLAKE2b-256 checksum
How to use checksums
729d7c4d87557f14bd73211837ad612e72b6f7870bb154f8da5ef5ba374fb662
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 23, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

0.9.9

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page