| Version | Supported |
|---|---|
| 1.3.x | ✅ Active |
| < 1.3 | ❌ End of life |
If you discover a security vulnerability in copycrow, please DO NOT open a public issue. Send a private report via:
- GitHub Security Advisories: Report a vulnerability
You will receive a response within 72 hours.
copycrow is designed with the following principles:
-
No credentials in code — user configuration (
copycrow.conf) is gitignored. All authentication is delegated to:- The system's
~/.ssh/configfor SSH pass(GPG-encrypted) for the Borg passphrase, consumed viaBORG_PASSCOMMAND
- The system's
-
Secrets never touch disk —
BORG_PASSPHRASEis never written anywhere by copycrow. Automation persists only non-secret data (theBORG_PASSCOMMANDcommand string) in~/.config/copycrow/borg.envwith0600permissions inside a0700directory. The file is removed by./copycrow.sh uninstall. For SSH keys protected by a passphrase, expose an agent to the user systemd session instead (e.g. a user-levelssh-agent.serviceorAddKeysToAgent yesin~/.ssh/config); the ephemeralSSH_AUTH_SOCKpath is deliberately not persisted because it goes stale after a reboot. -
Input validation, enforced end-to-end — every configuration value is validated while loading (fail-fast): shell metacharacters are rejected, path traversal is blocked,
hostmust be a well-formed SSH alias without a leading dash (blocking SSH option injection such as-oProxyCommand=),retentionis restricted toborg prune --keep-*flag/number pairs, andcompression/type/scheduleare whitelist-checked. Required fields (type,sources,host,remote_path) are verified before any command performs work. -
Encryption at rest — Borg repositories use
repokey(key derived from passphrase) by default. -
Safe restores — extracted archives require a sanitized archive name (charset-restricted, no traversal) and the target directory is resolved with
realpathand contained inside the project mount directory. Extraction runs in a hardened context (umask 077plus a post-extractchmod -R go-rwx), so archived files with permissive modes never become group/world readable. -
Concurrent-safe — job locking uses kernel
flock: the lock lives as long as the holder's file descriptor, so it is released by the OS even if the process crashes, and no two runs of the same job can interleave (verified by concurrency stress tests). -
User isolation — timers use
systemd --user, no root elevation. -
Failure hook runs without a shell — the optional
[global] on_failure_cmdvalue is charset-validated at load time (metacharacters; & | $ \< > ` are rejected, same class as other keys) and executed via plain word-splitting, never throughevalor a shell. Failure context reaches the hook only through environment variables (COPYCROW_FAILED_JOB,COPYCROW_FAILURE_ARCHIVE,COPYCROW_FAILURE_EXIT_CODE). A failing hook is logged but cannot alter the backup's result. -
SSH hardening recommendation — the documentation suggests a dedicated passphrase-less SSH key with
command="borg serve --restrict-to-path ..."in the server'sauthorized_keys(see README).
copycrow is a wrapper on top of Borg Backup for home users and individual sysadmins. It is not audited for use in enterprise or multi-tenant production environments.
We appreciate responsible reports. Contributors who report valid vulnerabilities will be credited in the CHANGELOG (with their permission).