Terminal.skills
Use Cases/Automate SOC 2 Compliance for a Growing Startup

Automate SOC 2 Compliance for a Growing Startup

A 25-person SaaS startup automates SOC 2 Type II compliance — using infrastructure-as-code for audit trails, automated vulnerability scanning in CI, access control policies enforced via code, encrypted secrets management, and continuous monitoring — turning a 6-month manual audit nightmare into a self-documenting, always-compliant system.

DevOps#sast#code-analysis#security#linting#patterns
Works with:claude-codeopenai-codexgemini-clicursor
$

Mei is the CTO of a 25-person SaaS startup that just landed their first enterprise prospect. The deal is worth $400K/year, but the procurement team requires SOC 2 Type II certification. Mei has heard horror stories: 6+ months of preparation, $50K+ in consulting fees, teams of people filling spreadsheets. She doesn't have the budget for a compliance team, so she automates everything — every control is enforced via code, every policy is a CI check, every audit question has an automated answer.

The Problem: 80+ Controls, Zero Compliance Team

SOC 2 Type II requires demonstrating that security controls work continuously over a review period (typically 6-12 months). The five trust service criteria — Security, Availability, Processing Integrity, Confidentiality, and Privacy — translate into 80+ specific controls. Doing this manually means:

  • Reviewing access permissions quarterly (and proving it)
  • Scanning for vulnerabilities regularly (and documenting results)
  • Managing secrets securely (and showing rotation logs)
  • Monitoring infrastructure for anomalies (and keeping incident records)
  • Maintaining change management processes (and tracking every deploy)

Mei's approach: make compliance a side effect of good engineering practices, all codified and automated.

Control 1: Secure Code — Static Analysis in CI

Every pull request is automatically scanned for security vulnerabilities before merge. This satisfies multiple SOC 2 controls around secure development practices.

yaml
# .github/workflows/security-scan.yml
name: Security Scan
on: pull_request

jobs:
  semgrep:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Semgrep SAST scan
        uses: returntocorp/semgrep-action@v1
        with:
          config: >-
            p/security-audit
            p/secrets
            p/owasp-top-ten
            p/typescript
          generateSarif: true

      - name: Upload SARIF results
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: semgrep.sarif

      # Fail PR if critical/high findings
      - name: Check for blocking findings
        run: |
          CRITICAL=$(cat semgrep.sarif | jq '[.runs[].results[] | select(.level == "error")] | length')
          if [ "$CRITICAL" -gt 0 ]; then
            echo "❌ $CRITICAL critical findings. Fix before merging."
            exit 1
          fi

The audit evidence is automatic: every PR shows the scan results, every merge proves the code was reviewed and scanned. No spreadsheet needed.

Control 2: Infrastructure Security — IaC Scanning

Infrastructure misconfigurations (public S3 buckets, unencrypted databases, overly permissive IAM) are caught before they reach production:

yaml
  checkov:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Checkov IaC scan
        uses: bridgecrewio/checkov-action@v12
        with:
          directory: ./infrastructure
          framework: terraform,dockerfile,kubernetes
          output_format: sarif
          soft_fail: false
          # SOC 2 relevant checks
          check: >-
            CKV_AWS_18,CKV_AWS_19,CKV_AWS_20,CKV_AWS_21,
            CKV_AWS_145,CKV_AWS_149,CKV_AWS_300

      # Generate compliance report
      - name: Compliance report
        run: |
          checkov -d ./infrastructure --framework terraform \
            --output json > /tmp/checkov-report.json

          # Store as audit artifact
          aws s3 cp /tmp/checkov-report.json \
            s3://audit-artifacts/checkov/$(date +%Y-%m-%d)/report.json

Every infrastructure change is scanned, and results are stored in an audit bucket with timestamps. When the auditor asks "how do you ensure infrastructure is configured securely?", Mei shows them the CI pipeline and the artifact bucket.

Control 3: Secrets Management

No hardcoded secrets, rotating credentials, audit logs for every access:

typescript
// lib/config.ts — All secrets from Infisical, never from env files
import { InfisicalClient } from "@infisical/sdk";

const infisical = new InfisicalClient({
  siteUrl: "https://vault.ourcompany.com",
  auth: { universalAuth: { clientId: process.env.INFISICAL_CLIENT_ID!, clientSecret: process.env.INFISICAL_CLIENT_SECRET! } },
});

export async function getSecrets() {
  const secrets = await infisical.listSecrets({
    environment: process.env.NODE_ENV === "production" ? "prod" : "dev",
    projectId: process.env.INFISICAL_PROJECT_ID!,
  });

  return {
    databaseUrl: secrets.find(s => s.secretName === "DATABASE_URL")!.secretValue,
    stripeKey: secrets.find(s => s.secretName === "STRIPE_SECRET_KEY")!.secretValue,
    jwtSecret: secrets.find(s => s.secretName === "JWT_SECRET")!.secretValue,
  };
}

