Imported from reason-machines/security-skills (
skills/pentesting-checklist-platform/SKILL.md). Install upstream withnpx skills add reason-machines/security-skills --skill pentesting-checklist-platform. Copyright stays with the author.
PentestingChecklist Platform Skill
Skill by ara.so — Security Skills collection.
Overview
PentestingChecklist is a comprehensive, interactive security assessment tool that provides structured checklists across 23 platforms including Web, API, Mobile, Cloud, Active Directory, Kubernetes, LLM, and more. Built with TypeScript and React, it runs entirely client-side with no backend, storing all data in localStorage for privacy.
Key Features:
- 1,000+ security checks across 23 platforms
- Hierarchical organization (Platform → Category → Technology → Check)
- Progress tracking and status management (Open/Closed/N/A)
- Per-check notes and evidence recording
- Severity filtering (Critical to Info)
- Global search across all content
- Export to Markdown, CSV, Excel, JSON
- Import/export for assessment continuity
Live Tool: https://checklist.m14r41.in/
Installation & Setup
Local Development
# Clone the repository
git clone https://github.com/m14r41/PentestingChecklist.git
cd PentestingChecklist
# Install dependencies
npm install
# Start development server
npm run dev
# Build for production
npm run build
# Preview production build
npm run preview
Using the Hosted Version
No installation required — navigate to https://checklist.m14r41.in/ in your browser. All data persists in browser localStorage.
Architecture & Code Structure
Key TypeScript Interfaces
// Core data structures
interface Check {
id: string;
title: string;
description: string;
severity: 'critical' | 'high' | 'medium' | 'low' | 'info';
tags: string[];
tools?: string[];
references?: string[];
status?: 'open' | 'closed' | 'na';
notes?: string;
}
interface Technology {
id: string;
name: string;
checks: Check[];
}
interface Category {
id: string;
name: string;
technologies: Technology[];
}
interface Platform {
id: string;
name: string;
description: string;
categories: Category[];
}
// Progress tracking
interface AssessmentProgress {
platformId: string;
totalChecks: number;
completedChecks: number;
percentage: number;
statusBreakdown: {
open: number;
closed: number;
na: number;
unchecked: number;
};
}
LocalStorage Schema
// Storage keys used by the application
const STORAGE_KEYS = {
CHECKLIST_STATE: 'pentesting-checklist-state',
USER_NOTES: 'pentesting-checklist-notes',
PROGRESS: 'pentesting-checklist-progress',
COLLAPSED_SECTIONS: 'pentesting-checklist-collapsed'
};
// Example: Reading stored progress
function loadProgress(): Map<string, AssessmentProgress> {
const stored = localStorage.getItem(STORAGE_KEYS.PROGRESS);
return stored ? new Map(JSON.parse(stored)) : new Map();
}
// Example: Saving check status
function updateCheckStatus(
platformId: string,
categoryId: string,
techId: string,
checkId: string,
status: 'open' | 'closed' | 'na'
): void {
const key = `${platformId}.${categoryId}.${techId}.${checkId}`;
const state = JSON.parse(localStorage.getItem(STORAGE_KEYS.CHECKLIST_STATE) || '{}');
state[key] = { ...state[key], status };
localStorage.setItem(STORAGE_KEYS.CHECKLIST_STATE, JSON.stringify(state));
}
Using the Checklist
Navigation Patterns
// Platform URLs follow the pattern
const platformUrls = {
allPlatforms: '/checklist',
web: '/web',
api: '/api',
mobile: '/mobile',
cloud: '/cloud',
activeDirectory: '/active-directory',
kubernetes: '/kubernetes',
llm: '/llm-security',
mcp: '/mcp-security'
// ... 23 platforms total
};
// Accessing specific platform data
function navigateToPlatform(platformId: string) {
window.location.href = `/${platformId}`;
}
Search Functionality
The global search (⌘K / Ctrl+K) searches across:
- Platform names
- Category names
- Technology names
- Check titles and descriptions
- Tags
- Tools
- References
// Search implementation pattern
interface SearchResult {
type: 'platform' | 'category' | 'technology' | 'check';
platformId: string;
categoryId?: string;
technologyId?: string;
checkId?: string;
title: string;
description?: string;
path: string;
}
function performSearch(query: string, platforms: Platform[]): SearchResult[] {
const results: SearchResult[] = [];
const lowerQuery = query.toLowerCase();
platforms.forEach(platform => {
platform.categories.forEach(category => {
category.technologies.forEach(tech => {
tech.checks.forEach(check => {
if (
check.title.toLowerCase().includes(lowerQuery) ||
check.description.toLowerCase().includes(lowerQuery) ||
check.tags.some(tag => tag.toLowerCase().includes(lowerQuery))
) {
results.push({
type: 'check',
platformId: platform.id,
categoryId: category.id,
technologyId: tech.id,
checkId: check.id,
title: check.title,
description: check.description,
path: `/${platform.id}#${check.id}`
});
}
});
});
});
});
return results;
}
Working with Assessment Data
Adding Notes to Checks
// Note storage structure
interface CheckNote {
checkId: string;
content: string;
timestamp: number;
findings?: string[];
screenshots?: string[];
}
function saveCheckNote(
platformId: string,
categoryId: string,
techId: string,
checkId: string,
note: string
): void {
const key = `${platformId}.${categoryId}.${techId}.${checkId}`;
const notes = JSON.parse(localStorage.getItem(STORAGE_KEYS.USER_NOTES) || '{}');
notes[key] = {
checkId,
content: note,
timestamp: Date.now(),
findings: []
};
localStorage.setItem(STORAGE_KEYS.USER_NOTES, JSON.stringify(notes));
}
function getCheckNote(checkKey: string): CheckNote | null {
const notes = JSON.parse(localStorage.getItem(STORAGE_KEYS.USER_NOTES) || '{}');
return notes[checkKey] || null;
}
Progress Calculation
function calculatePlatformProgress(platform: Platform): AssessmentProgress {
let totalChecks = 0;
let completedChecks = 0;
const statusBreakdown = { open: 0, closed: 0, na: 0, unchecked: 0 };
const state = JSON.parse(localStorage.getItem(STORAGE_KEYS.CHECKLIST_STATE) || '{}');
platform.categories.forEach(category => {
category.technologies.forEach(tech => {
tech.checks.forEach(check => {
totalChecks++;
const key = `${platform.id}.${category.id}.${tech.id}.${check.id}`;
const checkState = state[key];
if (checkState?.status) {
completedChecks++;
statusBreakdown[checkState.status]++;
} else {
statusBreakdown.unchecked++;
}
});
});
});
return {
platformId: platform.id,
totalChecks,
completedChecks,
percentage: Math.round((completedChecks / totalChecks) * 100),
statusBreakdown
};
}
Export Functionality
Export to JSON
interface ExportData {
version: string;
exportDate: string;
platforms: {
[platformId: string]: {
checks: {
[checkId: string]: {
status: 'open' | 'closed' | 'na';
notes: string;
timestamp: number;
};
};
};
};
}
function exportToJSON(): string {
const state = JSON.parse(localStorage.getItem(STORAGE_KEYS.CHECKLIST_STATE) || '{}');
const notes = JSON.parse(localStorage.getItem(STORAGE_KEYS.USER_NOTES) || '{}');
const exportData: ExportData = {
version: '1.0',
exportDate: new Date().toISOString(),
platforms: {}
};
// Merge state and notes
Object.keys(state).forEach(key => {
const [platformId, categoryId, techId, checkId] = key.split('.');
if (!exportData.platforms[platformId]) {
exportData.platforms[platformId] = { checks: {} };
}
exportData.platforms[platformId].checks[checkId] = {
status: state[key].status,
notes: notes[key]?.content || '',
timestamp: notes[key]?.timestamp || Date.now()
};
});
return JSON.stringify(exportData, null, 2);
}
Export to Markdown
function exportToMarkdown(platform: Platform, includeOnlyOpen = false): string {
let markdown = `# ${platform.name} Security Assessment\n\n`;
markdown += `**Export Date:** ${new Date().toLocaleDateString()}\n\n`;
const state = JSON.parse(localStorage.getItem(STORAGE_KEYS.CHECKLIST_STATE) || '{}');
const notes = JSON.parse(localStorage.getItem(STORAGE_KEYS.USER_NOTES) || '{}');
platform.categories.forEach(category => {
markdown += `## ${category.name}\n\n`;
category.technologies.forEach(tech => {
markdown += `### ${tech.name}\n\n`;
tech.checks.forEach(check => {
const key = `${platform.id}.${category.id}.${tech.id}.${check.id}`;
const checkState = state[key];
if (includeOnlyOpen && checkState?.status !== 'open') {
return;
}
markdown += `#### ${check.title}\n\n`;
markdown += `**Severity:** ${check.severity.toUpperCase()}\n\n`;
markdown += `**Status:** ${checkState?.status || 'Unchecked'}\n\n`;
markdown += `**Description:** ${check.description}\n\n`;
if (notes[key]?.content) {
markdown += `**Notes:**\n\n${notes[key].content}\n\n`;
}
if (check.tools && check.tools.length > 0) {
markdown += `**Tools:** ${check.tools.join(', ')}\n\n`;
}
markdown += '---\n\n';
});
});
});
return markdown;
}
Export to CSV
function exportToCSV(platform: Platform): string {
const headers = [
'Platform',
'Category',
'Technology',
'Check',
'Severity',
'Status',
'Notes',
'Tags',
'Tools'
];
let csv = headers.join(',') + '\n';
const state = JSON.parse(localStorage.getItem(STORAGE_KEYS.CHECKLIST_STATE) || '{}');
const notes = JSON.parse(localStorage.getItem(STORAGE_KEYS.USER_NOTES) || '{}');
platform.categories.forEach(category => {
category.technologies.forEach(tech => {
tech.checks.forEach(check => {
const key = `${platform.id}.${category.id}.${tech.id}.${check.id}`;
const checkState = state[key];
const checkNotes = notes[key]?.content || '';
const row = [
platform.name,
category.name,
tech.name,
`"${check.title}"`,
check.severity,
checkState?.status || 'unchecked',
`"${checkNotes.replace(/"/g, '""')}"`,
`"${check.tags.join('; ')}"`,
`"${(check.tools || []).join('; ')}"`
];
csv += row.join(',') + '\n';
});
});
});
return csv;
}
Import Functionality
function importFromJSON(jsonString: string): void {
try {
const importData: ExportData = JSON.parse(jsonString);
// Validate version compatibility
if (importData.version !== '1.0') {
throw new Error('Unsupported export version');
}
const currentState = JSON.parse(localStorage.getItem(STORAGE_KEYS.CHECKLIST_STATE) || '{}');
const currentNotes = JSON.parse(localStorage.getItem(STORAGE_KEYS.USER_NOTES) || '{}');
// Merge imported data
Object.entries(importData.platforms).forEach(([platformId, platformData]) => {
Object.entries(platformData.checks).forEach(([checkId, checkData]) => {
const key = checkId; // Full key already in format
currentState[key] = {
status: checkData.status,
timestamp: checkData.timestamp
};
if (checkData.notes) {
currentNotes[key] = {
checkId,
content: checkData.notes,
timestamp: checkData.timestamp
};
}
});
});
localStorage.setItem(STORAGE_KEYS.CHECKLIST_STATE, JSON.stringify(currentState));
localStorage.setItem(STORAGE_KEYS.USER_NOTES, JSON.stringify(currentNotes));
console.log('Import successful');
} catch (error) {
console.error('Import failed:', error);
throw error;
}
}
Common Workflows
Starting a New Assessment
// 1. Navigate to platform
function startWebAssessment() {
window.location.href = '/web';
}
// 2. Expand all categories for overview
function expandAllCategories() {
const collapsed = new Set();
localStorage.setItem(STORAGE_KEYS.COLLAPSED_SECTIONS, JSON.stringify([...collapsed]));
}
// 3. Filter by severity for prioritization
function filterBySeverity(minSeverity: 'critical' | 'high' | 'medium' | 'low') {
const severityLevels = ['critical', 'high', 'medium', 'low', 'info'];
const minIndex = severityLevels.indexOf(minSeverity);
// Apply filter in UI to show only checks >= minSeverity
}
Recording Findings
interface Finding {
checkId: string;
vulnerability: string;
impact: string;
steps: string[];
payload?: string;
evidence: string[];
remediation: string;
}
function recordFinding(
platformId: string,
categoryId: string,
techId: string,
checkId: string,
finding: Finding
): void {
// Mark as Open
updateCheckStatus(platformId, categoryId, techId, checkId, 'open');
// Save detailed notes
const noteContent = `
**Vulnerability:** ${finding.vulnerability}
**Impact:** ${finding.impact}
**Steps to Reproduce:**
${finding.steps.map((step, i) => `${i + 1}. ${step}`).join('\n')}
${finding.payload ? `**Payload:**\n\`\`\`\n${finding.payload}\n\`\`\`` : ''}
**Evidence:**
${finding.evidence.map(e => `- ${e}`).join('\n')}
**Remediation:**
${finding.remediation}
`.trim();
saveCheckNote(platformId, categoryId, techId, checkId, noteContent);
}
Filtering Open Findings
function getOpenFindings(platform: Platform): Array<{
check: Check;
category: string;
technology: string;
notes: string;
}> {
const openFindings: Array<any> = [];
const state = JSON.parse(localStorage.getItem(STORAGE_KEYS.CHECKLIST_STATE) || '{}');
const notes = JSON.parse(localStorage.getItem(STORAGE_KEYS.USER_NOTES) || '{}');
platform.categories.forEach(category => {
category.technologies.forEach(tech => {
tech.checks.forEach(check => {
const key = `${platform.id}.${category.id}.${tech.id}.${check.id}`;
if (state[key]?.status === 'open') {
openFindings.push({
check,
category: category.name,
technology: tech.name,
notes: notes[key]?.content || ''
});
}
});
});
});
return openFindings;
}
Platform-Specific Patterns
Web Application Assessment
const webAppChecks = {
authentication: [
'Test for username enumeration',
'Check password policy enforcement',
'Verify session timeout settings',
'Test for account lockout mechanisms'
],
authorization: [
'Test horizontal privilege escalation',
'Test vertical privilege escalation',
'Check for IDOR vulnerabilities',
'Verify API endpoint authorization'
],
injection: [
'Test for SQL injection',
'Check for XSS vulnerabilities',
'Test for command injection',
'Verify template injection protections'
]
};
API Security Assessment
const apiSecurityChecks = {
authentication: [
'Test broken object level authorization (BOLA)',
'Verify broken user authentication',
'Check for excessive data exposure',
'Test resource consumption limits'
],
authorization: [
'Test broken function level authorization',
'Verify mass assignment protections',
'Check security misconfiguration',
'Test for SSRF vulnerabilities'
]
};
Cloud Security Assessment
const cloudSecurityChecks = {
iam: [
'Review IAM policies for overprivileged roles',
'Check for unused access keys',
'Verify MFA enforcement',
'Test for privilege escalation paths'
],
storage: [
'Check for publicly accessible S3 buckets',
'Verify encryption at rest',
'Test bucket policy configurations',
'Check for sensitive data exposure'
],
compute: [
'Test instance metadata SSRF',
'Verify security group configurations',
'Check for exposed management interfaces',
'Test network segmentation'
]
};
Troubleshooting
Data Persistence Issues
// Check if localStorage is available
function isLocalStorageAvailable(): boolean {
try {
const test = '__localStorage_test__';
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch (e) {
return false;
}
}
// Clear corrupted data
function resetChecklistData(): void {
if (confirm('This will clear all assessment data. Continue?')) {
localStorage.removeItem(STORAGE_KEYS.CHECKLIST_STATE);
localStorage.removeItem(STORAGE_KEYS.USER_NOTES);
localStorage.removeItem(STORAGE_KEYS.PROGRESS);
localStorage.removeItem(STORAGE_KEYS.COLLAPSED_SECTIONS);
window.location.reload();
}
}
// Backup before clearing
function backupBeforeReset(): void {
const backup = {
state: localStorage.getItem(STORAGE_KEYS.CHECKLIST_STATE),
notes: localStorage.getItem(STORAGE_KEYS.USER_NOTES),
progress: localStorage.getItem(STORAGE_KEYS.PROGRESS)
};
const blob = new Blob([JSON.stringify(backup, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `checklist-backup-${Date.now()}.json`;
a.click();
}
Search Performance
// Debounced search for better performance
function createDebouncedSearch(delay = 300) {
let timeoutId: number;
return function(query: string, callback: (results: SearchResult[]) => void) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
const results = performSearch(query, getAllPlatforms());
callback(results);
}, delay);
};
}
const debouncedSearch = createDebouncedSearch();
Export Size Limitations
// For large assessments, export per platform
function exportPlatformIndividually(platformId: string): void {
const platform = getPlatformById(platformId);
const markdown = exportToMarkdown(platform);
const blob = new Blob([markdown], { type: 'text/markdown' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${platformId}-assessment-${Date.now()}.md`;
a.click();
URL.revokeObjectURL(url);
}
Best Practices
- Regular Backups: Export your assessment data regularly, especially before major changes
- Structured Notes: Use consistent formatting in notes for easier report generation
- Severity Filtering: Start with Critical/High severity checks, then work down
- Progress Tracking: Mark checks as N/A when not applicable to keep accurate progress metrics
- Evidence Recording: Include timestamps, URLs, and request/response data in notes
- Export Early: Don't wait until the end — export incremental findings
- Browser Compatibility: Recommended browsers are Chrome, Firefox, or Edge (modern versions)
Integration Patterns
Custom Automation Scripts
// Example: Automated check status update from external tool
function updateFromBurpSuite(findings: Array<{url: string, issue: string}>): void {
findings.forEach(finding => {
// Map Burp issues to checklist items
const checkMapping = mapBurpIssueToCheck(finding.issue);
if (checkMapping) {
updateCheckStatus(
checkMapping.platformId,
checkMapping.categoryId,
checkMapping.techId,
checkMapping.checkId,
'open'
);
saveCheckNote(
checkMapping.platformId,
checkMapping.categoryId,
checkMapping.techId,
checkMapping.checkId,
`Found by Burp Suite at ${finding.url}: ${finding.issue}`
);
}
});
}
This skill provides comprehensive coverage of the PentestingChecklist platform for AI agents to assist developers and security professionals in conducting structured security assessments.