diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 00000000..27350812 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2026-08-10 - [Fix SQL LIKE Wildcard Injection in Profile Lookup] +**Vulnerability:** Unescaped wildcard characters in the `did` input for the `LIKE '%:' || $1` clause inside `get_profile`. +**Learning:** The SQL `LIKE` clause doesn't automatically escape '%' and '_' even if they are passed as bound parameters via sqlx. This can lead to wildcard injection attacks and unexpected query matches. +**Prevention:** Whenever user input is dynamically included within a `LIKE` condition, explicitly escape '%', '_', and '\' characters in Rust, and use `ESCAPE '\\'` explicitly within the query. diff --git a/crates/gitlawb-node/src/db/mod.rs b/crates/gitlawb-node/src/db/mod.rs index 7d74449b..d93ace24 100644 --- a/crates/gitlawb-node/src/db/mod.rs +++ b/crates/gitlawb-node/src/db/mod.rs @@ -3511,12 +3511,14 @@ impl Db { } pub async fn get_profile(&self, did: &str) -> Result> { + let escaped_did = did.replace('\\', "\\\\").replace('%', "\\%").replace('_', "\\_"); let row = sqlx::query( "SELECT did, display_name, bio, avatar_url, website, socials, profile_cid, created_at, updated_at FROM agent_profiles - WHERE did = $1 OR did LIKE '%:' || $1", + WHERE did = $1 OR did LIKE '%:' || $2 ESCAPE '\\'", ) .bind(did) + .bind(&escaped_did) .fetch_optional(&self.pool) .await?;