API Reference

API Examples

Practical examples and code snippets for using the PentestPad API

Overview

This page provides practical examples for common API operations using different programming languages and tools.

Authentication Setup

First, set up authentication for all examples:

Environment Variables

export PENTESTPAD_API_KEY="pp_3kqz6Pj58v86KmPMkTmCUmpt2ZJWCqZR0LbGOHyD"
export PENTESTPAD_BASE_URL="https://your-instance.pentestpad.com/api/v1"

JavaScript/Node.js

const API_KEY = process.env.PENTESTPAD_API_KEY;
const BASE_URL = process.env.PENTESTPAD_BASE_URL;

const headers = {
  'Authorization': `Bearer ${API_KEY}`,
  'Content-Type': 'application/json'
};

Python

import os
import requests

API_KEY = os.getenv('PENTESTPAD_API_KEY')
BASE_URL = os.getenv('PENTESTPAD_BASE_URL')

headers = {
    'Authorization': f'Bearer {API_KEY}',
    'Content-Type': 'application/json'
}

Project Management

Create a Complete Project Workflow

JavaScript Example

async function createProjectWorkflow() {
  // 1. List available teams
  const teams = await fetch(`${BASE_URL}/teams`, { headers })
    .then(r => r.json());

  console.log('Available teams:', teams.data.map(t => t.name));

  // 2. Create new project
  const projectData = {
    name: 'E-commerce Security Assessment',
    description: 'Comprehensive security testing of online shopping platform',
    client_id: 1,
    team_id: teams.data[0].id, // Use first available team
    type_id: 1,
    status: 'not_started',
    start_date: '2024-04-01',
    end_date: '2024-04-30'
  };

  const project = await fetch(`${BASE_URL}/projects`, {
    method: 'POST',
    headers,
    body: JSON.stringify(projectData)
  }).then(r => r.json());

  console.log('Created project:', project.data.name, project.data.uuid);

  // 3. Update project status to in-progress
  await fetch(`${BASE_URL}/projects/${project.data.uuid}/status`, {
    method: 'POST',
    headers,
    body: JSON.stringify({ status: 'in_progress' })
  });

  console.log('Project status updated to: in_progress');
  return project.data;
}

Python Example

def create_project_workflow():
    # 1. List available teams
    teams_response = requests.get(f'{BASE_URL}/teams', headers=headers)
    teams = teams_response.json()

    print('Available teams:', [t['name'] for t in teams['data']])

    # 2. Create new project
    project_data = {
        'name': 'E-commerce Security Assessment',
        'description': 'Comprehensive security testing of online shopping platform',
        'client_id': 1,
        'team_id': teams['data'][0]['id'],  # Use first available team
        'type_id': 1,
        'status': 'not_started',
        'start_date': '2024-04-01',
        'end_date': '2024-04-30'
    }

    project_response = requests.post(
        f'{BASE_URL}/projects',
        headers=headers,
        json=project_data
    )
    project = project_response.json()

    print(f"Created project: {project['data']['name']} ({project['data']['uuid']})")

    # 3. Update project status
    status_response = requests.post(
        f"{BASE_URL}/projects/{project['data']['uuid']}/status",
        headers=headers,
        json={'status': 'in_progress'}
    )

    print('Project status updated to: in_progress')
    return project['data']

cURL Example

#!/bin/bash

# 1. Get teams
TEAMS=$(curl -s -H "Authorization: Bearer $PENTESTPAD_API_KEY" \
  "$PENTESTPAD_BASE_URL/teams")

echo "Available teams: $TEAMS"

# Extract first team ID (requires jq)
TEAM_ID=$(echo $TEAMS | jq -r '.data[0].id')

# 2. Create project
PROJECT=$(curl -s -X POST \
  -H "Authorization: Bearer $PENTESTPAD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "E-commerce Security Assessment",
    "description": "Comprehensive security testing",
    "client_id": 1,
    "team_id": '$TEAM_ID',
    "type_id": 1,
    "status": "not_started",
    "start_date": "2024-04-01",
    "end_date": "2024-04-30"
  }' \
  "$PENTESTPAD_BASE_URL/projects")

