Skip to content
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

What is the recommended way to pass untrusted strings to the AST builder? #1447

Open
tecywiz121 opened this issue Dec 18, 2023 · 1 comment
Labels
question Further information is requested

Comments

@tecywiz121
Copy link

#1219 is one approach for preventing SQL injection, but since gluesql has the AST bulider, I'm sure there's a safe way to pass untrusted strings into queries. I'm just not sure what that is.

Here's what I've found that "works", but I want to make sure it's the proper approach:

use gluesql::core::ast_builder::*;
use gluesql::memory_storage::MemoryStorage;
use gluesql::prelude::{Error, Glue, Value};

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Error> {
    let storage = MemoryStorage::default();
    let mut glue = Glue::new(storage);

    table("foobar")
        .create_table()
        .add_column("message TEXT")
        .execute(&mut glue)
        .await?;

    let message = r#"
        "Even the smallest person can change the course of the future."
            — Galadriel, The Fellowship of the Ring
    "#;

    // Naive approach:
    let err = table("foobar")
        .insert()
        .columns("message")
        .values(vec![message])
        .execute(&mut glue)
        .await
        .unwrap_err();
    println!();
    println!("naive approach failed with: \n\t{}", err);
    println!();

    // "Working" approach:
    table("foobar")
        .insert()
        .columns("message")
        .values(vec![ExprList::from(
            &[ExprNode::QuotedString(message.into())] as &[ExprNode],
        )])
        .execute(&mut glue)
        .await?;

    let result = table("foobar").select().execute(&mut glue).await?;

    let first = result.select().unwrap().next().unwrap();

    let text = match first.get("message") {
        Some(Value::Str(s)) => s,
        _ => unreachable!(),
    };

    println!("select result:\n\t{}", text);

    Ok(())
}

An example showing how to pass untrusted values safely would be greatly appreciated!

@panarch
Copy link
Member

panarch commented Jan 7, 2024

An example showing how to pass untrusted values safely would be greatly appreciated!
👍 thanks, it would be certainly good to add this.

there exists some sugar functions you can consider to use.
currently we don't have parameter binding but there is safe way when we use ast builder.
e.g.

pub fn col<'a, T: Into<Cow<'a, str>>>(value: T) -> ExprNode<'a> {
ExprNode::Identifier(value.into())
}
pub fn num<'a, T: Into<NumericNode<'a>>>(value: T) -> ExprNode<'a> {
ExprNode::Numeric(value.into())
}
pub fn text<'a, T: Into<Cow<'a, str>>>(value: T) -> ExprNode<'a> {
ExprNode::QuotedString(value.into())
}
pub fn date<'a, T: Into<Cow<'a, str>>>(date: T) -> ExprNode<'a> {
ExprNode::TypedString {
data_type: DataType::Date,
value: date.into(),
}
}
pub fn timestamp<'a, T: Into<Cow<'a, str>>>(timestamp: T) -> ExprNode<'a> {
ExprNode::TypedString {
data_type: DataType::Timestamp,
value: timestamp.into(),
}
}
pub fn time<'a, T: Into<Cow<'a, str>>>(time: T) -> ExprNode<'a> {
ExprNode::TypedString {
data_type: DataType::Time,
value: time.into(),
}
}
pub fn subquery<'a, T: Into<QueryNode<'a>>>(query_node: T) -> ExprNode<'a> {
ExprNode::Subquery(Box::new(query_node.into()))
}
pub fn null() -> ExprNode<'static> {
ExprNode::Expr(Cow::Owned(Expr::Literal(AstLiteral::Null)))
}

@panarch panarch added the question Further information is requested label Jan 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants