A comprehensive, production-grade platform for discovering quantum-vulnerable cryptography across your codebases, scoring crypto agility, tracking migration progress, and generating Cryptographic Bill of Materials (CBOM) for compliance.
PQCScanner solves the challenge of finding WHERE quantum-vulnerable cryptography is used across hundreds of repositories. It provides:
| Tool | Purpose |
|---|---|
| React | Component-based SPA |
| Vite | Build tool and dev server |
| Carbon Design System | UI components (dark theme) |
Native fetch API |
HTTP client (src/api/client.js) |
| React Hooks | State management (useState, useEffect) |
| Tool | Version | Purpose |
|---|---|---|
| FastAPI | 0.111.0 | REST API framework |
| Uvicorn | 0.29.0 | ASGI server |
| SQLAlchemy | 2.0.30 | ORM |
| Pydantic | 2.7.1 | Data validation |
| pydantic-settings | 2.2.1 | Settings management |
| psycopg2-binary | 2.9.9 | PostgreSQL driver |
| Alembic | 1.13.1 | Database migrations |
| Celery | 5.4.0 | Async task queue |
| Redis (Python) | 5.0.4 | Cache and message broker |
| GitPython | 3.1.43 | Remote repo cloning |
| httpx | 0.27.0 | HTTP client |
| bcrypt | 4.1.3 | Password hashing |
| Paramiko | 3.5.1 | SSH key parsing |
| cryptography | latest | X.509 cert and PKCS#12 parsing |
| Python | 3.12 | Runtime |
| Tool | Version | Purpose |
|---|---|---|
| PostgreSQL | 16-alpine | Primary database |
| Redis | 7-alpine | Cache and Celery broker |
| Docker + Docker Compose | latest | Container orchestration |
Browser
└── React + Vite + IBM Carbon (port 5173)
│
▼ HTTP/JSON (fetch API)
FastAPI (port 8000)
├── 8 Routers (auth, repos, scans, findings, cbom, secrets, reports, playbooks)
├── SQLAlchemy ORM ──► PostgreSQL 16 (port 5432)
├── Celery Workers ──► Redis 7 (port 6379)
└── Scanner Engine
├── engine.py (54+ regex patterns, agility scoring)
└── file_secrets.py (SSH/TLS/GPG/config parsing)
pqc-platform/
├── backend/
│ ├── app/
│ │ ├── main.py # FastAPI app entry point (8 routers)
│ │ ├── api/
│ │ │ ├── auth.py # Login, logout, session
│ │ │ ├── repos.py # Repo CRUD
│ │ │ ├── scans.py # Scan trigger & status polling
│ │ │ ├── findings.py # Finding query, resolve, archive
│ │ │ ├── cbom.py # CBOM + CycloneDX/CSV export
│ │ │ ├── reports.py # Dashboard summary stats
│ │ │ ├── playbooks.py # Remediation playbooks
│ │ │ └── secrets.py # SSH/TLS/GPG secret findings
│ │ ├── models/ # SQLAlchemy ORM models (5 entities)
│ │ ├── scanner/
│ │ │ ├── engine.py # Core crypto detection + agility scoring
│ │ │ └── file_secrets.py # SSH key, TLS cert, GPG, SSH config scanner
│ │ ├── db/ # PostgreSQL connection
│ │ ├── core/ # Config and settings
│ │ └── schemas/ # Pydantic request/response schemas
│ ├── cli_scan.py # Standalone CLI scanner
│ ├── migrations/ # SQL migration files
│ ├── requirements.txt
│ └── Dockerfile
├── frontend/
│ ├── src/
│ │ ├── App.jsx # Main app (5 views, dark theme)
│ │ ├── api/client.js # Fetch-based API client
│ │ └── hooks/useApi.js
│ ├── package.json
│ └── Dockerfile
├── .github/workflows/
│ └── pqc-scan.yml # GitHub Actions CI/CD
└── docker-compose.yml # Full stack orchestration
git clone https://github.com/VipinPS/PQCScanner.git
cd PQCScanner
docker compose up
# 1. Start PostgreSQL and Redis
docker run -e POSTGRES_USER=pqc_user -e POSTGRES_PASSWORD=pqc_pass \
-e POSTGRES_DB=pqc_platform -p 5432:5432 postgres:16-alpine
docker run -p 6379:6379 redis:7-alpine
# 2. Backend
cd backend
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000
# 3. Frontend
cd frontend
npm install && npm run dev
| Service | URL |
|---|---|
| Frontend UI | http://localhost:5173 |
| Backend API | http://localhost:8000 |
| Swagger UI (interactive docs) | http://localhost:8000/docs |
| ReDoc (clean docs) | http://localhost:8000/redoc |
| OpenAPI JSON schema | http://localhost:8000/openapi.json |
| Health check | http://localhost:8000/api/health |
Executive-level overview showing:
Tab-based investigation interface:
migrated_to evidenceFiltering: by repo, risk level, quantum status, algorithm, migration status, file path search
Actions: resolve with migration target, archive (soft-delete), bulk archive, restore
Complete crypto asset inventory aggregated across all repos:
20 algorithms × 6 languages with:
Languages: Python, Java, Go, TypeScript/Node.js, C#, Shell
Dedicated view for cryptographic material found in repositories:
Crypto agility is the ability to swap cryptographic algorithms without redesigning the application. The platform scores each repository on a 5-level maturity scale:
| Level | Label | Description |
|---|---|---|
| L1 | Hardcoded | Algorithm baked into code — e.g., RSA.generate(1024) or private key in source |
| L2 | Configurable | Algorithm set via config/env at deploy time — e.g., CRYPTO_ALGO="RSA-2048" |
| L3 | Hot-Swap | Registry/factory pattern — swappable at runtime without restart |
| L4 | Hybrid | Classical + PQC running simultaneously — e.g., X25519 + ML-KEM-768 |
| L5 | Fully Agile | Per-request algorithm negotiation, zero hardcoding, fully PQC-ready |
| Pattern | Score | Level Signal |
|---|---|---|
alg_negotiat / negotiateAlgorithm |
+4 | → L5 |
HybridKEM / X25519MLKEM / hybrid.*pqc |
+3 | → L4 |
ML-KEM / kyber / dilithium / ML-DSA |
+3 | → L4+ |
fips-203 / fips-204 |
+3 | → L4+ |
CryptoRegistry / AlgorithmProvider |
+2 | → L3 |
registerAlgorithm / registry.get( |
+2 | → L3 |
algorithm.*config / CRYPTO_ALGO |
+1 | → L2 |
alg_fallback / algorithm_fallback |
+1 | → L2 |
-----BEGIN RSA PRIVATE KEY----- |
-3 | → L1 anchor |
RSA.generate(1024 / RSA.generate(512 |
-2 | → L1/L2 anchor |
Rule 1 — Test/vendor files are ignored entirely
Rule 2 — Hardcoded private key → score anchored at L1 max
Weak RSA (512/1024) → score capped at L2 max
Rule 3 — Hybrid PQC detected → minimum score bumped to L3
Rule 4 — ≥10 CRITICAL findings → max capped at L2
1–9 CRITICAL findings → max capped at L3
score ≤ 0 → L1 Hardcoded
score 1–2 → L2 Configurable
score 3–4 → L3 Hot-Swap
score 5–7 → L4 Hybrid
score ≥ 8 → L5 Fully Agile
The scanner detects 54+ cryptographic algorithms across 12+ languages using compiled regex patterns.
Python, Java, Go, TypeScript/JavaScript, Kotlin, Rust, C, C++, C#, Dart, Ruby, Swift, YAML, Shell scripts
| Algorithm | Type | Quantum Status | Risk | NIST Replacement |
|---|---|---|---|---|
| MD5 | Hash | BROKEN | CRITICAL | SHA-3-256 |
| SHA-1 | Hash | WEAK | HIGH | SHA-3-256 |
| DES / 3DES | Symmetric | BROKEN | CRITICAL | AES-256-GCM |
| RC4 | Symmetric | BROKEN | CRITICAL | AES-256-GCM |
| Blowfish | Symmetric | BROKEN | CRITICAL | AES-256-GCM |
| AES-128 | Symmetric | VULNERABLE | MEDIUM | AES-256-GCM |
| RSA-1024 | Asymmetric | BROKEN | CRITICAL | ML-KEM-768 (FIPS 203) |
| RSA-2048 | Asymmetric | VULNERABLE | CRITICAL | ML-KEM-768 / ML-DSA-65 |
| RSA-4096 | Asymmetric | VULNERABLE | HIGH | ML-KEM-1024 / ML-DSA-87 |
| ECDSA | Signature | VULNERABLE | HIGH | ML-DSA-65 (FIPS 204) |
| ECDH | KEM | VULNERABLE | HIGH | ML-KEM-768 (FIPS 203) |
| X25519 | KEM | VULNERABLE | MEDIUM | ML-KEM-768 hybrid (FIPS 203) |
| TLS 1.0 | Protocol | BROKEN | CRITICAL | TLS 1.3 + ML-KEM hybrid |
| TLS 1.1 | Protocol | WEAK | HIGH | TLS 1.3 + ML-KEM hybrid |
| TLS 1.2 | Protocol | MONITOR | MEDIUM | TLS 1.3 + ML-KEM hybrid |
| JWT-RS256 | Signature | VULNERABLE | HIGH | ML-DSA-65 (FIPS 204) |
| PBKDF2 | KDF | VULNERABLE | MEDIUM | Argon2id |
| Hardcoded Keys | KeyMgmt | BROKEN | CRITICAL | HSM / KMS |
| SSH-WEAK-KEX | Protocol | VULNERABLE | HIGH | OpenSSH with ML-KEM |
| ML-KEM-512/768/1024 | KEM | SAFE | LOW | — (already PQC) |
| ML-DSA-44/65/87 | Signature | SAFE | LOW | — (already PQC) |
| SLH-DSA | Signature | SAFE | LOW | — (already PQC) |
.git, node_modules, __pycache__, vendor, dist, buildsshd_config, ssh_config)(file, algorithm, line) tripletDetects actual cryptographic material embedded in repositories — not just API usage patterns.
| Finding Type | Detection | Key Data Extracted |
|---|---|---|
| SSH_PRIVATE_KEY | PEM headers | Algorithm, key size, curve |
| SSH_PUBLIC_KEY | .pub / authorized_keys |
Key type (rsa, ecdsa, ed25519) |
| TLS_CERT | PEM cert blocks → X.509 parse | Subject, issuer, expiry, serial, sig hash |
| PKCS#12 | Binary header / .p12 / .pfx |
Algorithm from leaf cert |
| GPG_KEY | BEGIN PGP armored text |
Conservative RSA-2048/ECDSA assumption |
| SSH_CONFIG | sshd_config / ssh_config files |
KexAlgorithms, Ciphers, MACs values |
| Key Type | Risk |
|---|---|
| RSA ≤ 1024 bits | BROKEN / CRITICAL |
| RSA 2048+ bits | VULNERABLE / CRITICAL |
| ECDSA P-256/384 | VULNERABLE / HIGH |
| Ed25519 | VULNERABLE / MEDIUM |
| DSA (any size) | BROKEN / CRITICAL |
| Expired TLS cert | Escalated to CRITICAL |
| SHA-1 signed cert | WEAK / HIGH |
| SSH config with RC4/DES | BROKEN / CRITICAL |
total_usages, code_usages, secret_usages, affected_repos, risk_score, priority| Priority | Quantum Status | Risk Score |
|---|---|---|
| 1 (highest) | BROKEN | 10.0 |
| 2 | VULNERABLE | 8.5 |
| 3 | WEAK | 6.0 |
| 4 | MONITOR | 4.0 |
| 5 (lowest) | SAFE | 1.0 |
CycloneDX 1.5 JSON — Industry standard BOM format:
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"components": [{
"type": "cryptographic-asset",
"name": "RSA-2048",
"cryptoProperties": {
"assetType": "Asymmetric",
"algorithmProperties": { "nistQuantumSecurityLevel": 0 }
},
"x-pqc-status": "VULNERABLE",
"x-nist-replacement": "ML-KEM-768 / ML-DSA-65",
"x-total-usages": 42,
"x-affected-repos": 5
}]
}
CSV — One row per finding location for spreadsheet analysis and ticketing imports.
Available for 20 algorithms across 6 languages with step-by-step migration guides.
Algorithms: RSA-2048, RSA-1024, RSA-4096, ECDSA, ECDH, X25519, SHA-1, MD5, DES, RC4, Blowfish, AES-128, TLS-1.0, TLS-1.1, TLS-1.2, JWT-RS256, HARDCODED-KEY, PBKDF2, SSH-WEAK-KEX, CRYPTSETUP-WEAK-KDF
Languages: Python, Java, Go, TypeScript/Node.js, C#, Shell
Each playbook includes severity rating, quantum risk explanation, NIST replacement, migration effort estimate, library recommendations, and before/after code diffs.
Interactive docs available at http://localhost:8000/docs
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /api/auth/login |
Login (returns httpOnly session cookie) | None |
| POST | /api/auth/logout |
Clear session | Session |
| GET | /api/auth/me |
Current user and role | Session |
| Method | Endpoint | Description | Role |
|---|---|---|---|
| GET | /api/repos/ |
List all repositories | Public |
| POST | /api/repos/ |
Add repository (Git URL) | Admin |
| GET | /api/repos/{repo_id} |
Get repo details + agility scores | Public |
| DELETE | /api/repos/{repo_id} |
Delete repository | Admin |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/scans/{repo_id}/trigger |
Trigger async scan |
| GET | /api/scans/{repo_id}/runs |
Scan history |
| GET | /api/scans/runs/{scan_run_id} |
Poll scan status (pending/running/complete/failed) |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/findings/ |
Query findings (filter: repo, risk, algorithm, status) |
| GET | /api/findings/summary |
Aggregated counts by risk and status |
| PATCH | /api/findings/{id}/status |
Resolve finding, set migrated_to |
| PATCH | /api/findings/{id}/archive |
Soft-delete finding |
| PATCH | /api/findings/{id}/restore |
Restore archived finding |
| POST | /api/findings/archive-resolved |
Bulk archive all resolved findings |
| GET | /api/findings/archived |
Audit trail |
| GET | /api/findings/migration-stats |
Per-algorithm migration progress % |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/cbom/ |
CBOM entries (priority ordered) |
| GET | /api/cbom/export/cyclonedx |
Export as CycloneDX 1.5 JSON |
| GET | /api/cbom/export/csv |
Export as CSV |
| GET | /api/secrets/ |
Secret findings (SSH, TLS, GPG, SSH config) |
| GET | /api/secrets/summary |
Secrets aggregation + expiry counts |
| GET | /api/reports/dashboard |
Dashboard summary stats |
| GET | /api/health |
Liveness probe |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/playbooks/ |
List all available playbooks |
| GET | /api/playbooks/{algorithm} |
Full playbook for an algorithm |
| GET | /api/playbooks/{algorithm}/{language} |
Language-specific code example |
Session-based auth via httpOnly cookie (pqc_session, 8-hour TTL).
| Role | Capabilities |
|---|---|
| admin | Full access — add/delete repos, trigger scans, manage all data |
| dev | Trigger scans, resolve findings, view everything |
| reader | Read-only — view findings, CBOM, playbooks, reports |
Default credentials (development only):
| Username | Password | Role |
|---|---|---|
admin |
pqcadmin |
Admin |
dev |
pqcdev |
Dev |
reader |
pqcreader |
Reader |
Note: Change these credentials before any production deployment.
| Table | Purpose |
|---|---|
repos |
Repository registry + agility scores (level, label, score, hybrid flag, signals) |
scan_runs |
Scan execution history (status, file counts, finding counts, errors) |
findings |
Code-level crypto detections with full migration lifecycle |
secret_findings |
SSH/TLS/GPG/config detections with certificate metadata |
cbom_entries |
Aggregated crypto asset inventory (total usages, affected repos, risk score) |
open
├──► manually_resolved (developer resolves with migrated_to evidence)
├──► auto_resolved (not detected in 2 consecutive scans)
└──► re_opened (regression — finding reappears after resolution)
any state ──► archived (soft-delete, kept for audit trail)
Located in backend/migrations/:
add_agility_and_migration_tracking.sql — Agility fields on Repo, migration tracking on Findingadd_resolution_fields.sql — Archive fields on Finding and SecretFindingdeduplicate_findings.sql — Removes duplicate (scan_run_id, algo, line) entriesStandalone scanner — no server required. Useful for local development and CI pipelines.
# Scan a local directory
python cli_scan.py /path/to/repo
# Scan a remote GitHub repo
python cli_scan.py https://github.com/org/repo --branch main
# Output formats
python cli_scan.py /path/to/repo --format table # Colored table (default)
python cli_scan.py /path/to/repo --format json
python cli_scan.py /path/to/repo --format csv
python cli_scan.py /path/to/repo --format cyclonedx
# Filter and CI gating
python cli_scan.py /path/to/repo --min-risk HIGH
python cli_scan.py /path/to/repo --fail-on CRITICAL # Exits with code 1 if threshold met
# Skip secrets scanning
python cli_scan.py /path/to/repo --no-secrets
Environment variables:
GITHUB_TOKEN=ghp_xxx # For private GitHub repos
GITLAB_TOKEN=glpat_xxx # For GitLab repos
BITBUCKET_TOKEN=user:pass # For Bitbucket repos
The GitHub Actions workflow (.github/workflows/pqc-scan.yml) runs on every push, PR, and weekly schedule.
What it does:
Setup — add these GitHub Secrets:
PQC_API_URL = https://your-pqc-platform.com
PQC_API_TOKEN = your-api-token
GITHUB_TOKEN = (auto-provided by GitHub Actions)
| Standard | Algorithm | Replaces |
|---|---|---|
| FIPS 203 | ML-KEM (Kyber) | RSA/ECDH key exchange |
| FIPS 204 | ML-DSA (Dilithium) | RSA-sign/ECDSA signatures |
| FIPS 205 | SLH-DSA (SPHINCS+) | Stateless hash-based signatures |
.env)DATABASE_URL=postgresql://pqc_user:pqc_pass@localhost:5432/pqc_platform
REDIS_URL=redis://localhost:6379/0
SECRET_KEY=change-me-in-production
# Git provider tokens (set as needed)
GITHUB_TOKEN=ghp_xxx
GITHUB_ENTERPRISE_TOKEN=
GITLAB_TOKEN=glpat_xxx
BITBUCKET_TOKEN=user:app_password
GITEA_TOKEN=
# Optional SSH key auth alternative
SSH_KEY_PATH=/run/secrets/id_rsa
# Scan worker concurrency
SCAN_WORKERS=4
| Service | Image | Port |
|---|---|---|
| PostgreSQL | postgres:16-alpine | 5432 |
| Redis | redis:7-alpine | 6379 |
| Backend (FastAPI) | python:3.12 | 8000 |
| Frontend (Vite) | node:latest | 5173 |