Rust: Axum State captured by a closure is treated as remote

Cerrado
#22,912 1 comentario 0 reacciones 1 asignado Ver en GitHub

Los mantenedores suelen responder en 1 día

@hvitved ya está trabajando en esto.

Desde el 28/10/2026.

Evaluación

Este issue todavía no se ha evaluado.

Descripción

Description of the false positive

rust/path-injection reports a configured path from State<Arc<Config>> when the handler destructures State in its parameter and captures the binding in a spawn_blocking closure. The state is initialized with a constant during router construction; it is not request-derived. Reading state.0 into a local Arc clone before the same closure produces no finding. A request-derived Json value captured by the same API correctly produces a finding.

This is a remaining case after github/codeql#22466 and the State field-taint exclusion. That exclusion is already present in the tested library; adding it again would not address this reproduction.

Versions

  • CodeQL CLI 2.27.3; codeql/rust-queries 0.1.45; codeql/rust-all 0.2.24.
  • Rust pack build: a1575b98a8cc19bcfa7bb6cb41be688cb3dae65c.
  • Axum 0.8.4; Tokio 1.54.1, with feature rt.
  • Stock rust/path-injection query; default remote threat model; no custom source, barrier, or suppression model.

Code sample

use axum::{extract::State, routing::{get, post}, Json, Router};
use std::sync::Arc;

struct Config {
    path: String,
}

async fn destructured(State(config): State<Arc<Config>>) {
    let _ = tokio::task::spawn_blocking(move || std::fs::read_to_string(&config.path)).await;
}

async fn local_clone(state: State<Arc<Config>>) {
    let config = Arc::clone(&state.0);
    let _ = tokio::task::spawn_blocking(move || std::fs::read_to_string(&config.path)).await;
}

async fn request(Json(path): Json<String>) {
    let _ = tokio::task::spawn_blocking(move || std::fs::read_to_string(path)).await;
}

fn main() {
    let _: Router = Router::new()
        .route("/destructured", get(destructured))
        .route("/local-clone", get(local_clone))
        .route("/request", post(request))
        .with_state(Arc::new(Config { path: String::from("configured-file") }));
}

Expected and observed

Handler Expected findings Observed findings
destructured 0 1 false positive
local_clone 0 0
request 1 1

The false-positive path is destructured registration → State parameter → closure capture of config → config.path → std::fs::read_to_string. Only the Json handler should be a remote-to-file finding.

Reproduction with the upstream QL test runner

Place the sample in test.rs in a new test directory below rust/ql/test in a checkout of the codeql-cli/v2.27.3 tag, and add:

# options.yml
qltest_cargo_check: true
qltest_dependencies:
  - axum = { version = "=0.8.4" }
  - tokio = { version = "=1.54.1", features = ["rt"] }
# TaintedPath.qlref
query: queries/security/CWE-022/TaintedPath.ql

Run codeql test run <test-directory> with the listed packs. The sample compiles and the stock query returns the two findings shown above. A regression expecting only the Json finding fails with the additional State-to-file flow. No scanner or query was disabled.

Lenguaje dominante
CodeQL
Estrellas
10.3k
Forks
2.1k
Merge medio
2 d 4 h
PR fusionados (30 d)
118

Preparar el entorno

Abrir en Codespaces

Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de github/codeql

Todos los issues de github/codeql

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.