Code Security

The Question Every SOC 2 Auditor Will Ask That Most Engineering Teams Can't Answer

Amartya | CodeAnt AI Code Review Platform
Sonali Sood

Founding GTM, CodeAnt AI

The auditor sits across from your engineering lead. The SOC 2 Type II audit is in its third day. The auditor pulls up the penetration testing section of the audit program.

"Can you show me evidence that exploitable vulnerabilities identified during your penetration test were corrected and that the corrections were tested?"

Not: "Did you do a penetration test?" Not: "Can you show me the pentest report?" The specific question is about the correction of exploitable vulnerabilities and the verification of those corrections.

The engineering lead can produce the penetration test report. Finding a retest report that specifically confirms each vulnerability was patched and verified in production is harder. Documentation showing the timeline from finding to remediation to verification — even harder. Evidence that the retest was conducted against the same environment as the original test, with the same methodology — almost no one has this.

The auditor marks the control as having an exception. The SOC 2 report goes out with a qualified opinion on CC7.1. The company's enterprise sales cycle just got harder.

This scenario plays out thousands of times per year across companies going through SOC 2 audits for the first time or upgrading from Type I to Type II. Not because companies aren't doing security testing — many are — but because they're doing security testing without understanding what SOC 2 specifically requires the testing to produce as evidence.

This guide covers every dimension of SOC 2 penetration testing requirements: what the Trust Services Criteria actually say, what auditors actually look for (which is different from what the criteria literally say), how to scope the test correctly for SOC 2 compliance, what the evidence package must contain, how Type I and Type II requirements differ, and how to build a testing program that produces audit-ready evidence the first time.

Related: What Is AI Penetration Testing? The Complete Deep-Dive Guide | What Happens During a Pentest Retest? | Continuous Pentesting vs Annual: The Real Operational Difference

Part 1: The SOC 2 Framework: What It Is and Why Penetration Testing Is Required

SOC 2 at a Technical Level

SOC 2 (Service Organization Control 2) is an auditing standard developed by the American Institute of Certified Public Accountants (AICPA). It evaluates whether a service organization's controls meet the Trust Services Criteria (TSC) relevant to their service commitments and system requirements.

SOC 2 is NOT a certification. It is an attestation — a formal opinion from an independent CPA firm that your controls meet the criteria. The distinction matters operationally: there is no SOC 2 body that grants certificates. Your auditor firm produces a report that contains their opinion on your controls. That report is what you share with customers.




The Five Trust Services Criteria Categories

SOC 2 audits can cover any combination of five Trust Services Criteria categories. Security (CC) is always required — the other four are optional:

TSC Category

Abbreviation

Always Required?

Penetration Testing Relevance

Security

CC

Yes

Directly required — CC6, CC7, CC8, CC9

Availability

A

Optional

Infrastructure testing, DDoS resilience

Processing Integrity

PI

Optional

Business logic testing, data validation

Confidentiality

C

Optional

Data access controls, encryption validation

Privacy

P

Optional

PII handling, access control verification

Most SaaS companies pursuing SOC 2 cover Security + Availability at minimum. Companies handling significant personal data or providing financial processing add Confidentiality, Privacy, or Processing Integrity.

Why Penetration Testing Is Required

The SOC 2 framework does not use the word "penetration testing" in many places. This is where most companies go wrong — they look for an explicit requirement and don't find it stated as plainly as they expect, leading some to believe penetration testing is optional. It isn't.

The requirement emerges from multiple TSC points, particularly:




Auditors fill the gap between "detection procedures" and "penetration testing" through their professional judgment — and the AICPA's supplemental guidance explicitly references penetration testing as an appropriate procedure for meeting these criteria. Every Big Four firm and most regional CPA firms with SOC 2 practices treat annual penetration testing as a table-stakes control for the Security category.

Part 2: The TSC Controls That Penetration Testing Directly Supports

The Complete Control Mapping

Understanding exactly which controls penetration testing evidence supports is the prerequisite for scoping the test correctly. A penetration test that doesn't generate evidence for the right controls is a compliance gap regardless of how thorough the technical testing was.

DETAILED SOC 2 TSC → PENETRATION TESTING EVIDENCE MAPPING:

CC6: Logical and Physical Access Controls
├── CC6.1: The entity implements logical access security software,
│   infrastructure, and architectures over protected information assets
│   to protect them from security events.
│
│   Penetration testing evidence required:
│   □ Authentication bypass testing results
│   □ Authorization control effectiveness (IDOR testing)
│   □ Privilege escalation attempts and outcomes
│   □ Session management security assessment
│   □ API access control verification
│   Finding implication: Authentication bypass or IDOR findings are
│   direct CC6.1 control failures
│
├── CC6.2: Prior to issuing system credentials and granting system access,
│   the entity registers and authorizes new internal and external users.
│
│   Penetration testing evidence required:
│   □ Test of account creation flows without proper authorization
│   □ Verification that guest/anonymous accounts have minimum permissions
│   □ API endpoint testing for unauthorized account creation
│   Finding implication: Unauthenticated account creation is CC6.2 failure
│
├── CC6.3: The entity authorizes, modifies, or removes access to data,
│   software, functions, and other protected information assets based on
│   approved and documented access requests and the results of access reviews.
│
│   Penetration testing evidence required:
│   □ Role-based access control verification
│   □ Testing that terminated employee accounts are deactivated
│   □ Privilege creep testing (can regular user access admin functions?)
│   □ Cross-tenant access testing (for multi-tenant systems)
│
├── CC6.6: The entity implements logical access security measures to protect
│   against threats from sources outside its system boundaries.
│
│   Penetration testing evidence required:
│   □ External penetration testing results
│   □ Attack surface enumeration
│   □ Testing of all externally accessible endpoints
│   □ Firewall/WAF bypass testing
│   □ VPN and remote access security testing
│   THIS IS WHERE EXTERNAL PENETRATION TESTING IS MOST DIRECTLY REQUIRED
│
└── CC6.7: The entity restricts the transmission, movement, and removal
    of information to authorized internal and external users and processes.

    Penetration testing evidence required:
    □ Data exfiltration testing (simulated)
    □ CORS policy verification
    □ API response filtering testing
    □ Encryption verification for data in transit

CC7: System Operations
├── CC7.1: [See above — detection and monitoring]
│
│   Penetration testing evidence required:
│   □ Complete penetration test report showing findings
│   □ Evidence of remediation for each finding
│   □ Retest report confirming remediation effectiveness
│   □ Timeline documentation (finding → fix → verification)
│   THIS IS THE PRIMARY CONTROL WHERE PENTEST EVIDENCE LIVES
│
├── CC7.2: [See above — system monitoring]

DETAILED SOC 2 TSC → PENETRATION TESTING EVIDENCE MAPPING:

CC6: Logical and Physical Access Controls
├── CC6.1: The entity implements logical access security software,
│   infrastructure, and architectures over protected information assets
│   to protect them from security events.
│
│   Penetration testing evidence required:
│   □ Authentication bypass testing results
│   □ Authorization control effectiveness (IDOR testing)
│   □ Privilege escalation attempts and outcomes
│   □ Session management security assessment
│   □ API access control verification
│   Finding implication: Authentication bypass or IDOR findings are
│   direct CC6.1 control failures
│
├── CC6.2: Prior to issuing system credentials and granting system access,
│   the entity registers and authorizes new internal and external users.
│
│   Penetration testing evidence required:
│   □ Test of account creation flows without proper authorization
│   □ Verification that guest/anonymous accounts have minimum permissions
│   □ API endpoint testing for unauthorized account creation
│   Finding implication: Unauthenticated account creation is CC6.2 failure
│
├── CC6.3: The entity authorizes, modifies, or removes access to data,
│   software, functions, and other protected information assets based on
│   approved and documented access requests and the results of access reviews.
│
│   Penetration testing evidence required:
│   □ Role-based access control verification
│   □ Testing that terminated employee accounts are deactivated
│   □ Privilege creep testing (can regular user access admin functions?)
│   □ Cross-tenant access testing (for multi-tenant systems)
│
├── CC6.6: The entity implements logical access security measures to protect
│   against threats from sources outside its system boundaries.
│
│   Penetration testing evidence required:
│   □ External penetration testing results
│   □ Attack surface enumeration
│   □ Testing of all externally accessible endpoints
│   □ Firewall/WAF bypass testing
│   □ VPN and remote access security testing
│   THIS IS WHERE EXTERNAL PENETRATION TESTING IS MOST DIRECTLY REQUIRED
│
└── CC6.7: The entity restricts the transmission, movement, and removal
    of information to authorized internal and external users and processes.

    Penetration testing evidence required:
    □ Data exfiltration testing (simulated)
    □ CORS policy verification
    □ API response filtering testing
    □ Encryption verification for data in transit

CC7: System Operations
├── CC7.1: [See above — detection and monitoring]
│
│   Penetration testing evidence required:
│   □ Complete penetration test report showing findings
│   □ Evidence of remediation for each finding
│   □ Retest report confirming remediation effectiveness
│   □ Timeline documentation (finding → fix → verification)
│   THIS IS THE PRIMARY CONTROL WHERE PENTEST EVIDENCE LIVES
│
├── CC7.2: [See above — system monitoring]

DETAILED SOC 2 TSC → PENETRATION TESTING EVIDENCE MAPPING:

