Security Audit Preparation: Passing SOC 2, ISO 27001, and Compliance Reviews
A comprehensive guide to preparing for security audits, implementing controls, collecting evidence, and maintaining continuous compliance with industry standards.
Compliance Team
GRC & Security Assurance
Introduction
Security audits can be daunting, but with proper preparation and continuous compliance practices, they become manageable milestones rather than stressful events. This guide walks through preparing for major security audits including SOC 2, ISO 27001, and other compliance frameworks, with practical examples of controls, evidence collection, and audit readiness.
1. Understanding Audit Requirements
Common Audit Frameworks
- SOC 2 Type II: Trust Services Criteria (Security, Availability, Confidentiality, Processing Integrity, Privacy)
- ISO 27001: 114 controls across 14 domains
- PCI DSS: 12 requirements for payment card data security
- HIPAA: Administrative, physical, and technical safeguards for healthcare data
- GDPR: Data protection and privacy requirements
Control Mapping
// Control mapping across frameworks
const controlMapping = {
'ACCESS-001': {
description: 'Multi-factor authentication for all user accounts',
frameworks: {
'SOC2': ['CC6.1', 'CC6.2'],
'ISO27001': ['A.9.4.2', 'A.9.4.3'],
'NIST': ['IA-2'],
'PCI-DSS': ['8.3']
},
implementation: {
status: 'Implemented',
evidence: ['mfa-policy.pdf', 'okta-config-screenshot.png'],
responsible: 'IT Security Team',
reviewDate: '2026-01-15'
}
},
'ENCRYPT-001': {
description: 'Encryption of data at rest and in transit',
frameworks: {
'SOC2': ['CC6.7'],
'ISO27001': ['A.10.1.1', 'A.10.1.2'],
'PCI-DSS': ['3.4', '4.1']
},
implementation: {
status: 'Implemented',
evidence: ['encryption-config.json', 'tls-certificate.pem'],
responsible: 'DevOps Team'
}
}
};2. Pre-Audit Preparation
Evidence Collection System
// Automated evidence collection
class EvidenceCollector {
async collectMonthlyEvidence(): Promise<void> {
const month = new Date().toISOString().slice(0, 7);
const evidenceDir = `/audit-evidence/${month}`;
// Collect access logs
await this.exportAccessLogs(evidenceDir);
// Export user list with MFA status
await this.exportUserMFAStatus(evidenceDir);
// Collect vulnerability scan results
await this.exportVulnScans(evidenceDir);
// Export backup verification logs
await this.exportBackupLogs(evidenceDir);
// Collect security training completion records
await this.exportTrainingRecords(evidenceDir);
// Generate compliance report
await this.generateComplianceReport(evidenceDir);
}
async exportAccessLogs(dir: string): Promise<void> {
const logs = await db.auditLogs
.where('timestamp', '>=', startOfMonth())
.where('timestamp', '<', endOfMonth())
.select();
await fs.writeFile(
`${dir}/access-logs.json`,
JSON.stringify(logs, null, 2)
);
}
async exportUserMFAStatus(dir: string): Promise<void> {
const users = await db.users
.select('id', 'email', 'mfaEnabled', 'lastLogin')
.where('active', true);
const csv = this.convertToCSV(users);
await fs.writeFile(`${dir}/user-mfa-status.csv`, csv);
}
}
// Schedule monthly evidence collection
cron.schedule('0 0 1 * *', async () => {
const collector = new EvidenceCollector();
await collector.collectMonthlyEvidence();
});Control Testing Documentation
// Control testing framework
interface ControlTest {
controlId: string;
testDate: Date;
tester: string;
testProcedure: string;
sampleSize: number;
passed: number;
failed: number;
findings: string[];
evidence: string[];
}
class ControlTesting {
async testAccessControl(): Promise<ControlTest> {
// Test that terminated users are disabled within 24 hours
const terminatedUsers = await this.getTerminatedUsers();
const results = [];
for (const user of terminatedUsers.slice(0, 25)) { // Sample 25
const accessDisabled = await this.verifyAccessDisabled(user);
const timelyDisabled = this.withinSLA(user.terminationDate, user.disabledDate, 24);
results.push({
userId: user.id,
passed: accessDisabled && timelyDisabled,
details: `Terminated: ${user.terminationDate}, Disabled: ${user.disabledDate}`
});
}
const passed = results.filter(r => r.passed).length;
const failed = results.length - passed;
return {
controlId: 'ACCESS-002',
testDate: new Date(),
tester: '[email protected]',
testProcedure: 'Verify access revocation for terminated employees',
sampleSize: results.length,
passed,
failed,
findings: results.filter(r => !r.passed).map(r => r.details),
evidence: ['hr-system-export.csv', 'okta-user-status.json']
};
}
async testEncryptionInTransit(): Promise<ControlTest> {
const endpoints = [
'https://api.company.com',
'https://app.company.com',
'https://admin.company.com'
];
const results = await Promise.all(
endpoints.map(async url => {
const result = await this.testTLSConfig(url);
return {
url,
passed: result.tlsVersion >= 1.2 && result.strongCiphers,
details: `TLS ${result.tlsVersion}, Grade: ${result.grade}`
};
})
);
return {
controlId: 'ENCRYPT-002',
testDate: new Date(),
tester: '[email protected]',
testProcedure: 'SSL Labs scan of public endpoints',
sampleSize: endpoints.length,
passed: results.filter(r => r.passed).length,
failed: results.filter(r => !r.passed).length,
findings: results.filter(r => !r.passed).map(r => r.details),
evidence: ['ssllabs-reports.pdf']
};
}
}3. Key Audit Areas
Access Control Evidence
// Access review process
class AccessReview {
async performQuarterlyReview(): Promise<void> {
// Generate access review report
const report = {
reviewDate: new Date(),
reviewer: '[email protected]',
users: []
};
const users = await db.users.where('active', true).select();
for (const user of users) {
const access = await this.getUserAccess(user.id);
report.users.push({
userId: user.id,
email: user.email,
role: user.role,
permissions: access.permissions,
lastReviewed: user.lastAccessReview,
managerApproval: await this.getManagerApproval(user.id),
findings: this.validateAccess(user, access)
});
}
// Export for audit
await this.exportReport(report, 'access-review-Q1-2026.json');
// Create remediation tickets for issues
const issues = report.users.filter(u => u.findings.length > 0);
for (const issue of issues) {
await this.createRemediationTicket(issue);
}
}
}
// Evidence checklist for access control
const accessControlEvidence = {
'User Provisioning': [
'New hire onboarding checklist',
'Access request tickets',
'Manager approval emails',
'Role-based access matrix'
],
'User Deprovisioning': [
'Termination checklist',
'HR system export (termination dates)',
'Access revocation logs',
'Before/after screenshots'
],
'Access Reviews': [
'Quarterly access review reports',
'Manager sign-offs',
'Remediation tracking'
],
'Privileged Access': [
'Admin user list',
'Privileged access management tool config',
'Session recordings',
'Approval workflows'
]
};Change Management Evidence
// Track changes for audit trail
interface ChangeRecord {
id: string;
type: 'code' | 'infrastructure' | 'configuration';
description: string;
requestedBy: string;
approvedBy: string[];
implementedBy: string;
requestDate: Date;
approvalDate: Date;
implementationDate: Date;
testingEvidence: string[];
rollbackPlan: string;
impact: 'low' | 'medium' | 'high';
status: 'pending' | 'approved' | 'implemented' | 'verified';
}
// Integrate with GitHub/Jira
class ChangeManagement {
async getChangeEvidence(startDate: Date, endDate: Date): Promise<ChangeRecord[]> {
// Pull from GitHub PRs
const prs = await github.pulls.list({
owner: 'company',
repo: 'main-app',
state: 'closed',
since: startDate.toISOString()
});
return prs.data.map(pr => ({
id: `GH-${pr.number}`,
type: 'code',
description: pr.title,
requestedBy: pr.user.login,
approvedBy: pr.requested_reviewers.map(r => r.login),
implementedBy: pr.merged_by?.login,
requestDate: new Date(pr.created_at),
approvalDate: new Date(pr.merged_at),
implementationDate: new Date(pr.merged_at),
testingEvidence: [
`CI build #${pr.number}`,
`Test coverage report`
],
rollbackPlan: `Revert commit ${pr.merge_commit_sha}`,
impact: this.assessImpact(pr),
status: 'implemented'
}));
}
}
// Required change management evidence
const changeManagementEvidence = {
'Standard Changes': [
'Pull request history',
'Code review approvals',
'CI/CD pipeline logs',
'Deployment logs'
],
'Emergency Changes': [
'Incident tickets',
'Emergency change approvals',
'Post-implementation review',
'Lessons learned documentation'
],
'Infrastructure Changes': [
'Terraform/CloudFormation diffs',
'Infrastructure change requests',
'Approval workflows',
'Rollback procedures'
]
};4. Incident Response Documentation
// Incident response tracking
interface SecurityIncident {
id: string;
severity: 'low' | 'medium' | 'high' | 'critical';
category: string;
description: string;
detectedDate: Date;
detectedBy: string;
containmentDate: Date;
resolutionDate: Date;
affectedSystems: string[];
affectedUsers: number;
rootCause: string;
remediation: string[];
lessonsLearned: string;
notificationsSent: {
customers: boolean;
regulators: boolean;
date: Date;
};
}
const incidentEvidence = {
'Detection & Response': [
'Incident timeline',
'Detection alerts (SIEM logs)',
'Incident response team activation',
'Containment actions taken'
],
'Investigation': [
'Forensic analysis reports',
'Log analysis',
'Root cause analysis',
'Impact assessment'
],
'Communication': [
'Internal notifications',
'Customer communications',
'Regulatory notifications (if required)',
'Post-incident report'
],
'Remediation': [
'Remediation plan',
'Implementation evidence',
'Verification testing',
'Process improvements'
]
};5. Vulnerability Management
// Track vulnerability remediation
class VulnerabilityManagement {
async generateAuditReport(): Promise<object> {
const scans = await this.getQuarterlyScans();
return {
scanFrequency: 'Weekly automated, Quarterly manual penetration test',
toolsUsed: ['Snyk', 'Trivy', 'OWASP ZAP', 'External pentest firm'],
findings: {
critical: await this.countByEerity('critical'),
high: await this.countBySeverity('high'),
medium: await this.countBySeverity('medium'),
low: await this.countBySeverity('low')
},
slaCompliance: {
critical: await this.checkSLA('critical', 24), // 24 hours
high: await this.checkSLA('high', 168), // 7 days
medium: await this.checkSLA('medium', 720) // 30 days
},
remediationEvidence: await this.getRemediationTickets()
};
}
async checkSLA(severity: string, hoursAllowed: number): Promise<object> {
const vulns = await db.vulnerabilities
.where('severity', severity)
.where('status', 'resolved')
.select();
const violations = vulns.filter(v => {
const hours = (v.resolvedDate - v.detectedDate) / (1000 * 60 * 60);
return hours > hoursAllowed;
});
return {
total: vulns.length,
withinSLA: vulns.length - violations.length,
violations: violations.length,
complianceRate: `${((1 - violations.length / vulns.length) * 100).toFixed(1)}%`
};
}
}
// Required vulnerability management evidence
const vulnManagementEvidence = [
'Vulnerability scan schedules',
'Scan results (quarterly at minimum)',
'Risk acceptance forms for non-remediated items',
'Patch management logs',
'Penetration test reports',
'Remediation tracking (Jira tickets)',
'SLA compliance metrics'
];6. Continuous Monitoring
// Compliance dashboard
class ComplianceDashboard {
async generateStatus(): Promise<object> {
return {
controlsTested: await this.getControlTestingStatus(),
evidenceCollected: await this.getEvidenceStatus(),
findings: await this.getOpenFindings(),
readinessScore: await this.calculateReadiness()
};
}
async calculateReadiness(): Promise<number> {
const controls = await db.controls.select();
const implemented = controls.filter(c => c.status === 'implemented').length;
const tested = controls.filter(c => c.lastTestDate > thirtyDaysAgo()).length;
const evidenceReady = controls.filter(c => c.evidenceComplete).length;
return Math.round(
((implemented + tested + evidenceReady) / (controls.length * 3)) * 100
);
}
}
// Automated compliance monitoring
const monitoringChecks = {
daily: [
'Check backup completion',
'Verify MFA enforcement',
'Review failed login attempts',
'Check certificate expiration'
],
weekly: [
'Vulnerability scan results',
'Access review exceptions',
'Patch compliance',
'Security training completion'
],
monthly: [
'Control testing',
'Evidence collection',
'Vendor security reviews',
'Policy review status'
],
quarterly: [
'Full access reviews',
'Disaster recovery testing',
'Penetration testing',
'Executive compliance review'
]
};7. Audit Day Preparation
Audit Readiness Checklist:
- ✅ All policies reviewed and current (within 12 months)
- ✅ Evidence organized by control domain
- ✅ Control testing completed within last quarter
- ✅ All findings remediated or risk-accepted
- ✅ Key personnel trained on audit process
- ✅ Audit room/portal prepared
- ✅ Evidence request tracker ready
- ✅ Stakeholder availability confirmed
- ✅ Previous audit findings addressed
- ✅ Mock audit completed (internal review)
8. Post-Audit Actions
// Track audit findings to closure
interface AuditFinding {
id: string;
severity: 'observation' | 'deficiency' | 'material-weakness';
control: string;
description: string;
remediation: string;
owner: string;
dueDate: Date;
status: 'open' | 'in-progress' | 'closed';
evidence: string[];
}
class FindingRemediation {
async createRemediationPlan(findings: AuditFinding[]): Promise<void> {
for (const finding of findings) {
// Create Jira ticket
const ticket = await jira.createIssue({
project: 'COMPLIANCE',
summary: `[Audit] ${finding.control}: ${finding.description}`,
priority: this.mapSeverityToPriority(finding.severity),
dueDate: finding.dueDate,
assignee: finding.owner
});
// Schedule review
await this.scheduleFollowUp(ticket.id, finding.dueDate);
}
}
async verifyRemediation(findingId: string): Promise<boolean> {
// Re-test control
const controlTest = await this.testControl(findingId);
// Collect new evidence
const evidence = await this.collectEvidence(findingId);
// Document closure
await db.auditFindings.update(findingId, {
status: 'closed',
closureDate: new Date(),
closureEvidence: evidence,
verifiedBy: '[email protected]'
});
return controlTest.passed;
}
}Conclusion
Security audit preparation is an ongoing process, not a last-minute scramble. By implementing continuous compliance practices, automated evidence collection, and regular control testing, you transform audits from stressful events into routine validations of your security posture. The key is treating compliance as a business enabler that builds customer trust, not just a checkbox exercise.