Files
whetstone_DSL/editor/src/SecureByDefaultGenerator.h

203 lines
8.6 KiB
C++

#pragma once
// Step 489: Secure-by-Default Code Generation
// Emits secure templates and defaults for common risky operations.
#include <algorithm>
#include <cctype>
#include <string>
#include <vector>
enum class SecureSnippetKind {
SqlQuery,
InputHandling,
FileOperation,
NetworkCall,
CryptoOperation
};
struct SecureGenerationRequest {
std::string language;
SecureSnippetKind kind = SecureSnippetKind::InputHandling;
std::string operationName;
std::string securityRequirement;
};
struct SecureGenerationOutput {
std::string code;
std::vector<std::string> annotations;
std::vector<std::string> notes;
bool requiresHumanReview = false;
};
class SecureByDefaultGenerator {
public:
static SecureGenerationOutput generate(const SecureGenerationRequest& req) {
SecureGenerationOutput out;
const std::string lang = lower(req.language);
out.annotations.push_back("@Risk(security)");
out.annotations.push_back("@Review(required, human)");
if (!req.securityRequirement.empty()) {
out.annotations.push_back("@SecurityRequirement(" + req.securityRequirement + ")");
}
switch (req.kind) {
case SecureSnippetKind::SqlQuery:
out.code = generateSql(lang, req.operationName);
out.notes.push_back("Uses parameterized query placeholders");
break;
case SecureSnippetKind::InputHandling:
out.code = generateInputHandling(lang, req.operationName);
out.annotations.push_back("@InputValidation(raw)");
out.notes.push_back("Input marked raw until explicit sanitization");
break;
case SecureSnippetKind::FileOperation:
out.code = generateFileOperation(lang, req.operationName);
out.notes.push_back("Path traversal checks included");
break;
case SecureSnippetKind::NetworkCall:
out.code = generateNetworkCall(lang, req.operationName);
out.notes.push_back("Timeout and TLS verification included");
break;
case SecureSnippetKind::CryptoOperation:
out.code = generateCrypto(lang, req.operationName, out.requiresHumanReview);
out.annotations.push_back("@Automatability(human)");
out.notes.push_back("Crypto generated with modern algorithm defaults");
break;
}
if (contains(lower(out.code), "md5") || contains(lower(out.code), "sha1")) {
out.requiresHumanReview = true;
out.notes.push_back("Weak crypto keyword detected; escalation required");
}
return out;
}
private:
static std::string lower(const std::string& s) {
std::string out = s;
std::transform(out.begin(), out.end(), out.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return out;
}
static bool contains(const std::string& text, const std::string& needle) {
return text.find(needle) != std::string::npos;
}
static std::string opName(const std::string& n, const std::string& fallback) {
return n.empty() ? fallback : n;
}
static std::string generateSql(const std::string& lang, const std::string& op) {
const std::string f = opName(op, "fetchRecord");
if (lang == "python") {
return "def " + f + "(conn, user_id):\n"
" query = \"SELECT * FROM users WHERE id = %s\"\n"
" return conn.execute(query, (user_id,)).fetchone()\n";
}
if (lang == "typescript" || lang == "javascript") {
return "export async function " + f + "(db, userId) {\n"
" return db.query(\"SELECT * FROM users WHERE id = ?\", [userId]);\n"
"}\n";
}
if (lang == "rust") {
return "pub async fn " + f + "(pool: &DbPool, user_id: i64) -> Result<Row, Error> {\n"
" sqlx::query(\"SELECT * FROM users WHERE id = $1\").bind(user_id).fetch_one(pool).await\n"
"}\n";
}
return "STUB: use parameterized query for " + f + "\n";
}
static std::string generateInputHandling(const std::string& lang, const std::string& op) {
const std::string f = opName(op, "handleInput");
if (lang == "python") {
return "def " + f + "(raw_input: str):\n"
" # raw until sanitized\n"
" return raw_input\n";
}
if (lang == "typescript" || lang == "javascript") {
return "export function " + f + "(rawInput: string) {\n"
" // raw until sanitized\n"
" return rawInput;\n"
"}\n";
}
if (lang == "rust") {
return "pub fn " + f + "(raw_input: &str) -> &str {\n"
" raw_input\n"
"}\n";
}
return "STUB: treat input as raw until sanitized in " + f + "\n";
}
static std::string generateFileOperation(const std::string& lang, const std::string& op) {
const std::string f = opName(op, "readFileSafe");
if (lang == "python") {
return "def " + f + "(base_dir, candidate):\n"
" import os\n"
" resolved = os.path.realpath(os.path.join(base_dir, candidate))\n"
" if not resolved.startswith(os.path.realpath(base_dir)):\n"
" raise ValueError(\"path traversal blocked\")\n"
" with open(resolved, \"r\", encoding=\"utf-8\") as fh:\n"
" return fh.read()\n";
}
if (lang == "typescript" || lang == "javascript") {
return "export function " + f + "(baseDir, candidate) {\n"
" const path = require(\"path\");\n"
" const resolved = path.resolve(baseDir, candidate);\n"
" if (!resolved.startsWith(path.resolve(baseDir))) throw new Error(\"path traversal blocked\");\n"
" return require(\"fs\").readFileSync(resolved, \"utf8\");\n"
"}\n";
}
return "STUB: enforce canonical path prefix check in " + f + "\n";
}
static std::string generateNetworkCall(const std::string& lang, const std::string& op) {
const std::string f = opName(op, "fetchSecure");
if (lang == "python") {
return "def " + f + "(url):\n"
" import requests\n"
" return requests.get(url, timeout=5, verify=True)\n";
}
if (lang == "typescript" || lang == "javascript") {
return "export async function " + f + "(url) {\n"
" const controller = new AbortController();\n"
" setTimeout(() => controller.abort(), 5000);\n"
" return fetch(url, { signal: controller.signal });\n"
"}\n";
}
if (lang == "rust") {
return "pub async fn " + f + "(url: &str) -> Result<Response, reqwest::Error> {\n"
" reqwest::Client::builder().use_rustls_tls().timeout(std::time::Duration::from_secs(5)).build()?.get(url).send().await\n"
"}\n";
}
return "STUB: include timeout and TLS verification in " + f + "\n";
}
static std::string generateCrypto(const std::string& lang, const std::string& op,
bool& requiresHumanReview) {
requiresHumanReview = true;
const std::string f = opName(op, "encryptData");
if (lang == "python") {
return "def " + f + "(plaintext: bytes, key: bytes):\n"
" from cryptography.hazmat.primitives.ciphers.aead import AESGCM\n"
" nonce = b\"000000000000\"\n"
" return AESGCM(key).encrypt(nonce, plaintext, None)\n";
}
if (lang == "typescript" || lang == "javascript") {
return "export function " + f + "(plaintext, key, iv) {\n"
" const crypto = require(\"crypto\");\n"
" const cipher = crypto.createCipheriv(\"aes-256-gcm\", key, iv);\n"
" return Buffer.concat([cipher.update(plaintext), cipher.final()]);\n"
"}\n";
}
if (lang == "rust") {
return "pub fn " + f + "(plaintext: &[u8]) {\n"
" // STUB: use AES-GCM via audited crate; keep human review in loop\n"
"}\n";
}
return "STUB: use modern AEAD algorithm (AES-GCM/ChaCha20-Poly1305) in " + f + "\n";
}
};