CC6: Logical and Physical Access Controls
├── CC6.1: The entity implements logical access security software,
│   infrastructure, and architectures over protected information assets
│   to protect them from security events.
│
│   Penetration testing evidence required:
│   □ Authentication bypass testing results
│   □ Authorization control effectiveness (IDOR testing)
│   □ Privilege escalation attempts and outcomes
│   □ Session management security assessment
│   □ API access control verification
│   Finding implication: Authentication bypass or IDOR findings are
│   direct CC6.1 control failures
│
├── CC6.2: Prior to issuing system credentials and granting system access,
│   the entity registers and authorizes new internal and external users.
│
│   Penetration testing evidence required:
│   □ Test of account creation flows without proper authorization
│   □ Verification that guest/anonymous accounts have minimum permissions
│   □ API endpoint testing for unauthorized account creation
│   Finding implication: Unauthenticated account creation is CC6.2 failure
│
├── CC6.3: The entity authorizes, modifies, or removes access to data,
│   software, functions, and other protected information assets based on
│   approved and documented access requests and the results of access reviews.
│
│   Penetration testing evidence required:
│   □ Role-based access control verification
│   □ Testing that terminated employee accounts are deactivated
│   □ Privilege creep testing (can regular user access admin functions?)
│   □ Cross-tenant access testing (for multi-tenant systems)
│
├── CC6.6: The entity implements logical access security measures to protect
│   against threats from sources outside its system boundaries.
│
│   Penetration testing evidence required:
│   □ External penetration testing results
│   □ Attack surface enumeration
│   □ Testing of all externally accessible endpoints
│   □ Firewall/WAF bypass testing
│   □ VPN and remote access security testing
│   THIS IS WHERE EXTERNAL PENETRATION TESTING IS MOST DIRECTLY REQUIRED
│
└── CC6.7: The entity restricts the transmission, movement, and removal
    of information to authorized internal and external users and processes.

    Penetration testing evidence required:
    □ Data exfiltration testing (simulated)
    □ CORS policy verification
    □ API response filtering testing
    □ Encryption verification for data in transit

CC7: System Operations
├── CC7.1: [See above — detection and monitoring]
│
│   Penetration testing evidence required:
│   □ Complete penetration test report showing findings
│   □ Evidence of remediation for each finding
│   □ Retest report confirming remediation effectiveness
│   □ Timeline documentation (finding → fix → verification)
│   THIS IS THE PRIMARY CONTROL WHERE PENTEST EVIDENCE LIVES
│
├── CC7.2: [See above — system monitoring]

[IMAGE PLACEHOLDER: Visual TSC control hierarchy diagram. CC6 through CC9 shown as the primary security category. Each criterion shown as a node with branches showing: what the control requires, what evidence satisfies it, and what finding types represent control failures. Color coding: green nodes = satisfied by penetration test, red nodes = finding here = control exception. Sidebar shows the Type I vs Type II evidence requirement difference for each node.]

Part 3: SOC 2 Type I vs Type II — Radically Different Evidence Requirements

What Changes Between Type I and Type II

The difference between Type I and Type II evidence requirements for penetration testing is more than a matter of degree — it's a fundamental difference in what the audit is measuring.







The Observation Period and Testing Frequency




Part 4: Scoping the SOC 2 Penetration Test — What Must Be Included

The Scoping Principle: Follow the Data

The most important scoping principle for SOC 2 penetration testing: test everything that touches, stores, processes, or transmits the data your SOC 2 is about.

SOC 2 is fundamentally about the security of your service delivery — the systems and processes that handle your customers' data. The penetration test must cover those systems. Testing systems that don't touch customer data while excluding the systems that do is a scoping failure that auditors will identify.

def scope_soc2_penetration_test(
    organization_profile: dict,
    tsc_categories: list,
    customer_data_flows: list
) -> dict:
    """
    Generate appropriate SOC 2 penetration test scope based on:
    - Which TSC categories are in scope for the audit
    - How customer data flows through the system
    - Risk profile of individual system components
    """

    scope = {
        'required_components': [],  # Must test for SOC 2 compliance
        'recommended_components': [],  # Strong recommendation
        'optional_components': [],  # At discretion based on risk
        'explicitly_excluded': [],  # Never test (third-party, etc.)
        'rationale': {}
    }

    # ═══════════════════════════════════════════════════════
    # SECURITY (CC) — ALWAYS IN SCOPE
    # ═══════════════════════════════════════════════════════

    # CC6.6 requires testing of all external-facing attack surface
    scope['required_components'].append({
        'component': 'External web application',
        'tsc_control': 'CC6.6',
        'test_types': [
            'Authentication bypass testing',
            'Authorization and access control testing',
            'Session management testing',
            'Input validation (injection testing)',
            'Business logic testing',
            'API security testing'
        ],
        'evidence_required': [
            'Findings report with CVSS scores',
            'Proof of concept for all confirmed findings',
            'Remediation evidence per finding'
        ]
    })

    scope['required_components'].append({
        'component': 'API layer (all authenticated endpoints)',
        'tsc_control': 'CC6.1, CC6.3, CC6.6',
        'test_types': [
            'Authentication enforcement',
            'Authorization (IDOR, BOLA)',
            'Rate limiting',
            'Input validation',
            'JWT/token security',
            'Mass assignment',
            'API versioning security'
        ],
        'evidence_required': [
            'Complete endpoint coverage documentation',
            'Authentication test results',
            'Authorization control verification'
        ]
    })

    scope['required_components'].append({
        'component': 'Network infrastructure (externally accessible)',
        'tsc_control': 'CC6.6',
        'test_types': [
            'Port scanning and service enumeration',
            'Service vulnerability assessment',
            'Firewall rule testing',
            'SSL/TLS configuration testing',
            'Open redirect testing',
            'CORS policy testing'
        ],
        'evidence_required': [
            'Network scan results',
            'Service enumeration report',
            'TLS configuration report'
        ]
    })

    # Customer data systems are always required
    for data_flow in customer_data_flows:
        if data_flow.get('contains_pii') or data_flow.get('is_primary_storage'):
            scope['required_components'].append({
                'component': f"Customer data system: {data_flow['name']}",
                'tsc_control': 'CC6.1, CC6.3, C1.1 (if confidentiality in scope)',
                'test_types': [
                    'Data access control testing',
                    'Unauthorized access attempts',
                    'SQL/NoSQL injection testing',
                    'Data exposure testing'
                ],
                'evidence_required': [
                    'Access control test results',
                    'Data exposure findings documentation'
                ]
            })

    # ═══════════════════════════════════════════════════════
    # CLOUD INFRASTRUCTURE — REQUIRED IF CLOUD-HOSTED
    # ═══════════════════════════════════════════════════════

    if organization_profile.get('cloud_provider'):
        scope['required_components'].append({
            'component': 'Cloud infrastructure security',
            'tsc_control': 'CC6.6, CC8.1',
            'test_types': [
                'IAM role and permission review',
                'S3/GCS/Blob storage access control',
                'Security group and network ACL review',
                'Secrets management review',
                'Logging and monitoring configuration'
            ],
            'evidence_required': [
                'Cloud security posture assessment',
                'IAM privilege audit results',
                'Storage access control verification'
            ]
        })

    # ═══════════════════════════════════════════════════════
    # AVAILABILITY (A1) — IF IN SCOPE
    # ═══════════════════════════════════════════════════════

    if 'availability' in tsc_categories:
        scope['recommended_components'].append({
            'component': 'Availability and resilience testing',
            'tsc_control': 'A1.2',
            'test_types': [
                'Rate limiting effectiveness',
                'Resource exhaustion testing (low intensity)',
                'Failover testing',
                'Recovery procedure testing'
            ],
            'note': 'DoS testing requires separate authorization and careful scoping'
        })

    # ═══════════════════════════════════════════════════════
    # WHAT SHOULD NOT BE IN SCOPE
    # ═══════════════════════════════════════════════════════

    scope['explicitly_excluded'] = [
        {
            'component': 'Third-party SaaS systems (Salesforce, Workday, etc.)',
            'reason': 'These vendors have their own SOC 2 reports — test your integration, not their system',
            'correct_approach': 'Review vendor SOC 2 reports as part of vendor management (CC9.2)'
        },
        {
            'component': 'Payment processor infrastructure (Stripe, Braintree)',
            'reason': 'PCI-DSS scoping exemption; testing the processor directly is not permitted',
            'correct_approach': 'Test your integration security, not the processor itself'
        },
        {
            'component': 'Other customers\\' data or systems',
            'reason': 'Never in scope — testing would itself be a SOC 2 control failure',
            'correct_approach': 'Use test accounts with synthetic data'
        }
    ]

    return scope
def scope_soc2_penetration_test(
    organization_profile: dict,
    tsc_categories: list,
    customer_data_flows: list
) -> dict:
    """
    Generate appropriate SOC 2 penetration test scope based on:
    - Which TSC categories are in scope for the audit
    - How customer data flows through the system
    - Risk profile of individual system components
    """

    scope = {
        'required_components': [],  # Must test for SOC 2 compliance
        'recommended_components': [],  # Strong recommendation
        'optional_components': [],  # At discretion based on risk
        'explicitly_excluded': [],  # Never test (third-party, etc.)
        'rationale': {}
    }

    # ═══════════════════════════════════════════════════════
    # SECURITY (CC) — ALWAYS IN SCOPE
    # ═══════════════════════════════════════════════════════

    # CC6.6 requires testing of all external-facing attack surface
    scope['required_components'].append({
        'component': 'External web application',
        'tsc_control': 'CC6.6',
        'test_types': [
            'Authentication bypass testing',
            'Authorization and access control testing',
            'Session management testing',
            'Input validation (injection testing)',
            'Business logic testing',
            'API security testing'
        ],
        'evidence_required': [
            'Findings report with CVSS scores',
            'Proof of concept for all confirmed findings',
            'Remediation evidence per finding'
        ]
    })

    scope['required_components'].append({
        'component': 'API layer (all authenticated endpoints)',
        'tsc_control': 'CC6.1, CC6.3, CC6.6',
        'test_types': [
            'Authentication enforcement',
            'Authorization (IDOR, BOLA)',
            'Rate limiting',
            'Input validation',
            'JWT/token security',
            'Mass assignment',
            'API versioning security'
        ],
        'evidence_required': [
            'Complete endpoint coverage documentation',
            'Authentication test results',
            'Authorization control verification'
        ]
    })

    scope['required_components'].append({
        'component': 'Network infrastructure (externally accessible)',
        'tsc_control': 'CC6.6',
        'test_types': [
            'Port scanning and service enumeration',
            'Service vulnerability assessment',
            'Firewall rule testing',
            'SSL/TLS configuration testing',
            'Open redirect testing',
            'CORS policy testing'
        ],
        'evidence_required': [
            'Network scan results',
            'Service enumeration report',
            'TLS configuration report'
        ]
    })

    # Customer data systems are always required
    for data_flow in customer_data_flows:
        if data_flow.get('contains_pii') or data_flow.get('is_primary_storage'):
            scope['required_components'].append({
                'component': f"Customer data system: {data_flow['name']}",
                'tsc_control': 'CC6.1, CC6.3, C1.1 (if confidentiality in scope)',
                'test_types': [
                    'Data access control testing',
                    'Unauthorized access attempts',
                    'SQL/NoSQL injection testing',
                    'Data exposure testing'
                ],
                'evidence_required': [
                    'Access control test results',
                    'Data exposure findings documentation'
                ]
            })

    # ═══════════════════════════════════════════════════════
    # CLOUD INFRASTRUCTURE — REQUIRED IF CLOUD-HOSTED
    # ═══════════════════════════════════════════════════════

    if organization_profile.get('cloud_provider'):
        scope['required_components'].append({
            'component': 'Cloud infrastructure security',
            'tsc_control': 'CC6.6, CC8.1',
            'test_types': [
                'IAM role and permission review',
                'S3/GCS/Blob storage access control',
                'Security group and network ACL review',
                'Secrets management review',
                'Logging and monitoring configuration'
            ],
            'evidence_required': [
                'Cloud security posture assessment',
                'IAM privilege audit results',
                'Storage access control verification'
            ]
        })

    # ═══════════════════════════════════════════════════════
    # AVAILABILITY (A1) — IF IN SCOPE
    # ═══════════════════════════════════════════════════════

    if 'availability' in tsc_categories:
        scope['recommended_components'].append({
            'component': 'Availability and resilience testing',
            'tsc_control': 'A1.2',
            'test_types': [
                'Rate limiting effectiveness',
                'Resource exhaustion testing (low intensity)',
                'Failover testing',
                'Recovery procedure testing'
            ],
            'note': 'DoS testing requires separate authorization and careful scoping'
        })

    # ═══════════════════════════════════════════════════════
    # WHAT SHOULD NOT BE IN SCOPE
    # ═══════════════════════════════════════════════════════

    scope['explicitly_excluded'] = [
        {
            'component': 'Third-party SaaS systems (Salesforce, Workday, etc.)',
            'reason': 'These vendors have their own SOC 2 reports — test your integration, not their system',
            'correct_approach': 'Review vendor SOC 2 reports as part of vendor management (CC9.2)'
        },
        {
            'component': 'Payment processor infrastructure (Stripe, Braintree)',
            'reason': 'PCI-DSS scoping exemption; testing the processor directly is not permitted',
            'correct_approach': 'Test your integration security, not the processor itself'
        },
        {
            'component': 'Other customers\\' data or systems',
            'reason': 'Never in scope — testing would itself be a SOC 2 control failure',
            'correct_approach': 'Use test accounts with synthetic data'
        }
    ]

    return scope
