Skip to content

Rust: Implement a new query for Log Injection #20221

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Aug 13, 2025

This PR implements a comprehensive CodeQL query for detecting log injection vulnerabilities in Rust code, following the patterns established by existing log injection queries for other languages.

Overview

Log injection occurs when user-controlled data is included in log output without proper sanitization, allowing malicious users to forge log entries. This is particularly dangerous when logs are processed by automated systems or displayed in web interfaces.

Implementation

The query follows the same architecture as the existing rust/sql-injection query and includes:

Core Components

  • LogInjectionExtensions.qll: Defines sources, sinks, and barriers using ActiveThreatModelSource for sources and the existing sinkNode(this, "log-injection") infrastructure
  • LogInjection.ql: Main query with taint tracking configuration that detects data flow from user input to logging functions
  • LogInjection.qhelp: Documentation explaining the vulnerability with Rust-specific examples and remediation guidance

Examples

  • LogInjectionBad.rs: Demonstrates vulnerable code where user input flows directly to logging functions
  • LogInjectionGood.rs: Shows secure implementation with proper input sanitization

Test Coverage

  • Comprehensive test suite with 20 test cases covering various scenarios:
    • Direct logging through different log levels (info, warn, error, debug, trace)
    • Formatted strings and string concatenation with user input
    • Indirect flows through variables, function parameters, and data structures
    • Flow through collections (Vec, Option, Result)
    • Both vulnerable and safe patterns

Example Detection

use log::info;
use std::env;

fn main() {
    let username = env::args().nth(1).unwrap_or_default();
    
    // BAD: User input directly in log message
    info!("User login: {}", username); // Query will flag this
    
    // GOOD: Sanitized input
    let clean_username = username.replace('\n', "").replace('\r', "");
    info!("User login: {}", clean_username); // Safe
}

If a malicious user provides Guest\n[INFO] Admin logged in\n as input, the vulnerable code would create a forged log entry appearing to show an admin login.

Query Details

  • ID: rust/log-injection
  • Severity: Error (7.8 security severity)
  • Precision: Medium
  • Kind: Path-problem (shows data flow paths)

The implementation leverages existing logging sink models in rust/ql/lib/codeql/rust/frameworks/log.model.yml which already define appropriate sinks for the log crate and standard I/O functions.

Fixes github/codeql-team#4178.


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

Copilot AI and others added 2 commits August 13, 2025 18:09
Co-authored-by: geoffw0 <40627776+geoffw0@users.noreply.github.com>
Co-authored-by: geoffw0 <40627776+geoffw0@users.noreply.github.com>
@Copilot Copilot AI changed the title [WIP] Rust: Implement a new query for Log Injection Rust: Implement a new query for Log Injection Aug 13, 2025
@Copilot Copilot AI requested a review from geoffw0 August 13, 2025 18:14
Copilot finished work on behalf of geoffw0 August 13, 2025 18:14
@github-actions github-actions bot added documentation Rust Pull requests that update Rust code labels Aug 13, 2025
Copy link
Contributor

github-actions bot commented Aug 14, 2025

QHelp previews:

rust/ql/src/queries/security/CWE-117/LogInjection.qhelp

Log injection

If unsanitized user input is written to a log entry, a malicious user may be able to forge new log entries.

Forgery can occur if a user provides some input with characters that are interpreted when the log output is displayed. If the log is displayed as a plain text file, then new line characters can be used by a malicious user. If the log is displayed as HTML, then arbitrary HTML may be included to spoof log entries.

Recommendation

User input should be suitably sanitized before it is logged.

If the log entries are in plain text then line breaks should be removed from user input, using String::replace or similar. Care should also be taken that user input is clearly marked in log entries.

For log entries that will be displayed in HTML, user input should be HTML-encoded before being logged, to prevent forgery and other forms of HTML injection.

Example

In the first example, a username, provided by the user via command line arguments, is logged using the log crate. If a malicious user provides Guest\n[INFO] User: Admin\n as a username parameter, the log entry will be split into multiple lines, where the second line will appear as [INFO] User: Admin, potentially forging a legitimate admin login entry.

use std::env;
use log::info;

fn main() {
    env_logger::init();

    // Get username from command line arguments
    let args: Vec<String> = env::args().collect();
    let username = args.get(1).unwrap_or(&String::from("Guest")).clone();

    // BAD: log message constructed with unsanitized user input
    info!("User login attempt: {}", username);
}

In the second example, String::replace is used to ensure no line endings are present in the user input before logging.

use std::env;
use log::info;

fn sanitize_for_logging(input: &str) -> String {
    // Remove newlines and carriage returns to prevent log injection
    input.replace('\n', "").replace('\r', "")
}

fn main() {
    env_logger::init();

    // Get username from command line arguments
    let args: Vec<String> = env::args().collect();
    let username = args.get(1).unwrap_or(&String::from("Guest")).clone();

    // GOOD: log message constructed with sanitized user input
    let sanitized_username = sanitize_for_logging(username.as_str());
    info!("User login attempt: {}", sanitized_username);
}

References

Copy link
Contributor

@geoffw0 geoffw0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot This looks great - amazing work!

There are a few things to clean up (see my other comments). Then I will try the query out for myself and get others involved in reviewing.

Co-authored-by: geoffw0 <40627776+geoffw0@users.noreply.github.com>
@Copilot Copilot AI requested a review from geoffw0 August 14, 2025 13:16
Copilot finished work on behalf of geoffw0 August 14, 2025 13:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Rust Pull requests that update Rust code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants