Skip to main content

SOC 2 Audit Preparation Checklist

Ryan Dahlberg
Ryan Dahlberg
October 30, 2025 8 min read
Share:
SOC 2 Audit Preparation Checklist

Introduction

SOC 2 (Service Organization Control 2) is a critical compliance framework for SaaS companies that process customer data. A successful SOC 2 Type II audit demonstrates to customers and prospects that your organization has implemented effective controls for security, availability, processing integrity, confidentiality, and privacy.

This comprehensive guide walks through the entire SOC 2 preparation process, from initial assessment to audit completion.

Understanding SOC 2 Framework

Trust Service Criteria

SOC 2 is built around five Trust Service Criteria (TSC):

Security (Required): The system is protected against unauthorized access.

Availability: The system is available for operation and use as committed or agreed.

Processing Integrity: System processing is complete, valid, accurate, timely, and authorized.

Confidentiality: Information designated as confidential is protected as committed or agreed.

Privacy: Personal information is collected, used, retained, disclosed, and disposed of in conformance with privacy commitments.

Type I vs Type II

SOC 2 Type I: Evaluates controls at a specific point in time. Faster and less expensive but less valuable to customers.

SOC 2 Type II: Evaluates controls over a period (typically 6-12 months). More comprehensive and provides stronger assurance.

Pre-Audit Assessment

Control Environment Evaluation

class SOC2ControlAssessment {
  async performGapAnalysis() {
    const controlCategories = [
      'access_control',
      'change_management',
      'risk_management',
      'monitoring',
      'incident_response',
      'vendor_management',
      'data_protection',
      'physical_security',
    ];

    const gaps = [];

    for (const category of controlCategories) {
      const controls = await this.getRequiredControls(category);
      const currentState = await this.assessCurrentControls(category);

      for (const control of controls) {
        if (!currentState.implemented.includes(control.id)) {
          gaps.push({
            category,
            control: control.id,
            description: control.description,
            severity: control.required ? 'critical' : 'recommended',
            effort: control.implementationEffort,
          });
        }
      }
    }

    return this.prioritizeGaps(gaps);
  }

  prioritizeGaps(gaps) {
    return gaps.sort((a, b) => {
      // Critical gaps first
      if (a.severity === 'critical' && b.severity !== 'critical') return -1;
      if (a.severity !== 'critical' && b.severity === 'critical') return 1;

      // Then by effort (quick wins)
      const effortOrder = { low: 0, medium: 1, high: 2 };
      return effortOrder[a.effort] - effortOrder[b.effort];
    });
  }
}

Implementing Key Controls

Access Control

class AccessControlSystem {
  async implementRBAC() {
    // Define roles based on least privilege principle
    const roles = {
      viewer: {
        permissions: ['read:own_data', 'read:shared_data'],
        description: 'Can view assigned data',
      },
      editor: {
        permissions: ['read:own_data', 'write:own_data', 'read:shared_data'],
        description: 'Can edit assigned data',
      },
      admin: {
        permissions: [
          'read:all_data',
          'write:all_data',
          'manage:users',
          'manage:settings',
        ],
        description: 'Full administrative access',
      },
    };

    await this.db.collection('roles').insertMany(Object.entries(roles).map(
      ([name, config]) => ({ name, ...config })
    ));
  }

  async enforcePasswordPolicy() {
    return {
      minLength: 12,
      requireUppercase: true,
      requireLowercase: true,
      requireNumbers: true,
      requireSpecialChars: true,
      preventReuse: 5, // Last 5 passwords
      maxAge: 90, // days
      lockoutThreshold: 5,
      lockoutDuration: 30, // minutes
    };
  }

  async implementMFA() {
    // Require MFA for all users
    const mfaOptions = ['totp', 'sms', 'hardware_key'];

    return {
      required: true,
      methods: mfaOptions,
      gracePeriod: 7, // days to enroll
      backupCodes: 10,
    };
  }

  async logAccessEvents() {
    const event = {
      timestamp: new Date(),
      userId: 'user_id',
      action: 'login',
      resource: '/dashboard',
      ipAddress: '192.168.1.1',
      userAgent: 'Mozilla/5.0...',
      result: 'success',
      mfaUsed: true,
    };

    await this.db.collection('access_logs').insertOne(event);

    // Retain for audit period + 1 year
    await this.setRetention(event._id, 730); // days
  }
}

Change Management

