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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

copysec-1.0.0.tar.gz (17.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

copysec-1.0.0-py3-none-any.whl (21.9 kB view details)

Uploaded Python 3

File details

Details for the file copysec-1.0.0.tar.gz.

File metadata

  • Download URL: copysec-1.0.0.tar.gz
  • Upload date:
  • Size: 17.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for copysec-1.0.0.tar.gz
Algorithm Hash digest
SHA256 2313d50c55bf615b5cfb627fc5cbbdb794fa33aa1dbb59102828bccfe9b9eee8
MD5 a6cbf41a71d5de908b7a2510de4e5b48
BLAKE2b-256 d5904512b56f2223db838be57e5dbc4a8fcc708be0a4206401503ed05e0b7e47

See more details on using hashes here.

Provenance

The following attestation bundles were made for copysec-1.0.0.tar.gz:

Publisher: publish.yml on Lunixizm0/CopySec

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file copysec-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: copysec-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 21.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for copysec-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b5af789531a65f555a58883bc37dc67c9ddf1399186a6a0a05f44ae7501bde1c
MD5 ced035ab92d852e2e4430e86350e6fab
BLAKE2b-256 729d7c4d87557f14bd73211837ad612e72b6f7870bb154f8da5ef5ba374fb662

See more details on using hashes here.

Provenance

The following attestation bundles were made for copysec-1.0.0-py3-none-any.whl:

Publisher: publish.yml on Lunixizm0/CopySec

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 files

0.9.9

2 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