Developer
News and Updates
Get Support
Sign in
Get Support
Sign in
DOCUMENTATION
Cloud
Data Center
Resources
Sign in
Sign in
DOCUMENTATION
Cloud
Data Center
Resources
Sign in
Last updated Sep 25, 2025

Phase 3: Maximum security (Allowlist mode)

Goal: Implement zero-trust security with explicit type allowlisting

Timeline: 2-3 weeks

Security improvement: Maximum - only explicitly allowed types can be processed

This phase implements the highest level of security by migrating to allowlist mode, where only explicitly allowed types can be processed.

Step 3.1: Prepare allowlist configuration

Based on Phase 2 analysis, create allowlist configurations:

1
2
public class XStreamAllowlistConfig {
    
    public static void configureAllowlist(XStream xstream) {
        // Core application types (from Phase 2 analysis)
        xstream.allowTypes(new Class<?>[] {
            UserProfile.class,
            UserPreferences.class,
            ApplicationConfig.class,
            DatabaseConfig.class,
            SessionData.class,
            CacheEntry.class
        });
        
        // Essential framework types
        xstream.allowTypes(new Class<?>[] {
            ArrayList.class,
            LinkedList.class,
            HashMap.class,
            LinkedHashMap.class,
            HashSet.class,
            Date.class,
            BigDecimal.class,
            UUID.class,
            String.class,
            Integer.class,
            Long.class,
            Boolean.class
        });
        
        // Safe hierarchies (use judiciously)
        xstream.allowTypeHierarchy(Collection.class);
        xstream.allowTypeHierarchy(Map.class);
        xstream.allowTypeHierarchy(Number.class);
        xstream.allowTypeHierarchy(Enum.class);
    }
}

Step 3.2: Create environment-specific configs

Different environments may need different allowlist configurations:

Production configuration (minimal)

1
2
public class ProductionXStreamConfig {
    
    public static void configureProduction(XStream xstream) {
        // Minimal set for production
        xstream.allowTypes(PRODUCTION_CORE_TYPES);
        xstream.allowTypes(ESSENTIAL_FRAMEWORK_TYPES);
        
        // Conservative hierarchy allowlisting
        xstream.allowTypeHierarchy(Collection.class);
        xstream.allowTypeHierarchy(Map.class);
        
        // No test or development specific types
    }
    
    private static final Class<?>[] PRODUCTION_CORE_TYPES = {
        UserProfile.class,
        ApplicationConfig.class,
        SessionData.class
        // Minimal set for production use
    };
}

Development configuration (broader)

1
2
public class DevelopmentXStreamConfig {
    
    public static void configureDevelopment(XStream xstream) {
        // Include production types
        ProductionXStreamConfig.configureProduction(xstream);
        
        // Add development-specific types
        xstream.allowTypes(DEVELOPMENT_TYPES);
        xstream.allowTypes(TEST_FIXTURE_TYPES);
        
        // Broader hierarchies for development convenience
        xstream.allowTypeHierarchy(Serializable.class);
    }
    
    private static final Class<?>[] DEVELOPMENT_TYPES = {
        DebugInfo.class,
        TestDataBuilder.class,
        MockObject.class
        // Development and testing specific types
    };
}

Step 3.3: Gradual allowlist migration

Migrate one service/component at a time to minimize risk:

Service-by-service migration

1
2
@Configuration
public class GradualMigrationConfig {
    
    @Value("${migration.phase3.services:}")
    private Set<String> migratedServices;
    
    @Bean
    public XStream conditionalXStream() {
        String serviceName = getCurrentServiceName();
        
        if (migratedServices.contains(serviceName)) {
            // This service is migrated to allowlist mode
            return createAllowlistXStream();
        } else {
            // Still using blocklist mode
            return createBlocklistXStream();
        }
    }
    
    private XStream createAllowlistXStream() {
        XStream xstream = new BlocklistRestrictedXStream();
        // NO AnyTypePermission.ANY - this is allowlist mode
        XStreamAllowlistConfig.configureAllowlist(xstream);
        return xstream;
    }
    
    private XStream createBlocklistXStream() {
        XStream xstream = new BlocklistRestrictedXStream();
        xstream.addPermission(AnyTypePermission.ANY); // Still blocklist mode
        return xstream;
    }
}

Feature flag controlled migration

1
2
@Component
public class Phase3FeatureFlagService {
    
    @Value("${feature.allowlist-mode.enabled:false}")
    private boolean allowlistModeEnabled;
    
    @Value("${feature.allowlist-mode.percentage:0}")
    private int allowlistPercentage;
    
    public XStream createXStream(String context) {
        if (shouldUseAllowlistMode(context)) {
            return createAllowlistXStream(context);
        } else {
            return createBlocklistXStream();
        }
    }
    
    private boolean shouldUseAllowlistMode(String context) {
        if (!allowlistModeEnabled) {
            return false;
        }
        
        // Gradual rollout based on percentage
        int contextHash = Math.abs(context.hashCode());
        return (contextHash % 100) < allowlistPercentage;
    }
}