class ChangeManagementSystem {
  async createChangeRequest(change) {
    const changeRequest = {
      id: this.generateId(),
      requestedBy: change.requestedBy,
      requestedDate: new Date(),
      type: change.type, // 'standard', 'normal', 'emergency'
      description: change.description,
      justification: change.justification,
      affectedSystems: change.systems,
      riskAssessment: await this.assessRisk(change),
      status: 'pending_approval',
      approvers: this.determineApprovers(change),
    };

    await this.db.collection('change_requests').insertOne(changeRequest);

    // Notify approvers
    await this.notifyApprovers(changeRequest);

    return changeRequest;
  }

  async assessRisk(change) {
    let riskScore = 0;

    // Production changes are higher risk
    if (change.environment === 'production') riskScore += 30;

    // Database changes are higher risk
    if (change.systems.includes('database')) riskScore += 25;

    // Changes during business hours are higher risk
    if (this.isBusinessHours(change.scheduledTime)) riskScore += 15;

    // No rollback plan is higher risk
    if (!change.rollbackPlan) riskScore += 30;

    return {
      score: riskScore,
      level: riskScore > 60 ? 'high' : riskScore > 30 ? 'medium' : 'low',
      mitigations: this.suggestMitigations(change, riskScore),
    };
  }

  async implementChange(changeId) {
    const change = await this.getChange(changeId);

    // Verify approval
    if (!this.isApproved(change)) {
      throw new Error('Change not approved');
    }

    // Create backup before change
    const backup = await this.createBackup(change.affectedSystems);

    try {
      // Execute change
      const result = await this.executeChange(change);

      // Verify change
      const verification = await this.verifyChange(change);

      if (!verification.successful) {
        // Rollback if verification fails
        await this.rollback(change, backup);
        throw new Error('Change verification failed');
      }

      // Document completion
      await this.documentChange(change, result, verification);

      return { success: true, result };
    } catch (error) {
      await this.handleChangeFailure(change, backup, error);
      throw error;
    }
  }
}

Vulnerability Management

class VulnerabilityManagementProgram {
  async runVulnerabilityScans() {
    // Schedule regular scans
    const scanSchedule = {
      weekly: ['web_applications', 'apis'],
      monthly: ['infrastructure', 'network'],
      quarterly: ['penetration_testing'],
    };

    const scanResults = await this.executeScan('all_systems');

    // Categorize vulnerabilities
    const categorized = this.categorizeVulnerabilities(scanResults);

    // Create remediation tickets
    for (const vuln of categorized.critical) {
      await this.createRemediationTicket(vuln, 'P0', 24); // 24 hour SLA
    }

    for (const vuln of categorized.high) {
      await this.createRemediationTicket(vuln, 'P1', 7 * 24); // 7 days
    }

    for (const vuln of categorized.medium) {
      await this.createRemediationTicket(vuln, 'P2', 30 * 24); // 30 days
    }

    return {
      scanDate: new Date(),
      totalVulnerabilities: scanResults.length,
      bySeverity: {
        critical: categorized.critical.length,
        high: categorized.high.length,
        medium: categorized.medium.length,
        low: categorized.low.length,
      },
      remediationTickets: categorized.critical.length + categorized.high.length,
    };
  }

  categorizeVulnerabilities(results) {
    return {
      critical: results.filter((v) => v.cvss >= 9.0),
      high: results.filter((v) => v.cvss >= 7.0 && v.cvss < 9.0),
      medium: results.filter((v) => v.cvss >= 4.0 && v.cvss < 7.0),
      low: results.filter((v) => v.cvss < 4.0),
    };
  }
}

Evidence Collection

Automated Evidence Gathering

class SOC2EvidenceCollector {
  async collectMonthlyEvidence(month) {
    const evidence = {
      month,
      collectionDate: new Date(),
      items: [],
    };

    // Access reviews
    evidence.items.push({
      type: 'access_review',
      description: 'Quarterly access review completed',
      files: await this.exportAccessReview(month),
    });

    // Vulnerability scans
    evidence.items.push({
      type: 'vulnerability_scan',
      description: 'Monthly vulnerability scan results',
      files: await this.exportVulnerabilityScan(month),
    });

    // Change logs
    evidence.items.push({
      type: 'change_log',
      description: 'Approved changes implemented',
      files: await this.exportChangeLog(month),
    });

    // Security training
    evidence.items.push({
      type: 'security_training',
      description: 'Employee security training completion',
      files: await this.exportTrainingRecords(month),
    });

    // Backup verification
    evidence.items.push({
      type: 'backup_verification',
      description: 'Backup and restore testing',
      files: await this.exportBackupLogs(month),
    });

    // Incident log
    evidence.items.push({
      type: 'incident_log',
      description: 'Security incidents and responses',
      files: await this.exportIncidentLog(month),
    });

    await this.storeEvidence(evidence);
    return evidence;
  }

