mirror of
https://github.com/getml/sqlgen.git
synced 2026-01-05 17:09:50 -06:00
Added regex pattern validation
This commit is contained in:
@@ -20,6 +20,8 @@
|
||||
|
||||
[sqlgen::Literal](literal.md) - How to define fields that only allow for a fixed set of values.
|
||||
|
||||
[sqlgen::Pattern](pattern.md) - How to add regex pattern validation to avoid SQL injection.
|
||||
|
||||
## Supported databases
|
||||
|
||||
[postgres](postgres.md) - How to interact with PostgreSQL or a related database (Redshift, Aurora, Greenplum...).
|
||||
|
||||
110
docs/pattern.md
Normal file
110
docs/pattern.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# `sqlgen::Pattern`
|
||||
|
||||
The `sqlgen::Pattern` class provides a type-safe way to validate string fields against regular expressions in C++. It's particularly useful for preventing SQL injection by ensuring data conforms to expected patterns before it reaches the database.
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Definition
|
||||
|
||||
Define a pattern using a regular expression and a descriptive name:
|
||||
|
||||
```cpp
|
||||
// Define a pattern for usernames (alphanumeric with underscores)
|
||||
using Username = sqlgen::Pattern<"[a-zA-Z0-9_]+", "username">;
|
||||
|
||||
struct User {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
Username username;
|
||||
sqlgen::Email email;
|
||||
};
|
||||
```
|
||||
|
||||
### Built-in Patterns
|
||||
|
||||
SQLGen provides several commonly used patterns out of the box:
|
||||
|
||||
```cpp
|
||||
// Alphanumeric strings
|
||||
sqlgen::AlphaNumeric name;
|
||||
|
||||
// Base64 encoded strings
|
||||
sqlgen::Base64Encoded encoded_data;
|
||||
|
||||
// Email addresses
|
||||
sqlgen::Email email;
|
||||
|
||||
// UUID formats
|
||||
sqlgen::UUIDv1 uuid1;
|
||||
sqlgen::UUIDv2 uuid2;
|
||||
sqlgen::UUIDv3 uuid3;
|
||||
sqlgen::UUIDv4 uuid4;
|
||||
```
|
||||
|
||||
### Assignment and Validation
|
||||
|
||||
Assign values to pattern-validated fields:
|
||||
|
||||
```cpp
|
||||
struct Person {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
sqlgen::AlphaNumeric first_name;
|
||||
sqlgen::AlphaNumeric last_name;
|
||||
int age;
|
||||
};
|
||||
|
||||
const auto person = Person{
|
||||
.id = 1,
|
||||
.first_name = "Homer", // Valid: contains only alphanumeric characters
|
||||
.last_name = "Simpson", // Valid: contains only alphanumeric characters
|
||||
.age = 45
|
||||
};
|
||||
```
|
||||
|
||||
### Accessing Values
|
||||
|
||||
Access the underlying string value:
|
||||
|
||||
```cpp
|
||||
const auto person = Person{
|
||||
.first_name = "Homer",
|
||||
.last_name = "Simpson"
|
||||
};
|
||||
|
||||
// Get the value
|
||||
const std::string& value1 = person.first_name();
|
||||
const std::string& value2 = person.first_name.get();
|
||||
const std::string& value3 = person.first_name.value();
|
||||
```
|
||||
|
||||
## SQL Injection Prevention
|
||||
|
||||
Pattern validation is a crucial security feature that helps prevent SQL injection attacks by:
|
||||
|
||||
1. Enforcing strict input validation before data reaches the database
|
||||
2. Ensuring data conforms to expected formats
|
||||
3. Rejecting potentially malicious input that doesn't match the pattern
|
||||
|
||||
For example, using `AlphaNumeric` for usernames prevents injection of SQL commands:
|
||||
|
||||
```cpp
|
||||
struct User {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
sqlgen::AlphaNumeric username; // Rejects: "admin'; DROP TABLE users;--"
|
||||
sqlgen::Email email; // Rejects: "invalid@email'; DELETE FROM users;--"
|
||||
};
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Pattern validation occurs at runtime when values are assigned
|
||||
- The template parameters are:
|
||||
- `_regex`: The regular expression pattern to match against
|
||||
- `_name`: A descriptive name for the pattern (used in error messages)
|
||||
- The class supports:
|
||||
- Direct string assignment with validation
|
||||
- Multiple access methods for the underlying value
|
||||
- Reflection for SQL operations
|
||||
- Move and copy semantics
|
||||
- Built-in patterns provide common validation rules
|
||||
- Custom patterns can be defined for specific use cases
|
||||
- Pattern validation is an important part of a defense-in-depth security strategy
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "sqlgen/Iterator.hpp"
|
||||
#include "sqlgen/IteratorBase.hpp"
|
||||
#include "sqlgen/Literal.hpp"
|
||||
#include "sqlgen/Pattern.hpp"
|
||||
#include "sqlgen/PrimaryKey.hpp"
|
||||
#include "sqlgen/Range.hpp"
|
||||
#include "sqlgen/Ref.hpp"
|
||||
@@ -14,6 +15,7 @@
|
||||
#include "sqlgen/col.hpp"
|
||||
#include "sqlgen/limit.hpp"
|
||||
#include "sqlgen/order_by.hpp"
|
||||
#include "sqlgen/patterns.hpp"
|
||||
#include "sqlgen/read.hpp"
|
||||
#include "sqlgen/where.hpp"
|
||||
#include "sqlgen/write.hpp"
|
||||
|
||||
15
include/sqlgen/Pattern.hpp
Normal file
15
include/sqlgen/Pattern.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef SQLGEN_PATTERN_HPP_
|
||||
#define SQLGEN_PATTERN_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <rfl::internal::StringLiteral _regex,
|
||||
rfl::internal::StringLiteral _name>
|
||||
using Pattern = rfl::Pattern<_regex, _name>;
|
||||
|
||||
}; // namespace sqlgen
|
||||
|
||||
#endif
|
||||
|
||||
25
include/sqlgen/patterns.hpp
Normal file
25
include/sqlgen/patterns.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef SQLGEN_PATTERNS_HPP_
|
||||
#define SQLGEN_PATTERNS_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
using AlphaNumeric = rfl::AlphaNumeric;
|
||||
|
||||
using Base64Encoded = rfl::Base64Encoded;
|
||||
|
||||
using Email = rfl::Email;
|
||||
|
||||
using UUIDv1 = rfl::UUIDv1;
|
||||
|
||||
using UUIDv2 = rfl::UUIDv2;
|
||||
|
||||
using UUIDv3 = rfl::UUIDv3;
|
||||
|
||||
using UUIDv4 = rfl::UUIDv4;
|
||||
|
||||
} // namespace sqlgen
|
||||
|
||||
#endif
|
||||
|
||||
39
tests/sqlite/test_alpha_numeric.cpp
Normal file
39
tests/sqlite/test_alpha_numeric.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/sqlite.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_alpha_numeric {
|
||||
|
||||
struct Person {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
sqlgen::AlphaNumeric first_name;
|
||||
sqlgen::AlphaNumeric last_name;
|
||||
int age;
|
||||
};
|
||||
|
||||
TEST(sqlite, test_alpha_numeric) {
|
||||
const auto people1 = std::vector<Person>(
|
||||
{Person{
|
||||
.id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45},
|
||||
Person{.id = 1, .first_name = "Bart", .last_name = "Simpson", .age = 10},
|
||||
Person{.id = 2, .first_name = "Lisa", .last_name = "Simpson", .age = 8},
|
||||
Person{
|
||||
.id = 3, .first_name = "Maggie", .last_name = "Simpson", .age = 0}});
|
||||
|
||||
const auto conn = sqlgen::sqlite::connect();
|
||||
|
||||
sqlgen::write(conn, people1);
|
||||
|
||||
const auto people2 = sqlgen::read<std::vector<Person>>(conn).value();
|
||||
|
||||
const auto json1 = rfl::json::write(people1);
|
||||
const auto json2 = rfl::json::write(people2);
|
||||
|
||||
EXPECT_EQ(json1, json2);
|
||||
}
|
||||
|
||||
} // namespace test_alpha_numeric
|
||||
Reference in New Issue
Block a user