Soc2Rules.java
package com.cloudforgeci.api.core.rules;
import com.cloudforge.core.annotation.ComplianceFramework;
import com.cloudforge.core.enums.AuthMode;
import com.cloudforge.core.enums.ComplianceMode;
import com.cloudforge.core.enums.NetworkMode;
import com.cloudforge.core.enums.RuntimeType;
import com.cloudforge.core.enums.SecurityProfile;
import com.cloudforge.core.interfaces.FrameworkRules;
import com.cloudforgeci.api.core.SystemContext;
import com.cloudforgeci.api.interfaces.SecurityProfileConfiguration;
import software.amazon.awscdk.services.logs.RetentionDays;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.logging.Logger;
import java.util.Set;
/**
* SOC 2 (Service Organization Control 2) Trust Services Criteria compliance validation.
*
* SOC 2 is based on five Trust Services Criteria (TSC):
* - Security (Common Criteria - CC)
* - Availability (A)
* - Processing Integrity (PI)
* - Confidentiality (C)
* - Privacy (P)
*
* This validator focuses on the Security criteria (Common Criteria) which apply to all SOC 2 reports.
* Organizations can choose additional criteria based on their services.
*
* Trust Services Criteria Coverage:
* - CC6.1: Logical and Physical Access Controls
* - CC6.2: Access Management
* - CC6.6: Network Segmentation
* - CC6.7: Data Transmission
* - CC7.2: System Monitoring
* - CC7.3: Environmental Protections (Availability)
* - CC8.1: Change Management
*/
@ComplianceFramework(
value = "SOC2",
priority = 40,
displayName = "SOC 2",
description = "Validates SOC 2 Trust Services Criteria for service organizations"
)
public class Soc2Rules implements FrameworkRules<SystemContext> {
private static final Logger LOG = Logger.getLogger(Soc2Rules.class.getName());
/** Controls that still block STAGING synthesis: authentication, network isolation, and
* SSL/TLS in transit. Everything else in STAGING is a visible, non-blocking finding. */
private static final Set<String> STAGING_BLOCKING_RULES = Set.of(
"SOC2-CC6.2-Auth", "SOC2-C1.2-Network", "SOC2-CC6.7-SSL", "SOC2-CC6.7-TLS"
);
/**
* Install SOC 2 compliance validation rules.
* SOC 2 applies to production and staging environments serving customers.
*
* @since 3.0.0
*/
@Override
public void install(SystemContext ctx) {
// SOC 2 typically applies to production and staging
if (ctx.security != SecurityProfile.PRODUCTION && ctx.security != SecurityProfile.STAGING) {
LOG.info("SOC 2 validation rules typically apply to PRODUCTION and STAGING profiles");
return;
}
LOG.info("Installing SOC 2 Trust Services Criteria compliance validation for " + ctx.security);
// Get compliance mode (already resolved to enum with proper default)
ComplianceMode complianceMode = ctx.cfc.complianceMode();
LOG.info(" Compliance mode: " + complianceMode);
ctx.getNode().addValidation(() -> {
List<ComplianceRule> rules = new ArrayList<>();
// Common Criteria - Security
rules.addAll(validateAccessControls(ctx));
rules.addAll(validateNetworkSecurity(ctx));
rules.addAll(validateSystemMonitoring(ctx));
rules.addAll(validateChangeManagement(ctx));
rules.addAll(validateMatrixControls(
ctx.securityProfileConfig.get().orElseThrow(
() -> new IllegalStateException("SecurityProfileConfiguration not set")),
ctx.security,
ctx.runtime,
ctx.cfc.authMode(),
ctx.dbConnection.get().isPresent()));
// Availability Criteria
rules.addAll(validateAvailability(ctx));
// Confidentiality Criteria
rules.addAll(validateConfidentiality(ctx));
// Advisory findings never block synthesis, logged regardless of complianceMode so they
// show up in the compliance report and the matrix runner's log capture.
List<ComplianceRule> advisoryRules = rules.stream().filter(ComplianceRule::isAdvisory).toList();
if (!advisoryRules.isEmpty()) {
LOG.info("SOC 2 validation found " + advisoryRules.size() + " advisory recommendations (non-blocking):");
advisoryRules.forEach(r -> LOG.info(" [ADVISORY] " + r.ruleId() + ": " + r.description()));
}
return ComplianceEnforcement.resolve(
"SOC 2", rules, complianceMode, ctx.security, STAGING_BLOCKING_RULES, LOG);
});
}
/**
* CC6.1 & CC6.2: Logical and Physical Access Controls.
* The entity implements logical access security software, infrastructure, and architectures
* over protected information assets to protect them from security events.
*/
private List<ComplianceRule> validateAccessControls(SystemContext ctx) {
List<ComplianceRule> rules = new ArrayList<>();
var config = ctx.securityProfileConfig.get().orElseThrow(
() -> new IllegalStateException("SecurityProfileConfiguration not set")
);
// CC6.1: Access controls must be implemented
if (ctx.iamProfile == null) {
rules.add(ComplianceRule.fail(
"SOC2-CC6.1-IAM",
"IAM access controls required",
"IAMPasswordPolicyRule",
"Implement role-based access control (RBAC) to restrict access to system components."
));
} else {
rules.add(ComplianceRule.pass("SOC2-CC6.1-IAM", "IAM access controls enabled", "IAMPasswordPolicyRule"));
}
// CC6.2: Authentication required
AuthMode authMode = ctx.cfc.authMode();
if (authMode == AuthMode.NONE) {
rules.add(ComplianceRule.fail(
"SOC2-CC6.2-Auth",
"User authentication required for customer-facing systems",
"Configure authMode = 'alb-oidc', 'jenkins-oidc', or 'application-oidc' to authenticate users."
));
} else {
rules.add(ComplianceRule.pass("SOC2-CC6.2-Auth", "User authentication enabled"));
}
// CC6.1: Encryption for data protection
if (!config.isEbsEncryptionEnabled() || !config.isEfsEncryptionAtRestEnabled()) {
rules.add(ComplianceRule.fail(
"SOC2-CC6.1-Encryption",
"Encryption at rest required to protect confidential data",
"EbsEncryptionRule",
"Enable encryption for all storage volumes."
));
} else {
rules.add(ComplianceRule.pass("SOC2-CC6.1-Encryption", "Encryption at rest enabled", "EbsEncryptionRule"));
}
return rules;
}
/**
* CC6.6 & CC6.7: Network Segmentation and Data Transmission.
* The entity implements network segmentation and encrypts data in transmission.
*/
private List<ComplianceRule> validateNetworkSecurity(SystemContext ctx) {
List<ComplianceRule> rules = new ArrayList<>();
var config = ctx.securityProfileConfig.get().orElseThrow(
() -> new IllegalStateException("SecurityProfileConfiguration not set")
);
// CC6.6: Network segmentation
if (ctx.vpc.get().isEmpty()) {
rules.add(ComplianceRule.fail(
"SOC2-CC6.6-VPC",
"Network segmentation required",
"Implement VPC with proper subnet isolation."
));
} else {
rules.add(ComplianceRule.pass("SOC2-CC6.6-VPC", "VPC network segmentation enabled"));
}
if (ctx.albSg.get().isEmpty() || ctx.efsSg.get().isEmpty()) {
rules.add(ComplianceRule.fail(
"SOC2-CC6.6-SG",
"Security groups required for network access control",
"Configure security groups to restrict traffic between components."
));
} else {
rules.add(ComplianceRule.pass("SOC2-CC6.6-SG", "Security groups configured"));
}
// CC6.7: SSL/TLS must be enabled for encrypted data transmission
if (!ctx.cfc.enableSsl()) {
rules.add(ComplianceRule.fail(
"SOC2-CC6.7-SSL",
"SSL/TLS must be enabled for encrypted data transmission (CC6.7)",
"Set enableSsl=true for production SOC2 compliance."
));
} else {
rules.add(ComplianceRule.pass("SOC2-CC6.7-SSL", "SSL/TLS enabled for data transmission"));
// CC6.7: Encryption in transit - TLS certificate (only checked if SSL is enabled)
// Note: Private CA certificates (when no domain is specified) are automatically created
boolean hasUserCert = ctx.cert.get().isPresent();
boolean usePrivateCa = ctx.cfc.domain() == null && ctx.cfc.fqdn() == null;
if (!hasUserCert && !usePrivateCa) {
rules.add(ComplianceRule.fail(
"SOC2-CC6.7-TLS",
"TLS certificate required for encrypted data transmission",
"Configure a domain with HTTPS certificate or use Private CA (SSL without domain)."
));
} else {
String certType = usePrivateCa ? "Private CA certificate" : "TLS certificate";
rules.add(ComplianceRule.pass("SOC2-CC6.7-TLS", certType + " configured"));
}
}
if (!config.isEfsEncryptionInTransitEnabled()) {
rules.add(ComplianceRule.fail(
"SOC2-CC6.7-EFS",
"Encryption in transit required for internal data transfer",
"Enable EFS in-transit encryption."
));
} else {
rules.add(ComplianceRule.pass("SOC2-CC6.7-EFS", "EFS in-transit encryption enabled"));
}
// CC6.6: WAF for web application protection (recommended)
if (!config.isWafEnabled()) {
rules.add(ComplianceRule.fail(
"SOC2-CC6.6-WAF",
"Web Application Firewall recommended for production systems",
"Enable WAF to protect against common web attacks."
));
} else {
rules.add(ComplianceRule.pass("SOC2-CC6.6-WAF", "WAF protection enabled"));
}
return rules;
}
/**
* CC7.2: System Monitoring.
* The entity monitors system components and the operation of those components
* for anomalies indicative of malicious acts, natural disasters, and errors.
*/
private List<ComplianceRule> validateSystemMonitoring(SystemContext ctx) {
List<ComplianceRule> rules = new ArrayList<>();
var config = ctx.securityProfileConfig.get().orElseThrow(
() -> new IllegalStateException("SecurityProfileConfiguration not set")
);
// CC7.2: Security monitoring advisory (per ComplianceMatrix - recommended but not required)
if (config.isSecurityMonitoringEnabled()) {
rules.add(ComplianceRule.pass("SOC2-CC7.2-Monitoring", "Security monitoring enabled"));
} else {
rules.add(ComplianceRule.advisory("SOC2-CC7.2-Monitoring",
"Security monitoring is advisory for SOC2 (recommended but not required)",
"Enable securityMonitoringEnabled for CloudWatch alarms on anomalous activity."));
}
// CC7.2: Threat detection
// NOTE: GuardDuty validation is now handled by ThreatProtectionRules using ComplianceMatrix
// which marks it as ADVISORY for SOC2 (recommended but not required)
if (config.isGuardDutyEnabled()) {
rules.add(ComplianceRule.pass("SOC2-CC7.2-GuardDuty", "GuardDuty threat detection enabled"));
} else {
rules.add(ComplianceRule.advisory("SOC2-CC7.2-GuardDuty",
"GuardDuty is advisory for SOC2 (recommended but not required)",
"Enable guardDutyEnabled for automated threat detection."));
}
// CC7.2: Log collection for monitoring
if (!config.isCloudTrailEnabled()) {
rules.add(ComplianceRule.fail(
"SOC2-CC7.2-CloudTrail",
"Audit logging required for system monitoring",
"CloudTrailEnabledRule",
"Enable CloudTrail to record all API activity."
));
} else {
rules.add(ComplianceRule.pass("SOC2-CC7.2-CloudTrail", "CloudTrail audit logging enabled", "CloudTrailEnabledRule"));
}
if (!config.isFlowLogsEnabled()) {
rules.add(ComplianceRule.fail(
"SOC2-CC7.2-FlowLogs",
"Network monitoring required",
"Enable VPC Flow Logs to monitor network traffic."
));
} else {
rules.add(ComplianceRule.pass("SOC2-CC7.2-FlowLogs", "VPC Flow Logs enabled"));
}
// CC7.2: Configuration compliance monitoring
if (!config.isAwsConfigEnabled()) {
rules.add(ComplianceRule.fail(
"SOC2-CC7.2-Config",
"Configuration compliance monitoring recommended",
"Enable AWS Config to detect configuration drift and non-compliant resources."
));
} else {
rules.add(ComplianceRule.pass("SOC2-CC7.2-Config", "AWS Config compliance monitoring enabled"));
}
// CC7.2: Log retention for audit trail
// SOC2 requires adequate log retention for security monitoring and incident investigation
// SOC2 standard: 365 days (ONE_YEAR) minimum for audit trails
var retentionDays = config.getLogRetentionDays();
if (!isRetentionSufficient(retentionDays)) {
String currentRetention = (retentionDays != null) ? retentionDays.toString() : "not set";
rules.add(ComplianceRule.fail(
"SOC2-CC7.2-LogRetention",
"Log retention must be at least 365 days for audit trails (SOC2 CC7.2)",
"CloudWatchLogGroupRetention",
"Log retention must be at least 365 days (ONE_YEAR). Current: " +
currentRetention + ". " +
"SOC2 CC7.2 requires adequate log retention for security monitoring and incident investigation."
));
} else {
rules.add(ComplianceRule.pass(
"SOC2-CC7.2-LogRetention",
"Log retention meets 365-day requirement (SOC2 CC7.2)",
"CloudWatchLogGroupRetention"
));
}
return rules;
}
/**
* Check if log retention meets SOC2 requirement (365 days / 1 year minimum).
* CC7.2: System monitoring requires adequate log retention for audit trails.
* SOC2 requires minimum 1 year retention for security and audit logs.
*/
private boolean isRetentionSufficient(RetentionDays retention) {
// SOC2 CC7.2 requires 1 year minimum retention for security monitoring and audit trails
// Reference: SOC2 CC7.2, AICPA Trust Services Criteria
return retention == RetentionDays.ONE_YEAR ||
retention == RetentionDays.THIRTEEN_MONTHS ||
retention == RetentionDays.EIGHTEEN_MONTHS ||
retention == RetentionDays.TWO_YEARS ||
retention == RetentionDays.THREE_YEARS ||
retention == RetentionDays.FIVE_YEARS ||
retention == RetentionDays.SIX_YEARS ||
retention == RetentionDays.SEVEN_YEARS ||
retention == RetentionDays.EIGHT_YEARS ||
retention == RetentionDays.NINE_YEARS ||
retention == RetentionDays.TEN_YEARS ||
retention == RetentionDays.INFINITE;
}
/**
* CC8.1: Change Management.
* The entity authorizes, designs, develops or acquires, configures, documents,
* tests, approves, and implements changes to infrastructure, data, software,
* and procedures to meet its objectives.
*/
private List<ComplianceRule> validateChangeManagement(SystemContext ctx) {
List<ComplianceRule> rules = new ArrayList<>();
var config = ctx.securityProfileConfig.get().orElseThrow(
() -> new IllegalStateException("SecurityProfileConfiguration not set")
);
// CC8.1: Change tracking through CloudTrail
if (!config.isCloudTrailEnabled()) {
rules.add(ComplianceRule.fail(
"SOC2-CC8.1-CloudTrail",
"CloudTrail required for change tracking and audit",
"CloudTrailEnabledRule",
"Enable CloudTrail to record all infrastructure changes."
));
} else {
rules.add(ComplianceRule.pass("SOC2-CC8.1-CloudTrail", "CloudTrail change tracking enabled", "CloudTrailEnabledRule"));
}
// CC8.1: Configuration change detection
if (!config.isAwsConfigEnabled()) {
rules.add(ComplianceRule.fail(
"SOC2-CC8.1-Config",
"AWS Config recommended for change detection",
"Enable AWS Config to track configuration changes and maintain compliance."
));
} else {
rules.add(ComplianceRule.pass("SOC2-CC8.1-Config", "AWS Config change detection enabled"));
}
// CC8.1: Infrastructure as Code provides change control
rules.add(ComplianceRule.pass("SOC2-CC8.1-IaC", "Infrastructure as Code (CDK) provides version-controlled change management"));
LOG.info("SOC 2 CC8.1: Infrastructure as Code (CDK) provides version-controlled change management");
return rules;
}
/**
* Controls that {@link ComplianceMatrix} marks REQUIRED for SOC2 and that the checks above do not
* already cover. Each check reads the security profile, so it fails when the profile leaves a
* required control off. A control the matrix does not mark REQUIRED for SOC2 produces no rule.
*/
List<ComplianceRule> validateMatrixControls(
SecurityProfileConfiguration config,
SecurityProfile profile,
RuntimeType runtime,
AuthMode authMode,
boolean databaseProvisioned) {
List<ComplianceRule> rules = new ArrayList<>();
// CC6.1: multi-factor authentication for authenticated access
if (authMode != AuthMode.NONE) {
addRequired(rules, ComplianceMatrix.SecurityControl.AUTHENTICATION, "SOC2-CC6.1-MFA",
"Multi-factor authentication required for user access (CC6.1)",
"Require MFA on the identity provider that fronts the application.",
config.isMfaRequired());
}
// CC6.6: instance metadata protection applies to EC2 only; Fargate has no instance metadata service
if (runtime == RuntimeType.EC2) {
addRequired(rules, ComplianceMatrix.SecurityControl.EC2_IMDSV2, "SOC2-CC6.6-IMDSv2",
"EC2 instances must require IMDSv2 (CC6.1/CC6.6)",
"Set imdsv2Required=true.",
config.isImdsv2Required());
}
// Log-data confidentiality, anomaly detection, DNS visibility and audit-trail integrity are
// production controls: the STAGING profile leaves them off by design ("optional for testing").
if (profile == SecurityProfile.PRODUCTION) {
addRequired(rules, ComplianceMatrix.SecurityControl.CLOUDWATCH_LOGS_KMS_ENCRYPTION,
"SOC2-CC6.1-LogEncryption",
"CloudWatch log groups must be encrypted with a customer-managed KMS key (CC6.1)",
"Enable cloudWatchLogsKmsEncryptionEnabled.",
config.isCloudWatchLogsKmsEncryptionEnabled());
addRequired(rules, ComplianceMatrix.SecurityControl.CLOUDTRAIL_INSIGHTS, "SOC2-CC7.2-CloudTrailInsights",
"CloudTrail Insights required to detect anomalous API activity (CC7.2)",
"Enable CloudTrail Insights.",
config.isCloudTrailInsightsEnabled());
addRequired(rules, ComplianceMatrix.SecurityControl.ROUTE53_QUERY_LOGGING, "SOC2-CC7.2-Route53QueryLogging",
"Route53 DNS query logging required for network monitoring (CC7.2)",
"Enable Route53 query logging.",
config.isRoute53QueryLoggingEnabled());
addRequired(rules, ComplianceMatrix.SecurityControl.S3_OBJECT_LOCK, "SOC2-CC7.2-AuditLogImmutability",
"S3 Object Lock required so audit logs cannot be altered or deleted (CC7.2)",
"Enable S3 Object Lock on the audit-log buckets.",
config.isS3ObjectLockEnabled());
// CC7.2: continuous, automated evidence collection for the controls above -- either
// AWS Audit Manager or AWS Config's per-framework Conformance-Pack-style rules
// (ComplianceFactory's createAllFrameworkConfigRules and friends, already deployed
// whenever isAwsConfigEnabled() is true) satisfy this. Audit Manager is no longer a
// safe sole requirement: AWS closed it to new accounts as of April 30, 2026 (existing
// per-account setups still work, but a fresh account can never enable it), and AWS's
// own migration guidance for this exact change names Config Conformance Packs as the
// replacement. isAuditManagerEnabled() now reads
// DeploymentConfig#auditManagerServiceEnabled, a field independent of
// DeploymentConfig#auditManagerEnabled (the master gate SecurityRules.install() uses
// to decide whether any FrameworkRules validation runs at all) -- so this check is no
// longer circular the way it would be against that flag.
addRequired(rules, ComplianceMatrix.SecurityControl.AUDIT_MANAGER, "SOC2-CC7.2-AuditManager",
"Continuous compliance evidence collection required (CC7.2): AWS Audit Manager or "
+ "AWS Config",
"Enable auditManagerServiceEnabled (only if Audit Manager is already set up in "
+ "this account -- AWS closed it to new accounts as of April 30, 2026) or "
+ "awsConfigEnabled.",
config.isAuditManagerEnabled() || config.isAwsConfigEnabled());
// CC7.2: log retention -- the matrix doesn't cite a specific period for SOC2 the way
// HIPAA (6 years) or FedRAMP (3 years) do, so this uses PCI-DSS's 1-year minimum as a
// reasonable floor for forensic analysis; SOC2 audits typically examine a trailing
// 12-month period.
addRequired(rules, ComplianceMatrix.SecurityControl.LOG_RETENTION, "SOC2-CC7.2-MatrixLogRetention",
"Log retention must be at least 1 year for forensic analysis (CC7.2)",
"Set logRetentionDays to at least 365.",
isRetentionSufficient(config.getLogRetentionDays()));
}
// A1.2/A1.3: database availability and protection, only when a database was actually provisioned
if (databaseProvisioned && profile == SecurityProfile.PRODUCTION) {
addRequired(rules, ComplianceMatrix.SecurityControl.DATABASE_MULTI_AZ, "SOC2-A1.2-DatabaseMultiAZ",
"RDS Multi-AZ required for database availability (A1.2)",
"Enable Multi-AZ on the RDS instance.",
config.isRdsDatabaseMultiAzEnabled());
addRequired(rules, ComplianceMatrix.SecurityControl.DELETION_PROTECTION,
"SOC2-A1.3-DatabaseDeletionProtection",
"RDS deletion protection required to prevent accidental data loss (A1.3)",
"Enable RDS deletion protection.",
config.isRdsDeletionProtectionEnabled());
}
return rules;
}
private static void addRequired(List<ComplianceRule> rules, ComplianceMatrix.SecurityControl control,
String ruleId, String description, String remediation, boolean enabled) {
if (!control.isRequired("SOC2")) {
return;
}
rules.add(enabled
? ComplianceRule.pass(ruleId, description)
: ComplianceRule.fail(ruleId, description, remediation));
}
/**
* Auto-scaling is only meaningful for an application that supports running several instances and
* is not deliberately pinned to one; a single-instance deployment has nothing to scale.
*
* @param specSupportsScaling whether the application spec supports more than one instance
* @param maxInstances the effective maximum instance count, or {@code null} when unspecified
*/
static boolean autoScalingApplies(boolean specSupportsScaling, Integer maxInstances) {
return specSupportsScaling && (maxInstances == null || maxInstances > 1);
}
/**
* A1.1, A1.2, A1.3: System Availability.
* The entity maintains, monitors, and evaluates system availability.
* (Only validated if organization claims Availability criteria)
*/
private List<ComplianceRule> validateAvailability(SystemContext ctx) {
List<ComplianceRule> rules = new ArrayList<>();
var config = ctx.securityProfileConfig.get().orElseThrow(
() -> new IllegalStateException("SecurityProfileConfiguration not set")
);
// Skip availability checks outside production and staging
if (ctx.security != SecurityProfile.PRODUCTION && ctx.security != SecurityProfile.STAGING) {
return rules;
}
// A1.2: High availability configuration
if (!config.isMultiAzEnforced()) {
rules.add(ComplianceRule.fail(
"SOC2-A1.2-MultiAZ",
"Multi-AZ deployment required for high availability in production",
"Deploy across multiple availability zones."
));
} else {
rules.add(ComplianceRule.pass("SOC2-A1.2-MultiAZ", "Multi-AZ high availability enabled"));
}
// A1.2: Auto-scaling for availability, for applications that can run more than one instance
Integer maxInstances = ctx.maxInstanceCapacity.get().orElse(ctx.cfc.maxInstanceCapacity());
boolean specSupportsScaling = ctx.applicationSpec.get()
.map(com.cloudforge.core.interfaces.ApplicationSpec::supportsAutoScaling).orElse(true);
if (autoScalingApplies(specSupportsScaling, maxInstances)) {
if (!config.isAutoScalingEnabled()) {
rules.add(ComplianceRule.fail(
"SOC2-A1.2-AutoScaling",
"Auto-scaling recommended for handling demand and maintaining availability",
"Enable auto-scaling to handle traffic spikes."
));
} else {
rules.add(ComplianceRule.pass("SOC2-A1.2-AutoScaling", "Auto-scaling enabled"));
}
}
// A1.3: Backup and recovery
if (!config.isAutomatedBackupEnabled()) {
rules.add(ComplianceRule.fail(
"SOC2-A1.3-Backup",
"Automated backups required for disaster recovery",
"Enable automated backups to ensure data can be recovered."
));
} else {
rules.add(ComplianceRule.pass("SOC2-A1.3-Backup", "Automated backups enabled"));
}
// Cross-region copy is a recommendation, so it never fails: it reports a pass only when a
// destination vault is configured and backups are actually copied there.
String destinationVault = ctx.cfc.backupCrossRegionVaultArn();
if (config.isCrossRegionBackupEnabled() && destinationVault != null && !destinationVault.isBlank()) {
rules.add(ComplianceRule.pass("SOC2-A1.3-CrossRegion", "Cross-region backup copy configured"));
} else {
rules.add(ComplianceRule.advisory("SOC2-A1.3-CrossRegion",
"Cross-region backup copy is a recommendation, not required",
"Set crossRegionBackupEnabled = true and backupCrossRegionVaultArn for geographic redundancy."));
}
return rules;
}
/**
* C1.1, C1.2: Confidentiality.
* The entity protects confidential information to meet commitments and system requirements.
* (Only validated if organization claims Confidentiality criteria)
*/
private List<ComplianceRule> validateConfidentiality(SystemContext ctx) {
List<ComplianceRule> rules = new ArrayList<>();
var config = ctx.securityProfileConfig.get().orElseThrow(
() -> new IllegalStateException("SecurityProfileConfiguration not set")
);
// C1.1: Encryption for confidential data
if (!config.isEbsEncryptionEnabled()) {
rules.add(ComplianceRule.fail(
"SOC2-C1.1-EBS",
"EBS encryption required for confidential data at rest",
"EbsEncryptionRule",
"Enable EBS encryption."
));
} else {
rules.add(ComplianceRule.pass("SOC2-C1.1-EBS", "EBS encryption enabled", "EbsEncryptionRule"));
}
if (!config.isEfsEncryptionAtRestEnabled()) {
rules.add(ComplianceRule.fail(
"SOC2-C1.1-EFS",
"EFS encryption required for confidential file storage",
"Enable EFS encryption at rest."
));
} else {
rules.add(ComplianceRule.pass("SOC2-C1.1-EFS", "EFS encryption enabled"));
}
if (!config.isS3EncryptionEnabled()) {
rules.add(ComplianceRule.fail(
"SOC2-C1.1-S3",
"S3 encryption required for confidential data in object storage",
"S3BucketEncryptionRule",
"Enable S3 encryption."
));
} else {
rules.add(ComplianceRule.pass("SOC2-C1.1-S3", "S3 encryption enabled", "S3BucketEncryptionRule"));
}
// C1.2: Access restrictions for confidential data
if (ctx.cfc.networkMode() == NetworkMode.PUBLIC) {
rules.add(ComplianceRule.fail(
"SOC2-C1.2-Network",
"Private network mode required for confidential data",
"Use 'private-with-nat' to restrict access to confidential systems."
));
} else {
rules.add(ComplianceRule.pass("SOC2-C1.2-Network", "Private network mode configured"));
}
return rules;
}
/**
* Controls checked across every {@code validate*} method above -- see {@link
* FrameworkRules#claimedControls}. Controls the matrix marks REQUIRED for SOC2 but left off
* this list belong to always-load cross-framework classes instead (CDN_SECURITY,
* CERTIFICATE_MANAGEMENT, CONTAINER_SECURITY, CREDENTIAL_ROTATION, DATABASE_ACCESS_CONTROL,
* DATABASE_LOGGING, DATABASE_PITR, INSTANCE_METADATA_SECURITY, LAMBDA_SECURITY,
* ROOT_ACCOUNT_PROTECTION, SNS_KMS_ENCRYPTION, API_SECURITY -- see KeyManagementRules,
* DatabaseSecurityRules, IamSecurityRules and similar), except LAMBDA_SECURITY and
* DATABASE_ACCESS_CONTROL (genuine gaps: no class anywhere checks them).
*/
@Override
public Set<String> claimedControls() {
return Set.of(
"ACCESS_CONTROL", "AUTHENTICATION", "ENCRYPTION_AT_REST", "NETWORK_SEGMENTATION",
"ENCRYPTION_IN_TRANSIT", "HTTPS_STRICT", "WAF_PROTECTION", "SECURITY_MONITORING",
"THREAT_DETECTION", "AUDIT_LOGGING", "NETWORK_FLOW_LOGS", "VULNERABILITY_MANAGEMENT",
"CHANGE_MANAGEMENT", "HIGH_AVAILABILITY", "BACKUP_RECOVERY",
"EC2_IMDSV2", "CLOUDWATCH_LOGS_KMS_ENCRYPTION", "CLOUDTRAIL_INSIGHTS",
"ROUTE53_QUERY_LOGGING", "S3_OBJECT_LOCK", "DATABASE_MULTI_AZ", "DELETION_PROTECTION",
"LOG_RETENTION", "AUDIT_MANAGER"
);
}
/**
* Generate SOC 2 Trust Services Criteria compliance report.
*/
public String generateComplianceReport(SystemContext ctx) {
StringBuilder report = new StringBuilder();
report.append("\n=== SOC 2 Trust Services Criteria Compliance Report ===\n\n");
var config = ctx.securityProfileConfig.get().orElseThrow(
() -> new IllegalStateException("SecurityProfileConfiguration not set")
);
report.append("Security Profile: ").append(ctx.security).append("\n");
report.append("Service Type: ").append(ctx.cfc.tier()).append("\n\n");
report.append("Common Criteria - Security (CC):\n");
report.append(" ✓ CC6.1 - Access Controls: ").append(ctx.iamProfile != null ? "ENABLED" : "DISABLED").append("\n");
report.append(" ✓ CC6.2 - Authentication: ").append(ctx.cfc.authMode() != AuthMode.NONE ? "ENABLED" : "DISABLED").append("\n");
report.append(" ✓ CC6.6 - Network Segmentation: ").append(ctx.vpc.get().isPresent() ? "ENABLED" : "DISABLED").append("\n");
report.append(" ✓ CC6.7 - Encryption in Transit: ").append(ctx.cfc.enableSsl() && (ctx.cert.get().isPresent() || (ctx.cfc.domain() == null && ctx.cfc.fqdn() == null)) ? "ENABLED" : "DISABLED").append("\n");
report.append(" ✓ CC7.2 - System Monitoring: ").append(config.isSecurityMonitoringEnabled() ? "ENABLED" : "DISABLED").append("\n");
report.append(" ✓ CC8.1 - Change Management: ").append(config.isCloudTrailEnabled() ? "ENABLED" : "DISABLED").append("\n");
report.append("\n");
if (ctx.security == SecurityProfile.PRODUCTION || ctx.security == SecurityProfile.STAGING) {
report.append("Availability Criteria (A):\n");
report.append(" ✓ A1.2 - High Availability: ").append(config.isMultiAzEnforced() ? "ENABLED" : "DISABLED").append("\n");
report.append(" ✓ A1.2 - Auto-Scaling: ").append(config.isAutoScalingEnabled() ? "ENABLED" : "DISABLED").append("\n");
report.append(" ✓ A1.3 - Backup & Recovery: ").append(config.isAutomatedBackupEnabled() ? "ENABLED" : "DISABLED").append("\n");
report.append("\n");
}
report.append("Confidentiality Criteria (C):\n");
report.append(" ✓ C1.1 - Encryption at Rest: ").append(config.isEbsEncryptionEnabled() ? "ENABLED" : "DISABLED").append("\n");
report.append(" ✓ C1.2 - Access Restrictions: ").append(ctx.cfc.networkMode() != NetworkMode.PUBLIC ? "ENABLED" : "DISABLED").append("\n");
report.append("\n");
report.append("Note: SOC 2 audit requires independent CPA firm examination.\n");
report.append("Type I: Design of controls | Type II: Operating effectiveness over time\n");
report.append("\n");
appendAdvisorySection(report, ctx);
return report.toString();
}
/**
* Recommendations for controls that are ADVISORY-tier (not blocking) and currently off.
* Runs the same checks {@link #install} does, so this reflects live findings, not a
* separate hand-maintained list.
*/
private void appendAdvisorySection(StringBuilder report, SystemContext ctx) {
List<ComplianceRule> rules = new ArrayList<>();
rules.addAll(validateAccessControls(ctx));
rules.addAll(validateNetworkSecurity(ctx));
rules.addAll(validateSystemMonitoring(ctx));
rules.addAll(validateChangeManagement(ctx));
rules.addAll(validateAvailability(ctx));
rules.addAll(validateConfidentiality(ctx));
List<ComplianceRule> advisories = rules.stream().filter(ComplianceRule::isAdvisory).toList();
if (advisories.isEmpty()) {
return;
}
report.append("Recommendations (advisory, non-blocking):\n");
for (ComplianceRule rule : advisories) {
report.append(" - ").append(rule.ruleId()).append(": ").append(rule.description()).append("\n");
rule.recommendation().ifPresent(r -> report.append(" ").append(r).append("\n"));
}
report.append("\n");
}
}