def scope_soc2_penetration_test(
    organization_profile: dict,
    tsc_categories: list,
    customer_data_flows: list
) -> dict:
    """
    Generate appropriate SOC 2 penetration test scope based on:
    - Which TSC categories are in scope for the audit
    - How customer data flows through the system
    - Risk profile of individual system components
    """

    scope = {
        'required_components': [],  # Must test for SOC 2 compliance
        'recommended_components': [],  # Strong recommendation
        'optional_components': [],  # At discretion based on risk
        'explicitly_excluded': [],  # Never test (third-party, etc.)
        'rationale': {}
    }

    # ═══════════════════════════════════════════════════════
    # SECURITY (CC) — ALWAYS IN SCOPE
    # ═══════════════════════════════════════════════════════

    # CC6.6 requires testing of all external-facing attack surface
    scope['required_components'].append({
        'component': 'External web application',
        'tsc_control': 'CC6.6',
        'test_types': [
            'Authentication bypass testing',
            'Authorization and access control testing',
            'Session management testing',
            'Input validation (injection testing)',
            'Business logic testing',
            'API security testing'
        ],
        'evidence_required': [
            'Findings report with CVSS scores',
            'Proof of concept for all confirmed findings',
            'Remediation evidence per finding'
        ]
    })

    scope['required_components'].append({
        'component': 'API layer (all authenticated endpoints)',
        'tsc_control': 'CC6.1, CC6.3, CC6.6',
        'test_types': [
            'Authentication enforcement',
            'Authorization (IDOR, BOLA)',
            'Rate limiting',
            'Input validation',
            'JWT/token security',
            'Mass assignment',
            'API versioning security'
        ],
        'evidence_required': [
            'Complete endpoint coverage documentation',
            'Authentication test results',
            'Authorization control verification'
        ]
    })

    scope['required_components'].append({
        'component': 'Network infrastructure (externally accessible)',
        'tsc_control': 'CC6.6',
        'test_types': [
            'Port scanning and service enumeration',
            'Service vulnerability assessment',
            'Firewall rule testing',
            'SSL/TLS configuration testing',
            'Open redirect testing',
            'CORS policy testing'
        ],
        'evidence_required': [
            'Network scan results',
            'Service enumeration report',
            'TLS configuration report'
        ]
    })

    # Customer data systems are always required
    for data_flow in customer_data_flows:
        if data_flow.get('contains_pii') or data_flow.get('is_primary_storage'):
            scope['required_components'].append({
                'component': f"Customer data system: {data_flow['name']}",
                'tsc_control': 'CC6.1, CC6.3, C1.1 (if confidentiality in scope)',
                'test_types': [
                    'Data access control testing',
                    'Unauthorized access attempts',
                    'SQL/NoSQL injection testing',
                    'Data exposure testing'
                ],
                'evidence_required': [
                    'Access control test results',
                    'Data exposure findings documentation'
                ]
            })

    # ═══════════════════════════════════════════════════════
    # CLOUD INFRASTRUCTURE — REQUIRED IF CLOUD-HOSTED
    # ═══════════════════════════════════════════════════════

    if organization_profile.get('cloud_provider'):
        scope['required_components'].append({
            'component': 'Cloud infrastructure security',
            'tsc_control': 'CC6.6, CC8.1',
            'test_types': [
                'IAM role and permission review',
                'S3/GCS/Blob storage access control',
                'Security group and network ACL review',
                'Secrets management review',
                'Logging and monitoring configuration'
            ],
            'evidence_required': [
                'Cloud security posture assessment',
                'IAM privilege audit results',
                'Storage access control verification'
            ]
        })

    # ═══════════════════════════════════════════════════════
    # AVAILABILITY (A1) — IF IN SCOPE
    # ═══════════════════════════════════════════════════════

    if 'availability' in tsc_categories:
        scope['recommended_components'].append({
            'component': 'Availability and resilience testing',
            'tsc_control': 'A1.2',
            'test_types': [
                'Rate limiting effectiveness',
                'Resource exhaustion testing (low intensity)',
                'Failover testing',
                'Recovery procedure testing'
            ],
            'note': 'DoS testing requires separate authorization and careful scoping'
        })

    # ═══════════════════════════════════════════════════════
    # WHAT SHOULD NOT BE IN SCOPE
    # ═══════════════════════════════════════════════════════

    scope['explicitly_excluded'] = [
        {
            'component': 'Third-party SaaS systems (Salesforce, Workday, etc.)',
            'reason': 'These vendors have their own SOC 2 reports — test your integration, not their system',
            'correct_approach': 'Review vendor SOC 2 reports as part of vendor management (CC9.2)'
        },
        {
            'component': 'Payment processor infrastructure (Stripe, Braintree)',
            'reason': 'PCI-DSS scoping exemption; testing the processor directly is not permitted',
            'correct_approach': 'Test your integration security, not the processor itself'
        },
        {
            'component': 'Other customers\\' data or systems',
            'reason': 'Never in scope — testing would itself be a SOC 2 control failure',
            'correct_approach': 'Use test accounts with synthetic data'
        }
    ]

    return scope

The SOC 2 Scope Decision Matrix

System Component

Always In Scope?

TSC Controls

Evidence Required

Notes

Public-facing web application

Yes

CC6.6

Full pentest report + retest

Primary requirement

Authenticated API (all endpoints)

Yes

CC6.1, CC6.3

Coverage map + auth test results

Document endpoint count

Admin panel/back office

Yes

CC6.1, CC6.3

Privilege escalation test results

High-value target

Customer data database

Yes

CC6.1, C1.1

Access control verification

Often tested via app

Cloud IAM and permissions

Yes (cloud-hosted)

CC6.6, CC8.1

IAM privilege audit

Cloud security review

Internal admin tools

Recommended

CC6.3

Internal access control testing

If accessible by employees

CI/CD pipeline

Recommended

CC8.1

Pipeline secrets review

High supply chain risk

Mobile applications

Recommended

CC6.6

Mobile app pentest

If customer-facing

Internal network (if on-prem)

Yes (on-prem)

CC6.6

Internal network assessment

N/A for pure SaaS

Staging/QA environments

No

N/A

N/A

Test production

Third-party SaaS tools

No

CC9.2

Vendor SOC 2 review

Review their reports

Employee workstations

No

CC6.6

Endpoint management evidence

Different control

[IMAGE PLACEHOLDER: Data flow diagram showing a typical SaaS application architecture. User → CDN → Load Balancer → Application Server → Database → Object Storage → Third-Party Services. Overlaid boxes showing which components are in scope (green borders: application server, load balancer, object storage, database) and which are out of scope (gray borders: CDN infrastructure owned by Cloudflare, third-party APIs). Arrow showing "test your side of the integration" for third-party connections. Annotations showing which TSC control each in-scope component satisfies.]

Part 5: The SOC 2 Penetration Test Report — What Auditors Need to See

The Gap Between a Good Security Report and a Good SOC 2 Evidence Report

A technically excellent penetration test report and a SOC 2-compliant penetration test report are not the same thing. Security reports are designed to tell engineers what to fix. SOC 2 evidence reports are designed to tell auditors that controls are working. These are different communication goals that require different document structures.

ELEMENTS REQUIRED IN A SOC 2 PENETRATION TEST REPORT:

1. TESTING FIRM QUALIFICATIONS
   ├── Firm name and relevant certifications (CREST, OSCP, CISSP, etc.)
   ├── Individual tester qualifications
   ├── Years of experience in the relevant domain
   └── Independence declaration (no conflicts of interest)

   Why auditors need this: CC7.1 requires "qualified" personnel conduct
   vulnerability assessment. The report must demonstrate qualification.