echo "Created project: $PROJECT"

# Extract project UUID
PROJECT_UUID=$(echo $PROJECT | jq -r '.data.uuid')

# 3. Update status
curl -s -X POST \
  -H "Authorization: Bearer $PENTESTPAD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "in_progress"}' \
  "$PENTESTPAD_BASE_URL/projects/$PROJECT_UUID/status"

echo "Project status updated"

Finding Management

Create Findings from Different Sources

JavaScript Example

async function createFindingsWorkflow(projectId) {
  // 1. Create finding from scratch
  const sqlInjection = await fetch(`${BASE_URL}/projects/${projectId}/findings`, {
    method: 'POST',
    headers,
    body: JSON.stringify({
      title: 'SQL Injection in User Profile',
      description: 'User profile update functionality vulnerable to SQL injection',
      impact: 'high',
      probability: 'medium',
      poc: '1. Login to application\n2. Navigate to profile\n3. Update bio with: "); DROP TABLE users; --',
      risks: 'Complete database compromise possible',
      remediation: 'Use parameterized queries for all database operations',
      cvss: 'CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H',
      cvss_score: 8.8,
      affected_hosts: [
        {
          endpoint: 'https://app.example.com/profile',
          port: 443,
          protocol: 'HTTPS',
          description: 'User profile management'
        }
      ],
      categories: [1, 5], // Injection + Authentication
      extra_fields: {
        cwe_id: 'CWE-89',
        owasp_category: 'A03:2021',
        tool_detected: 'Manual Testing'
      }
    })
  }).then(r => r.json());

  console.log('Created SQL Injection finding:', sqlInjection.data.uuid);

  // 2. Create finding from template
  const templateFinding = await fetch(`${BASE_URL}/projects/${projectId}/findings/template`, {
    method: 'POST',
    headers,
    body: JSON.stringify({
      template_id: 15, // XSS template
      title: 'XSS in Search Functionality',
      affected_hosts: [
        {
          endpoint: 'https://app.example.com/search',
          port: 443,
          description: 'Main search feature'
        }
      ]
    })
  }).then(r => r.json());

  console.log('Created template-based finding:', templateFinding.data.uuid);

  // 3. Update finding status
  await fetch(`${BASE_URL}/findings/${sqlInjection.data.uuid}/remediation`, {
    method: 'PATCH',
    headers,
    body: JSON.stringify({
      remediation_stage: 'requested',
      status: 'in-progress',
      remediation: 'Requested client to implement parameterized queries. Provided code examples.'
    })
  });

  console.log('Updated finding remediation status');
  return [sqlInjection.data, templateFinding.data];
}

Python Example

def create_findings_workflow(project_id):
    # 1. Create finding from scratch
    finding_data = {
        'title': 'SQL Injection in User Profile',
        'description': 'User profile update functionality vulnerable to SQL injection',
        'impact': 'high',
        'probability': 'medium',
        'poc': '1. Login to application\n2. Navigate to profile\n3. Update bio with: "); DROP TABLE users; --',
        'risks': 'Complete database compromise possible',
        'remediation': 'Use parameterized queries for all database operations',
        'cvss': 'CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H',
        'cvss_score': 8.8,
        'affected_hosts': [
            {
                'endpoint': 'https://app.example.com/profile',
                'port': 443,
                'protocol': 'HTTPS',
                'description': 'User profile management'
            }
        ],
        'categories': [1, 5],  # Injection + Authentication
        'extra_fields': {
            'cwe_id': 'CWE-89',
            'owasp_category': 'A03:2021',
            'tool_detected': 'Manual Testing'
        }
    }

    sql_injection = requests.post(
        f'{BASE_URL}/projects/{project_id}/findings',
        headers=headers,
        json=finding_data
    ).json()

    print(f"Created SQL Injection finding: {sql_injection['data']['uuid']}")

    # 2. Create from template
    template_data = {
        'template_id': 15,  # XSS template
        'title': 'XSS in Search Functionality',
        'affected_hosts': [
            {
                'endpoint': 'https://app.example.com/search',
                'port': 443,
                'description': 'Main search feature'
            }
        ]
    }

    template_finding = requests.post(
        f'{BASE_URL}/projects/{project_id}/findings/template',
        headers=headers,
        json=template_data
    ).json()

    print(f"Created template-based finding: {template_finding['data']['uuid']}")

    # 3. Update remediation status
    requests.patch(
        f"{BASE_URL}/findings/{sql_injection['data']['uuid']}/remediation",
        headers=headers,
        json={
            'remediation_stage': 'requested',
            'status': 'in-progress',
            'remediation': 'Requested client to implement parameterized queries. Provided code examples.'
        }
    )

    print('Updated finding remediation status')
    return [sql_injection['data'], template_finding['data']]

