From 34d1df3eb9e46326f75a142ba31263e211e382df Mon Sep 17 00:00:00 2001 From: Jayson Grace Date: Thu, 21 May 2026 14:31:23 -0600 Subject: [PATCH 01/12] ci(renovate): enable fork processing on l50/ares Renovate skips forks by default. l50/ares is the production target for this workflow run, so opt in via RENOVATE_FORK_PROCESSING=enabled. --- .github/workflows/renovate.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/renovate.yaml b/.github/workflows/renovate.yaml index 1a375b8c..c27febed 100644 --- a/.github/workflows/renovate.yaml +++ b/.github/workflows/renovate.yaml @@ -77,6 +77,8 @@ jobs: RENOVATE_AUTODISCOVER: true RENOVATE_AUTODISCOVER_FILTER: "${{ github.repository }}" RENOVATE_DRY_RUN: "${{ inputs.dryRun }}" + # Required: renovate refuses to process forks unless explicitly enabled. + RENOVATE_FORK_PROCESSING: enabled RENOVATE_INTERNAL_CHECKS_FILTER: strict RENOVATE_PLATFORM: github RENOVATE_PLATFORM_COMMIT: true From e8b95f907cdd03bb1e5f913ce37ec460eefbe38f Mon Sep 17 00:00:00 2001 From: Jayson Grace Date: Thu, 21 May 2026 14:59:06 -0600 Subject: [PATCH 02/12] feat: add remote hashcat backend support (#9) **Key Changes:** - Added optional remote cracking mode that delegates hashcat jobs to an HTTP service when configured - Implemented authenticated job submission, polling, timeout handling, and potfile retrieval for remote jobs - Preserved local hashcat execution as the default path when remote service configuration is absent - Scoped remote execution to simple wordlist attacks so service-owned GPU and wordlist resources remain isolated **Added:** - Remote hashcat client module - Adds HTTP integration for submitting jobs, polling job status, retrieving cracked results, handling bearer authentication, and normalizing local wordlist paths to remote-safe basenames - Remote service configuration support - Enables remote mode through HASHCAT_SERVICE_URL and requires HASHCAT_TOKEN for authenticated requests - Remote result handling - Returns crackd logs, potfile contents, remote errors, exit codes, and timeout failures through the existing ToolOutput structure **Changed:** - Hashcat cracking flow - Updates crack_with_hashcat to check for remote service configuration first and delegate to the remote backend when available, while keeping the existing local hashcat behavior unchanged otherwise --- ares-tools/src/cracker.rs | 6 + ares-tools/src/cracker/remote.rs | 190 +++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 ares-tools/src/cracker/remote.rs diff --git a/ares-tools/src/cracker.rs b/ares-tools/src/cracker.rs index 2f9c4d3e..844441c8 100644 --- a/ares-tools/src/cracker.rs +++ b/ares-tools/src/cracker.rs @@ -7,6 +7,8 @@ use crate::args::{optional_bool, optional_i64, optional_str, required_str}; use crate::executor::CommandBuilder; use crate::ToolOutput; +mod remote; + /// Default wordlists tried in order. const DEFAULT_WORDLISTS: &[&str] = &[ "/usr/share/wordlists/rockyou.txt", @@ -88,6 +90,10 @@ fn capitalize(s: &str) -> String { /// Tries multiple wordlists in order (rockyou, seclists). When `use_dynamic_wordlist` /// is true (default), also prepends a username-derived candidate list. pub async fn crack_with_hashcat(args: &Value) -> Result { + if let Some(url) = remote::service_url() { + return remote::crack(args, &url).await; + } + let hash_value = required_str(args, "hash_value")?; let explicit_wordlist = optional_str(args, "wordlist_path"); let explicit_rules = optional_str(args, "rules_file"); diff --git a/ares-tools/src/cracker/remote.rs b/ares-tools/src/cracker/remote.rs new file mode 100644 index 00000000..0415dfdd --- /dev/null +++ b/ares-tools/src/cracker/remote.rs @@ -0,0 +1,190 @@ +//! Remote hashcat backend. +//! +//! When `HASHCAT_SERVICE_URL` (and `HASHCAT_TOKEN`) are set in the cracker +//! agent's env, [`crack_with_hashcat`](super::crack_with_hashcat) delegates to +//! an HTTP service instead of spawning hashcat locally. The remote service +//! owns the GPU and the wordlist directory; the agent becomes a thin client. +//! +//! Expected service contract: +//! - `POST /jobs` with `{hash_mode, attack_mode, hashes[], wordlist?, mask?}` +//! and `Authorization: Bearer ` → `{job_id, status}`. +//! - `GET /jobs/{id}` → `{status, log_tail?, error?}` where status is one of +//! `starting | running | done | error`. +//! - `GET /jobs/{id}/potfile` → `{cracked: [":", ...]}`. +//! +//! Scope of remote mode: wordlist attack (`-a 0`) with a single wordlist by +//! basename. Rules-based and dynamic username wordlists stay local-only — +//! the service's wordlist directory is its own concern. + +use std::time::{Duration, Instant}; + +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +use crate::args::{optional_i64, optional_str, required_str}; +use crate::ToolOutput; + +use super::{detect_hashcat_mode, DEFAULT_MAX_TIME_MINUTES}; + +const DEFAULT_REMOTE_WORDLIST: &str = "rockyou.txt"; +const POLL_INTERVAL_SECS: u64 = 5; + +/// Returns the configured remote service URL, or `None` if remote mode is off. +pub(super) fn service_url() -> Option<String> { + std::env::var("HASHCAT_SERVICE_URL") + .ok() + .filter(|s| !s.is_empty()) +} + +fn service_token() -> Result<String> { + std::env::var("HASHCAT_TOKEN") + .context("HASHCAT_SERVICE_URL is set but HASHCAT_TOKEN is missing") +} + +fn http_client() -> reqwest::Client { + reqwest::Client::builder() + .timeout(Duration::from_secs(30)) + .build() + .unwrap_or_default() +} + +#[derive(Serialize)] +struct JobSubmission<'a> { + hash_mode: i64, + attack_mode: i64, + hashes: Vec<&'a str>, + #[serde(skip_serializing_if = "Option::is_none")] + wordlist: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + mask: Option<&'a str>, +} + +#[derive(Deserialize)] +struct JobIdResponse { + job_id: String, +} + +#[derive(Deserialize)] +struct JobStateResponse { + status: String, + #[serde(default)] + log_tail: String, + #[serde(default)] + error: Option<String>, +} + +#[derive(Deserialize, Default)] +struct PotfileResponse { + #[serde(default)] + cracked: Vec<String>, +} + +/// Take the basename of a path. Remote services typically refuse absolute +/// paths and only accept filenames within their own wordlist directory. +fn basename(path: &str) -> String { + std::path::Path::new(path) + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or(path) + .to_string() +} + +pub(super) async fn crack(args: &Value, base_url: &str) -> Result<ToolOutput> { + let hash_value = required_str(args, "hash_value")?; + let token = service_token()?; + let mode = + optional_i64(args, "hashcat_mode").unwrap_or_else(|| detect_hashcat_mode(hash_value)); + let max_time_minutes = optional_i64(args, "max_time_minutes") + .unwrap_or(DEFAULT_MAX_TIME_MINUTES) + .max(DEFAULT_MAX_TIME_MINUTES); + let max_time_secs = (max_time_minutes * 60) as u64; + let wordlist = optional_str(args, "wordlist_path") + .map(basename) + .unwrap_or_else(|| DEFAULT_REMOTE_WORDLIST.to_string()); + + let client = http_client(); + let url = base_url.trim_end_matches('/'); + + let submission = JobSubmission { + hash_mode: mode, + attack_mode: 0, + hashes: vec![hash_value], + wordlist: Some(wordlist), + mask: None, + }; + + // Submit. + let job_id = { + let resp = client + .post(format!("{url}/jobs")) + .bearer_auth(&token) + .json(&submission) + .send() + .await + .context("crackd: failed to POST /jobs")?; + let status = resp.status(); + let body = resp.text().await.unwrap_or_default(); + if !status.is_success() { + return Ok(ToolOutput { + stdout: String::new(), + stderr: format!("crackd submission failed ({status}): {body}"), + exit_code: Some(1), + success: false, + }); + } + serde_json::from_str::<JobIdResponse>(&body) + .context("crackd: unexpected /jobs response shape")? + .job_id + }; + + // Poll. + let started = Instant::now(); + let (terminal_status, last_log, last_error) = loop { + let resp = client + .get(format!("{url}/jobs/{job_id}")) + .bearer_auth(&token) + .send() + .await + .context("crackd: failed to GET /jobs/{id}")?; + let body = resp.text().await.unwrap_or_default(); + let state: JobStateResponse = + serde_json::from_str(&body).context("crackd: unexpected /jobs/{id} response shape")?; + if matches!(state.status.as_str(), "done" | "error") { + break (state.status, state.log_tail, state.error); + } + if started.elapsed().as_secs() > max_time_secs { + return Ok(ToolOutput { + stdout: state.log_tail, + stderr: format!("crackd job {job_id} exceeded {max_time_secs}s budget"), + exit_code: Some(124), + success: false, + }); + } + tokio::time::sleep(Duration::from_secs(POLL_INTERVAL_SECS)).await; + }; + + // Pull potfile — partial cracks are useful even on error. + let potfile: PotfileResponse = { + let resp = client + .get(format!("{url}/jobs/{job_id}/potfile")) + .bearer_auth(&token) + .send() + .await + .context("crackd: failed to GET /jobs/{id}/potfile")?; + resp.json().await.unwrap_or_default() + }; + + let stdout = format!( + "{last_log}\n--- crackd potfile ---\n{}", + potfile.cracked.join("\n") + ); + let success = terminal_status == "done"; + + Ok(ToolOutput { + stdout, + stderr: last_error.unwrap_or_default(), + exit_code: Some(if success { 0 } else { 1 }), + success, + }) +} From b012a140475c972016839b443aeae4dae25058c0 Mon Sep 17 00:00:00 2001 From: Jayson Grace <jayson.e.grace@gmail.com> Date: Thu, 21 May 2026 15:12:38 -0600 Subject: [PATCH 03/12] ci: rebuild templates when rust ares sources change (#10) --- .github/workflows/build-and-push-templates.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/build-and-push-templates.yaml b/.github/workflows/build-and-push-templates.yaml index 20ed2805..496ddb64 100644 --- a/.github/workflows/build-and-push-templates.yaml +++ b/.github/workflows/build-and-push-templates.yaml @@ -8,6 +8,15 @@ on: - 'warpgate-templates/**' - 'ansible/**' - '.github/workflows/build-and-push-templates.yaml' + # Template images bake the Rust `ares` binary from these crates; + # rebuild when their source changes too. + - 'ares-cli/**' + - 'ares-core/**' + - 'ares-llm/**' + - 'ares-rust/**' + - 'ares-tools/**' + - 'Cargo.toml' + - 'Cargo.lock' workflow_dispatch: inputs: template_filter: From a1e949701f136d099263fdcf78fc4110146e1b06 Mon Sep 17 00:00:00 2001 From: Jayson Grace <jayson.e.grace@gmail.com> Date: Thu, 21 May 2026 15:32:56 -0600 Subject: [PATCH 04/12] ci: automerge non-major renovate updates **Added:** - Renovate package rule to automerge patch and minor Cargo, Ansible Galaxy, Galaxy collection, and pre-commit updates via PR - .github/renovate.json5 --- .github/renovate.json5 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 9426f088..20604501 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -68,6 +68,21 @@ automerge: true, automergeType: 'pr', }, + { + description: 'Auto merge non-major Rust, Ansible Galaxy, and pre-commit updates', + matchManagers: [ + 'cargo', + 'galaxy', + 'galaxy-collection', + 'pre-commit', + ], + matchUpdateTypes: [ + 'patch', + 'minor', + ], + automerge: true, + automergeType: 'pr', + }, { description: 'Group opentelemetry-rust monorepo with tracing-opentelemetry so version bumps land together (tracing-opentelemetry pins a specific opentelemetry minor version, so they must update atomically)', matchPackageNames: [ From 338f346c3d6737feeb11a304daf66c1d80f791b6 Mon Sep 17 00:00:00 2001 From: "ares-renovate[bot]" <286782180+ares-renovate[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 21:34:49 +0000 Subject: [PATCH 05/12] chore(deps): update rust crate local-ip-address to v0.6.13 (#1) | datasource | package | from | to | | ---------- | ---------------- | ------ | ------ | | crate | local-ip-address | 0.6.12 | 0.6.13 | Co-authored-by: ares-renovate[bot] <286782180+ares-renovate[bot]@users.noreply.github.com> --- Cargo.lock | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d82c5b15..c40e6173 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,7 +62,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -73,7 +73,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -898,7 +898,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1815,9 +1815,9 @@ checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "local-ip-address" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7b0187df4e614e42405b49511b82ff7a1774fbd9a816060ee465067847cac22" +checksum = "aa08fb2b1ec3ea84575e94b489d06d4ce0cbf052d12acd515838f50e3c3d63e3" dependencies = [ "libc", "neli", @@ -1964,7 +1964,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2847,7 +2847,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2905,7 +2905,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3219,7 +3219,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -3534,7 +3534,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4281,7 +4281,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] From 2d71f455a6a06a0499bb69a39b5c4c1412b4b57c Mon Sep 17 00:00:00 2001 From: "ares-renovate[bot]" <286782180+ares-renovate[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 21:35:09 +0000 Subject: [PATCH 06/12] chore(deps): update rust crate serde_json to v1.0.150 (#2) | datasource | package | from | to | | ---------- | ---------- | ------- | ------- | | crate | serde_json | 1.0.149 | 1.0.150 | Co-authored-by: ares-renovate[bot] <286782180+ares-renovate[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c40e6173..1ae8850b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3023,9 +3023,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", From aeb91d4593417caa6d882c041e80c9af05ab8e83 Mon Sep 17 00:00:00 2001 From: "ares-renovate[bot]" <286782180+ares-renovate[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 17:20:24 -0600 Subject: [PATCH 07/12] chore(deps): update dependency ansible-core to v2.21.0 (#3) | datasource | package | from | to | | ---------- | ------------ | ------ | ------ | | pypi | ansible-core | 2.20.5 | 2.21.0 | Co-authored-by: ares-renovate[bot] <286782180+ares-renovate[bot]@users.noreply.github.com> --- .hooks/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.hooks/requirements.txt b/.hooks/requirements.txt index 5d094727..883c3ea1 100644 --- a/.hooks/requirements.txt +++ b/.hooks/requirements.txt @@ -1,4 +1,4 @@ -ansible-core==2.20.5 +ansible-core==2.21.0 ansible-lint==26.4.0 docker==7.1.0 docsible==0.8.0 From 6a7458a3d2309fc1111c9064dff8a776023de685 Mon Sep 17 00:00:00 2001 From: "ares-renovate[bot]" <286782180+ares-renovate[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 17:20:28 -0600 Subject: [PATCH 08/12] chore(deps): update dependency ansible.posix to v2.2.0 (#4) | datasource | package | from | to | | ----------------- | ------------- | ----- | ----- | | galaxy-collection | ansible.posix | 2.1.0 | 2.2.0 | Co-authored-by: ares-renovate[bot] <286782180+ares-renovate[bot]@users.noreply.github.com> --- ansible/requirements.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/requirements.yml b/ansible/requirements.yml index 925ac61a..c093911a 100644 --- a/ansible/requirements.yml +++ b/ansible/requirements.yml @@ -9,7 +9,7 @@ collections: - name: community.docker version: 5.2.0 - name: ansible.posix - version: 2.1.0 + version: 2.2.0 - name: community.general version: 12.6.1 - name: grafana.grafana From b5a797dafb67137fe0b91f354f6463f08e17bd43 Mon Sep 17 00:00:00 2001 From: "ares-renovate[bot]" <286782180+ares-renovate[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 17:20:31 -0600 Subject: [PATCH 09/12] chore(deps): update rust crate sqlx to 0.9 (#5) | datasource | package | from | to | | ---------- | ------- | ----- | ----- | | crate | sqlx | 0.8.6 | 0.9.0 | Co-authored-by: ares-renovate[bot] <286782180+ares-renovate[bot]@users.noreply.github.com> --- Cargo.lock | 327 +++++++++++++---------------------------------------- Cargo.toml | 2 +- 2 files changed, 78 insertions(+), 251 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ae8850b..3bdbddb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,7 +149,7 @@ dependencies = [ "bytes", "chrono", "futures", - "md-5 0.11.0", + "md-5", "opentelemetry", "opentelemetry-otlp", "opentelemetry_sdk", @@ -515,6 +515,12 @@ dependencies = [ "cc", ] +[[package]] +name = "cmov" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746" + [[package]] name = "colorchoice" version = "1.0.5" @@ -683,6 +689,15 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + [[package]] name = "curve25519-dalek" version = "4.1.3" @@ -815,9 +830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", - "const-oid 0.9.6", "crypto-common 0.1.7", - "subtle", ] [[package]] @@ -829,6 +842,7 @@ dependencies = [ "block-buffer 0.12.0", "const-oid 0.10.2", "crypto-common 0.2.1", + "ctutils", ] [[package]] @@ -903,13 +917,12 @@ dependencies = [ [[package]] name = "etcetera" -version = "0.8.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96" dependencies = [ "cfg-if", - "home", - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -953,9 +966,9 @@ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "flume" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" dependencies = [ "futures-core", "futures-sink", @@ -974,6 +987,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1211,10 +1230,19 @@ name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.2.0", ] [[package]] @@ -1225,11 +1253,11 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "hashlink" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230" dependencies = [ - "hashbrown 0.15.5", + "hashbrown 0.16.1", ] [[package]] @@ -1316,29 +1344,20 @@ dependencies = [ [[package]] name = "hkdf" -version = "0.12.4" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" dependencies = [ "hmac", ] [[package]] name = "hmac" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "home" -version = "0.5.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" -dependencies = [ - "windows-sys 0.61.2", + "digest 0.11.3", ] [[package]] @@ -1757,9 +1776,6 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] [[package]] name = "leb128fmt" @@ -1779,18 +1795,6 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" -[[package]] -name = "libredox" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" -dependencies = [ - "bitflags", - "libc", - "plain", - "redox_syscall 0.7.5", -] - [[package]] name = "libsqlite3-sys" version = "0.30.1" @@ -1854,16 +1858,6 @@ dependencies = [ "regex-automata", ] -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest 0.10.7", -] - [[package]] name = "md-5" version = "0.11.0" @@ -1986,22 +1980,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-bigint-dig" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" -dependencies = [ - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.6", - "smallvec", - "zeroize", -] - [[package]] name = "num-conv" version = "0.2.1" @@ -2017,17 +1995,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -2035,7 +2002,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", ] [[package]] @@ -2158,7 +2124,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.18", + "redox_syscall", "smallvec", "windows-link", ] @@ -2294,17 +2260,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - [[package]] name = "pkcs8" version = "0.10.2" @@ -2321,12 +2276,6 @@ version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" -[[package]] -name = "plain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" - [[package]] name = "portable-atomic" version = "1.13.1" @@ -2631,15 +2580,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "redox_syscall" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b" -dependencies = [ - "bitflags", -] - [[package]] name = "regex" version = "1.12.3" @@ -2773,26 +2713,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rsa" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" -dependencies = [ - "const-oid 0.9.6", - "digest 0.10.7", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "signature", - "spki", - "subtle", - "zeroize", -] - [[package]] name = "rstest" version = "0.26.1" @@ -3081,13 +3001,13 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" dependencies = [ "cfg-if", - "cpufeatures 0.2.17", - "digest 0.10.7", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -3243,9 +3163,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" +checksum = "378620ccc25c62c89d8be1c819e76a88d59bdcc3304733330788948e619bfd71" dependencies = [ "sqlx-core", "sqlx-macros", @@ -3256,12 +3176,13 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" +checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb" dependencies = [ "base64", "bytes", + "cfg-if", "chrono", "crc", "crossbeam-queue", @@ -3271,12 +3192,11 @@ dependencies = [ "futures-intrusive", "futures-io", "futures-util", - "hashbrown 0.15.5", + "hashbrown 0.16.1", "hashlink", "indexmap", "log", "memchr", - "once_cell", "percent-encoding", "serde", "serde_json", @@ -3292,9 +3212,9 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" +checksum = "bd2b84f2bc39a5705ef27ec785a11c934a41bbd4a24941e257927cddc26b60bf" dependencies = [ "proc-macro2", "quote", @@ -3305,15 +3225,15 @@ dependencies = [ [[package]] name = "sqlx-macros-core" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" +checksum = "fb8d96de5fdc85a5c4ec813432b523ec637e80ba98f046555f75f7908ddac7c3" dependencies = [ + "cfg-if", "dotenvy", "either", "heck", "hex", - "once_cell", "proc-macro2", "quote", "serde", @@ -3324,59 +3244,44 @@ dependencies = [ "sqlx-postgres", "sqlx-sqlite", "syn", + "thiserror", "tokio", "url", ] [[package]] name = "sqlx-mysql" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" +checksum = "90b8020fe17c5f2c245bfa2505d7ef59c5604839527c740266ad2214acebea27" dependencies = [ - "atoi", - "base64", "bitflags", "byteorder", "bytes", "chrono", "crc", - "digest 0.10.7", + "digest 0.11.3", "dotenvy", "either", - "futures-channel", "futures-core", - "futures-io", "futures-util", "generic-array", - "hex", - "hkdf", - "hmac", - "itoa", "log", - "md-5 0.10.6", - "memchr", - "once_cell", "percent-encoding", - "rand 0.8.6", - "rsa", "serde", "sha1", - "sha2 0.10.9", - "smallvec", + "sha2 0.11.0", "sqlx-core", - "stringprep", "thiserror", "tracing", "uuid", - "whoami", ] [[package]] name = "sqlx-postgres" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" +checksum = "87a2bdd6e83f6b3ea525ca9fee568030508b58355a43d0b2c1674d5f79dcd65e" dependencies = [ "atoi", "base64", @@ -3392,16 +3297,14 @@ dependencies = [ "hex", "hkdf", "hmac", - "home", "itoa", "log", - "md-5 0.10.6", + "md-5", "memchr", - "once_cell", - "rand 0.8.6", + "rand 0.10.1", "serde", "serde_json", - "sha2 0.10.9", + "sha2 0.11.0", "smallvec", "sqlx-core", "stringprep", @@ -3413,13 +3316,14 @@ dependencies = [ [[package]] name = "sqlx-sqlite" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" +checksum = "488e99c397a62007e4229aec669a179816339afc6d2620ca6fa420dbee2e982c" dependencies = [ "atoi", "chrono", "flume", + "form_urlencoded", "futures-channel", "futures-core", "futures-executor", @@ -3429,7 +3333,6 @@ dependencies = [ "log", "percent-encoding", "serde", - "serde_urlencoded", "sqlx-core", "thiserror", "tracing", @@ -4117,12 +4020,6 @@ dependencies = [ "wit-bindgen 0.51.0", ] -[[package]] -name = "wasite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" - [[package]] name = "wasm-bindgen" version = "0.2.121" @@ -4261,13 +4158,9 @@ dependencies = [ [[package]] name = "whoami" -version = "1.6.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" -dependencies = [ - "libredox", - "wasite", -] +checksum = "998767ef88740d1f5b0682a9c53c24431453923962269c2db68ee43788c5a40d" [[package]] name = "widestring" @@ -4354,15 +4247,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -4390,21 +4274,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -4438,12 +4307,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -4456,12 +4319,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -4474,12 +4331,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -4504,12 +4355,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -4522,12 +4367,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -4540,12 +4379,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -4558,12 +4391,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index 6a2aeeea..aa3c0c87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ anyhow = "1" clap = { version = "4.5.23", features = ["derive", "env"] } serde_yaml = "0.9" regex = "1" -sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "chrono", "json", "uuid"] } +sqlx = { version = "0.9", features = ["runtime-tokio", "postgres", "chrono", "json", "uuid"] } tera = "1" hickory-resolver = { version = "0.26", default-features = false, features = ["tokio", "system-config"] } From 2df7064a1d364188cc0a0a5514ef71b5982e308f Mon Sep 17 00:00:00 2001 From: "ares-renovate[bot]" <286782180+ares-renovate[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 17:20:35 -0600 Subject: [PATCH 10/12] chore(deps): update dependency community.general to v13 (#7) | datasource | package | from | to | | ----------------- | ----------------- | ------ | ------ | | galaxy-collection | community.general | 12.6.1 | 13.0.0 | Co-authored-by: ares-renovate[bot] <286782180+ares-renovate[bot]@users.noreply.github.com> --- ansible/requirements.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/requirements.yml b/ansible/requirements.yml index c093911a..496a355b 100644 --- a/ansible/requirements.yml +++ b/ansible/requirements.yml @@ -11,7 +11,7 @@ collections: - name: ansible.posix version: 2.2.0 - name: community.general - version: 12.6.1 + version: 13.0.0 - name: grafana.grafana version: 6.1.0 - name: https://github.com/CowDogMoo/ansible-collection-workstation.git From a062dbf38d90b3e95427e7b25630377d7d2c3b73 Mon Sep 17 00:00:00 2001 From: Jayson Grace <jayson.e.grace@gmail.com> Date: Thu, 21 May 2026 18:29:13 -0600 Subject: [PATCH 11/12] chore: remove opentelemetry renovate version cap **Removed:** - Removed the temporary Renovate allowedVersions cap that blocked opentelemetry Rust crates from updating to 0.32 and later versions --- .github/renovate.json5 | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 20604501..5f7ff778 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -94,16 +94,6 @@ ], groupName: 'opentelemetry', }, - { - description: 'Cap opentelemetry-rust monorepo crates below 0.32 until tracing-opentelemetry ships a release that depends on opentelemetry 0.32. Without this, renovate creates a partial group PR (opentelemetry 0.32 + tracing-opentelemetry 0.32.1, which still pins opentelemetry 0.31) that fails to compile due to two opentelemetry versions in the dep graph. Remove this cap once https://crates.io/crates/tracing-opentelemetry publishes a version supporting opentelemetry 0.32.', - matchPackageNames: [ - 'opentelemetry', - 'opentelemetry_sdk', - 'opentelemetry-otlp', - 'opentelemetry-semantic-conventions', - ], - allowedVersions: '<0.32', - }, ], customManagers: [ { From a50493a0971e2e40a4ae3c9619cc2bc376969981 Mon Sep 17 00:00:00 2001 From: Jayson Grace <jayson.e.grace@gmail.com> Date: Thu, 21 May 2026 20:46:53 -0600 Subject: [PATCH 12/12] fix: assert safety for dynamic sqlx history queries **Changed:** - Wrapped dynamically assembled history queries with `AssertSqlSafe` so sqlx accepts SQL built from static fragments with bound user values - `ares-cli/src/history` - Documented and applied the same safety assertion to credential hash search queries that construct placeholder lists dynamically - `ares-core/src/persistent_store/queries/credentials.rs` --- ares-cli/src/history/cost.rs | 3 ++- ares-cli/src/history/list.rs | 3 ++- ares-cli/src/history/search.rs | 5 +++-- ares-core/src/persistent_store/queries/credentials.rs | 11 +++++++---- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/ares-cli/src/history/cost.rs b/ares-cli/src/history/cost.rs index fcc4b7d1..511cbe2c 100644 --- a/ares-cli/src/history/cost.rs +++ b/ares-cli/src/history/cost.rs @@ -1,5 +1,6 @@ use anyhow::Result; use chrono::Utc; +use sqlx::AssertSqlSafe; use super::connect_postgres; use super::types::CostRow; @@ -36,7 +37,7 @@ pub(crate) async fn history_cost( bind_idx += 1; query.push_str(&format!(" ORDER BY started_at DESC LIMIT ${bind_idx}")); - let mut q = sqlx::query_as::<_, CostRow>(&query); + let mut q = sqlx::query_as::<_, CostRow>(AssertSqlSafe(query)); if let Some(ref d) = domain { q = q.bind(format!("%{d}%")); diff --git a/ares-cli/src/history/list.rs b/ares-cli/src/history/list.rs index 8ee154bd..b6c21e9e 100644 --- a/ares-cli/src/history/list.rs +++ b/ares-cli/src/history/list.rs @@ -1,5 +1,6 @@ use anyhow::Result; use chrono::Utc; +use sqlx::AssertSqlSafe; use super::connect_postgres; use super::types::OperationRow; @@ -48,7 +49,7 @@ pub(crate) async fn history_list( bind_idx += 1; query.push_str(&format!(" ORDER BY started_at DESC LIMIT ${bind_idx}")); - let mut q = sqlx::query_as::<_, OperationRow>(&query); + let mut q = sqlx::query_as::<_, OperationRow>(AssertSqlSafe(query)); if let Some(ref d) = domain { q = q.bind(format!("%{d}%")); diff --git a/ares-cli/src/history/search.rs b/ares-cli/src/history/search.rs index 449c639e..ed7352bb 100644 --- a/ares-cli/src/history/search.rs +++ b/ares-cli/src/history/search.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use sqlx::AssertSqlSafe; use super::connect_postgres; use super::types::{CredentialSearchRow, HashSearchRow}; @@ -40,7 +41,7 @@ pub(crate) async fn history_search_creds( bind_idx += 1; query.push_str(&format!(" ORDER BY c.created_at DESC LIMIT ${bind_idx}")); - let mut q = sqlx::query_as::<_, CredentialSearchRow>(&query); + let mut q = sqlx::query_as::<_, CredentialSearchRow>(AssertSqlSafe(query)); if let Some(ref d) = domain { q = q.bind(d); @@ -139,7 +140,7 @@ pub(crate) async fn history_search_hashes( bind_idx += 1; query.push_str(&format!(" ORDER BY h.created_at DESC LIMIT ${bind_idx}")); - let mut q = sqlx::query_as::<_, HashSearchRow>(&query); + let mut q = sqlx::query_as::<_, HashSearchRow>(AssertSqlSafe(query)); if let Some(ref d) = domain { q = q.bind(d); diff --git a/ares-core/src/persistent_store/queries/credentials.rs b/ares-core/src/persistent_store/queries/credentials.rs index 88356c7e..2a27b371 100644 --- a/ares-core/src/persistent_store/queries/credentials.rs +++ b/ares-core/src/persistent_store/queries/credentials.rs @@ -1,6 +1,7 @@ //! Credential and hash search queries across all operations. use anyhow::Result; +use sqlx::AssertSqlSafe; use super::rows::{CredentialRow, HashRow}; use super::HistoricalQueryService; @@ -198,17 +199,19 @@ impl HistoricalQueryService { ); // Bind dynamically — sqlx doesn't support dynamic binds easily, - // so we use query_scalar pattern with explicit bind count + // so we use query_scalar pattern with explicit bind count. + // SQL is built from static fragments plus $N placeholder indices only; + // user-controlled values are passed via .bind() — safe to assert. match bind_values.len() { 1 => { - sqlx::query_as::<_, HashRow>(&sql) + sqlx::query_as::<_, HashRow>(AssertSqlSafe(sql)) .bind(&bind_values[0]) .bind(limit) .fetch_all(&self.pool) .await? } 2 => { - sqlx::query_as::<_, HashRow>(&sql) + sqlx::query_as::<_, HashRow>(AssertSqlSafe(sql)) .bind(&bind_values[0]) .bind(&bind_values[1]) .bind(limit) @@ -216,7 +219,7 @@ impl HistoricalQueryService { .await? } 3 => { - sqlx::query_as::<_, HashRow>(&sql) + sqlx::query_as::<_, HashRow>(AssertSqlSafe(sql)) .bind(&bind_values[0]) .bind(&bind_values[1]) .bind(&bind_values[2])