janus-sec
A terminal-first credential exposure auditor.
Your SSH keys, AWS credentials, and kube config are only as safe as their file
permissions. A chmod 644 typo, a bad tarball extraction, or a cloud sync
tool can quietly leave a private key readable by every user on your machine
— and there's usually no warning until something goes wrong.
janus-sec scans the credential files you already have, tells you exactly
what's wrong and why it matters, and fixes it with one confirmed keystroke.
Nothing leaves your machine — no network calls, no file content is ever
read, no sudo.
$ janus-sec
Scanned 4 file(s), 8 not present.
[HIGH] /home/user/.ssh/id_rsa
Issue: world_readable (mode 644)
Why: This file is readable by any local user on this machine.
Credential files should only be readable by their owner.
Fix: chmod 600 /home/user/.ssh/id_rsa
[MEDIUM] /home/user/.aws/credentials
Issue: group_readable (mode 640)
Why: This file is readable by group 'docker', which is not your
primary group. Other accounts in that group can read this file.
Fix: chmod 600 /home/user/.aws/credentials
2 finding(s) total, 1 HIGH risk.
Why this exists
The underlying check here is genuinely simple — find ~/.ssh -perm /077
does most of the mechanical work in one line. That's not the point.
The gap janus-sec closes isn't "how do I check this," it's "I never
thought to check this, and if I did, I wouldn't trust a one-off command
to tell me what to do about it or keep a record that I did it."
Concretely, what a one-liner doesn't give you:
- An explanation.
-rw-r--r--means nothing to most people at a glance. "This private key is readable by any local user on this machine" changes behavior. - The right fix for each file type. SSH keys,
known_hosts, AWS credentials, and kube configs don't all want the same target mode. - A safe way to apply it. Confirmation by default,
--dry-runto preview, and a re-check immediately before writing (the file you looked at when scanning might not be the file you're about to change). - A record. An append-only audit log of every fix, so "what did I change and when" has a real answer.
- A CI gate.
--ciexits non-zero on any HIGH-risk finding, so this can fail a pipeline, not just print a warning nobody reads.
What it checks
| Check | Risk | Auto-fix? |
|---|---|---|
| World-readable / world-writable | HIGH | Yes |
| Owned by a different user | HIGH | No — would require sudo, out of scope |
| Group-readable (non-primary group) | MEDIUM | Yes |
| Symlink pointing outside its expected directory | MEDIUM | No — target choice is a judgment call |
Scans by default: ~/.ssh/*, ~/.aws/*, ~/.kube/config, ~/.npmrc,
~/.git-credentials, ~/.docker/config.json. Missing files are silently
skipped, not errors — most people won't have all of these.
Safety properties
- No network access, ever.
- Never reads file contents — only metadata (permissions, ownership). It can't tell you if a secret has leaked; it tells you if the file is more exposed than it should be.
- Never elevates privileges. No
sudo, nochown. If a fix would require that,janus-secreports the problem and stops — it doesn't attempt it. - Every fix requires confirmation (or an explicit
--yesfor scripted use), and--dry-runpreviews changes with zero side effects. - Append-only audit log of every fix applied, at
~/.local/state/janus-sec/audit.log.
Install
Not yet published to PyPI — install from source for now:
git clone https://github.com/A-S-Manoj/janus-sec.git
cd janus-sec
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
Usage
# Scan (also the default with no arguments - read-only, safe to run any time)
janus-sec
janus-sec scan
# Interactive TUI
janus-sec tui
# Machine-readable output
janus-sec scan --format json
# CI mode: exit 1 if any HIGH-risk finding exists
janus-sec scan --ci
# Fix everything fixable, with confirmation
janus-sec fix
# Fix one specific file
janus-sec fix ~/.ssh/id_rsa
# Preview fixes without changing anything
janus-sec fix --dry-run
# Skip the confirmation prompt (for scripts)
janus-sec fix --yes
Configuration
~/.config/janus-sec/config.toml (optional — everything works with no
config file at all):
# Suppress a specific check on a specific file - not the whole file,
# just that one issue, so other real problems on it still get caught.
[[ignore]]
path = "/home/user/.ssh/known_hosts"
check_type = "group_readable"
note = "shared dev box, group access is intentional"
# Treat a group as known-safe, machine-wide.
[[allowlist]]
group = "wheel"
action = "suppress" # or "downgrade_to_low"
note = "trusted admin group on my machines"
Development
Same setup as above, plus test dependencies:
pip install pytest pytest-asyncio
pytest -v
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file janus_sec-0.1.0.tar.gz.
File metadata
- Download URL: janus_sec-0.1.0.tar.gz
- Upload date:
- Size: 26.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f19f329a057fd15b5e56b112676a97585f92a701c4583d4cc1d2b686c86aed88
|
|
| MD5 |
dd77d857ec3a24efc2201012bab592d3
|
|
| BLAKE2b-256 |
8a0fa348a67853a39280f99ac0662671ca6b947182cafbfe6da944732698727b
|
File details
Details for the file janus_sec-0.1.0-py3-none-any.whl.
File metadata
- Download URL: janus_sec-0.1.0-py3-none-any.whl
- Upload date:
- Size: 24.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9e82856d4b218cae8dbaf36bd3a58da76e271c622969224f34589a6a30a7bd90
|
|
| MD5 |
8975763deb120195e24647c204f58c06
|
|
| BLAKE2b-256 |
90de07e816dda157a1c61a53acb071bc6fe893b9791f2ecb914d9ec8f724695d
|