CSV Import Workflow

Complete CSV Import Process

JavaScript Example

async function csvImportWorkflow(projectId, csvFile) {
  // 1. Get field mapping information first
  const fieldInfo = await fetch(`${BASE_URL}/csv/field-mapping`, { headers })
    .then(r => r.json());

  console.log('CSV Field Requirements:');
  Object.entries(fieldInfo.data.field_mapping).forEach(([field, description]) => {
    console.log(`  ${field}: ${description}`);
  });

  // 2. Validate CSV file
  if (csvFile.size > 10 * 1024 * 1024) {
    throw new Error('File too large. Maximum size is 10MB.');
  }

  const fileName = csvFile.name.toLowerCase();
  if (!fileName.endsWith('.csv') && !fileName.endsWith('.txt')) {
    throw new Error('Invalid file format. Use .csv or .txt files.');
  }

  // 3. Import CSV
  const formData = new FormData();
  formData.append('file', csvFile);

  const importResult = await fetch(`${BASE_URL}/projects/${projectId}/findings/import-csv`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}` // Note: Don't include Content-Type for FormData
    },
    body: formData
  }).then(r => r.json());

  // 4. Process results
  console.log(`Import completed: ${importResult.data.success_count} successes, ${importResult.data.error_count} errors`);

  if (importResult.data.error_count > 0) {
    console.log('Errors encountered:');
    importResult.data.errors.forEach(error => {
      console.log(`  Row ${error.row}: ${error.error}`);
    });
  }

  console.log('Successfully imported findings:');
  importResult.data.imported_findings.forEach(finding => {
    console.log(`  Row ${finding.row}: ${finding.title} (${finding.uuid})`);
  });

  return importResult;
}

// Usage example with file input
document.getElementById('csvFileInput').addEventListener('change', async (event) => {
  const file = event.target.files[0];
  if (file) {
    try {
      await csvImportWorkflow('project-uuid-here', file);
    } catch (error) {
      console.error('Import failed:', error.message);
    }
  }
});

Python Example

import csv
from io import StringIO

def csv_import_workflow(project_id, csv_file_path):
    # 1. Get field mapping information
    field_info = requests.get(f'{BASE_URL}/csv/field-mapping', headers=headers).json()

    print('CSV Field Requirements:')
    for field, description in field_info['data']['field_mapping'].items():
        print(f'  {field}: {description}')

    # 2. Validate file
    import os
    file_size = os.path.getsize(csv_file_path)
    if file_size > 10 * 1024 * 1024:  # 10MB
        raise ValueError('File too large. Maximum size is 10MB.')

    if not csv_file_path.lower().endswith(('.csv', '.txt')):
        raise ValueError('Invalid file format. Use .csv or .txt files.')

    # 3. Import CSV
    with open(csv_file_path, 'rb') as f:
        files = {'file': (os.path.basename(csv_file_path), f, 'text/csv')}
        response = requests.post(
            f'{BASE_URL}/projects/{project_id}/findings/import-csv',
            headers={'Authorization': f'Bearer {API_KEY}'},  # No Content-Type for multipart
            files=files
        )

    import_result = response.json()

    # 4. Process results
    success_count = import_result['data']['success_count']
    error_count = import_result['data']['error_count']

    print(f'Import completed: {success_count} successes, {error_count} errors')

    if error_count > 0:
        print('Errors encountered:')
        for error in import_result['data']['errors']:
            print(f"  Row {error['row']}: {error['error']}")

    print('Successfully imported findings:')
    for finding in import_result['data']['imported_findings']:
        print(f"  Row {finding['row']}: {finding['title']} ({finding['uuid']})")

    return import_result

# Create sample CSV for testing
def create_sample_csv(filename='sample_findings.csv'):
    sample_data = [
        {
            'title': 'SQL Injection in Login Form',
            'impact': 'high',
            'probability': 'medium',
            'description': 'Login form vulnerable to SQL injection attacks',
            'poc': "1. Navigate to /login\n2. Enter: admin' OR 1=1 --\n3. Observe bypass",
            'risks': 'Unauthorized access, data breach',
            'remediation': 'Use parameterized queries',
            'affected_hosts': 'https://example.com/login,https://api.example.com/auth',
            'categories': '1,2',
            'extra_fields': '{"cwe_id": "CWE-89", "owasp": "A03:2021"}'
        },
        {
            'title': 'Cross-Site Scripting (XSS)',
            'impact': 'medium',
            'probability': 'high',
            'description': 'Reflected XSS in search parameter',
            'poc': '1. Go to /search?q=<script>alert(1)</script>\n2. Observe execution',
            'risks': 'Session hijacking, credential theft',
            'remediation': 'Implement proper output encoding',
            'affected_hosts': '{"endpoint": "https://example.com/search", "port": 443}',
            'categories': '3',
            'status': 'draft'
        }
    ]

    with open(filename, 'w', newline='', encoding='utf-8') as f:
        if sample_data:
            writer = csv.DictWriter(f, fieldnames=sample_data[0].keys())
            writer.writeheader()
            writer.writerows(sample_data)

    print(f'Created sample CSV: {filename}')
    return filename

Error Handling

Robust Error Handling Example

JavaScript Example

class PentestPadAPI {
  constructor(apiKey, baseUrl) {
    this.apiKey = apiKey;
    this.baseUrl = baseUrl;
    this.headers = {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    };
  }

  async request(endpoint, options = {}) {
    const url = `${this.baseUrl}${endpoint}`;
    const config = {
      headers: this.headers,
      ...options
    };

    try {
      const response = await fetch(url, config);
      const data = await response.json();

      if (!response.ok) {
        throw new APIError(data.message || 'Request failed', response.status, data);
      }

      return data;
    } catch (error) {
      if (error instanceof APIError) {
        throw error;
      }

      // Network or other errors
      throw new APIError(`Network error: ${error.message}`, 0, error);
    }
  }

  async getProjects(filters = {}) {
    const params = new URLSearchParams(filters).toString();
    const endpoint = `/projects${params ? `?${params}` : ''}`;

    try {
      return await this.request(endpoint);
    } catch (error) {
      console.error('Failed to get projects:', error.message);
      throw error;
    }
  }

  async createFinding(projectId, findingData) {
    try {
      return await this.request(`/projects/${projectId}/findings`, {
        method: 'POST',
        body: JSON.stringify(findingData)
      });
    } catch (error) {
      if (error.status === 422) {
        console.error('Validation errors:', error.data.errors);
      }
      throw error;
    }
  }
}

class APIError extends Error {
  constructor(message, status, data) {
    super(message);
    this.name = 'APIError';
    this.status = status;
    this.data = data;
  }
}

Python Example

import requests
from requests.exceptions import RequestException, ConnectionError, Timeout
import json

class PentestPadAPI:
    def __init__(self, api_key, base_url):
        self.api_key = api_key
        self.base_url = base_url
        self.headers = {
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json'
        }

    def request(self, endpoint, method='GET', data=None, files=None):
        url = f'{self.base_url}{endpoint}'
        headers = self.headers.copy()

        # Remove Content-Type for file uploads
        if files:
            headers.pop('Content-Type', None)

        try:
            response = requests.request(
                method=method,
                url=url,
                headers=headers,
                json=data if data and not files else None,
                files=files,
                timeout=30
            )

            # Try to parse JSON response
            try:
                response_data = response.json()
            except json.JSONDecodeError:
                response_data = {'message': response.text}

            if not response.ok:
                raise APIError(
                    response_data.get('message', 'Request failed'),
                    response.status_code,
                    response_data
                )

            return response_data

        except ConnectionError:
            raise APIError('Connection failed. Check your network or API endpoint.', 0)
        except Timeout:
            raise APIError('Request timed out. Try again later.', 0)
        except RequestException as e:
            raise APIError(f'Request failed: {str(e)}', 0)

class APIError(Exception):
    def __init__(self, message, status, data=None):
        super().__init__(message)
        self.message = message
        self.status = status
        self.data = data or {}

Templates Management

Create and Use Vulnerability Templates

JavaScript Example

async function manageVulnerabilityTemplates() {
  // 1. Create a new vulnerability template
  const templateData = {
    title: 'Buffer Overflow Template',
    description: 'Template for buffer overflow vulnerabilities',
    impact: 'Critical',
    probability: 'Medium',
    cvss: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H',
    cvss_score: 9.8,
    poc: '1. Identify vulnerable function\n2. Create overflow payload\n3. Execute shellcode',
    risks: 'Remote code execution, system compromise',
    remediation: '1. Use safe string functions\n2. Implement stack canaries\n3. Enable DEP/ASLR',
    categories: [1, 4],
    project_types: [1, 3]
  };

  const template = await fetch(`${BASE_URL}/templates/vulnerabilities`, {
    method: 'POST',
    headers,
    body: JSON.stringify(templateData)
  }).then(r => r.json());

  console.log('Created template:', template.data.id);

  // 2. Use template to create finding in project
  const finding = await fetch(`${BASE_URL}/projects/${projectId}/findings/template`, {
    method: 'POST',
    headers,
    body: JSON.stringify({
      template_id: template.data.id,
      title: 'Buffer Overflow in Authentication Module',
      affected_hosts: [
        {
          endpoint: 'https://app.example.com/auth',
          port: 443,
          description: 'Authentication service'
        }
      ]
    })
  }).then(r => r.json());

  console.log('Created finding from template:', finding.data.uuid);

  return { template: template.data, finding: finding.data };
}

Python Example

def manage_vulnerability_templates():
    # 1. Create vulnerability template
    template_data = {
        'title': 'CSRF Template',
        'description': 'Cross-Site Request Forgery template',
        'impact': 'Medium',
        'probability': 'High',
        'cvss': 'CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N',
        'cvss_score': 6.5,
        'poc': '1. Craft malicious form\n2. Host on attacker site\n3. Trick user into submission',
        'risks': 'Unauthorized actions on behalf of user',
        'remediation': '1. Implement CSRF tokens\n2. Verify Referer header\n3. Use SameSite cookies',
        'categories': [2, 5],
        'project_types': [1]
    }

    template_response = requests.post(
        f'{BASE_URL}/templates/vulnerabilities',
        headers=headers,
        json=template_data
    )
    template = template_response.json()

    print(f"Created template: {template['data']['id']}")

    # 2. List all templates
    templates_response = requests.get(
        f'{BASE_URL}/templates/vulnerabilities',
        headers=headers
    )
    templates = templates_response.json()

    print(f"Total templates: {templates['total']}")

    return template['data']

Import Templates from CSV

JavaScript Example

async function importTemplatesFromCsv(csvFile) {
  // 1. Get field mapping first
  const fieldMapping = await fetch(`${BASE_URL}/templates/vulnerabilities/csv/field-mapping`, {
    headers
  }).then(r => r.json());

  console.log('Required fields:', Object.keys(fieldMapping.data.field_mapping));

  // 2. Import vulnerability templates
  const formData = new FormData();
  formData.append('file', csvFile);

  const importResult = await fetch(`${BASE_URL}/templates/vulnerabilities/import-csv`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}` // Only auth header for multipart
    },
    body: formData
  }).then(r => r.json());

  console.log(`Imported ${importResult.data.success_count} vulnerability templates`);

  if (importResult.data.error_count > 0) {
    console.log('Errors:', importResult.data.errors);
  }

  return importResult;
}