Step 3.4: Handle migration issues

Common issue patterns and solutions

Issue: CannotResolveClassException for legitimate type

1
2
// Problem: Type not in allowlist
try {
    Object result = xstream.fromXML(xml);
} catch (CannotResolveClassException e) {
    logger.warn("Type not in allowlist: {}", e.getMessage());
    // Solution: Add missing type to allowlist
}

// Fix: Add the missing type
public class AllowlistUpdater {
    
    public void addMissingType(XStream xstream, String className) {
        try {
            Class<?> clazz = Class.forName(className);
            
            // Validate it's safe to add
            if (isSafeToAllow(clazz)) {
                xstream.allowTypes(new Class<?>[] { clazz });
                logger.info("Added safe type to allowlist: {}", className);
            } else {
                logger.error("Unsafe type requested for allowlist: {}", className);
            }
        } catch (ClassNotFoundException e) {
            logger.error("Cannot load class for allowlist: {}", className);
        }
    }
    
    private boolean isSafeToAllow(Class<?> clazz) {
        // Check against security policies
        return !clazz.getName().contains("ProcessBuilder") &&
               !clazz.getName().contains("Runtime") &&
               // Add more safety checks
               true;
    }
}

Issue: Too many types to allowlist individually

1
2
// Solution: Use hierarchy allowlisting carefully
public class HierarchyAllowlistStrategy {
    
    public void allowSafeHierarchies(XStream xstream) {
        // Safe hierarchies that are commonly needed
        xstream.allowTypeHierarchy(Collection.class);
        xstream.allowTypeHierarchy(Map.class);
        xstream.allowTypeHierarchy(Number.class);
        xstream.allowTypeHierarchy(Enum.class);
        
        // Application-specific hierarchies
        xstream.allowTypeHierarchy(BaseDataObject.class);
        xstream.allowTypeHierarchy(ConfigurationItem.class);
    }
}

Issue: Dynamic or unknown types

1
2
// Solution: Handle dynamic types with careful analysis
public class DynamicTypeHandler {
    
    public void handleDynamicTypes(XStream xstream, Set<String> observedDynamicTypes) {
        for (String typeName : observedDynamicTypes) {
            if (isDynamicProxy(typeName)) {
                // Allow the interface, not the proxy
                String interfaceName = extractInterfaceName(typeName);
                allowTypeByName(xstream, interfaceName);
            } else if (isGenerated(typeName)) {
                // Handle generated classes carefully
                analyzeGeneratedType(xstream, typeName);
            }
        }
    }
    
    private boolean isDynamicProxy(String typeName) {
        return typeName.contains("$Proxy");
    }
    
    private void analyzeGeneratedType(XStream xstream, String typeName) {
        // Careful analysis of generated types
        logger.info("Analyzing generated type: {}", typeName);
        // Only allow if it's from trusted generation
    }
}

Step 3.5: Testing and validation

Comprehensive testing strategy

1
2
@SpringBootTest
@TestPropertySource(properties = {
    "migration.phase3.enabled=true",
    "xstream.mode=allowlist"
})
class Phase3MigrationTest {
    
    @Autowired
    private XStream xstream;
    
    @Test
    void shouldProcessAllowlistedTypes() {
        // Test all types that should be allowed
        for (Class<?> allowedType : getAllowedTypes()) {
            Object instance = createTestInstance(allowedType);
            
            String xml = xstream.toXML(instance);
            Object restored = xstream.fromXML(xml);
            
            assertThat(restored).isInstanceOf(allowedType);
        }
    }
    
    @Test
    void shouldRejectNonAllowlistedTypes() {
        // Test that non-allowlisted types are rejected
        String nonAllowlistedXml = createNonAllowlistedXml();
        
        assertThrows(CannotResolveClassException.class, () -> {
            xstream.fromXML(nonAllowlistedXml);
        });
    }
    
    @Test
    void shouldStillBlockDangerousTypes() {
        // Verify dangerous types remain blocked
        assertThrows(IllegalArgumentException.class, () -> {
            xstream.allowTypes(new Class<?>[] { ProcessBuilder.class });
        });
    }
}

Load testing with allowlist mode

1
2
@Test
public class Phase3LoadTest {
    
    @Test
    void shouldHandleLoadWithAllowlistMode() {
        XStream xstream = createAllowlistXStream();
        ExecutorService executor = Executors.newFixedThreadPool(10);
        
        List<Future<Boolean>> futures = new ArrayList<>();
        
        // Submit many concurrent operations
        for (int i = 0; i < 1000; i++) {
            futures.add(executor.submit(() -> {
                try {
                    Object testData = createTestData();
                    String xml = xstream.toXML(testData);
                    Object restored = xstream.fromXML(xml);
                    return restored != null;
                } catch (Exception e) {
                    logger.error("Load test operation failed", e);
                    return false;
                }
            }));
        }
        
        // Verify all operations completed successfully
        long successCount = futures.stream()
            .mapToLong(future -> {
                try {
                    return future.get() ? 1 : 0;
                } catch (Exception e) {
                    return 0;
                }
            })
            .sum();
        
        assertThat(successCount).isEqualTo(1000);
    }
}