2. SCOPE STATEMENT (EXPLICIT)
   ├── Exact systems tested (IPs, URLs, account IDs)
   ├── Systems NOT tested and why (with acknowledgment of limitations)
   ├── Testing methodology (black box, gray box, white box)
   ├── Testing dates and duration
   └── Testing environment (production confirmed explicitly)

   Why auditors need this: Auditor must verify scope covered
   customer-data-processing systems. "Our web application" is insufficient.
   The report must specifically name the systems.

3. METHODOLOGY DESCRIPTION
   ├── Testing phases executed
   ├── Standards followed (OWASP, PTES, NIST SP 800-115)
   ├── Tools used (Burp Suite, Nmap, Metasploit, etc.)
   └── Manual vs automated component breakdown

   Why auditors need this: Demonstrates systematic coverage,
   not opportunistic testing.

4. FINDINGS — REQUIRED STRUCTURE PER FINDING
   ├── Unique finding identifier (for retest tracking)
   ├── Finding title
   ├── CVSS 4.0 score AND vector string
   ├── Severity classification (Critical/High/Medium/Low/Informational)
   ├── Affected system(s) — specific, not "the web application"
   ├── Technical description (enough for auditor to understand the issue)
   ├── Proof of concept (screenshot, HTTP request/response, command output)
   ├── Business impact (in plain language for the auditor)
   ├── Root cause classification (authentication, authorization, configuration, etc.)
   ├── Remediation recommendation (specific, actionable)
   └── Compliance mapping (which CC or other criterion this affects)

   SOC 2 specific addition: REMEDIATION STATUS FIELD
   Status: [Open / Remediated / Accepted Risk / Transferred]
   This field tracks the lifecycle for audit evidence purposes.

5. EXECUTIVE SUMMARY (NON-TECHNICAL)
   ├── Overall security posture assessment
   ├── Key risk areas identified
   ├── Highest severity findings summary
   └── Trend analysis (if re-engagement)

   Why auditors need this: Management review evidence for CC5.3

6. FINDINGS SUMMARY TABLE
   Required columns:
   Finding ID | Title | CVSS | Severity | Status | Remediation Date | Retest Date

   Why auditors need this: At-a-glance evidence for CC7.1
   Auditors will scan this table before reading any findings detail.
   If it's absent, they'll ask for it separately and note the gap.

7. COVERAGE ATTESTATION
   A statement from the testing firm that the scope covered
   [specific systems] and that limitations are documented.

   Example: "This assessment covered all external-facing systems
   processing customer data as of [date]. The systems listed in
   Section 3 represent the complete scope of customer-data-processing
   infrastructure as confirmed by [Organization]

ELEMENTS REQUIRED IN A SOC 2 PENETRATION TEST REPORT:

1. TESTING FIRM QUALIFICATIONS
   ├── Firm name and relevant certifications (CREST, OSCP, CISSP, etc.)
   ├── Individual tester qualifications
   ├── Years of experience in the relevant domain
   └── Independence declaration (no conflicts of interest)

   Why auditors need this: CC7.1 requires "qualified" personnel conduct
   vulnerability assessment. The report must demonstrate qualification.

2. SCOPE STATEMENT (EXPLICIT)
   ├── Exact systems tested (IPs, URLs, account IDs)
   ├── Systems NOT tested and why (with acknowledgment of limitations)
   ├── Testing methodology (black box, gray box, white box)
   ├── Testing dates and duration
   └── Testing environment (production confirmed explicitly)

   Why auditors need this: Auditor must verify scope covered
   customer-data-processing systems. "Our web application" is insufficient.
   The report must specifically name the systems.

3. METHODOLOGY DESCRIPTION
   ├── Testing phases executed
   ├── Standards followed (OWASP, PTES, NIST SP 800-115)
   ├── Tools used (Burp Suite, Nmap, Metasploit, etc.)
   └── Manual vs automated component breakdown

   Why auditors need this: Demonstrates systematic coverage,
   not opportunistic testing.

4. FINDINGS — REQUIRED STRUCTURE PER FINDING
   ├── Unique finding identifier (for retest tracking)
   ├── Finding title
   ├── CVSS 4.0 score AND vector string
   ├── Severity classification (Critical/High/Medium/Low/Informational)
   ├── Affected system(s) — specific, not "the web application"
   ├── Technical description (enough for auditor to understand the issue)
   ├── Proof of concept (screenshot, HTTP request/response, command output)
   ├── Business impact (in plain language for the auditor)
   ├── Root cause classification (authentication, authorization, configuration, etc.)
   ├── Remediation recommendation (specific, actionable)
   └── Compliance mapping (which CC or other criterion this affects)

   SOC 2 specific addition: REMEDIATION STATUS FIELD
   Status: [Open / Remediated / Accepted Risk / Transferred]
   This field tracks the lifecycle for audit evidence purposes.

5. EXECUTIVE SUMMARY (NON-TECHNICAL)
   ├── Overall security posture assessment
   ├── Key risk areas identified
   ├── Highest severity findings summary
   └── Trend analysis (if re-engagement)

   Why auditors need this: Management review evidence for CC5.3

6. FINDINGS SUMMARY TABLE
   Required columns:
   Finding ID | Title | CVSS | Severity | Status | Remediation Date | Retest Date

   Why auditors need this: At-a-glance evidence for CC7.1
   Auditors will scan this table before reading any findings detail.
   If it's absent, they'll ask for it separately and note the gap.

7. COVERAGE ATTESTATION
   A statement from the testing firm that the scope covered
   [specific systems] and that limitations are documented.

   Example: "This assessment covered all external-facing systems
   processing customer data as of [date]. The systems listed in
   Section 3 represent the complete scope of customer-data-processing
   infrastructure as confirmed by [Organization]

ELEMENTS REQUIRED IN A SOC 2 PENETRATION TEST REPORT:

1. TESTING FIRM QUALIFICATIONS
   ├── Firm name and relevant certifications (CREST, OSCP, CISSP, etc.)
   ├── Individual tester qualifications
   ├── Years of experience in the relevant domain
   └── Independence declaration (no conflicts of interest)

   Why auditors need this: CC7.1 requires "qualified" personnel conduct
   vulnerability assessment. The report must demonstrate qualification.

2. SCOPE STATEMENT (EXPLICIT)
   ├── Exact systems tested (IPs, URLs, account IDs)
   ├── Systems NOT tested and why (with acknowledgment of limitations)
   ├── Testing methodology (black box, gray box, white box)
   ├── Testing dates and duration
   └── Testing environment (production confirmed explicitly)

   Why auditors need this: Auditor must verify scope covered
   customer-data-processing systems. "Our web application" is insufficient.
   The report must specifically name the systems.

3. METHODOLOGY DESCRIPTION
   ├── Testing phases executed
   ├── Standards followed (OWASP, PTES, NIST SP 800-115)
   ├── Tools used (Burp Suite, Nmap, Metasploit, etc.)
   └── Manual vs automated component breakdown

   Why auditors need this: Demonstrates systematic coverage,
   not opportunistic testing.

4. FINDINGS — REQUIRED STRUCTURE PER FINDING
   ├── Unique finding identifier (for retest tracking)
   ├── Finding title
   ├── CVSS 4.0 score AND vector string
   ├── Severity classification (Critical/High/Medium/Low/Informational)
   ├── Affected system(s) — specific, not "the web application"
   ├── Technical description (enough for auditor to understand the issue)
   ├── Proof of concept (screenshot, HTTP request/response, command output)
   ├── Business impact (in plain language for the auditor)
   ├── Root cause classification (authentication, authorization, configuration, etc.)
   ├── Remediation recommendation (specific, actionable)
   └── Compliance mapping (which CC or other criterion this affects)

   SOC 2 specific addition: REMEDIATION STATUS FIELD
   Status: [Open / Remediated / Accepted Risk / Transferred]
   This field tracks the lifecycle for audit evidence purposes.

5. EXECUTIVE SUMMARY (NON-TECHNICAL)
   ├── Overall security posture assessment
   ├── Key risk areas identified
   ├── Highest severity findings summary
   └── Trend analysis (if re-engagement)

   Why auditors need this: Management review evidence for CC5.3

6. FINDINGS SUMMARY TABLE
   Required columns:
   Finding ID | Title | CVSS | Severity | Status | Remediation Date | Retest Date

   Why auditors need this: At-a-glance evidence for CC7.1
   Auditors will scan this table before reading any findings detail.
   If it's absent, they'll ask for it separately and note the gap.

7. COVERAGE ATTESTATION
   A statement from the testing firm that the scope covered
   [specific systems] and that limitations are documented.

   Example: "This assessment covered all external-facing systems
   processing customer data as of [date]. The systems listed in
   Section 3 represent the complete scope of customer-data-processing
   infrastructure as confirmed by [Organization]

The Retest Report — The Most Underspecified Document

The retest report is the document auditors are most likely to cite as missing or insufficient. Here is the minimum structure:

SOC 2 RETEST REPORT REQUIREMENTS:

HEADER INFORMATION:
  Original Engagement Reference: [ID]
  Original Test Date: [DATE]
  Retest Date: [DATE]
  Testing Environment: Production (MUST BE EXPLICIT)
  Production Version at Retest: [Version/Commit/Build ID]
  Retest Performed By: [Names — same firm as original]

EXECUTIVE SUMMARY:
  Total original findings: N
  Findings retested: N (must equal total — cannot skip)
  Status: X Remediated, Y Partially Remediated, Z Not Remediated

CVSS DELTA:
  Original aggregate risk: [Score]
  Post-remediation aggregate risk: [Score]
  Risk reduction: [Percentage]

  This section is increasingly expected by sophisticated auditors
  as a quantitative measure of control improvement.

PER-FINDING RETEST RESULTS:
  Finding ID: [ID matching original report]
  Original Finding: [Title]
  Original CVSS: [Score]
  Remediation Applied: [Summary of what was changed]
  Deployment Confirmation: [Evidence that fix is in production]
  Retest Method: [What was tested to verify]
  Retest Result: [What was observed]
  Status: REMEDIATED | PARTIALLY_REMEDIATED | NOT_REMEDIATED
  New CVSS: [Score if not fully remediated, 0.0 if remediated]

  Auditor note: The retest result must show the SPECIFIC HTTP request
  or command that was executed and the SPECIFIC response observed.
  "The finding was confirmed remediated" is insufficient.
  "GET /api/users/43 returned 403 Forbidden (previously returned 200 OK
   with user data from account 43)" is sufficient.