Python Example with Sample CSV Creation

def import_vulnerability_templates():
    # 1. Create sample CSV
    import csv

    sample_templates = [
        {
            'title': 'SQL Injection Template',
            'impact': 'High',
            'probability': 'Medium',
            'description': 'SQL injection vulnerability template',
            'poc': '1. Find injection point\n2. Test with quotes\n3. Extract data',
            'risks': 'Data breach, authentication bypass',
            'remediation': 'Use parameterized queries',
            'cvss': 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N',
            'cvss_score': '8.1',
            'categories': '1,2',
            'project_types': '1',
            'extra_fields': '{"cwe_id": "CWE-89", "owasp": "A03:2021"}'
        },
        {
            'title': 'XSS Template',
            'impact': 'Medium',
            'probability': 'High',
            'description': 'Cross-site scripting template',
            'poc': '1. Find reflection point\n2. Test XSS payload\n3. Execute JavaScript',
            'risks': 'Session hijacking, credential theft',
            'remediation': 'Implement output encoding and CSP',
            'cvss': 'CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N',
            'cvss_score': '6.1',
            'categories': '2,3',
            'project_types': '1',
            'extra_fields': '{"cwe_id": "CWE-79", "owasp": "A03:2021"}'
        }
    ]

    # Write to CSV
    filename = 'vulnerability_templates.csv'
    with open(filename, 'w', newline='', encoding='utf-8') as f:
        if sample_templates:
            writer = csv.DictWriter(f, fieldnames=sample_templates[0].keys())
            writer.writeheader()
            writer.writerows(sample_templates)

    # 2. Import the CSV
    with open(filename, 'rb') as f:
        files = {'file': (filename, f, 'text/csv')}
        response = requests.post(
            f'{BASE_URL}/templates/vulnerabilities/import-csv',
            headers={'Authorization': f'Bearer {API_KEY}'},
            files=files
        )

    result = response.json()
    print(f"Import completed: {result['data']['success_count']} templates imported")

    return result

Quick Examples

Get All Projects

curl -H "Authorization: Bearer your_api_key" \
     https://your-instance.pentestpad.com/api/v1/projects

Create a Finding

curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "SQL Injection",
    "impact": "High",
    "probability": "Medium",
    "description": "SQL injection vulnerability found"
  }' \
  https://your-instance.pentestpad.com/api/v1/projects/project-uuid/findings

Create Vulnerability Template

curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "CSRF Template",
    "impact": "Medium",
    "probability": "High",
    "description": "Cross-Site Request Forgery template"
  }' \
  https://your-instance.pentestpad.com/api/v1/templates/vulnerabilities

Import Findings CSV

curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  -F "file=@findings.csv" \
  https://your-instance.pentestpad.com/api/v1/projects/project-uuid/findings/import-csv

Import Templates CSV

curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  -F "file=@vulnerability_templates.csv" \
  https://your-instance.pentestpad.com/api/v1/templates/vulnerabilities/import-csv

This comprehensive guide provides practical, real-world examples for integrating with the PentestPad API across different programming languages and use cases.