  async prepareAuditPackage() {
    const auditPeriodStart = new Date('2024-01-01');
    const auditPeriodEnd = new Date('2024-12-31');

    const package = {
      period: { start: auditPeriodStart, end: auditPeriodEnd },
      documents: {
        policies: await this.collectPolicies(),
        procedures: await this.collectProcedures(),
        evidence: await this.collectAllEvidence(auditPeriodStart, auditPeriodEnd),
        systemDescription: await this.generateSystemDescription(),
      },
    };

    return package;
  }
}

Audit Preparation Timeline

6 Months Before Audit

  • Complete gap analysis
  • Implement critical controls
  • Begin evidence collection
  • Establish documentation processes

3 Months Before Audit

  • Complete control implementation
  • Test controls for effectiveness
  • Conduct internal audit
  • Remediate identified issues

1 Month Before Audit

  • Finalize all documentation
  • Prepare system description
  • Organize evidence repository
  • Brief audit team

During Audit

  • Provide requested evidence promptly
  • Answer auditor questions thoroughly
  • Document all interactions
  • Track open items

Common Audit Findings

Inadequate Access Reviews

Issue: User access not reviewed regularly.

Remediation: Implement quarterly access reviews with documentation.

async function performAccessReview() {
  const users = await getAllUsers();
  const review = {
    date: new Date(),
    reviewer: 'security_team',
    findings: [],
  };

  for (const user of users) {
    const access = await getUserAccess(user.id);
    const appropriate = await validateAccess(user, access);

    if (!appropriate) {
      review.findings.push({
        userId: user.id,
        issue: 'excessive_permissions',
        currentAccess: access,
        recommendedAccess: await getAppropriateAccess(user),
      });
    }
  }

  return review;
}

Missing Change Documentation

Issue: Production changes made without approval.

Remediation: Enforce change management process with automated controls.

Insufficient Backup Testing

Issue: Backups not regularly tested for restoration.

Remediation: Monthly backup restoration tests with documentation.

Incomplete Security Training

Issue: Not all employees completed security training.

Remediation: Mandatory annual training with tracking and follow-up.

Post-Audit Activities

Addressing Findings

class FindingRemediationTracker {
  async trackFinding(finding) {
    const remediationPlan = {
      findingId: finding.id,
      description: finding.description,
      severity: finding.severity,
      rootCause: await this.performRootCauseAnalysis(finding),
      remediationSteps: this.createRemediationPlan(finding),
      assignedTo: this.assignOwner(finding),
      targetDate: this.calculateTargetDate(finding.severity),
      status: 'open',
    };

    await this.db.collection('remediation_plans').insertOne(remediationPlan);

    return remediationPlan;
  }

  calculateTargetDate(severity) {
    const days = {
      critical: 7,
      high: 30,
      medium: 60,
      low: 90,
    };

    const target = new Date();
    target.setDate(target.getDate() + days[severity]);
    return target;
  }
}

Maintaining SOC 2 Compliance

Continuous Monitoring

class ContinuousComplianceMonitoring {
  async monitorControls() {
    const controls = await this.getAllControls();
    const monitoring = {
      date: new Date(),
      controlsTested: 0,
      controlsPassing: 0,
      controlsFailing: 0,
      findings: [],
    };

    for (const control of controls) {
      monitoring.controlsTested++;

      const result = await this.testControl(control);

      if (result.passing) {
        monitoring.controlsPassing++;
      } else {
        monitoring.controlsFailing++;
        monitoring.findings.push({
          control: control.id,
          issue: result.issue,
          evidence: result.evidence,
        });

        // Alert on control failure
        await this.alertControlFailure(control, result);
      }
    }

    await this.storeMonitoringResults(monitoring);
    return monitoring;
  }
}

Conclusion

SOC 2 compliance is a significant undertaking that requires careful planning, consistent execution, and continuous monitoring. By following this checklist and implementing robust controls, your organization can successfully achieve and maintain SOC 2 certification.

Key success factors:

  • Start early with thorough gap analysis
  • Implement controls systematically
  • Maintain comprehensive documentation
  • Collect evidence continuously
  • Test controls regularly
  • Address findings promptly
  • Monitor compliance ongoing

Remember that SOC 2 is not just a checkbox—it’s a commitment to protecting customer data and maintaining trust through demonstrated security practices.

#Compliance #SOC 2 #Audit #Security #Trust Services