OPEN FINDINGS SECTION:
  For any finding NOT remediated:
  Finding ID: [ID]
  Reason Not Remediated: [Technical explanation]
  Risk Acceptance: [Documented? Yes/No]
  Risk Owner: [Name and title]
  Target Remediation Date: [Date]
  Compensating Controls: [What mitigates risk in the interim]

SOC 2 RETEST REPORT REQUIREMENTS:

HEADER INFORMATION:
  Original Engagement Reference: [ID]
  Original Test Date: [DATE]
  Retest Date: [DATE]
  Testing Environment: Production (MUST BE EXPLICIT)
  Production Version at Retest: [Version/Commit/Build ID]
  Retest Performed By: [Names — same firm as original]

EXECUTIVE SUMMARY:
  Total original findings: N
  Findings retested: N (must equal total — cannot skip)
  Status: X Remediated, Y Partially Remediated, Z Not Remediated

CVSS DELTA:
  Original aggregate risk: [Score]
  Post-remediation aggregate risk: [Score]
  Risk reduction: [Percentage]

  This section is increasingly expected by sophisticated auditors
  as a quantitative measure of control improvement.

PER-FINDING RETEST RESULTS:
  Finding ID: [ID matching original report]
  Original Finding: [Title]
  Original CVSS: [Score]
  Remediation Applied: [Summary of what was changed]
  Deployment Confirmation: [Evidence that fix is in production]
  Retest Method: [What was tested to verify]
  Retest Result: [What was observed]
  Status: REMEDIATED | PARTIALLY_REMEDIATED | NOT_REMEDIATED
  New CVSS: [Score if not fully remediated, 0.0 if remediated]

  Auditor note: The retest result must show the SPECIFIC HTTP request
  or command that was executed and the SPECIFIC response observed.
  "The finding was confirmed remediated" is insufficient.
  "GET /api/users/43 returned 403 Forbidden (previously returned 200 OK
   with user data from account 43)" is sufficient.

OPEN FINDINGS SECTION:
  For any finding NOT remediated:
  Finding ID: [ID]
  Reason Not Remediated: [Technical explanation]
  Risk Acceptance: [Documented? Yes/No]
  Risk Owner: [Name and title]
  Target Remediation Date: [Date]
  Compensating Controls: [What mitigates risk in the interim]

SOC 2 RETEST REPORT REQUIREMENTS:

HEADER INFORMATION:
  Original Engagement Reference: [ID]
  Original Test Date: [DATE]
  Retest Date: [DATE]
  Testing Environment: Production (MUST BE EXPLICIT)
  Production Version at Retest: [Version/Commit/Build ID]
  Retest Performed By: [Names — same firm as original]

EXECUTIVE SUMMARY:
  Total original findings: N
  Findings retested: N (must equal total — cannot skip)
  Status: X Remediated, Y Partially Remediated, Z Not Remediated

CVSS DELTA:
  Original aggregate risk: [Score]
  Post-remediation aggregate risk: [Score]
  Risk reduction: [Percentage]

  This section is increasingly expected by sophisticated auditors
  as a quantitative measure of control improvement.

PER-FINDING RETEST RESULTS:
  Finding ID: [ID matching original report]
  Original Finding: [Title]
  Original CVSS: [Score]
  Remediation Applied: [Summary of what was changed]
  Deployment Confirmation: [Evidence that fix is in production]
  Retest Method: [What was tested to verify]
  Retest Result: [What was observed]
  Status: REMEDIATED | PARTIALLY_REMEDIATED | NOT_REMEDIATED
  New CVSS: [Score if not fully remediated, 0.0 if remediated]

  Auditor note: The retest result must show the SPECIFIC HTTP request
  or command that was executed and the SPECIFIC response observed.
  "The finding was confirmed remediated" is insufficient.
  "GET /api/users/43 returned 403 Forbidden (previously returned 200 OK
   with user data from account 43)" is sufficient.

OPEN FINDINGS SECTION:
  For any finding NOT remediated:
  Finding ID: [ID]
  Reason Not Remediated: [Technical explanation]
  Risk Acceptance: [Documented? Yes/No]
  Risk Owner: [Name and title]
  Target Remediation Date: [Date]
  Compensating Controls: [What mitigates risk in the interim]

Part 6: Building the Complete SOC 2 Evidence Package

The Evidence Package Structure

The evidence package is the collection of documents you provide to auditors. It must be organized, navigable, and complete:

SOC 2 PENETRATION TESTING EVIDENCE PACKAGE STRUCTURE:

FOLDER 1: POLICY AND PROCEDURE
  ├── penetration_testing_policy_v[X]_[DATE].pdf
  │     Required contents:
  │     □ Testing frequency (annual minimum)
  │     □ Who is responsible for commissioning tests
  │     □ Scope definition process
  │     □ Qualified vendor requirements
  │     □ Finding SLA (critical, high, medium, low)
  │     □ Remediation verification requirements
  │     □ Risk acceptance process
  │     □ Evidence retention period
  │     □ Approval signatures (CISO or equivalent)
  │
  ├── vulnerability_management_policy_v[X]_[DATE].pdf
  │     The SLA table that auditors will test:
  │     Critical: 48 hours
  │     High: 7 days
  │     Medium: 30 days
  │     Low: 90 days
  │     (Your actual SLAs — must be met, not aspirational)
  │
  └── penetration_testing_vendor_qualification_criteria.pdf
        Evidence that the firm used meets your qualification standards

FOLDER 2: CURRENT PERIOD PENETRATION TEST
  ├── engagement_authorization_letter_signed.pdf
  │     Proof of authorized testing
  │
  ├── penetration_test_report_[FIRM]_[DATE].pdf
  │     The complete finding report
  │
  ├── pentest_scope_confirmation.pdf
  │     Confirmation that scope covered customer data systems
  │
  └── pentest_executive_briefing_[DATE].pdf
        Evidence that management reviewed findings (CC5.3)

FOLDER 3: REMEDIATION EVIDENCE
  ├── remediation_tracking_log.xlsx
  │     Columns: Finding ID | Title | CVSS | Discovery Date |
  │     Assigned To | Remediation Date | PR/Ticket | Deployed Date | Status
  │     This is the SLA compliance evidence auditors check
  │
  ├── per_finding_remediation/
  │   ├── FIND-001_remediation_evidence.pdf
  │   │   Contains: Code diff or config change + deployment proof
  │   ├── FIND-002_remediation_evidence.pdf
  │   └── [one file per finding]
  │
  └── risk_acceptance_register.pdf
        Signed risk acceptances for any unremediated findings
        Required: Finding description, risk owner name/title,
        acceptance date, compensating controls, review date

FOLDER 4: RETEST EVIDENCE
  ├── retest_report_[FIRM]_[DATE].pdf
  │     Complete retest with per-finding verification
  │
  └── production_deployment_confirmation.pdf
        Evidence that remediations are in production
        (deployment log, version confirmation, commit hash)

FOLDER 5: HISTORICAL EVIDENCE (Type II requirement)
  ├── prior_period_pentest_report.pdf
  │     Shows continuous testing across observation period
  │
  └── prior_period_remediation_log.pdf
        Shows findings from previous period were addressed

FOLDER 6: SUPPORTING EVIDENCE
  ├── vendor_qualification_evidence/
  │   ├── [Testing firm] CREST certification.pdf
  │   ├── [Testing firm] professional liability insurance.pdf
  │   └── [Testing firm] qualifications statement.pdf
  │
  └── management_review_evidence/
      ├── security_committee_meeting_minutes_[DATE].pdf
      │     Minutes showing pentest findings were reviewed by management
      └── board_security_briefing_[DATE]

SOC 2 PENETRATION TESTING EVIDENCE PACKAGE STRUCTURE:

FOLDER 1: POLICY AND PROCEDURE
  ├── penetration_testing_policy_v[X]_[DATE].pdf
  │     Required contents:
  │     □ Testing frequency (annual minimum)
  │     □ Who is responsible for commissioning tests
  │     □ Scope definition process
  │     □ Qualified vendor requirements
  │     □ Finding SLA (critical, high, medium, low)
  │     □ Remediation verification requirements
  │     □ Risk acceptance process
  │     □ Evidence retention period
  │     □ Approval signatures (CISO or equivalent)
  │
  ├── vulnerability_management_policy_v[X]_[DATE].pdf
  │     The SLA table that auditors will test:
  │     Critical: 48 hours
  │     High: 7 days
  │     Medium: 30 days
  │     Low: 90 days
  │     (Your actual SLAs — must be met, not aspirational)
  │
  └── penetration_testing_vendor_qualification_criteria.pdf
        Evidence that the firm used meets your qualification standards

FOLDER 2: CURRENT PERIOD PENETRATION TEST
  ├── engagement_authorization_letter_signed.pdf
  │     Proof of authorized testing
  │
  ├── penetration_test_report_[FIRM]_[DATE].pdf
  │     The complete finding report
  │
  ├── pentest_scope_confirmation.pdf
  │     Confirmation that scope covered customer data systems
  │
  └── pentest_executive_briefing_[DATE].pdf
        Evidence that management reviewed findings (CC5.3)

FOLDER 3: REMEDIATION EVIDENCE
  ├── remediation_tracking_log.xlsx
  │     Columns: Finding ID | Title | CVSS | Discovery Date |
  │     Assigned To | Remediation Date | PR/Ticket | Deployed Date | Status
  │     This is the SLA compliance evidence auditors check
  │
  ├── per_finding_remediation/
  │   ├── FIND-001_remediation_evidence.pdf
  │   │   Contains: Code diff or config change + deployment proof
  │   ├── FIND-002_remediation_evidence.pdf
  │   └── [one file per finding]
  │
  └── risk_acceptance_register.pdf
        Signed risk acceptances for any unremediated findings
        Required: Finding description, risk owner name/title,
        acceptance date, compensating controls, review date

FOLDER 4: RETEST EVIDENCE
  ├── retest_report_[FIRM]_[DATE].pdf
  │     Complete retest with per-finding verification
  │
  └── production_deployment_confirmation.pdf
        Evidence that remediations are in production
        (deployment log, version confirmation, commit hash)

