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

クローズ
#22,912 コメント 1 件 リアクション 0 件 担当者 1 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@hvitved がすでに取り組んでいます。

2026年10月28日 から。

評価

この issue はまだ評価されていません。

説明

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.

主要言語
CodeQL
スター
10.3k
フォーク
2.1k
平均マージ
2日 2時間
マージ済み PR(30日)
123

環境構築

Codespaces で開く

このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

github/codeql のほかの issue

github/codeql の issue をすべて見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。