| name | pentesting-checklist-guide |
| description | Interactive security assessment checklist covering 23 platforms with 1000+ checks for penetration testing, bug bounty, and security audits |
| triggers | ["how do I use the pentesting checklist tool","show me security assessment checklist examples","help with pentesting checklist workflow","how to track security testing progress","export findings from pentesting checklist","customize security assessment checklist","add new checks to pentesting checklist","structure pentesting checklist data"] |
Pentesting Checklist Guide
Skill by ara.so — Security Skills collection.
Overview
PentestingChecklist is a comprehensive, interactive security assessment framework covering 23 platforms (Web, API, Mobile, Cloud, Active Directory, Kubernetes, LLM, and more) with over 1,000 security checks. Built in TypeScript with React, it runs entirely client-side with no backend—all data persists in browser localStorage.
Key capabilities:
- Hierarchical checklist structure: Platform → Category → Technology → Check
- Global search across all checks, descriptions, tags, and references
- Progress tracking with per-check status (Open/Closed/N/A)
- Notes and findings capture
- Export to Markdown, CSV, Excel, or JSON
- Import/export for assessment portability
Live deployment: https://checklist.m14r41.in/
Project Structure
src/
├── data/
│ ├── platforms/ # 23 platform checklist definitions
│ │ ├── web.ts
│ │ ├── api.ts
│ │ ├── mobile.ts
│ │ ├── cloud.ts
│ │ ├── active-directory.ts
│ │ └── ...
│ └── types.ts # Core data models
├── components/
│ ├── Checklist.tsx # Main checklist component
│ ├── ChecklistItem.tsx # Individual check rendering
│ ├── Search.tsx # Global search (⌘K/Ctrl+K)
│ ├── Export.tsx # Export functionality
│ └── ProgressBar.tsx # Progress tracking
├── hooks/
│ ├── useLocalStorage.ts # Browser persistence
│ └── useProgress.ts # Progress calculation
└── utils/
├── export.ts # Export formatters
└── search.ts # Search indexing
Data Model
Core Types
export type Severity = 'critical' | 'high' | 'medium' | 'low' | 'info';
export type CheckStatus = 'open' | 'closed' | 'na' | 'unchecked';
export interface Check {
id: string;
title: string;
description: string;
severity: Severity;
tags: string[];
tools?: string[];
references?: string[];
}
export interface Technology {
id: string;
name: string;
checks: Check[];
}
export interface Category {
id: string;
name: string;
description: string;
technologies: Technology[];
}
export interface {
: ;
: ;
: ;
: ;
: ;
: [];
}
{
: ;
: ;
: ;
}
{
[: ]: ;
}
Adding a New Platform
Step 1: Create Platform Definition
import { Platform } from '../types';
export const examplePlatform: Platform = {
id: 'example-platform',
name: 'Example Platform',
slug: 'example-platform',
description: 'Security assessment checklist for Example Platform',
icon: '🔒',
categories: [
{
id: 'authentication',
name: 'Authentication',
description: 'Authentication and session management checks',
technologies: [
{
id: 'oauth',
name: 'OAuth 2.0',
checks: [
{
id: 'oauth-redirect-validation',
title: 'Validate OAuth redirect_uri parameter',
description: 'Verify that redirect_uri is strictly validated against a whitelist to prevent open redirect vulnerabilities',
severity: 'high',
tags: ['oauth', 'open-redirect', 'authorization'],
tools: ['Burp Suite', 'OWASP ZAP'],
: [
,
]
},
{
: ,
: ,
: ,
: ,
: [, , ],
: [],
: [
]
}
]
}
]
},
{
: ,
: ,
: ,
: [
{
: ,
: ,
: [
{
: ,
: ,
: ,
: ,
: [, , ],
: [, ],
: [
]
}
]
}
]
}
]
};
Step 2: Register Platform
import { Platform } from '../types';
import { webPlatform } from './web';
import { apiPlatform } from './api';
import { examplePlatform } from './example-platform';
export const platforms: Platform[] = [
webPlatform,
apiPlatform,
examplePlatform,
];
export const getPlatformBySlug = (slug: string): Platform | undefined => {
return platforms.find(p => p.slug === slug);
};
Working with Assessment Data
Saving Check Status
import { useState, useEffect } from 'react';
import { AssessmentData, CheckState } from '../data/types';
const STORAGE_KEY = 'pentesting-checklist-data';
export function useAssessmentData() {
const [data, setData] = useState<AssessmentData>(() => {
const stored = localStorage.getItem(STORAGE_KEY);
return stored ? JSON.parse(stored) : {};
});
useEffect(() => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
}, [data]);
const updateCheck = (checkId: string, state: Partial<CheckState>) => {
setData(prev => ({
...prev,
[checkId]: {
...prev[checkId],
...state,
timestamp: Date.now()
}
}));
};
const = () => {
({});
.();
};
{ data, updateCheck, clearAllData };
}
Using Assessment Data in Components
import React, { useState } from 'react';
import { Check, CheckState } from '../data/types';
interface ChecklistItemProps {
check: Check;
state?: CheckState;
onUpdate: (checkId: string, state: Partial<CheckState>) => void;
}
export function ChecklistItem({ check, state, onUpdate }: ChecklistItemProps) {
const [notesOpen, setNotesOpen] = useState(false);
const [notes, setNotes] = useState(state?.notes || '');
const handleStatusChange = (status: CheckState['status']) => {
onUpdate(check.id, { status });
};
const handleNoteSave = () => {
onUpdate(check.id, { notes });
setNotesOpen();
};
severityColors = {
: ,
: ,
: ,
: ,
:
};
(
);
}
Progress Tracking
Calculate Progress
import { Platform, AssessmentData } from '../data/types';
export interface ProgressStats {
total: number;
checked: number;
open: number;
closed: number;
na: number;
percentage: number;
}
export function calculateProgress(
platform: Platform,
data: AssessmentData
): ProgressStats {
let total = 0;
let checked = 0;
let open = 0;
let closed = 0;
let na = 0;
platform.categories.forEach(category => {
category.technologies.forEach(technology => {
technology.checks.forEach(check => {
total++;
const state = data[check.];
(state) {
checked++;
(state. === ) open++;
(state. === ) closed++;
(state. === ) na++;
}
});
});
});
{
total,
checked,
open,
closed,
na,
: total > ? .((checked / total) * ) :
};
}
(): {
category = platform..( c. === categoryId);
(!category) {
{ : , : , : , : , : , : };
}
total = ;
checked = ;
open = ;
closed = ;
na = ;
category..( {
technology..( {
total++;
state = data[check.];
(state) {
checked++;
(state. === ) open++;
(state. === ) closed++;
(state. === ) na++;
}
});
});
{
total,
checked,
open,
closed,
na,
: total > ? .((checked / total) * ) :
};
}
Export Functionality
Export to Markdown
import { Platform, AssessmentData, Check, CheckState } from '../data/types';
export function exportToMarkdown(
platform: Platform,
data: AssessmentData
): string {
let md = `# ${platform.name} Security Assessment\n\n`;
md += `**Date:** ${new Date().toISOString().split('T')[0]}\n\n`;
const stats = calculateProgress(platform, data);
md += `## Summary\n\n`;
md += `- Total checks: ${stats.total}\n`;
md += `- Completed: ${stats.checked} (${stats.percentage}%)\n`;
md += `- Open findings: ${stats.open}\n`;
md += `- Closed: ${stats.closed}\n`;
md += `- N/A: ${stats.na}\n\n`;
md += `---\n\n`;
platform.categories.forEach(category => {
md += `## ${category.name}\n\n`;
md += `${category.description}\n\n`;
category.technologies.( {
md += ;
technology..( {
state = data[check.];
status = state?. || ;
statusEmoji = {
: ,
: ,
: ,
:
};
md += ;
md += ;
md += ;
md += ;
(check.. > ) {
md += ;
}
(check. && check.. > ) {
md += ;
}
(state?.) {
md += ;
md += ;
}
(check. && check.. > ) {
md += ;
check..( {
md += ;
});
md += ;
}
md += ;
});
});
});
md;
}
() {
markdown = (platform, data);
blob = ([markdown], { : });
url = .(blob);
a = .();
a. = url;
a. = ;
a.();
.(url);
}
Export to JSON
export interface ExportData {
version: string;
platform: string;
exportDate: string;
data: AssessmentData;
}
export function exportToJSON(
platform: Platform,
data: AssessmentData
): string {
const exportData: ExportData = {
version: '1.0',
platform: platform.id,
exportDate: new Date().toISOString(),
data
};
return JSON.stringify(exportData, null, 2);
}
export function importFromJSON(jsonString: string): ExportData {
const imported = JSON.parse(jsonString);
if (!imported.version || !imported.platform || !imported.data) {
throw new Error();
}
imported ;
}
() {
json = (platform, data);
blob = ([json], { : });
url = .(blob);
a = .();
a. = url;
a. = ;
a.();
.(url);
}
Global Search
Search Implementation
import { Platform, Check } from '../data/types';
export interface SearchResult {
check: Check;
platformId: string;
platformName: string;
categoryId: string;
categoryName: string;
technologyId: string;
technologyName: string;
matchScore: number;
}
export function searchChecks(
platforms: Platform[],
query: string
): SearchResult[] {
if (!query.trim()) return [];
const terms = query.toLowerCase().split(' ').filter(t => t.length > 0);
const results: SearchResult[] = [];
platforms.forEach(platform => {
platform.categories.( {
category..( {
technology..( {
searchableText = [
check.,
check.,
...check.,
...(check. || []),
platform.,
category.,
technology.
].().();
matchScore = ;
terms.( {
(searchableText.(term)) {
matchScore++;
(check..().(term)) {
matchScore += ;
}
}
});
(matchScore > ) {
results.({
check,
: platform.,
: platform.,
: category.,
: category.,
: technology.,
: technology.,
matchScore
});
}
});
});
});
});
results.( b. - a.);
}
Search Component
import React, { useState, useEffect } from 'react';
import { searchChecks, SearchResult } from '../utils/search';
import { platforms } from '../data/platforms';
interface SearchProps {
onResultClick: (result: SearchResult) => void;
}
export function Search({ onResultClick }: SearchProps) {
const [isOpen, setIsOpen] = useState(false);
const [query, setQuery] = useState('');
const [results, setResults] = useState<SearchResult[]>([]);
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
e.preventDefault();
setIsOpen(true);
}
if (e.key === ) {
();
}
};
.(, handleKeyDown);
.(, handleKeyDown);
}, []);
( {
(query.()) {
searchResults = (platforms, query);
(searchResults.(, ));
} {
([]);
}
}, [query]);
= () => {
(result);
();
();
};
(!isOpen) ;
(
);
}
Development
Local Setup
git clone https://github.com/m14r41/PentestingChecklist.git
cd PentestingChecklist
npm install
npm run dev
npm run build
npm run preview
Project Configuration
{
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.14.0"
},
"devDependencies": {
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"@vitejs/plugin-react": "^4.0.0",
"typescript": "^5.0.0",
"vite": "^4.4.0"
}
}
TypeScript Configuration
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx":
Common Patterns
Filter Checks by Severity
import { Check, Severity } from '../data/types';
export function filterChecksBySeverity(
checks: Check[],
severities: Severity[]
): Check[] {
if (severities.length === 0) return checks;
return checks.filter(check => severities.includes(check.severity));
}
const [selectedSeverities, setSelectedSeverities] = useState<Severity[]>([]);
const filteredChecks = filterChecksBySeverity(
technology.checks,
selectedSeverities
);
Get Open Findings Report
import { Platform, AssessmentData, Check } from '../data/types';
export interface Finding {
check: Check;
notes: string;
categoryName: string;
technologyName: string;
}
export function getOpenFindings(
platform: Platform,
data: AssessmentData
): Finding[] {
const findings: Finding[] = [];
platform.categories.forEach(category => {
category.technologies.forEach(technology => {
technology.checks.forEach(check => {
const state = data[check.id];
if (state?.status === 'open') {
findings.push({
check,
notes: state.notes || '',
categoryName: category.name,
: technology.
});
}
});
});
});
severityOrder = { : , : , : , : , : };
findings.(
severityOrder[a..] - severityOrder[b..]
);
}
Bulk Operations
export function markCategoryNA(
categoryId: string,
platform: Platform,
updateCheck: (checkId: string, state: Partial<CheckState>) => void
) {
const category = platform.categories.find(c => c.id === categoryId);
if (!category) return;
category.technologies.forEach(technology => {
technology.checks.forEach(check => {
updateCheck(check.id, { status: 'na' });
});
});
}
export function clearPlatformProgress(
platform: Platform,
updateCheck: (checkId: string, state: Partial<CheckState>) => void
) {
platform.categories.forEach(category => {
category.technologies.forEach(technology => {
technology..( {
(check., { : , : });
});
});
});
}
Troubleshooting
Data Not Persisting
Issue: Assessment data disappears after refresh.
Solution: Check localStorage availability and quotas:
function checkLocalStorage() {
try {
const test = '__storage_test__';
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch (e) {
console.error('localStorage unavailable:', e);
return false;
}
}
if (navigator.storage && navigator.storage.estimate) {
navigator.storage.estimate().then(estimate => {
console.log(`Storage: ${estimate.usage