FOLDER 5: HISTORICAL EVIDENCE (Type II requirement)
  ├── prior_period_pentest_report.pdf
  │     Shows continuous testing across observation period
  │
  └── prior_period_remediation_log.pdf
        Shows findings from previous period were addressed

FOLDER 6: SUPPORTING EVIDENCE
  ├── vendor_qualification_evidence/
  │   ├── [Testing firm] CREST certification.pdf
  │   ├── [Testing firm] professional liability insurance.pdf
  │   └── [Testing firm] qualifications statement.pdf
  │
  └── management_review_evidence/
      ├── security_committee_meeting_minutes_[DATE].pdf
      │     Minutes showing pentest findings were reviewed by management
      └── board_security_briefing_[DATE]

SOC 2 PENETRATION TESTING EVIDENCE PACKAGE STRUCTURE:

FOLDER 1: POLICY AND PROCEDURE
  ├── penetration_testing_policy_v[X]_[DATE].pdf
  │     Required contents:
  │     □ Testing frequency (annual minimum)
  │     □ Who is responsible for commissioning tests
  │     □ Scope definition process
  │     □ Qualified vendor requirements
  │     □ Finding SLA (critical, high, medium, low)
  │     □ Remediation verification requirements
  │     □ Risk acceptance process
  │     □ Evidence retention period
  │     □ Approval signatures (CISO or equivalent)
  │
  ├── vulnerability_management_policy_v[X]_[DATE].pdf
  │     The SLA table that auditors will test:
  │     Critical: 48 hours
  │     High: 7 days
  │     Medium: 30 days
  │     Low: 90 days
  │     (Your actual SLAs — must be met, not aspirational)
  │
  └── penetration_testing_vendor_qualification_criteria.pdf
        Evidence that the firm used meets your qualification standards

FOLDER 2: CURRENT PERIOD PENETRATION TEST
  ├── engagement_authorization_letter_signed.pdf
  │     Proof of authorized testing
  │
  ├── penetration_test_report_[FIRM]_[DATE].pdf
  │     The complete finding report
  │
  ├── pentest_scope_confirmation.pdf
  │     Confirmation that scope covered customer data systems
  │
  └── pentest_executive_briefing_[DATE].pdf
        Evidence that management reviewed findings (CC5.3)

FOLDER 3: REMEDIATION EVIDENCE
  ├── remediation_tracking_log.xlsx
  │     Columns: Finding ID | Title | CVSS | Discovery Date |
  │     Assigned To | Remediation Date | PR/Ticket | Deployed Date | Status
  │     This is the SLA compliance evidence auditors check
  │
  ├── per_finding_remediation/
  │   ├── FIND-001_remediation_evidence.pdf
  │   │   Contains: Code diff or config change + deployment proof
  │   ├── FIND-002_remediation_evidence.pdf
  │   └── [one file per finding]
  │
  └── risk_acceptance_register.pdf
        Signed risk acceptances for any unremediated findings
        Required: Finding description, risk owner name/title,
        acceptance date, compensating controls, review date

FOLDER 4: RETEST EVIDENCE
  ├── retest_report_[FIRM]_[DATE].pdf
  │     Complete retest with per-finding verification
  │
  └── production_deployment_confirmation.pdf
        Evidence that remediations are in production
        (deployment log, version confirmation, commit hash)

FOLDER 5: HISTORICAL EVIDENCE (Type II requirement)
  ├── prior_period_pentest_report.pdf
  │     Shows continuous testing across observation period
  │
  └── prior_period_remediation_log.pdf
        Shows findings from previous period were addressed

FOLDER 6: SUPPORTING EVIDENCE
  ├── vendor_qualification_evidence/
  │   ├── [Testing firm] CREST certification.pdf
  │   ├── [Testing firm] professional liability insurance.pdf
  │   └── [Testing firm] qualifications statement.pdf
  │
  └── management_review_evidence/
      ├── security_committee_meeting_minutes_[DATE].pdf
      │     Minutes showing pentest findings were reviewed by management
      └── board_security_briefing_[DATE]

The Remediation SLA Compliance Evidence

This is where most companies fail their Type II audit without realizing it. The auditor doesn't just check that findings were remediated — they check whether they were remediated within the stated SLA.

import json
from datetime import datetime, timedelta
from typing import List, Dict, Optional

def verify_sla_compliance(
    findings: List[Dict],
    sla_policy: Dict,  # {'CRITICAL': 2, 'HIGH': 7, 'MEDIUM': 30, 'LOW': 90}
    audit_period_start: datetime,
    audit_period_end: datetime
) -> Dict:
    """
    Verify penetration test finding SLA compliance for SOC 2 audit evidence.
    Produces the evidence document auditors will examine.
    """

    results = {
        'audit_period': {
            'start': audit_period_start.isoformat(),
            'end': audit_period_end.isoformat()
        },
        'sla_policy': sla_policy,
        'findings_analysis': [],
        'compliance_summary': {},
        'exceptions': [],
        'audit_ready': False
    }

    compliance_by_severity = {severity: {'total': 0, 'within_sla': 0, 'breached': 0}
                               for severity in sla_policy}

    for finding in findings:
        finding_id = finding['id']
        severity = finding['severity'].upper()
        discovery_date = datetime.fromisoformat(finding['discovery_date'])
        remediation_date = finding.get('remediation_date')
        retest_confirmed = finding.get('retest_confirmed', False)
        retest_date = finding.get('retest_date')
        status = finding.get('status', 'OPEN')

        # Skip findings outside audit period
        if discovery_date < audit_period_start or discovery_date > audit_period_end:
            continue

        sla_days = sla_policy.get(severity, 90)
        sla_deadline = discovery_date + timedelta(days=sla_days)

        analysis = {
            'finding_id': finding_id,
            'title': finding.get('title'),
            'severity': severity,
            'cvss': finding.get('cvss'),
            'discovery_date': discovery_date.isoformat(),
            'sla_days': sla_days,
            'sla_deadline': sla_deadline.isoformat(),
            'status': status,
        }

        if status == 'REMEDIATED' and remediation_date:
            rem_date = datetime.fromisoformat(remediation_date)
            days_to_remediate = (rem_date - discovery_date).days
            within_sla = rem_date <= sla_deadline

            analysis.update({
                'remediation_date': remediation_date,
                'days_to_remediate': days_to_remediate,
                'within_sla': within_sla,
                'retest_confirmed': retest_confirmed,
                'retest_date': retest_date,
                'audit_evidence_status': (
                    'COMPLETE' if within_sla and retest_confirmed
                    else 'INCOMPLETE_MISSING_RETEST' if within_sla and not retest_confirmed
                    else 'SLA_BREACH'
                )
            })

            if severity in compliance_by_severity:
                compliance_by_severity[severity]['total'] += 1
                if within_sla:
                    compliance_by_severity[severity]['within_sla'] += 1
                else:
                    compliance_by_severity[severity]['breached'] += 1

                    results['exceptions'].append({
                        'finding_id': finding_id,
                        'severity': severity,
                        'days_over_sla': days_to_remediate - sla_days,
                        'exception_type': 'SLA_BREACH',
                        'auditor_risk': 'HIGH — auditor will flag this as control exception',
                        'mitigation': 'Document root cause and corrective action'
                    })

        elif status == 'ACCEPTED_RISK':
            risk_acceptance = finding.get('risk_acceptance', {})
            analysis.update({
                'risk_acceptance_date': risk_acceptance.get('date'),
                'risk_owner': risk_acceptance.get('owner'),
                'risk_owner_title': risk_acceptance.get('owner_title'),
                'compensating_controls': risk_acceptance.get('compensating_controls'),
                'review_date': risk_acceptance.get('review_date'),
                'audit_evidence_status': (
                    'COMPLETE' if all([
                        risk_acceptance.get('date'),
                        risk_acceptance.get('owner'),
                        risk_acceptance.get('compensating_controls')
                    ])
                    else 'INCOMPLETE_RISK_ACCEPTANCE'
                )
            })

            if not all([risk_acceptance.get('date'),
                        risk_acceptance.get('owner'),
                        risk_acceptance.get('compensating_controls')]):
                results['exceptions'].append({
                    'finding_id': finding_id,
                    'severity': severity,
                    'exception_type': 'INCOMPLETE_RISK_ACCEPTANCE',
                    'auditor_risk': 'HIGH — risk acceptance without proper documentation',
                    'mitigation': 'Complete risk acceptance form with owner signature'
                })

        elif status == 'OPEN':
            days_open = (datetime.now() - discovery_date).days
            days_past_sla = max(0, days_open - sla_days)

            analysis.update({
                'days_open': days_open,
                'days_past_sla': days_past_sla,
                'audit_evidence_status': 'SLA_BREACH' if days_past_sla > 0 else 'WITHIN_SLA',
            })

            if days_past_sla > 0:
                results['exceptions'].append({
                    'finding_id': finding_id,
                    'severity': severity,
                    'days_past_sla': days_past_sla,
                    'exception_type': 'OPEN_PAST_SLA',
                    'auditor_risk': 'CRITICAL — open finding past SLA is a direct control failure',
                    'mitigation': 'Remediate immediately or obtain signed risk acceptance'
                })

        results['findings_analysis'].append(analysis)

    # Build compliance summary
    for severity, counts in compliance_by_severity.items():
        if counts['total'] > 0:
            compliance_rate = counts['within_sla'] / counts['total'] * 100
            results['compliance_summary'][severity] = {
                'total_findings': counts['total'],
                'within_sla': counts['within_sla'],
                'sla_breached': counts['breached'],
                'compliance_rate': f"{compliance_rate:.1f}%",
                'status': 'PASS' if compliance_rate == 100 else
                          'ACCEPTABLE' if compliance_rate >= 90 else
                          'FAIL'
            }

    # Determine overall audit readiness
    critical_exceptions = [e for e in results['exceptions']
                           if e.get('auditor_risk') in ['CRITICAL', 'HIGH']]

    results['audit_ready'] = len(critical_exceptions) == 0
    results['exception_count'] = len(results['exceptions'])
    results['critical_exception_count'] = len(critical_exceptions)

    return results
import json
from datetime import datetime, timedelta
from typing import List, Dict, Optional

