| name | pentest-lyan-web-security-testing |
| description | Autonomous web penetration testing skill with threat modeling, JavaScript analysis, and multi-role verification |
| triggers | ["run pentest-lyan security test","start web penetration test with lyan","analyze web application security using pentest-lyan","perform black box testing with lyan","test web app for vulnerabilities using pentest-lyan","conduct authorized security assessment with lyan","scan web application with pentest-lyan","resume pentest-lyan security test"] |
Pentest-Lyan Web Security Testing
Skill by ara.so — Security Skills collection.
Pentest-Lyan is an autonomous web penetration testing framework that performs self-directed threat modeling across 12 dimensions, complete JavaScript analysis, and cross-role verification. It validates business impact (not just HTTP 200 responses), maintains state across sessions, and generates both technical Markdown and delivery-ready Word reports.
What It Does
- Autonomous Threat Modeling: Identifies threats based on 12 thinking dimensions rather than fixed vulnerability checklists
- Deep JavaScript Analysis: Reads and understands business logic, signing mechanisms, and frontend parameters before testing APIs
- Business Impact Validation: Verifies database-layer changes, data access, and state transitions actually occur
- Multi-Role Testing: Builds permission matrices and performs cross-role privilege escalation checks
- Stateful Testing: Supports resume (
--resume), multi-project isolation, and session pooling
- Dual Report Formats: Markdown for technical details, Word for delivery; separates critical findings from configuration issues
Installation
Prerequisites
- Python 3.8+ with the project dependencies
- Playwright MCP Server (recommended for browser automation and session pooling)
Setup Steps
git clone https://github.com/HeaSec/Pentest-Lyan.git
cd Pentest-Lyan
pip install -r requirements.txt
cp -r . ~/.config/claude-code/skills/pentest-lyan/
Playwright MCP Setup (Optional but Recommended)
npm install -g @playwright/mcp-server
Project Structure
pentest-lyan/
├── SKILL.md # Main orchestrator (3 phases, 9 exit conditions)
├── gates.md # HARD GATE definitions (G1-G7)
├── schema.md # Data schema index
├── references/ # Process documentation (read by agent as needed)
│ ├── discovery-guide.md
│ ├── attack-guide.md
│ ├── threat-modeling.md
│ ├── validation-guide.md
│ ├── cross-role-testing.md
│ ├── audit-guide.md
│ ├── report-template.md
│ ├── docx-template.md
│ └── post-delivery.md
├── schema/ # Field specification schemas (*.schema.json)
├── scripts/ # Utility scripts
│ └── render_docx.py
└── templates/ # Word document templates
Basic Usage
Starting a New Security Test
/pentest-lyan https://target.example.com
/pentest-lyan https://target.example.com
账号:
- admin/Admin@123 (role_level: high_privilege)
- user1/User@123 (role_level: standard)
- user2/User@123 (role_level: standard)
Important: No need to declare authorization scope/timeframe — issuing the command implies authorization.
Multi-Project Management
/pentest-lyan --list
/pentest-lyan --resume <project-id>
/pentest-lyan https://target.example.com --project custom-id
Default project-id is extracted from the hostname's first segment.
Testing Phases
Pentest-Lyan executes in three sequential phases:
Phase 1: Discovery
- Deep JavaScript analysis (business logic, signing, parameters)
- Complete API endpoint discovery
- Session pool creation
- Permission matrix generation
Phase 2: Attack
- Feature-level testing pipeline
- Dynamic sub-module discovery
- Cross-role privilege verification
- Business impact validation
Phase 3: Audit
- Schema validation
- Completeness auditing
- System-level checks
- Report generation (Markdown + Word)
Account Requirements (G1 Gate)
The G1 gate checks account availability but does not block testing:
- ≥2 accounts: Normal flow, full cross-role testing
- 0-1 accounts: Degraded mode, limited privilege testing; if registration page found, report prompts user to register additional accounts
role_level must be explicitly declared by the user (not inferred from username):
high_privilege: Admin, superuser, manager
standard: Regular user
limited: Guest, read-only
Working with State Files
Pentest-Lyan maintains state in pentest-data/<project-id>/:
pentest-data/
└── <project-id>/
├── state.json # Main state (phase, coverage, findings)
├── index.json # Project metadata
├── pages/ # Discovered pages/APIs
├── sessions/ # Session data (contains credentials)
├── modules/ # Feature module state
└── coverage/ # Coverage tracking
State File Example
{
"project_id": "example-com",
"phase": "discovery",
"target": "https://example.com",
"accounts": [
{
"username": "admin",
"password": "REDACTED",
"role_level": "high_privilege"
}
],
"discovered_endpoints": [],
"findings": [],
"coverage": {}
}
Security Warning: pentest-data/ contains plaintext credentials. Add to .gitignore:
echo "pentest-data/" >> .gitignore
echo "pentest-report/" >> .gitignore
Threat Modeling Dimensions
Pentest-Lyan uses 12 autonomous threat dimensions:
- Authentication: Session, token, multi-factor bypasses
- Authorization: Horizontal/vertical privilege escalation, IDOR
- Input Validation: Injection, XSS, path traversal
- Business Logic: Workflow bypasses, race conditions
- Data Exposure: Sensitive data leaks, information disclosure
- Session Management: Fixation, hijacking, timeout issues
- Cryptography: Weak algorithms, improper key management
- Configuration: Default credentials, unnecessary services
- API Security: Rate limiting, parameter tampering
- File Handling: Upload bypasses, arbitrary file operations
- Error Handling: Stack traces, verbose errors
- Third-Party: Dependency vulnerabilities, external service abuse
Code Examples
Extending Validation Logic (Python)
import requests
import json
def validate_discount_manipulation(session, endpoint, payload):
"""
Verify that discount manipulation actually affects the database.
Returns True if vulnerability is confirmed.
"""
resp = session.post(
endpoint,
json=payload,
headers={"Content-Type": "application/json"}
)
if resp.status_code != 200:
return False
order_id = resp.json().get("order_id")
verify_resp = session.get(f"/api/orders/{order_id}")
if verify_resp.status_code == 200:
order_data = verify_resp.json()
expected_price = payload.get("discounted_price")
actual_price = order_data.get("total_price")
return actual_price == expected_price
return False
Reading JavaScript for Signing Logic
import re
import requests
def extract_signing_function(js_url):
"""
Download and parse JavaScript to find signing functions.
"""
resp = requests.get(js_url)
js_content = resp.text
signing_patterns = [
r'function\s+sign\([^)]*\)\s*{([^}]+)}',
r'const\s+sign\s*=\s*\([^)]*\)\s*=>\s*{([^}]+)}',
r'\.sign\s*=\s*function\([^)]*\)\s*{([^}]+)}'
]
for pattern in signing_patterns:
matches = re.findall(pattern, js_content)
if matches:
return {
"signing_function": matches[0],
"algorithm": detect_algorithm(matches[0])
}
return None
def detect_algorithm(func_body):
"""Detect crypto algorithm from function body."""
if 'md5' in func_body.lower():
return 'md5'
elif 'sha256' in func_body.lower():
return 'sha256'
elif 'hmac' in func_body.lower():
return 'hmac'
return 'unknown'
Permission Matrix Generation
def build_permission_matrix(endpoints, sessions):
"""
Test each endpoint with each role to build permission matrix.
Returns matrix of {endpoint: {role: accessible}}.
"""
matrix = {}
for endpoint in endpoints:
matrix[endpoint["path"]] = {}
for session_info in sessions:
role = session_info["role_level"]
session = session_info["session"]
resp = session.get(
endpoint["url"],
allow_redirects=False
)
accessible = resp.status_code in [200, 201, 204]
matrix[endpoint["path"]][role] = {
"accessible": accessible,
"status_code": resp.status_code
}
return matrix
Cross-Role Privilege Testing
def test_privilege_escalation(matrix, sessions):
"""
Use permission matrix to find privilege escalation vulnerabilities.
"""
findings = []
for endpoint, roles in matrix.items():
if (roles.get("high_privilege", {}).get("accessible") and
not roles.get("standard", {}).get("accessible")):
standard_session = get_session_by_role(sessions, "standard")
high_priv_user_id = get_user_id_by_role(sessions, "high_privilege")
test_url = endpoint.replace("{user_id}", str(high_priv_user_id))
resp = standard_session.get(test_url)
if resp.status_code in [200, 201]:
findings.append({
"type": "privilege_escalation",
"endpoint": endpoint,
"description": f"Standard user can access high_privilege endpoint",
"evidence": {
"url": test_url,
"status": resp.status_code,
"attacker_role": "standard",
"victim_role": "high_privilege"
}
})
return findings
Report Generation
Markdown Report
Generated automatically at end of audit phase:
pentest-report/<project-id>_<timestamp>.md
Structure:
- Executive Summary
- Findings (Critical → High → Medium → Low)
- Coverage Analysis (answers: input surface, behavior surface, depth)
- Appendix (configuration issues, not_vulnerable with unruled_out)
Word Report
import sys
from docx import Document
from docx.shared import Inches, Pt, RGBColor
def render_docx(markdown_path, output_path, template_path=None):
"""
Convert Markdown report to Word document.
Args:
markdown_path: Path to .md report
output_path: Path for output .docx
template_path: Optional custom template
"""
doc = Document(template_path) if template_path else Document()
with open(markdown_path, 'r', encoding='utf-8') as f:
content = f.read()
sections = parse_markdown(content)
for section in sections:
if section['type'] == 'heading1':
doc.add_heading(section['text'], level=1)
elif section['type'] == 'finding':
add_finding_block(doc, section['data'])
elif section['type'] == 'code':
add_code_block(doc, section['text'])
doc.save(output_path)
Configuration
Environment Variables
export PENTEST_DATA_DIR="/secure/location/pentest-data"
export PENTEST_REPORT_DIR="/reports"
export PLAYWRIGHT_MCP_URL="http://localhost:3000"
export PENTEST_RATE_LIMIT="5"
export PENTEST_TIMEOUT="30"
Custom Gate Configuration
Edit gates.md to adjust gate thresholds:
# G1: Account Gate
Threshold: ≥2 accounts (degraded mode if fewer)
# G2: JavaScript Coverage Gate
Threshold: ≥80% of main bundles analyzed
# G3: Endpoint Discovery Gate
Threshold: ≥5 unique endpoints or complete sitemap
Common Patterns
Pattern 1: Testing with Multiple Accounts
/pentest-lyan https://api.example.com
Accounts:
- admin@example.com/SecurePass123! (role_level: high_privilege)
- user@example.com/UserPass456! (role_level: standard)
- guest@example.com/GuestPass789! (role_level: limited)
Pattern 2: Resuming After Interruption
/pentest-lyan --list
/pentest-lyan --resume example-com
Pattern 3: Custom Threat Module
Create a custom module in references/custom-threats/:
# custom-payment-logic.md
## Threat: Payment Amount Manipulation
**Coverage Questions:**
- Input surface: price, quantity, discount_code, currency
- Behavior surface: calculate_total, apply_discount, process_payment
- Depth: Database transaction verification, audit log check
**Test Steps:**
1. Capture legitimate payment request
2. Modify `amount` parameter to $0.01
3. Replay request
4. Verify database: SELECT amount FROM orders WHERE order_id = ?
5. Check audit logs for amount mismatch
**Validation:**
- Status 200 alone is NOT sufficient
- Must confirm database shows manipulated amount
- Must confirm payment gateway charged manipulated amount
Troubleshooting
Issue: "No endpoints discovered"
Cause: JavaScript not fully analyzed or SPA routing not detected.
Solution:
ls pentest-data/<project-id>/pages/
curl http://localhost:3000/health
rm pentest-data/<project-id>/state.json
/pentest-lyan --resume <project-id>
Issue: "G1 gate: insufficient accounts"
Cause: Fewer than 2 accounts provided.
Solution: This is a soft gate — testing continues in degraded mode.
- Provide additional accounts and re-run
- Or continue and register accounts manually if prompted in report
Issue: "Validation failed: status 200 but no business impact"
Cause: Response is 200 but database/state unchanged.
Solution: This is correct behavior (not a bug).
- Pentest-Lyan requires verification of actual state change
- Check validation-guide.md for proper validation steps
- Customize validators if needed (see Code Examples above)
Issue: Session expires during testing
Cause: Long-running tests exceed session timeout.
Solution:
def refresh_session(session_info):
"""Re-authenticate if session expired."""
login_resp = requests.post(
f"{session_info['base_url']}/api/login",
json={
"username": session_info["username"],
"password": session_info["password"]
}
)
return login_resp.cookies.get("session_token")
Issue: Report missing findings
Cause: Findings not properly recorded in state.json.
Solution:
python -c "
import json
with open('pentest-data/<project-id>/state.json') as f:
state = json.load(f)
print(f'Findings: {len(state.get(\"findings\", []))}')
"
cat schema/findings.schema.json
Issue: Word report rendering fails
Cause: Missing python-docx or template corruption.
Solution:
pip install python-docx
python scripts/render_docx.py \
pentest-report/<project-id>.md \
output.docx
python scripts/render_docx.py \
pentest-report/<project-id>.md \
output.docx \
--template templates/custom-template.docx
Security Best Practices
-
Credential Management:
- Never commit
pentest-data/ to version control
- Use environment variables for sensitive config
- Follow post-delivery cleanup (see
references/post-delivery.md)
-
Authorization:
- Only test systems you have explicit permission to test
- Issuing
/pentest-lyan <target> implies authorization
-
Data Handling:
- Encrypt
pentest-data/ and pentest-report/ after testing
- Clear sessions after delivery:
rm -rf pentest-data/<project-id>/sessions/
-
Rate Limiting:
- Set
PENTEST_RATE_LIMIT to avoid overwhelming targets
- Default: 5 req/s (configurable)
Advanced Usage
Custom Validation Pipeline
from typing import Dict, Any, List
def extended_validation_pipeline(finding: Dict[str, Any]) -> bool:
"""
Extended validation requiring multiple confirmation steps.
"""
validators = [
validate_http_response,
validate_database_state,
validate_audit_log,
validate_side_effects
]
results = []
for validator in validators:
result = validator(finding)
results.append(result)
log_validation_step(
finding_id=finding['id'],
validator=validator.__name__,
result=result
)
return all(results)
def validate_database_state(finding: Dict[str, Any]) -> bool:
"""Verify database reflects the exploit."""
pass
def validate_audit_log(finding: Dict[str, Any]) -> bool:
"""Check if suspicious activity was logged."""
pass
def validate_side_effects(finding: Dict[str, Any]) -> bool:
"""Verify no unintended side effects occurred."""
pass
Integration with CI/CD
name: Automated Security Testing
on:
schedule:
- cron: '0 2 * * 0'
jobs:
pentest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install Pentest-Lyan
run: |
git clone https://github.com/HeaSec/Pentest-Lyan.git
cd Pentest-Lyan
pip install -r requirements.txt
- name: Run Security Test
env:
TEST_ADMIN_USER: ${{ secrets.TEST_ADMIN_USER }}
TEST_ADMIN_PASS: ${{ secrets.TEST_ADMIN_PASS }}
TEST_USER_USER: ${{ secrets.TEST_USER_USER }}
TEST_USER_PASS: ${{ secrets.TEST_USER_PASS }}
run: |
/pentest-lyan https://staging.example.com \
--account "$TEST_ADMIN_USER/$TEST_ADMIN_PASS (role_level: high_privilege)" \
--account "$TEST_USER_USER/$TEST_USER_PASS (role_level: standard)"
- name: Upload Report
uses: actions/upload-artifact@v3
with:
name: security-report
path: pentest-report/*.md
Additional Resources
License: MIT
Repository: https://github.com/HeaSec/Pentest-Lyan
Issues: https://github.com/HeaSec/Pentest-Lyan/issues