mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-26 14:54:55 +00:00
code: hpp,dbal,cpp (16 files)
This commit is contained in:
@@ -14,21 +14,21 @@ namespace entities {
|
||||
namespace session {
|
||||
|
||||
/**
|
||||
* Delete a session by ID (logout)
|
||||
* Delete a session by ID
|
||||
*/
|
||||
inline Result<bool> remove(InMemoryStore& store, const std::string& id) {
|
||||
if (id.empty()) {
|
||||
return Error::validationError("Session ID cannot be empty");
|
||||
}
|
||||
|
||||
|
||||
auto it = store.sessions.find(id);
|
||||
if (it == store.sessions.end()) {
|
||||
return Error::notFound("Session not found: " + id);
|
||||
}
|
||||
|
||||
|
||||
store.session_tokens.erase(it->second.token);
|
||||
store.sessions.erase(it);
|
||||
|
||||
|
||||
return Result<bool>(true);
|
||||
}
|
||||
|
||||
|
||||
39
dbal/cpp/src/security/contains_sql_keyword.hpp
Normal file
39
dbal/cpp/src/security/contains_sql_keyword.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file contains_sql_keyword.hpp
|
||||
* @brief SQL keyword detection
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
namespace dbal::security {
|
||||
|
||||
/**
|
||||
* Check if string matches a SQL keyword (case-insensitive)
|
||||
* @param value String to check
|
||||
* @return true if matches SQL keyword
|
||||
*/
|
||||
inline bool contains_sql_keyword(const std::string& value) {
|
||||
static const char* keywords[] = {
|
||||
"SELECT", "INSERT", "UPDATE", "DELETE", "DROP", "CREATE", "ALTER",
|
||||
"TRUNCATE", "GRANT", "REVOKE", "UNION", "JOIN", "WHERE", "FROM",
|
||||
"TABLE", "DATABASE", "INDEX", "VIEW", "PROCEDURE", "FUNCTION",
|
||||
"TRIGGER", "EXEC", "EXECUTE", "SCHEMA", nullptr
|
||||
};
|
||||
|
||||
std::string upper = value;
|
||||
std::transform(upper.begin(), upper.end(), upper.begin(),
|
||||
[](unsigned char c) { return std::toupper(c); });
|
||||
|
||||
for (const char** kw = keywords; *kw != nullptr; ++kw) {
|
||||
if (upper == *kw) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace dbal::security
|
||||
20
dbal/cpp/src/security/generate_nonce.hpp
Normal file
20
dbal/cpp/src/security/generate_nonce.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file generate_nonce.hpp
|
||||
* @brief Secure nonce generation for replay attack prevention
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include "secure_random_hex.hpp"
|
||||
|
||||
namespace dbal::security {
|
||||
|
||||
/**
|
||||
* Generate a secure nonce (32 hex chars = 128 bits)
|
||||
* @return Cryptographically random nonce
|
||||
*/
|
||||
inline std::string generate_nonce() {
|
||||
return secure_random_hex(16);
|
||||
}
|
||||
|
||||
} // namespace dbal::security
|
||||
20
dbal/cpp/src/security/generate_request_id.hpp
Normal file
20
dbal/cpp/src/security/generate_request_id.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file generate_request_id.hpp
|
||||
* @brief Secure request ID generation
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include "secure_random_hex.hpp"
|
||||
|
||||
namespace dbal::security {
|
||||
|
||||
/**
|
||||
* Generate a secure request ID (32 hex chars = 128 bits)
|
||||
* @return Cryptographically random request ID
|
||||
*/
|
||||
inline std::string generate_request_id() {
|
||||
return secure_random_hex(16);
|
||||
}
|
||||
|
||||
} // namespace dbal::security
|
||||
20
dbal/cpp/src/security/generate_token.hpp
Normal file
20
dbal/cpp/src/security/generate_token.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file generate_token.hpp
|
||||
* @brief Secure token generation
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include "secure_random_hex.hpp"
|
||||
|
||||
namespace dbal::security {
|
||||
|
||||
/**
|
||||
* Generate a secure token (64 hex chars = 256 bits)
|
||||
* @return Cryptographically random token
|
||||
*/
|
||||
inline std::string generate_token() {
|
||||
return secure_random_hex(32);
|
||||
}
|
||||
|
||||
} // namespace dbal::security
|
||||
47
dbal/cpp/src/security/hmac_sha256.hpp
Normal file
47
dbal/cpp/src/security/hmac_sha256.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file hmac_sha256.hpp
|
||||
* @brief HMAC-SHA256 signature computation
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
namespace dbal::security {
|
||||
|
||||
/**
|
||||
* Compute HMAC-SHA256 signature
|
||||
* @param key Secret key bytes
|
||||
* @param key_len Length of key
|
||||
* @param data Data to sign
|
||||
* @return Hex-encoded signature
|
||||
*/
|
||||
inline std::string hmac_sha256(
|
||||
const unsigned char* key,
|
||||
size_t key_len,
|
||||
const std::string& data
|
||||
) {
|
||||
unsigned char result[EVP_MAX_MD_SIZE];
|
||||
unsigned int result_len = 0;
|
||||
|
||||
HMAC(
|
||||
EVP_sha256(),
|
||||
key, static_cast<int>(key_len),
|
||||
reinterpret_cast<const unsigned char*>(data.c_str()), data.size(),
|
||||
result, &result_len
|
||||
);
|
||||
|
||||
std::string hex;
|
||||
hex.reserve(result_len * 2);
|
||||
|
||||
static const char hex_chars[] = "0123456789abcdef";
|
||||
for (unsigned int i = 0; i < result_len; ++i) {
|
||||
hex += hex_chars[(result[i] >> 4) & 0x0F];
|
||||
hex += hex_chars[result[i] & 0x0F];
|
||||
}
|
||||
|
||||
return hex;
|
||||
}
|
||||
|
||||
} // namespace dbal::security
|
||||
33
dbal/cpp/src/security/is_safe_filename.hpp
Normal file
33
dbal/cpp/src/security/is_safe_filename.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file is_safe_filename.hpp
|
||||
* @brief Filename safety validation
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace dbal::security {
|
||||
|
||||
/**
|
||||
* Check if a filename is safe (no path separators, no special names)
|
||||
* @param filename Filename to validate
|
||||
* @return true if safe
|
||||
*/
|
||||
inline bool is_safe_filename(const std::string& filename) {
|
||||
if (filename.empty()) return false;
|
||||
|
||||
if (filename.find('/') != std::string::npos) return false;
|
||||
if (filename.find('\\') != std::string::npos) return false;
|
||||
|
||||
if (filename == "." || filename == "..") return false;
|
||||
|
||||
if (filename.find('\0') != std::string::npos) return false;
|
||||
|
||||
for (char c : filename) {
|
||||
if (static_cast<unsigned char>(c) < 32) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dbal::security
|
||||
39
dbal/cpp/src/security/is_valid_identifier.hpp
Normal file
39
dbal/cpp/src/security/is_valid_identifier.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file is_valid_identifier.hpp
|
||||
* @brief Database identifier validation
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
|
||||
namespace dbal::security {
|
||||
|
||||
/**
|
||||
* Validate a string identifier (table names, column names, etc.)
|
||||
* Only allows: a-z, A-Z, 0-9, underscore. Must start with letter/underscore.
|
||||
*
|
||||
* @param identifier String to validate
|
||||
* @param max_length Maximum allowed length (default 64)
|
||||
* @return true if valid
|
||||
*/
|
||||
inline bool is_valid_identifier(const std::string& identifier, size_t max_length = 64) {
|
||||
if (identifier.empty() || identifier.size() > max_length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char first = identifier[0];
|
||||
if (!std::isalpha(static_cast<unsigned char>(first)) && first != '_') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (char c : identifier) {
|
||||
if (!std::isalnum(static_cast<unsigned char>(c)) && c != '_') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dbal::security
|
||||
38
dbal/cpp/src/security/is_valid_uuid.hpp
Normal file
38
dbal/cpp/src/security/is_valid_uuid.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file is_valid_uuid.hpp
|
||||
* @brief UUID v4 format validation
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
|
||||
namespace dbal::security {
|
||||
|
||||
/**
|
||||
* Validate UUID v4 format
|
||||
* @param uuid String to validate
|
||||
* @return true if valid UUID v4
|
||||
*/
|
||||
inline bool is_valid_uuid(const std::string& uuid) {
|
||||
if (uuid.size() != 36) return false;
|
||||
|
||||
for (size_t i = 0; i < uuid.size(); ++i) {
|
||||
char c = uuid[i];
|
||||
|
||||
if (i == 8 || i == 13 || i == 18 || i == 23) {
|
||||
if (c != '-') return false;
|
||||
} else if (i == 14) {
|
||||
if (c != '4') return false;
|
||||
} else if (i == 19) {
|
||||
c = std::tolower(static_cast<unsigned char>(c));
|
||||
if (c != '8' && c != '9' && c != 'a' && c != 'b') return false;
|
||||
} else {
|
||||
if (!std::isxdigit(static_cast<unsigned char>(c))) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dbal::security
|
||||
39
dbal/cpp/src/security/sanitize_string.hpp
Normal file
39
dbal/cpp/src/security/sanitize_string.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file sanitize_string.hpp
|
||||
* @brief String sanitization utility
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace dbal::security {
|
||||
|
||||
/**
|
||||
* Sanitize string by removing/replacing dangerous characters
|
||||
* @param input Input string
|
||||
* @param allow_newlines Whether to allow newlines
|
||||
* @return Sanitized string
|
||||
*/
|
||||
inline std::string sanitize_string(const std::string& input, bool allow_newlines = false) {
|
||||
std::string result;
|
||||
result.reserve(input.size());
|
||||
|
||||
for (char c : input) {
|
||||
unsigned char uc = static_cast<unsigned char>(c);
|
||||
|
||||
if (c == '\0') continue;
|
||||
|
||||
if (uc < 32) {
|
||||
if (allow_newlines && (c == '\n' || c == '\r' || c == '\t')) {
|
||||
result += c;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
result += c;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace dbal::security
|
||||
46
dbal/cpp/src/security/secure_random_bytes.hpp
Normal file
46
dbal/cpp/src/security/secure_random_bytes.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file secure_random_bytes.hpp
|
||||
* @brief Cryptographically secure random byte generation
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <bcrypt.h>
|
||||
#pragma comment(lib, "bcrypt.lib")
|
||||
#else
|
||||
#include <fstream>
|
||||
#endif
|
||||
|
||||
namespace dbal::security {
|
||||
|
||||
/**
|
||||
* Generate cryptographically secure random bytes
|
||||
* @param buffer Destination buffer
|
||||
* @param size Number of bytes to generate
|
||||
* @throws std::runtime_error on failure
|
||||
*/
|
||||
inline void secure_random_bytes(unsigned char* buffer, size_t size) {
|
||||
#ifdef _WIN32
|
||||
NTSTATUS status = BCryptGenRandom(
|
||||
nullptr, buffer, static_cast<ULONG>(size), BCRYPT_USE_SYSTEM_PREFERRED_RNG
|
||||
);
|
||||
if (!BCRYPT_SUCCESS(status)) {
|
||||
throw std::runtime_error("BCryptGenRandom failed");
|
||||
}
|
||||
#else
|
||||
std::ifstream urandom("/dev/urandom", std::ios::binary);
|
||||
if (!urandom) {
|
||||
throw std::runtime_error("Failed to open /dev/urandom");
|
||||
}
|
||||
urandom.read(reinterpret_cast<char*>(buffer), static_cast<std::streamsize>(size));
|
||||
if (!urandom) {
|
||||
throw std::runtime_error("Failed to read from /dev/urandom");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace dbal::security
|
||||
34
dbal/cpp/src/security/secure_random_hex.hpp
Normal file
34
dbal/cpp/src/security/secure_random_hex.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file secure_random_hex.hpp
|
||||
* @brief Secure random hex string generation
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "secure_random_bytes.hpp"
|
||||
|
||||
namespace dbal::security {
|
||||
|
||||
/**
|
||||
* Generate a secure random hex string
|
||||
* @param bytes Number of random bytes (output will be 2x this length)
|
||||
* @return Hex-encoded random string
|
||||
*/
|
||||
inline std::string secure_random_hex(size_t bytes) {
|
||||
std::vector<unsigned char> buffer(bytes);
|
||||
secure_random_bytes(buffer.data(), bytes);
|
||||
|
||||
static const char hex_chars[] = "0123456789abcdef";
|
||||
std::string result;
|
||||
result.reserve(bytes * 2);
|
||||
|
||||
for (unsigned char b : buffer) {
|
||||
result += hex_chars[(b >> 4) & 0x0F];
|
||||
result += hex_chars[b & 0x0F];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace dbal::security
|
||||
30
dbal/cpp/src/security/timing_safe_equal.hpp
Normal file
30
dbal/cpp/src/security/timing_safe_equal.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file timing_safe_equal.hpp
|
||||
* @brief Timing-safe string comparison to prevent timing attacks
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace dbal::security {
|
||||
|
||||
/**
|
||||
* Timing-safe string comparison (prevents timing attacks)
|
||||
* @param a First string
|
||||
* @param b Second string
|
||||
* @return true if equal
|
||||
*/
|
||||
inline bool timing_safe_equal(const std::string& a, const std::string& b) {
|
||||
if (a.size() != b.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
volatile unsigned char result = 0;
|
||||
for (size_t i = 0; i < a.size(); ++i) {
|
||||
result |= static_cast<unsigned char>(a[i]) ^ static_cast<unsigned char>(b[i]);
|
||||
}
|
||||
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
} // namespace dbal::security
|
||||
38
dbal/cpp/src/security/validate_length.hpp
Normal file
38
dbal/cpp/src/security/validate_length.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file validate_length.hpp
|
||||
* @brief String length validation
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace dbal::security {
|
||||
|
||||
/**
|
||||
* Validate string length within bounds
|
||||
* @param value String to validate
|
||||
* @param min_len Minimum length
|
||||
* @param max_len Maximum length
|
||||
* @param field_name Field name for error messages
|
||||
* @throws std::runtime_error if validation fails
|
||||
*/
|
||||
inline void validate_length(
|
||||
const std::string& value,
|
||||
size_t min_len,
|
||||
size_t max_len,
|
||||
const char* field_name = "value"
|
||||
) {
|
||||
if (value.size() < min_len) {
|
||||
throw std::runtime_error(
|
||||
std::string(field_name) + " too short (min " + std::to_string(min_len) + ")"
|
||||
);
|
||||
}
|
||||
if (value.size() > max_len) {
|
||||
throw std::runtime_error(
|
||||
std::string(field_name) + " too long (max " + std::to_string(max_len) + ")"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dbal::security
|
||||
63
dbal/cpp/src/security/validate_path.hpp
Normal file
63
dbal/cpp/src/security/validate_path.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file validate_path.hpp
|
||||
* @brief Secure path validation to prevent directory traversal
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace dbal::security {
|
||||
|
||||
/**
|
||||
* Validate and resolve a path safely within a base directory
|
||||
* Prevents: ../, encoded traversal, symlink escapes, null bytes
|
||||
*
|
||||
* @param base_path Allowed base directory (must be absolute)
|
||||
* @param user_path User-supplied relative path
|
||||
* @return Resolved safe absolute path
|
||||
* @throws std::runtime_error if path is invalid or escapes base
|
||||
*/
|
||||
inline std::string validate_path(
|
||||
const std::string& base_path,
|
||||
const std::string& user_path
|
||||
) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
if (user_path.find('\0') != std::string::npos) {
|
||||
throw std::runtime_error("Path contains null byte");
|
||||
}
|
||||
|
||||
if (user_path.find("..") != std::string::npos) {
|
||||
throw std::runtime_error("Path contains traversal sequence");
|
||||
}
|
||||
|
||||
if (!user_path.empty() && (user_path[0] == '/' || user_path[0] == '\\')) {
|
||||
throw std::runtime_error("Absolute paths not allowed");
|
||||
}
|
||||
|
||||
if (user_path.find('%') != std::string::npos) {
|
||||
throw std::runtime_error("Encoded characters not allowed");
|
||||
}
|
||||
|
||||
fs::path base = fs::canonical(base_path);
|
||||
fs::path combined = base / user_path;
|
||||
fs::path resolved = fs::weakly_canonical(combined);
|
||||
|
||||
std::string base_str = base.string();
|
||||
std::string resolved_str = resolved.string();
|
||||
|
||||
if (!base_str.empty() && base_str.back() != fs::path::preferred_separator) {
|
||||
base_str += fs::path::preferred_separator;
|
||||
}
|
||||
|
||||
if (resolved_str.compare(0, base_str.size(), base_str) != 0 &&
|
||||
resolved_str != base.string()) {
|
||||
throw std::runtime_error("Path escapes allowed directory");
|
||||
}
|
||||
|
||||
return resolved_str;
|
||||
}
|
||||
|
||||
} // namespace dbal::security
|
||||
11
frontends/nextjs/src/app/api/health/route.ts
Normal file
11
frontends/nextjs/src/app/api/health/route.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
import { PERMISSION_LEVELS } from '@/app/levels/levels-data'
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({
|
||||
status: 'ok',
|
||||
levelCount: PERMISSION_LEVELS.length,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user