Install
$ agentstack add skill-kaivyy-perseus-crypto ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Perseus Crypto Specialist
Context & Authorization
IMPORTANT: This skill performs cryptographic security analysis on the user's own codebase. This is defensive security testing to find crypto weaknesses before they lead to data breaches.
Authorization: The user owns this codebase and has explicitly requested this specialized analysis.
Multi-Language Support
| Language | Libraries | |----------|-----------| | JavaScript/TypeScript | jsonwebtoken, jose, bcrypt, crypto, node-forge | | Go | golang.org/x/crypto, crypto/*, jwt-go, golang-jwt | | PHP | openssl, password_hash, sodium, firebase/php-jwt | | Python | PyJWT, cryptography, bcrypt, passlib, hashlib | | Rust | jsonwebtoken, ring, rust-crypto, argon2, bcrypt | | Java | jjwt, Bouncy Castle, Java Cryptography Architecture | | Ruby | jwt, bcrypt, rbnacl, openssl | | C# | System.IdentityModel.Tokens.Jwt, BCrypt.Net |
Overview
This specialist skill performs comprehensive cryptographic analysis including JWT security, hashing, encryption, and key management across all major languages.
When to Use: After /scan identifies JWT usage, password hashing, encryption, or secrets handling.
Goal: Ensure cryptographic implementations follow security best practices.
Engagement Mode Compatibility
| Mode | Specialist Behavior | |------|---------------------| | PRODUCTION_SAFE | Configuration and implementation analysis, minimal runtime checks | | STAGING_ACTIVE | Controlled token/crypto validation with throttling | | LAB_FULL | Extensive verification of crypto edge cases in lab | | LAB_RED_TEAM | Adversarial misuse simulation on test identities and synthetic keys |
Safety Gates (Required)
- Read
deliverables/engagement_profile.mdbefore any runtime validation. - Default to
PRODUCTION_SAFEif mode is unspecified. - Enforce kill-switch thresholds from engagement profile.
- Never expose real secrets, keys, or live credentials in outputs.
Cryptographic Issues Covered
| Category | Issues | Impact | |----------|--------|--------| | JWT | Algorithm confusion, weak secrets, missing validation | Auth bypass | | Hashing | MD5/SHA1 for passwords, missing salt, weak iterations | Credential theft | | Encryption | Weak ciphers, ECB mode, hardcoded keys | Data exposure | | Random | Predictable RNG, weak seeds | Token prediction | | Key Management | Hardcoded keys, insecure storage | Full compromise |
Execution Instructions
Step 0: Mode & Scope Alignment
- Load mode/scope/limits from
deliverables/engagement_profile.md. - Respect
deliverables/verification_scope.mdwhen present. - Keep production checks passive-first and evidence-based.
Phase 1: JWT Analysis (4 Parallel Agents)
- JWT Algorithm Analyst:
- "Find all JWT verification code. Check for algorithm validation."
Language-Specific Patterns: ```javascript // Node.js - VULNERABLE jwt.verify(token, secret); // Accepts any algorithm
// Node.js - SAFE jwt.verify(token, secret, { algorithms: ['HS256'] }); `` ``go // Go - VULNERABLE token, _ := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) { return secret, nil // No algorithm check! })
// Go - SAFE token, := jwt.Parse(tokenString, func(t jwt.Token) (interface{}, error) { if , ok := t.Method.(jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected method: %v", t.Header["alg"]) } return secret, nil }) `` ``python # Python - VULNERABLE jwt.decode(token, secret) # Accepts any algorithm
# Python - SAFE jwt.decode(token, secret, algorithms=['HS256']) `` ``php // PHP - VULNERABLE JWT::decode($token, $key); // Check library defaults
// PHP - SAFE JWT::decode($token, new Key($key, 'HS256')); `` `rust // Rust - SAFE (explicit by design) decode::(&token, &DecodingKey::from_secret(secret), &Validation::new(Algorithm::HS256)) ``
- JWT Secret Analyst:
- "Find JWT signing secrets across languages."
Patterns: ```javascript // VULNERABLE - Weak secret const secret = 'secret123'; const secret = 'password';
// VULNERABLE - Hardcoded jwt.sign(payload, 'my-super-secret-key');
// SAFE - From environment, strong const secret = process.env.JWT_SECRET; // Must be 32+ chars `` ``go // VULNERABLE var jwtSecret = []byte("weak-secret")
// SAFE var jwtSecret = []byte(os.Getenv("JWTSECRET")) `` ``python # VULNERABLE SECRETKEY = "secret"
# SAFE SECRETKEY = os.environ.get("JWTSECRET") ```
- JWT Claims Analyst:
- "Analyze JWT claim validation."
Required Validations: | Claim | Purpose | Check | |-------|---------|-------| | exp | Expiration | Token not expired | | iat | Issued At | Not issued in future | | nbf | Not Before | Token is active | | iss | Issuer | Trusted issuer | | aud | Audience | Intended recipient |
- JWT Key Management Analyst:
- "Check RS256/ES256 key handling."
Issues:
- Private key in repository
- Key without rotation
- Public key as HMAC secret (algorithm confusion)
Phase 2: Password Hashing Analysis (3 Parallel Agents)
- Hash Algorithm Analyst:
- "Find all password hashing across languages."
Language-Specific Patterns: ```javascript // Node.js - VULNERABLE crypto.createHash('md5').update(password).digest('hex'); crypto.createHash('sha1').update(password).digest('hex'); crypto.createHash('sha256').update(password).digest('hex'); // No salt!
// Node.js - SAFE await bcrypt.hash(password, 12); await argon2.hash(password); `` ``go // Go - VULNERABLE md5.Sum([]byte(password)) sha256.Sum256([]byte(password))
// Go - SAFE bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) argon2.IDKey([]byte(password), salt, 1, 64*1024, 4, 32) `` ``python # Python - VULNERABLE hashlib.md5(password.encode()).hexdigest() hashlib.sha256(password.encode()).hexdigest()
# Python - SAFE bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12)) from passlib.hash import argon2 argon2.hash(password) `` ``php // PHP - VULNERABLE md5($password); sha1($password); hash('sha256', $password);
// PHP - SAFE passwordhash($password, PASSWORDARGON2ID); passwordhash($password, PASSWORDBCRYPT, ['cost' => 12]); `` `rust // Rust - SAFE bcrypt::hash(password, bcrypt::DEFAULT_COST)?; argon2::hash_encoded(password.as_bytes(), &salt, &config)?; ` ``java // Java - VULNERABLE MessageDigest.getInstance("MD5").digest(password.getBytes());
// Java - SAFE BCrypt.hashpw(password, BCrypt.gensalt(12)); ```
- Hash Comparison Analyst:
- "Check for timing-safe comparison."
Patterns: ```javascript // VULNERABLE - Timing attack if (storedHash === computedHash) { ... }
// SAFE crypto.timingSafeEqual(Buffer.from(storedHash), Buffer.from(computedHash)) `` ``go // VULNERABLE if storedHash == computedHash { ... }
// SAFE subtle.ConstantTimeCompare([]byte(storedHash), []byte(computedHash)) `` ``python # VULNERABLE if storedhash == computedhash: ...
# SAFE hmac.comparedigest(storedhash, computed_hash) ```
- Password Policy Analyst:
- "Check password strength enforcement."
Phase 3: Encryption Analysis (4 Parallel Agents)
- Cipher Selection Analyst:
- "Find all encryption operations."
Vulnerable Ciphers: | Cipher | Status | Use Instead | |--------|--------|-------------| | DES | Broken | AES-256-GCM | | 3DES | Deprecated | AES-256-GCM | | RC4 | Broken | AES-256-GCM | | Blowfish | Weak | AES-256-GCM | | AES-ECB | Insecure | AES-256-GCM | | AES-CBC | OK (with HMAC) | AES-256-GCM preferred |
Language Patterns: ```javascript // Node.js - VULNERABLE crypto.createCipher('des', key); crypto.createCipheriv('aes-128-ecb', key, '');
// Node.js - SAFE crypto.createCipheriv('aes-256-gcm', key, iv); `` ``go // Go - VULNERABLE des.NewCipher(key) cipher.NewCBCEncrypter(block, iv) // Without HMAC
// Go - SAFE cipher.NewGCM(block) `` ``python # Python - VULNERABLE from Crypto.Cipher import DES cipher = AES.new(key, AES.MODE_ECB)
# Python - SAFE cipher = AES.new(key, AES.MODE_GCM, nonce=nonce) ```
- IV/Nonce Analyst:
- "Check IV/nonce generation."
Issues: ```javascript // VULNERABLE - Static IV const iv = Buffer.from('0000000000000000', 'hex');
// VULNERABLE - Predictable const iv = Buffer.from(Date.now().toString());
// SAFE - Random const iv = crypto.randomBytes(16); ```
- Key Derivation Analyst:
- "Check key derivation from passwords."
Patterns: ```javascript // VULNERABLE - Direct use const key = Buffer.from(password);
// SAFE - PBKDF2 crypto.pbkdf2Sync(password, salt, 100000, 32, 'sha256');
// SAFE - scrypt crypto.scryptSync(password, salt, 32); ```
- Key Management Analyst:
- "Find encryption key storage issues."
Phase 4: Random Number Analysis (2 Parallel Agents)
- PRNG Analyst:
- "Find insecure random number generation."
Language-Specific: ```javascript // VULNERABLE Math.random()
// SAFE crypto.randomBytes(32) crypto.randomUUID() `` ``go // VULNERABLE math/rand.Int()
// SAFE crypto/rand.Read(buf) `` ``python # VULNERABLE random.random() random.randint()
# SAFE secrets.tokenbytes(32) secrets.tokenhex(32) secrets.tokenurlsafe(32) `` ``php // VULNERABLE rand() mtrand()
// SAFE randombytes(32) randomint(0, PHPINTMAX) `` `rust // Use rand crate with OsRng use rand::rngs::OsRng; let random: u64 = OsRng.gen(); ` ``java // VULNERABLE new Random().nextInt()
// SAFE new SecureRandom().nextInt() ```
- Token Generation Analyst:
- "Check security token generation."
Phase 5: Secrets in Code (3 Parallel Agents)
- Hardcoded Secrets Scanner:
- "Deep scan for hardcoded secrets."
Patterns: ``` # AWS AKIA[0-9A-Z]{16}
# GitHub ghp[a-zA-Z0-9]{36} githubpat[a-zA-Z0-9]{22}[a-zA-Z0-9]{59}
# Stripe sklive[a-zA-Z0-9]{24} rklive[a-zA-Z0-9]{24}
# Private Keys -----BEGIN (RSA|EC|OPENSSH|PGP) PRIVATE KEY-----
# Generic (password|secret|key|token|api_key)\s[:=]\s['\"][^'\"]+['\"] ```
- Secret Exposure Analyst:
- "Check where secrets might leak."
Locations:
- Log files
- Error messages
- API responses
- Client-side code
- Git history
- Environment Variable Analyst:
- "Check .env file security."
Issues:
- .env in repository
- .env.example with real secrets
- Missing .env in .gitignore
Output Requirements
Create deliverables/crypto_security_analysis.md:
# Cryptographic Security Analysis
## Summary
| Category | Issues | Critical | High | Medium |
|----------|--------|----------|------|--------|
| JWT | X | Y | Z | W |
| Hashing | X | Y | Z | W |
| Encryption | X | Y | Z | W |
| Random | X | Y | Z | W |
| Secrets | X | Y | Z | W |
## Language/Framework Detected
- Primary: [e.g., Node.js, Go, Python]
- Crypto Libraries: [e.g., crypto, bcrypt, jose]
## JWT Security Status
| Check | Status | Details |
|-------|--------|---------|
| Algorithm Validation | FAIL | Accepts 'none' algorithm |
| Secret Strength | FAIL | 8 character secret |
| Expiration | PASS | 1 hour expiry enforced |
| Issuer Validation | WARN | Not validated |
## Critical Findings
### [CRYPTO-001] JWT Algorithm Confusion
**Severity:** Critical
**Language:** Node.js
**Location:** `middleware/auth.js:23`
**Vulnerable Code:**
```javascript
const decoded = jwt.verify(token, publicKey);
Attack:
- Take valid RS256 token
- Change header to HS256
- Sign with public key as secret
- Server verifies with public key as HMAC secret
Remediation:
const decoded = jwt.verify(token, publicKey, {
algorithms: ['RS256']
});
[CRYPTO-002] MD5 Password Hashing
Severity: Critical Language: Python Location: models/user.py:45
Vulnerable Code:
hashed = hashlib.md5(password.encode()).hexdigest()
Remediation:
import bcrypt
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
Password Hashing Status
| Language | Algorithm | Cost/Rounds | Status | |----------|-----------|-------------|--------| | Node.js | bcrypt | 10 | WARN (use 12+) | | Python | MD5 | N/A | CRITICAL | | Go | bcrypt | 14 | OK |
Encryption Status
| Usage | Cipher | Mode | Status | |-------|--------|------|--------| | File encryption | AES-256 | ECB | CRITICAL | | API encryption | AES-128 | GCM | WARN (use 256) |
Random Number Generation
| Usage | Method | Status | |-------|--------|--------| | Session tokens | Math.random() | CRITICAL | | Password reset | crypto.randomBytes() | OK |
Hardcoded Secrets Found
| Type | Location | Severity | |------|----------|----------| | AWS Access Key | config/aws.js:3 | Critical | | JWT Secret | auth/jwt.js:5 | Critical | | Database Password | .env.example:8 | High |
Recommendations
Immediate Actions
- Implement algorithm validation for JWT
- Migrate password hashing to Argon2id or bcrypt (cost 12+)
- Move all secrets to environment variables
- Replace Math.random() with crypto.randomBytes()
Hashing Migration Guide
// Before
const hash = md5(password);
// After
const hash = await bcrypt.hash(password, 12);
// Or with Argon2
const hash = await argon2.hash(password, {
type: argon2.argon2id,
memoryCost: 65536,
timeCost: 3,
parallelism: 4
});
**Next Step:** JWT vulnerabilities can be verified with crafted tokens.
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [kaivyy](https://github.com/kaivyy)
- **Source:** [kaivyy/perseus](https://github.com/kaivyy/perseus)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.