def verify_sla_compliance(
    findings: List[Dict],
    sla_policy: Dict,  # {'CRITICAL': 2, 'HIGH': 7, 'MEDIUM': 30, 'LOW': 90}
    audit_period_start: datetime,
    audit_period_end: datetime
) -> Dict:
    """
    Verify penetration test finding SLA compliance for SOC 2 audit evidence.
    Produces the evidence document auditors will examine.
    """

    results = {
        'audit_period': {
            'start': audit_period_start.isoformat(),
            'end': audit_period_end.isoformat()
        },
        'sla_policy': sla_policy,
        'findings_analysis': [],
        'compliance_summary': {},
        'exceptions': [],
        'audit_ready': False
    }

    compliance_by_severity = {severity: {'total': 0, 'within_sla': 0, 'breached': 0}
                               for severity in sla_policy}

    for finding in findings:
        finding_id = finding['id']
        severity = finding['severity'].upper()
        discovery_date = datetime.fromisoformat(finding['discovery_date'])
        remediation_date = finding.get('remediation_date')
        retest_confirmed = finding.get('retest_confirmed', False)
        retest_date = finding.get('retest_date')
        status = finding.get('status', 'OPEN')

        # Skip findings outside audit period
        if discovery_date < audit_period_start or discovery_date > audit_period_end:
            continue

        sla_days = sla_policy.get(severity, 90)
        sla_deadline = discovery_date + timedelta(days=sla_days)

        analysis = {
            'finding_id': finding_id,
            'title': finding.get('title'),
            'severity': severity,
            'cvss': finding.get('cvss'),
            'discovery_date': discovery_date.isoformat(),
            'sla_days': sla_days,
            'sla_deadline': sla_deadline.isoformat(),
            'status': status,
        }

        if status == 'REMEDIATED' and remediation_date:
            rem_date = datetime.fromisoformat(remediation_date)
            days_to_remediate = (rem_date - discovery_date).days
            within_sla = rem_date <= sla_deadline

            analysis.update({
                'remediation_date': remediation_date,
                'days_to_remediate': days_to_remediate,
                'within_sla': within_sla,
                'retest_confirmed': retest_confirmed,
                'retest_date': retest_date,
                'audit_evidence_status': (
                    'COMPLETE' if within_sla and retest_confirmed
                    else 'INCOMPLETE_MISSING_RETEST' if within_sla and not retest_confirmed
                    else 'SLA_BREACH'
                )
            })

            if severity in compliance_by_severity:
                compliance_by_severity[severity]['total'] += 1
                if within_sla:
                    compliance_by_severity[severity]['within_sla'] += 1
                else:
                    compliance_by_severity[severity]['breached'] += 1

                    results['exceptions'].append({
                        'finding_id': finding_id,
                        'severity': severity,
                        'days_over_sla': days_to_remediate - sla_days,
                        'exception_type': 'SLA_BREACH',
                        'auditor_risk': 'HIGH — auditor will flag this as control exception',
                        'mitigation': 'Document root cause and corrective action'
                    })

        elif status == 'ACCEPTED_RISK':
            risk_acceptance = finding.get('risk_acceptance', {})
            analysis.update({
                'risk_acceptance_date': risk_acceptance.get('date'),
                'risk_owner': risk_acceptance.get('owner'),
                'risk_owner_title': risk_acceptance.get('owner_title'),
                'compensating_controls': risk_acceptance.get('compensating_controls'),
                'review_date': risk_acceptance.get('review_date'),
                'audit_evidence_status': (
                    'COMPLETE' if all([
                        risk_acceptance.get('date'),
                        risk_acceptance.get('owner'),
                        risk_acceptance.get('compensating_controls')
                    ])
                    else 'INCOMPLETE_RISK_ACCEPTANCE'
                )
            })

            if not all([risk_acceptance.get('date'),
                        risk_acceptance.get('owner'),
                        risk_acceptance.get('compensating_controls')]):
                results['exceptions'].append({
                    'finding_id': finding_id,
                    'severity': severity,
                    'exception_type': 'INCOMPLETE_RISK_ACCEPTANCE',
                    'auditor_risk': 'HIGH — risk acceptance without proper documentation',
                    'mitigation': 'Complete risk acceptance form with owner signature'
                })

        elif status == 'OPEN':
            days_open = (datetime.now() - discovery_date).days
            days_past_sla = max(0, days_open - sla_days)

            analysis.update({
                'days_open': days_open,
                'days_past_sla': days_past_sla,
                'audit_evidence_status': 'SLA_BREACH' if days_past_sla > 0 else 'WITHIN_SLA',
            })

            if days_past_sla > 0:
                results['exceptions'].append({
                    'finding_id': finding_id,
                    'severity': severity,
                    'days_past_sla': days_past_sla,
                    'exception_type': 'OPEN_PAST_SLA',
                    'auditor_risk': 'CRITICAL — open finding past SLA is a direct control failure',
                    'mitigation': 'Remediate immediately or obtain signed risk acceptance'
                })

        results['findings_analysis'].append(analysis)

    # Build compliance summary
    for severity, counts in compliance_by_severity.items():
        if counts['total'] > 0:
            compliance_rate = counts['within_sla'] / counts['total'] * 100
            results['compliance_summary'][severity] = {
                'total_findings': counts['total'],
                'within_sla': counts['within_sla'],
                'sla_breached': counts['breached'],
                'compliance_rate': f"{compliance_rate:.1f}%",
                'status': 'PASS' if compliance_rate == 100 else
                          'ACCEPTABLE' if compliance_rate >= 90 else
                          'FAIL'
            }

    # Determine overall audit readiness
    critical_exceptions = [e for e in results['exceptions']
                           if e.get('auditor_risk') in ['CRITICAL', 'HIGH']]

    results['audit_ready'] = len(critical_exceptions) == 0
    results['exception_count'] = len(results['exceptions'])
    results['critical_exception_count'] = len(critical_exceptions)

    return results
import json
from datetime import datetime, timedelta
from typing import List, Dict, Optional

def verify_sla_compliance(
    findings: List[Dict],
    sla_policy: Dict,  # {'CRITICAL': 2, 'HIGH': 7, 'MEDIUM': 30, 'LOW': 90}
    audit_period_start: datetime,
    audit_period_end: datetime
) -> Dict:
    """
    Verify penetration test finding SLA compliance for SOC 2 audit evidence.
    Produces the evidence document auditors will examine.
    """

    results = {
        'audit_period': {
            'start': audit_period_start.isoformat(),
            'end': audit_period_end.isoformat()
        },
        'sla_policy': sla_policy,
        'findings_analysis': [],
        'compliance_summary': {},
        'exceptions': [],
        'audit_ready': False
    }

    compliance_by_severity = {severity: {'total': 0, 'within_sla': 0, 'breached': 0}
                               for severity in sla_policy}

    for finding in findings:
        finding_id = finding['id']
        severity = finding['severity'].upper()
        discovery_date = datetime.fromisoformat(finding['discovery_date'])
        remediation_date = finding.get('remediation_date')
        retest_confirmed = finding.get('retest_confirmed', False)
        retest_date = finding.get('retest_date')
        status = finding.get('status', 'OPEN')

        # Skip findings outside audit period
        if discovery_date < audit_period_start or discovery_date > audit_period_end:
            continue

        sla_days = sla_policy.get(severity, 90)
        sla_deadline = discovery_date + timedelta(days=sla_days)

        analysis = {
            'finding_id': finding_id,
            'title': finding.get('title'),
            'severity': severity,
            'cvss': finding.get('cvss'),
            'discovery_date': discovery_date.isoformat(),
            'sla_days': sla_days,
            'sla_deadline': sla_deadline.isoformat(),
            'status': status,
        }

        if status == 'REMEDIATED' and remediation_date:
            rem_date = datetime.fromisoformat(remediation_date)
            days_to_remediate = (rem_date - discovery_date).days
            within_sla = rem_date <= sla_deadline

            analysis.update({
                'remediation_date': remediation_date,
                'days_to_remediate': days_to_remediate,
                'within_sla': within_sla,
                'retest_confirmed': retest_confirmed,
                'retest_date': retest_date,
                'audit_evidence_status': (
                    'COMPLETE' if within_sla and retest_confirmed
                    else 'INCOMPLETE_MISSING_RETEST' if within_sla and not retest_confirmed
                    else 'SLA_BREACH'
                )
            })

            if severity in compliance_by_severity:
                compliance_by_severity[severity]['total'] += 1
                if within_sla:
                    compliance_by_severity[severity]['within_sla'] += 1
                else:
                    compliance_by_severity[severity]['breached'] += 1

                    results['exceptions'].append({
                        'finding_id': finding_id,
                        'severity': severity,
                        'days_over_sla': days_to_remediate - sla_days,
                        'exception_type': 'SLA_BREACH',
                        'auditor_risk': 'HIGH — auditor will flag this as control exception',
                        'mitigation': 'Document root cause and corrective action'
                    })

        elif status == 'ACCEPTED_RISK':
            risk_acceptance = finding.get('risk_acceptance', {})
            analysis.update({
                'risk_acceptance_date': risk_acceptance.get('date'),
                'risk_owner': risk_acceptance.get('owner'),
                'risk_owner_title': risk_acceptance.get('owner_title'),
                'compensating_controls': risk_acceptance.get('compensating_controls'),
                'review_date': risk_acceptance.get('review_date'),
                'audit_evidence_status': (
                    'COMPLETE' if all([
                        risk_acceptance.get('date'),
                        risk_acceptance.get('owner'),
                        risk_acceptance.get('compensating_controls')
                    ])
                    else 'INCOMPLETE_RISK_ACCEPTANCE'
                )
            })

            if not all([risk_acceptance.get('date'),
                        risk_acceptance.get('owner'),
                        risk_acceptance.get('compensating_controls')]):
                results['exceptions'].append({
                    'finding_id': finding_id,
                    'severity': severity,
                    'exception_type': 'INCOMPLETE_RISK_ACCEPTANCE',
                    'auditor_risk': 'HIGH — risk acceptance without proper documentation',
                    'mitigation': 'Complete risk acceptance form with owner signature'
                })

        elif status == 'OPEN':
            days_open = (datetime.now() - discovery_date).days
            days_past_sla = max(0, days_open - sla_days)

            analysis.update({
                'days_open': days_open,
                'days_past_sla': days_past_sla,
                'audit_evidence_status': 'SLA_BREACH' if days_past_sla > 0 else 'WITHIN_SLA',
            })

            if days_past_sla > 0:
                results['exceptions'].append({
                    'finding_id': finding_id,
                    'severity': severity,
                    'days_past_sla': days_past_sla,
                    'exception_type': 'OPEN_PAST_SLA',
                    'auditor_risk': 'CRITICAL — open finding past SLA is a direct control failure',
                    'mitigation': 'Remediate immediately or obtain signed risk acceptance'
                })

        results['findings_analysis'].append(analysis)

    # Build compliance summary
    for severity, counts in compliance_by_severity.items():
        if counts['total'] > 0:
            compliance_rate = counts['within_sla'] / counts['total'] * 100
            results['compliance_summary'][severity] = {
                'total_findings': counts['total'],
                'within_sla': counts['within_sla'],
                'sla_breached': counts['breached'],
                'compliance_rate': f"{compliance_rate:.1f}%",
                'status': 'PASS' if compliance_rate == 100 else
                          'ACCEPTABLE' if compliance_rate >= 90 else
                          'FAIL'
            }

    # Determine overall audit readiness
    critical_exceptions = [e for e in results['exceptions']
                           if e.get('auditor_risk') in ['CRITICAL', 'HIGH']]

    results['audit_ready'] = len(critical_exceptions) == 0
    results['exception_count'] = len(results['exceptions'])
    results['critical_exception_count'] = len(critical_exceptions)

    return results