// Infisical provides:
// - Audit log of every secret access (who, when, which secret)
// - Automatic rotation reminders
// - Secret versioning (rollback if rotation breaks something)
// - IP allowlisting (secrets only accessible from production IPs)

The SOC 2 evidence: Infisical's audit log shows every secret access with timestamps, user IDs, and IP addresses. Automatic rotation policies prove secrets are rotated quarterly.

Control 4: Access Control Reviews

Instead of quarterly manual reviews, access is defined as code and continuously validated:

typescript
// scripts/access-audit.ts — Runs weekly via cron
// Generates evidence for SOC 2 access review controls

async function auditAccess() {
  const report: AuditReport = {
    date: new Date().toISOString(),
    findings: [],
    summary: { totalUsers: 0, activeUsers: 0, inactiveUsers: 0, excessivePermissions: 0 },
  };

  // Check GitHub org members
  const githubMembers = await octokit.orgs.listMembers({ org: "ourcompany" });
  const activeEmployees = await hr.getActiveEmployees();
  const activeEmails = new Set(activeEmployees.map(e => e.email));

  for (const member of githubMembers.data) {
    const profile = await octokit.users.getByUsername({ username: member.login });
    if (!activeEmails.has(profile.data.email)) {
      report.findings.push({
        severity: "high",
        type: "orphaned-account",
        detail: `GitHub user ${member.login} (${profile.data.email}) not found in active employee list`,
        recommendation: "Remove from GitHub organization",
      });
    }
  }

  // Check AWS IAM for excessive permissions
  const iamUsers = await iam.listUsers({}).promise();
  for (const user of iamUsers.Users) {
    const policies = await iam.listAttachedUserPolicies({ UserName: user.UserName }).promise();
    const hasAdmin = policies.AttachedPolicies.some(p => p.PolicyName === "AdministratorAccess");
    if (hasAdmin) {
      report.findings.push({
        severity: "medium",
        type: "excessive-permission",
        detail: `IAM user ${user.UserName} has AdministratorAccess`,
        recommendation: "Replace with least-privilege policy",
      });
      report.summary.excessivePermissions++;
    }
  }

  // Store report for auditor
  await s3.putObject({
    Bucket: "audit-artifacts",
    Key: `access-reviews/${new Date().toISOString().split("T")[0]}/report.json`,
    Body: JSON.stringify(report, null, 2),
  }).promise();

  // Alert on findings
  if (report.findings.some(f => f.severity === "high")) {
    await slack.send("#security", `⚠️ Access audit found ${report.findings.length} issues. Review: ${reportUrl}`);
  }

  return report;
}

Control 5: Continuous Monitoring and Incident Response

All services report to a centralized observability stack. Anomalies trigger alerts, and every alert creates an incident record:

typescript
// lib/monitoring.ts — Structured incident logging
import { trace, metrics } from "@opentelemetry/api";

const meter = metrics.getMeter("compliance");
const incidentCounter = meter.createCounter("security_incidents", {
  description: "Security incidents detected",
});

async function handleSecurityAlert(alert: SecurityAlert) {
  incidentCounter.add(1, { severity: alert.severity, type: alert.type });

  // Create incident record (SOC 2 requires documented incident response)
  const incident = await db.incidents.create({
    data: {
      alertId: alert.id,
      severity: alert.severity,
      type: alert.type,
      description: alert.description,
      status: "open",
      detectedAt: new Date(),
      assignedTo: getOnCallEngineer(),
    },
  });

  // Notify
  await slack.send("#incidents", {
    text: `🚨 Security incident INC-${incident.id}: ${alert.description}`,
    blocks: [
      { type: "section", text: { type: "mrkdwn", text: `*Severity:* ${alert.severity}\n*Type:* ${alert.type}\n*Assigned:* ${incident.assignedTo}` } },
      { type: "actions", elements: [
        { type: "button", text: { type: "plain_text", text: "Acknowledge" }, action_id: `ack-${incident.id}` },
      ] },
    ],
  });

  return incident;
}

Results

Mei's team achieves SOC 2 Type II certification in 4 months (vs. typical 6-12 months), spending $12K total (vs. $50K+ typical).

  • Audit readiness: Auditor accessed artifact bucket and reviewed CI pipelines; 90% of evidence was already collected automatically
  • Findings: Zero critical findings; 3 minor observations (all addressed same week)
  • Time investment: 2 engineers, ~4 hours/week each for 4 months; not a full-time compliance role
  • CI security: Semgrep caught 47 vulnerabilities in PRs before merge over the review period (all documented)
  • IaC scanning: Checkov prevented 12 infrastructure misconfigurations from reaching production
  • Secret hygiene: Zero hardcoded secrets found in codebase; all rotated on schedule
  • Access reviews: Weekly automated reviews caught 2 orphaned accounts within 1 day of employee departure
  • Incident response: 8 security alerts during review period, all acknowledged within 15 minutes, resolved within 4 hours
  • Enterprise deal: Closed the $400K contract 3 weeks after certification; 2 more enterprise prospects in pipeline
  • Ongoing effort: <2 hours/week to maintain compliance; everything is automated and continuous