Step 3.6: Production deployment

Blue-green deployment strategy

1
2
@Configuration
public class BlueGreenMigrationConfig {
    
    @Value("${deployment.color:blue}")
    private String deploymentColor;
    
    @Bean
    public XStream deploymentSpecificXStream() {
        if ("green".equals(deploymentColor)) {
            // Green deployment uses allowlist mode
            return createAllowlistXStream();
        } else {
            // Blue deployment still uses blocklist mode
            return createBlocklistXStream();
        }
    }
    
    private XStream createAllowlistXStream() {
        XStream xstream = new BlocklistRestrictedXStream();
        XStreamAllowlistConfig.configureAllowlist(xstream);
        return xstream;
    }
}

Monitoring and alerting

1
2
@Component
public class Phase3MonitoringService {
    
    private final MeterRegistry meterRegistry;
    private final AlertService alertService;
    
    @EventListener
    public void onCannotResolveClass(CannotResolveClassException e) {
        // Track types that are being rejected
        meterRegistry.counter("xstream.allowlist.rejected", 
            "class", e.getMessage(),
            "phase", "phase3").increment();
        
        // Alert on unexpected rejections
        if (isUnexpectedRejection(e)) {
            alertService.sendAlert("Unexpected type rejection in Phase 3: " + e.getMessage());
        }
    }
    
    @EventListener
    public void onSuccessfulProcessing(XStreamProcessingEvent e) {
        meterRegistry.counter("xstream.allowlist.success",
            "type", e.getResultType().getSimpleName(),
            "phase", "phase3").increment();
    }
}

Step 3.7: Rollback procedures

Emergency rollback to blocklist mode

1
2
@Component
public class EmergencyRollbackService {
    
    @Value("${emergency.rollback.enabled:false}")
    private boolean emergencyRollback;
    
    public XStream createXStreamWithRollback() {
        if (emergencyRollback) {
            logger.warn("Emergency rollback activated - using blocklist mode");
            return createEmergencyBlocklistXStream();
        }
        
        return createAllowlistXStream();
    }
    
    private XStream createEmergencyBlocklistXStream() {
        XStream xstream = new BlocklistRestrictedXStream();
        xstream.addPermission(AnyTypePermission.ANY); // Back to blocklist mode
        
        // Apply existing configuration
        applyLegacyConfiguration(xstream);
        
        return xstream;
    }
}

Gradual rollback strategy

1
2
public class GradualRollbackService {
    
    public XStream createXStreamWithGradualRollback(String context) {
        RollbackStrategy strategy = determineRollbackStrategy();
        
        switch (strategy) {
            case IMMEDIATE:
                return createBlocklistXStream();
            case GRADUAL:
                return shouldRollbackContext(context) ? 
                    createBlocklistXStream() : createAllowlistXStream();
            case NONE:
                return createAllowlistXStream();
            default:
                return createBlocklistXStream(); // Safe default
        }
    }
}

Success criteria for Phase 3

Phase 3 is complete when:

  1. All XStream instances use BlocklistRestrictedXStream in allowlist mode
  2. No AnyTypePermission.ANY in production configurations
  3. All legitimate types are explicitly allowed in allowlist
  4. Security tests pass with allowlist configuration
  5. Functional tests pass with allowlist configuration
  6. Production monitoring shows no unexpected failures
  7. Performance is acceptable under production load
  8. Security team approval has been obtained

Post-migration maintenance

Regular allowlist reviews

1
2
@Component
@ConditionalOnProperty("migration.maintenance.enabled")
public class AllowlistMaintenanceService {
    
    @Scheduled(cron = "0 0 2 * * MON") // Weekly review
    public void performAllowlistReview() {
        AllowlistReviewReport report = AllowlistReviewReport.builder()
            .timestamp(Instant.now())
            .currentAllowedTypes(getCurrentAllowedTypes())
            .actuallyUsedTypes(getActuallyUsedTypes())
            .unusedTypes(findUnusedTypes())
            .recommendations(generateRecommendations())
            .build();
        
        // Send to development team
        notificationService.sendReport(report);
        
        // Store for historical analysis
        reportStorage.storeReport(report);
    }
}

Documentation updates

1
2
/**
 * Post-Migration Documentation Template
 * 
 * Migration Completed: 2024-12-19
 * Mode: Allowlist (Maximum Security)
 * 
 * Current Allowlist:
 * - Application types: 15
 * - Framework types: 23
 * - Hierarchy allowlists: 4
 * 
 * Security Level: MAXIMUM
 * Last Security Review: 2024-12-19
 * Next Review Due: 2025-01-19
 * 
 * Maintenance Contact: Development Team
 * Security Contact: Security Team
 */

Congratulations! With Phase 3 complete, you now have maximum XStream security with zero-trust type processing.

Rate this page: