Cipher
Cipher is a local static-analysis scanner for security and supply-chain review. It inspects a project path and runs four focused checks:
- Authentication
- Over-Privilege
- CVE Lookup
- Typosquatting / Shadowing
The goal of this repository is not to guess at security problems. It is to produce high-signal findings from concrete code and manifest evidence, then present them in a format that is useful for triage.
Installing the CLI package
This repo can also be installed as a local CLI package:
pip install -e .
cipher-scan . --fail-on high
The package is intentionally local-path only for v0.1: it scans the checked-out repository on disk, without any Render, Ollama, or remote download dependency.
Use --fail-on none when you want a report-only run without a failing exit code.
Example local usage
cipher-scan .
cipher-scan . --fail-on high --format json
cipher-scan . --fail-on high --output cipher-scan-report.json
Private repositories work the same way as any other local repo: use GitHub Actions or a local checkout, then scan the checked-out path with cipher-scan ..
What This Repository Contains
At the top level, Cipher has a very small shape:
s:\Cipher
├── checks/
├── main/
├── test/
├── run_checks.py
└── README.md
The important part is the checks/ package. Each subpackage owns one security analysis area and exposes a public scan() interface. run_checks.py is the simple combined runner that executes all four checks in order for a local project path.
System Architecture
The overall flow is intentionally simple.
flowchart TD
A[User provides local project path] --> B[run_checks.py]
B --> C[AuthenticationCheck]
B --> D[OverPrivilegeCheck]
B --> E[CVELookupCheck]
B --> F[TyposquattingCheck]
C --> G[Findings]
D --> G
E --> G
F --> G
G --> H[Readable console summary]
The combined runner does not try to interpret findings. It simply calls each check, catches errors per check, and prints a clean summary so one failure does not stop the rest of the scan.
Why The Architecture Looks This Way
The design is deliberately modular.
- Each check can be tested and improved independently.
- Each check has its own data model assumptions and heuristics.
- The runner remains small and stable even if one security area evolves.
- A failure in one analysis path does not block the others.
That separation matters because the four checks solve different problems:
- Authentication looks for missing guards on sensitive handlers.
- Over-Privilege looks for excessive capabilities and dangerous combinations.
- CVE Lookup looks for vulnerable dependencies in manifests and lockfiles.
- Typosquatting looks for suspicious near-matches and tool-name shadowing.
How A Scan Works
The scan lifecycle is the same at a high level for all checks:
sequenceDiagram
participant U as User
participant R as run_checks.py
participant C as Check implementation
participant O as Output
U->>R: Provide project path
R->>C: scan(project_root)
C->>C: Discover files and build context
C->>C: Apply rules / heuristics
C->>C: Return findings
R->>O: Print findings and summary
Each check follows that same pattern, but the internal analysis differs.
Check Overview
1) Authentication Check
The Authentication Check scans source code for security-sensitive entry points that appear to be exposed without a clear authentication guard.
What it looks for:
- MCP-style tool handlers
- Route handlers that appear to do privileged work
- Weak secret defaults and fail-open secret checks
- Empty allowlists and risky CORS-style trust patterns
- Non-loopback service bindings without a visible auth gate
- Inconsistent auth enforcement across sibling routes
Architecture view:
flowchart LR
A[Source files] --> B[Parser / AST extraction]
B --> C[Auth signal detector]
C --> D[Implementation validator]
D --> E[Security scorer]
E --> F[Finding]
The important idea is that the module does not rely on a single string match. It combines structure, decorators, routes, imports, and auth-related evidence before emitting a finding.
2) Over-Privilege Check
The Over-Privilege Check looks at tool and capability declarations, especially MCP configuration, and asks a simple question: does this tool request more power than it needs?
What it looks for:
- filesystem read/write/execute capabilities
- network access
- credential access
- database access
- system or process privileges
- dangerous capability combinations such as data exfiltration or privilege escalation chains
Architecture view:
flowchart LR
A[mcp.json / capability config] --> B[Capability extractor]
B --> C[Dangerous combo detector]
C --> D[Least-privilege evaluator]
D --> E[Risk calculator]
E --> F[Finding]
This check is policy-driven. The capability taxonomy and combo table define the security meaning, while the evaluator turns those declarations into findings and severity.
3) CVE Lookup Check
The CVE Lookup Check is a dependency vulnerability scan for supported manifests and lockfiles.
Supported inputs:
requirements.txtandrequirements-*.txtpyproject.tomlpackage.jsonpackage-lock.jsonpoetry.lock
Architecture view:
flowchart LR
A[Dependency manifests] --> B[Dependency extractor]
B --> C[Version resolution / lockfile hints]
C --> D[OSV-compatible lookup]
D --> E[Cache]
D --> F[Finding builder]
E --> D
F --> G[Aggregated findings]
This check is designed to stay offline-safe when network lookup is unavailable. It also keeps the output low-noise by aggregating by package and merging vulnerability identifiers into a single finding when possible.
4) Typosquatting / Shadowing Check
The Typosquatting Check scans package names and MCP tool names for suspicious near-matches.
What it looks for:
- package names that closely resemble known popular package names
- MCP tool names that closely resemble known tool names
- duplicate tool names that can shadow one another across configs
Architecture view:
flowchart LR
A[Manifest and MCP names] --> B[Name extractor]
B --> C[Similarity detector]
B --> D[Shadowing detector]
C --> E[Finding builder]
D --> E
E --> F[Offline-safe findings]
This check is also offline-safe. It works from a local trusted-name catalog and local project files, not from external registry queries.
Combined Runner
run_checks.py is the main entry point for demo use.
What it does:
- accepts one local project path argument
- runs the four checks in order
- prints each check name and finding count
- prints each finding as
severity | title | file_path - catches errors per check and continues scanning the rest
Example:
python run_checks.py .\test\Cipher-demo-main
That runner is intentionally small. It is a presentation layer, not a fifth scanner.
Repository Map
flowchart TD
A[Repository root] --> B[checks/]
A --> C[main/]
A --> D[test/]
A --> E[run_checks.py]
B --> B1[authentication/]
B --> B2[over_privilege/]
B --> B3[cve_lookup/]
B --> B4[typosquatting/]
B1 --> B1a[auth_check.py]
B1 --> B1b[self_test.py]
B2 --> B2a[over_privilege_check.py]
B2 --> B2b[self_test.py]
B3 --> B3a[cve_check.py]
B3 --> B3b[self_test.py]
B4 --> B4a[typosquatting_check.py]
B4 --> B4b[self_test.py]
Directory Roles
checks/contains the actual analysis logic.main/contains shared project data and support code.test/contains a vulnerable demo repository that the checks can scan.run_checks.pyis the combined CLI runner.
Output Philosophy
Cipher is designed for triage, not for maximum noise.
That means:
- findings should be explainable from the evidence shown
- duplicate emissions should be reduced when they do not add value
- remediation should point toward an obvious next action
- severity should reflect the strength of the signal, not just the existence of a match
In practice, a useful finding usually includes:
- severity
- title
- file path
- evidence
- remediation guidance
Example End-to-End Scan
If you scan the bundled demo target, the runner will print a grouped summary like this:
Scanning: S:\Cipher\test\Cipher-demo-main
== AuthenticationCheck ==
Findings: 4
- high | Missing authentication for sensitive MCP tool handler | server.py
== OverPrivilegeCheck ==
Findings: 6
- high | Dangerous combination: Data Exfiltration | S:\Cipher\test\Cipher-demo-main\mcp.json
== CVELookupCheck ==
Findings: 4
- critical | Known npm dependency vulnerability: axios | S:\Cipher\test\Cipher-demo-main\package.json
== TyposquattingCheck ==
Findings: 2
- medium | Potential package name typosquatting | S:\Cipher\test\Cipher-demo-main\package.json
The exact findings depend on the target project, but the format stays the same.
Working With The Individual Checks
Each check keeps the same public scan interface.
from checks.authentication.auth_check import AuthenticationCheck
from checks.over_privilege.over_privilege_check import OverPrivilegeCheck
from checks.cve_lookup.cve_check import CVELookupCheck
from checks.typosquatting.typosquatting_check import TyposquattingCheck
root = r"S:\Cipher\test\Cipher-demo-main"
auth_findings = AuthenticationCheck(root).scan()
priv_findings = OverPrivilegeCheck(root).scan()
cve_findings = CVELookupCheck(root).scan()
typo_findings = TyposquattingCheck(root).scan()
That interface consistency is important because it lets the combined runner stay simple.
Testing
The repository includes package-level self-tests for each check. The tests are designed to protect the behavior of the public scan interface and the project-specific heuristics.
Typical commands:
python -m unittest checks.authentication.self_test
python -m unittest checks.over_privilege.self_test
python -m unittest checks.cve_lookup.self_test
python -m unittest checks.typosquatting.self_test
Practical Notes
- The scanner works on a local path only.
- The CVE check prefers exact version evidence and can fall back to cache behavior when needed.
- The typosquatting check is offline-safe and uses a local trusted-name set.
- The runner is intentionally minimal so it can be used in demos without extra setup.
Short Version
If you want the shortest mental model possible, think of Cipher like this:
- Point it at a local project directory.
- It inspects source files and manifests.
- Each check produces security findings from a different angle.
- The combined runner prints the result in a readable, triage-friendly summary.
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 cipher_mcp_scan-0.1.0.tar.gz.
File metadata
- Download URL: cipher_mcp_scan-0.1.0.tar.gz
- Upload date:
- Size: 44.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
168c489c6b2b3936db824dbb05271ec17c7819132c5e0b7243db572a1ba455dd
|
|
| MD5 |
39b0da5d9fbe254a587549b328a0a958
|
|
| BLAKE2b-256 |
4178ff0a1359d8c567f81d39da06a5cf0eb22d111df433bcd24f879f3051a5f5
|
File details
Details for the file cipher_mcp_scan-0.1.0-py3-none-any.whl.
File metadata
- Download URL: cipher_mcp_scan-0.1.0-py3-none-any.whl
- Upload date:
- Size: 45.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d83b502964f399ccc1c39861e3d592b04270e469c3869970d877e905add3f673
|
|
| MD5 |
faa9d2812ce8e8ed344c1a38e066b951
|
|
| BLAKE2b-256 |
61d2c7079d4b440736d18909a0520ab11863750500f069a253a3a810ffd6749c
|