1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/*!
Test Postgres-compatible statements against a set of CORS-like rules.

## Why

Postgres has a rich `ROLE` system for managing privileges around data in a database.
But those privileges are often too permissive by default, and are difficult to restrict with by statement or function name.

[`postguard`](#) provides a [`Guard`] statement analyzer for protecting databases from malicious or invalid queries.
This [`Guard`] can be used in any Rust application that has access to statements (perhaps from untrusted sources) before they are run.
Under the hood, [`postguard`](#) uses the [`libpg_query`](https://github.com/pganalyze/libpg_query) library to parse queries down to a syntax tree
before checking the entire tree for disallowed nodes.

## Usage

```rust
use postguard::{AllowedFunctions, AllowedStatements, Command, Guard};

// If AllowedFunctions and AllowedStatements both are set to their 'All' variants
// then no parsing is done and all statements pass the guard
#[test]
fn it_does_nothing_by_default() {
    Guard::new(AllowedStatements::All, AllowedFunctions::All)
        .guard("create table test (id serial primary key)")
        .expect("all statements are permitted by default");
}

// Statements are checked against the list of allowed statements when a 'List' variant
// is provided. Statement-checking is done recursively, so nested disallowed statements
// are also caught by the guard
#[test]
fn it_restricts_statements() {
    let statement_guard = Guard::new(AllowedStatements::List(vec![Command::Select]));

    statement_guard
        .guard("create table test (id serial primary key)")
        .expect_err("privileged statements are restricted");

    statement_guard
        .guard("select * from items")
        .expect("select statements are permitted");

    statement_guard
        .guard("insert into items default values")
        .expect_err("commands in top-level queries are restricted");

    statement_guard
        .guard("
            with cte as (
                insert into items default values
                returning id
            )
            select * from cte
        ")
        .expect_err("disallowed commands in nested queries or expressions are restricted");
}

// Functions are also guarded by name. To disallow all functions, leave the 'List' empty.
#[test]
fn it_restricts_functions() {
    let statement_guard = Guard::new(
        AllowedStatements::List(vec![Command::Select]),
        AllowedFunctions::List(vec!["uuid_generate_v4".to_string()])
    );

    statement_guard
        .guard("select pg_sleep(1)")
        .expect_err("built-in functions and stored procs are restricted");

    statement_guard
        .guard("select uuid_generate_v4() as id")
        .expect("allowed functions are permitted");
}
```
*/
#![deny(missing_docs, unreachable_pub)]

mod error;
mod guard;
#[cfg(test)]
mod test;

pub use error::*;
pub use guard::*;