/**
 * Symbolic Evaluation Test File - Java
 *
 * This file contains multiple source→sink flows for testing symbolic evaluation.
 * Each section demonstrates dangerous (unsafe) and safe patterns.
 *
 * Patterns covered:
 * - SQL injection (Statement vs PreparedStatement)
 * - Command injection (Runtime.exec)
 * - Path traversal (File operations)
 * - Spring annotations and request handling
 * - JDBC connection patterns
 * - Object creation and method chains
 */

package com.example.symbolic;

import java.io.*;
import java.sql.*;
import java.util.*;
import javax.servlet.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.*;

// =============================================================================
// SQL INJECTION PATTERNS
// =============================================================================

@Repository
public class UserRepository {
    private Connection connection;

    public UserRepository(Connection connection) {
        this.connection = connection;
    }

    // UNSAFE: String concatenation with user input
    public User getUserUnsafeConcat(String userId) throws SQLException {
        String query = "SELECT * FROM users WHERE id = '" + userId + "'";
        Statement stmt = connection.createStatement();
        ResultSet rs = stmt.executeQuery(query);  // Sink: danger
        return mapUser(rs);
    }

    // UNSAFE: String format with user input
    public User getUserUnsafeFormat(String username) throws SQLException {
        String query = String.format("SELECT * FROM users WHERE username = '%s'", username);
        Statement stmt = connection.createStatement();
        ResultSet rs = stmt.executeQuery(query);  // Sink: danger
        return mapUser(rs);
    }

    // SAFE: PreparedStatement with parameters
    public User getUserSafePrepared(String userId) throws SQLException {
        String query = "SELECT * FROM users WHERE id = ?";
        PreparedStatement stmt = connection.prepareStatement(query);
        stmt.setString(1, userId);  // Safe: parameterized
        ResultSet rs = stmt.executeQuery();
        return mapUser(rs);
    }

    // SAFE: Constant query
    public List<User> getAllUsersSafe() throws SQLException {
        String query = "SELECT * FROM users";
        Statement stmt = connection.createStatement();
        ResultSet rs = stmt.executeQuery(query);  // Safe: constant
        return mapUsers(rs);
    }

    private User mapUser(ResultSet rs) throws SQLException {
        if (rs.next()) {
            return new User(rs.getString("id"), rs.getString("username"));
        }
        return null;
    }

    private List<User> mapUsers(ResultSet rs) throws SQLException {
        List<User> users = new ArrayList<>();
        while (rs.next()) {
            users.add(new User(rs.getString("id"), rs.getString("username")));
        }
        return users;
    }
}

// =============================================================================
// COMMAND INJECTION PATTERNS
// =============================================================================

@Service
public class CommandService {
    // UNSAFE: Runtime.exec with concatenated command
    public String executeUnsafeConcat(String filename) throws IOException {
        String command = "cat " + filename;
        Process process = Runtime.getRuntime().exec(command);  // Sink: danger
        return readProcessOutput(process);
    }

    // UNSAFE: ProcessBuilder with shell
    public String executeUnsafeProcessBuilder(String path) throws IOException {
        ProcessBuilder pb = new ProcessBuilder("sh", "-c", "ls " + path);
        Process process = pb.start();  // Sink: danger
        return readProcessOutput(process);
    }

    // SAFE: ProcessBuilder with array (no shell interpretation)
    public String executeSafeArray(String filename) throws IOException {
        ProcessBuilder pb = new ProcessBuilder("cat", filename);
        Process process = pb.start();  // Safer: no shell
        return readProcessOutput(process);
    }

    // SAFE: Constant command
    public String listDirectorySafe() throws IOException {
        Process process = Runtime.getRuntime().exec("ls -la");  // Safe
        return readProcessOutput(process);
    }

    private String readProcessOutput(Process process) throws IOException {
        BufferedReader reader = new BufferedReader(
            new InputStreamReader(process.getInputStream())
        );
        StringBuilder output = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            output.append(line).append("\n");
        }
        return output.toString();
    }
}

// =============================================================================
// FILE OPERATIONS PATTERNS
// =============================================================================

@Service
public class FileService {
    private String basePath;

    public FileService(String basePath) {
        this.basePath = basePath;
    }

    // UNSAFE: Path concatenation
    public String readFileUnsafe(String filename) throws IOException {
        String path = basePath + "/" + filename;
        return new String(java.nio.file.Files.readAllBytes(
            java.nio.file.Paths.get(path)  // Sink: path traversal
        ));
    }

    // UNSAFE: File constructor with user input
    public String readFileUnsafeFile(String filename) throws IOException {
        File file = new File(basePath, filename);
        FileReader reader = new FileReader(file);  // Sink: danger
        BufferedReader br = new BufferedReader(reader);
        return br.readLine();
    }

    // SAFE: Validated path
    public String readFileSafe(String filename) throws IOException {
        if (filename.contains("..") || filename.startsWith("/")) {
            throw new SecurityException("Invalid filename");
        }
        String path = basePath + "/" + filename;
        return new String(java.nio.file.Files.readAllBytes(
            java.nio.file.Paths.get(path)  // Safe after validation
        ));
    }
}

// =============================================================================
// SPRING REST CONTROLLER (Sources)
// =============================================================================

@RestController
@RequestMapping("/api")
public class UserController {
    private UserRepository userRepository;
    private CommandService commandService;
    private FileService fileService;

    // UNSAFE: RequestParam to SQL
    @GetMapping("/user/unsafe")
    public User getUserUnsafe(@RequestParam String id) throws SQLException {
        return userRepository.getUserUnsafeConcat(id);  // Source → Sink
    }

    // SAFE: RequestParam with PreparedStatement
    @GetMapping("/user/safe")
    public User getUserSafe(@RequestParam String id) throws SQLException {
        return userRepository.getUserSafePrepared(id);  // Safe
    }

    // UNSAFE: PathVariable to command
    @GetMapping("/exec/unsafe/{filename}")
    public String execUnsafe(@PathVariable String filename) throws IOException {
        return commandService.executeUnsafeConcat(filename);  // Danger
    }

    // UNSAFE: RequestBody to file read
    @PostMapping("/file/unsafe")
    public String readFileUnsafe(@RequestBody FileRequest request) throws IOException {
        return fileService.readFileUnsafe(request.getFilename());  // Danger
    }
}

// =============================================================================
// VARIABLE PROPAGATION PATTERNS
// =============================================================================

@Service
public class DataProcessor {
    // UNSAFE: Assignment propagates danger
    public String processUnsafe(String input) {
        String processed = input;  // Assignment propagates
        String transformed = processed.toUpperCase();  // Method on dangerous
        return transformed;
    }

    // UNSAFE: Multiple assignments
    public String chainAssignmentUnsafe(String value) {
        String a = value;
        String b = a;
        String c = b;
        return c;  // Danger propagates through chain
    }

    // SAFE: Reassignment to constant
    public String processInputSafe(String input) {
        String processed = input;
        processed = "constant_value";  // Reassignment breaks danger
        return processed;
    }
}

// =============================================================================
// CONTROL FLOW PATTERNS
// =============================================================================

@Service
public class ControlFlowService {
    // UNSAFE: Danger through conditional
    public String conditionalUnsafe(String userInput) {
        String result;
        if (userInput != null && userInput.length() > 0) {
            result = userInput;  // Danger in true branch
        } else {
            result = "default";
        }
        return result;
    }

    // UNSAFE: Danger in loop
    public String loopUnsafe(List<String> items) {
        StringBuilder result = new StringBuilder();
        for (String item : items) {
            result.append(item);  // Danger accumulates
        }
        return result.toString();
    }

    // SAFE: Both branches are constants
    public String conditionalSafe(boolean flag) {
        return flag ? "option_a" : "option_b";  // Both safe
    }
}

// =============================================================================
// SUPPORTING CLASSES
// =============================================================================

class User {
    private String id;
    private String username;

    public User(String id, String username) {
        this.id = id;
        this.username = username;
    }

    public String getId() { return id; }
    public String getUsername() { return username; }
}

class FileRequest {
    private String filename;

    public String getFilename() { return filename; }
    public void setFilename(String filename) { this.filename = filename; }
}