Part 7: What SOC 2 Auditors Actually Ask — The Real Questions

The Auditor Interview Script

Different auditor firms ask different questions, but experienced SOC 2 auditors follow a predictable pattern. Knowing these questions in advance allows you to prepare complete answers rather than scrambling for documentation.

AUDITOR QUESTIONS AND WHAT THEY'RE REALLY ASKING:

Q: "Do you have a penetration testing policy?"
   What they want: Written policy document, approved by management
   What fails: "Yes, we do penetration testing" without a formal document
   Perfect answer: Present the policy document with approval signatures and date

Q: "When was your last penetration test conducted?"
   What they want: Date within the observation period
   What fails: "6 months before the period started" or "we're scheduling it now"
   Perfect answer: [Date within period] by [Named firm], report reference [ID]

AUDITOR QUESTIONS AND WHAT THEY'RE REALLY ASKING:

Q: "Do you have a penetration testing policy?"
   What they want: Written policy document, approved by management
   What fails: "Yes, we do penetration testing" without a formal document
   Perfect answer: Present the policy document with approval signatures and date

Q: "When was your last penetration test conducted?"
   What they want: Date within the observation period
   What fails: "6 months before the period started" or "we're scheduling it now"
   Perfect answer: [Date within period] by [Named firm], report reference [ID]

AUDITOR QUESTIONS AND WHAT THEY'RE REALLY ASKING:

Q: "Do you have a penetration testing policy?"
   What they want: Written policy document, approved by management
   What fails: "Yes, we do penetration testing" without a formal document
   Perfect answer: Present the policy document with approval signatures and date

Q: "When was your last penetration test conducted?"
   What they want: Date within the observation period
   What fails: "6 months before the period started" or "we're scheduling it now"
   Perfect answer: [Date within period] by [Named firm], report reference [ID]

The Questions That Catch Organizations Off Guard

These questions are less common but catch organizations that aren't fully prepared:




Part 8: SOC 2-Compliant Penetration Testing by Company Stage

The Startup: First SOC 2 Type I

Companies pursuing their first SOC 2 Type I report often have limited security maturity and limited budgets. The minimum viable penetration testing program for Type I:




The Growth-Stage Company: Type II with Engineering Velocity

Companies with active development teams have the additional challenge of maintaining testing coverage despite continuous code changes:




The Enterprise: Type II with Customer Commitments

Enterprise companies face the additional challenge of meeting customer-specific security requirements beyond baseline SOC 2:




Part 9: SOC 2 Penetration Testing Across the Five TSC Categories

Security (CC) — The Mandatory Category

Every SOC 2 engagement requires the Security category. The specific common criteria that penetration testing most directly satisfies:

CC Control

Description

Penetration Testing Evidence

Auditor Priority

CC6.1

Logical access security controls

Authentication bypass test results, IDOR findings, privilege escalation results

High

CC6.2

Access credential registration

Unauthenticated account creation testing

Medium

CC6.3

Access modification and removal

Role-based access testing, terminated user testing

High

CC6.6

External threat protection

External penetration test results (primary evidence)

Critical

CC6.7

Information transmission controls

CORS, TLS, data exposure testing

High

CC7.1

Detection and monitoring

Full pentest findings + remediation + retest

Critical

CC7.2

Anomaly monitoring

SIEM/detection evidence during testing

Medium

CC7.3

Security event evaluation

Finding triage and classification documentation

Medium

CC8.1

Change management testing

Pre-deployment security testing evidence

High

CC9.1

Risk mitigation

Risk register showing pentest findings as input

Medium

Availability (A) — When Required

The availability category adds infrastructure resilience testing to the scope:




Confidentiality (C) — When Required




Part 10: Selecting a Penetration Testing Provider for SOC 2

What SOC 2 Specifically Requires from Your Provider

SOC 2 doesn't prescribe specific certifications for penetration testers, but auditors apply professional judgment about whether the testing was conducted by a "qualified" party. The practical requirements:




The SOC 2 Penetration Testing Vendor Evaluation Matrix

Use this matrix when evaluating providers for SOC 2-compliance purposes:

Evaluation Criterion

Weight

Score 1–5

Notes

External independent firm

Required

Pass/Fail

Non-negotiable

Formal written report delivery

Required

Pass/Fail

Non-negotiable

Retest included in scope

Required

Pass/Fail

Non-negotiable

Firm certifications (CREST, ISO 27001)

High

1–5

+1 per relevant cert

Individual tester certifications (OSCP, CISSP)

High

1–5

Per lead tester

SOC 2 engagement experience

High

1–5

Ask for references

Report includes CVSS scores

High

1–5

Required for CC7.1

Critical finding escalation SLA

High

1–5

4–24 hours preferred

Production environment testing

High

1–5

Confirm explicit commitment

Cloud infrastructure review

Medium

1–5

If cloud-hosted

Per-finding remediation verification

Medium

1–5

In retest report

CVSS delta reporting

Medium

1–5

For trend analysis

API-specific testing capability

Medium

1–5

Critical for SaaS

Code review capability (white box)

Low

1–5

Enhanced testing

Availability for auditor questions

Low

1–5

Some auditors ask

Part 11: Common SOC 2 Penetration Testing Mistakes and How to Avoid Them

The Twelve Most Expensive Mistakes




Part 12: The SOC 2 Penetration Testing Timeline — Working Backwards from Your Audit Date

The 90-Day Countdown




[IMAGE PLACEHOLDER: Gantt chart showing the 90-day timeline. Horizontal bars for each activity: Vendor selection (T-90 to T-80), Penetration testing (T-75 to T-65), Report delivery (T-65 to T-60), Remediation (T-60 to T-45), Retest (T-30 to T-25), Evidence compilation (T-15 to T-0). Color coding: blue = testing activity, green = remediation activity, orange = documentation activity, red = audit. Critical path highlighted showing that each step must complete before the next begins.]

Frequently Asked Questions

Q: Do we need penetration testing for SOC 2 Type I or only Type II?

You need evidence of penetration testing for both Type I and Type II, but the depth of evidence differs significantly. For Type I, auditors verify that your control design includes penetration testing — having a policy, a completed report, and a remediation plan is typically sufficient. For Type II, auditors verify that the control operated effectively — they want the retest report confirming remediations worked, SLA compliance evidence, and documentation that findings fed into the risk management process. Most companies discover this gap when upgrading from Type I to Type II: the Type I evidence they collected isn't sufficient for Type II.

The Evidence Package Is the Control

SOC 2 auditors don't experience your security controls firsthand. They experience the evidence of your security controls — the documents, reports, records, and logs that demonstrate the controls operated as described. For penetration testing, the quality of the evidence package determines whether the control receives a clean opinion or an exception.

A technically excellent penetration test with poor evidence collection produces the same auditor outcome as no penetration test: a qualified opinion and an exception on CC7.1. A methodologically thorough test with complete evidence — policy, report, remediation tracking, retest report, management review, risk acceptances — produces a clean opinion regardless of whether the underlying findings were significant.

This is not a counsel to prioritize documentation over security. It's a recognition that for compliance purposes, undocumented security work is indistinguishable from no security work. The penetration test that finds and remediates a critical authentication bypass — but lacks a retest report confirming the fix — produces the same SOC 2 evidence gap as a test that didn't happen.

Build the evidence package before testing starts. Know exactly what documents you need. Engage a testing firm that understands SOC 2 evidence requirements and produces reports structured to satisfy auditor expectations. Schedule remediation time into the engagement timeline, not as an afterthought. Get the retest done, get the retest report, add it to the package.

That's the entire program. It's not complicated — but it requires discipline about documentation at every step, from policy through authorization through finding through remediation through verification.

CodeAnt AI's SOC 2-ready engagement model delivers every element of the evidence package as a standard deliverable: SOC 2-structured finding reports with TSC control mapping, critical finding escalation within 4 hours, retest included as a standard component, CVSS delta reporting for trend analysis, and evidence package compilation guidance that maps every deliverable to the auditor question it answers.

→ Book a 30-minute scoping call. Testing starts within 24 hours. Book your call here.

Continue reading:

  • What Is AI Penetration Testing? The Complete Deep-Dive Guide

  • What Happens During a Pentest Retest?

  • Continuous Pentesting vs Annual: The Real Operational Difference

  • What Is a CVSS Score? A Technical Breakdown for Engineers

  • How to Choose an AI Penetration Testing Provider

FAQs

Can we use automated vulnerability scanning instead of penetration testing?

How long does the penetration test report remain valid for SOC 2 purposes?

What if our development team finds a vulnerability internally before the penetration test? Does that count?

We're a startup with limited budget. What's the minimum we can spend and still pass SOC 2?

Our auditor firm has a "preferred penetration testing provider." Do we have to use them?

Table of Contents

Start Your 14-Day Free Trial

AI code reviews, security, and quality trusted by modern engineering teams. No credit card required!

Share blog: