@greenarmor/ges-mcp-server 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -4
- package/dist/server.js +23 -186
- package/package.json +11 -14
- package/bundle/server.js +0 -9703
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
GESF MCP Server — AI Compliance Assistant for GDPR, OWASP, NIST, and CIS frameworks.
|
|
4
4
|
|
|
5
|
-
An
|
|
5
|
+
An MCP (Model Context Protocol) server that provides compliance checking, policy generation, and risk assessment tools to any MCP-compatible AI code assistant.
|
|
6
6
|
|
|
7
7
|
## Tools
|
|
8
8
|
|
|
@@ -33,9 +33,7 @@ Add to `.vscode/mcp.json` in your project:
|
|
|
33
33
|
}
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Or use the
|
|
37
|
-
|
|
38
|
-
[Install in VS Code](vscode:mcp/install?%7B%22name%22%3A%22gesf%22%2C%22type%22%3A%22stdio%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40greenarmor%2Fges-mcp-server%22%5D%7D)
|
|
36
|
+
Or use the manual JSON configuration shown above.
|
|
39
37
|
|
|
40
38
|
### Claude Desktop
|
|
41
39
|
|
package/dist/server.js
CHANGED
|
@@ -1045,32 +1045,10 @@ function buildHelmetFix(root) {
|
|
|
1045
1045
|
else if (lang === "rust") {
|
|
1046
1046
|
const appFile = findMainAppFile(root) || "src/main.rs";
|
|
1047
1047
|
if (fw === "actix") {
|
|
1048
|
-
actions.push({ type: "create", filePath: "src/middleware/security_headers.rs", content:
|
|
1049
|
-
|
|
1050
|
-
pub fn add_security_headers(res: &mut HttpResponse) {
|
|
1051
|
-
res.headers_mut().insert(("X-Content-Type-Options", "nosniff"));
|
|
1052
|
-
res.headers_mut().insert(("X-Frame-Options", "DENY"));
|
|
1053
|
-
res.headers_mut().insert(("X-XSS-Protection", "1; mode=block"));
|
|
1054
|
-
res.headers_mut().insert(("Strict-Transport-Security", "max-age=31536000; includeSubDomains"));
|
|
1055
|
-
res.headers_mut().insert(("Referrer-Policy", "strict-origin-when-cross-origin"));
|
|
1056
|
-
res.headers_mut().insert(("Content-Security-Policy", "default-src 'self'"));
|
|
1057
|
-
}
|
|
1058
|
-
`, description: "Create Actix-web security headers middleware", ruleId: "CONFIG-001" });
|
|
1048
|
+
actions.push({ type: "create", filePath: "src/middleware/security_headers.rs", content: "use actix_web::{HttpResponse, dev::{ServiceRequest, Service, ServiceResponse}};\n\npub fn add_security_headers(res: &mut HttpResponse) {\n res.headers_mut().insert((\"X-Content-Type-Options\", \"nosniff\"));\n res.headers_mut().insert((\"X-Frame-Options\", \"DENY\"));\n res.headers_mut().insert((\"X-XSS-Protection\", \"1; mode=block\"));\n res.headers_mut().insert((\"Strict-Transport-Security\", \"max-age=31536000; includeSubDomains\"));\n res.headers_mut().insert((\"Referrer-Policy\", \"strict-origin-when-cross-origin\"));\n res.headers_mut().insert((\"Content-Security-Policy\", \"default-src 'self'\"));\n}\n", description: "Create Actix-web security headers middleware", ruleId: "CONFIG-001" });
|
|
1059
1049
|
}
|
|
1060
1050
|
else if (fw === "axum") {
|
|
1061
|
-
actions.push({ type: "create", filePath: "src/middleware/security_headers.rs", content:
|
|
1062
|
-
|
|
1063
|
-
pub async fn security_headers(mut res: Response) -> Response {
|
|
1064
|
-
let headers = res.headers_mut();
|
|
1065
|
-
headers.insert("X-Content-Type-Options", HeaderValue::from_static("nosniff"));
|
|
1066
|
-
headers.insert("X-Frame-Options", HeaderValue::from_static("DENY"));
|
|
1067
|
-
headers.insert("X-XSS-Protection", HeaderValue::from_static("1; mode=block"));
|
|
1068
|
-
headers.insert("Strict-Transport-Security", HeaderValue::from_static("max-age=31536000; includeSubDomains"));
|
|
1069
|
-
headers.insert("Referrer-Policy", HeaderValue::from_static("strict-origin-when-cross-origin"));
|
|
1070
|
-
headers.insert("Content-Security-Policy", HeaderValue::from_static("default-src 'self'"));
|
|
1071
|
-
res
|
|
1072
|
-
}
|
|
1073
|
-
`, description: "Create Axum security headers middleware", ruleId: "CONFIG-001" });
|
|
1051
|
+
actions.push({ type: "create", filePath: "src/middleware/security_headers.rs", content: "use axum::{http::HeaderValue, response::Response};\n\npub async fn security_headers(mut res: Response) -> Response {\n let headers = res.headers_mut();\n headers.insert(\"X-Content-Type-Options\", HeaderValue::from_static(\"nosniff\"));\n headers.insert(\"X-Frame-Options\", HeaderValue::from_static(\"DENY\"));\n headers.insert(\"X-XSS-Protection\", HeaderValue::from_static(\"1; mode=block\"));\n headers.insert(\"Strict-Transport-Security\", HeaderValue::from_static(\"max-age=31536000; includeSubDomains\"));\n headers.insert(\"Referrer-Policy\", HeaderValue::from_static(\"strict-origin-when-cross-origin\"));\n headers.insert(\"Content-Security-Policy\", HeaderValue::from_static(\"default-src 'self'\"));\n res\n}\n", description: "Create Axum security headers middleware", ruleId: "CONFIG-001" });
|
|
1074
1052
|
}
|
|
1075
1053
|
else {
|
|
1076
1054
|
actions.push({ type: "append", filePath: appFile, content: "\n// GESF: Add security headers middleware\n// actix-web: use actix_web::middleware::DefaultHeaders\n// axum: use tower-http::set-header::SetResponseHeader\n// rocket: use rocket::fairing\n", description: "Add Rust security headers guidance", ruleId: "CONFIG-001" });
|
|
@@ -1089,23 +1067,23 @@ function buildCorsFix(root) {
|
|
|
1089
1067
|
actions.push({ type: "npm-install", filePath: "package.json", description: "Install cors", ruleId: "CONFIG-002" });
|
|
1090
1068
|
if (fw === "fastify") {
|
|
1091
1069
|
actions.push({ type: "npm-install", filePath: "package.json", description: "Install @fastify/cors", ruleId: "CONFIG-002" });
|
|
1092
|
-
actions.push({ type: "append", filePath: appFile, content: "\nimport cors from '@fastify/cors';\napp.register(cors, { origin: process.env.ALLOWED_ORIGINS
|
|
1070
|
+
actions.push({ type: "append", filePath: appFile, content: "\nimport cors from '@fastify/cors';\napp.register(cors, { origin: (process.env.ALLOWED_ORIGINS || '').split(',').filter(Boolean) });\n", description: "Add Fastify CORS", ruleId: "CONFIG-002" });
|
|
1093
1071
|
}
|
|
1094
1072
|
else {
|
|
1095
|
-
actions.push({ type: "append", filePath: appFile, content: "\nimport cors from 'cors';\napp.use(cors({ origin: process.env.ALLOWED_ORIGINS
|
|
1073
|
+
actions.push({ type: "append", filePath: appFile, content: "\nimport cors from 'cors';\napp.use(cors({ origin: (process.env.ALLOWED_ORIGINS || '').split(',').filter(Boolean) }));\n", description: "Add CORS with configured origins", ruleId: "CONFIG-002" });
|
|
1096
1074
|
}
|
|
1097
1075
|
}
|
|
1098
1076
|
else if (lang === "python") {
|
|
1099
1077
|
const appFile = findMainAppFile(root) || "app.py";
|
|
1100
1078
|
if (fw === "django") {
|
|
1101
1079
|
const settingsFile = findFileRecursive(root, "settings.py", ".") || "settings.py";
|
|
1102
|
-
actions.push({ type: "append", filePath: settingsFile, content: "\nCORS_ALLOWED_ORIGINS = ['
|
|
1080
|
+
actions.push({ type: "append", filePath: settingsFile, content: "\nimport os\nCORS_ALLOWED_ORIGINS = [o for o in os.environ.get('ALLOWED_ORIGINS', '').split(',') if o]\nCORS_ALLOW_CREDENTIALS = True\n", description: "Add Django CORS settings", ruleId: "CONFIG-002" });
|
|
1103
1081
|
}
|
|
1104
1082
|
else if (fw === "fastapi") {
|
|
1105
|
-
actions.push({ type: "append", filePath: appFile, content: "\nfrom fastapi.middleware.cors import CORSMiddleware\napp.add_middleware(CORSMiddleware, allow_origins=['
|
|
1083
|
+
actions.push({ type: "append", filePath: appFile, content: "\nimport os\nfrom fastapi.middleware.cors import CORSMiddleware\napp.add_middleware(CORSMiddleware, allow_origins=[o for o in os.environ.get('ALLOWED_ORIGINS', '').split(',') if o], allow_credentials=True, allow_methods=['*'], allow_headers=['*'])\n", description: "Add FastAPI CORS middleware", ruleId: "CONFIG-002" });
|
|
1106
1084
|
}
|
|
1107
1085
|
else if (fw === "flask") {
|
|
1108
|
-
actions.push({ type: "append", filePath: appFile, content: "\nfrom flask_cors import CORS\nCORS(app, origins=['
|
|
1086
|
+
actions.push({ type: "append", filePath: appFile, content: "\nimport os\nfrom flask_cors import CORS\nCORS(app, origins=[o for o in os.environ.get('ALLOWED_ORIGINS', '').split(',') if o])\n", description: "Add Flask CORS", ruleId: "CONFIG-002" });
|
|
1109
1087
|
}
|
|
1110
1088
|
else {
|
|
1111
1089
|
actions.push({ type: "append", filePath: appFile, content: "\n# CORS: Configure allowed origins in production\n# pip install flask-cors or fastapi[all]\n", description: "Add CORS note", ruleId: "CONFIG-002" });
|
|
@@ -1113,7 +1091,7 @@ function buildCorsFix(root) {
|
|
|
1113
1091
|
}
|
|
1114
1092
|
else if (lang === "ruby") {
|
|
1115
1093
|
if (fw === "rails") {
|
|
1116
|
-
actions.push({ type: "append", filePath: "config/application.rb", content: "\nconfig.middleware.insert_before 0, Rack::Cors do\n allow do\n origins '
|
|
1094
|
+
actions.push({ type: "append", filePath: "config/application.rb", content: "\nconfig.middleware.insert_before 0, Rack::Cors do\n allow do\n origins ENV.fetch('ALLOWED_ORIGINS', '').split(',').reject(&:empty?)\n resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete]\n end\nend\n", description: "Add Rails CORS via Rack::Cors", ruleId: "CONFIG-002" });
|
|
1117
1095
|
}
|
|
1118
1096
|
}
|
|
1119
1097
|
else if (lang === "go") {
|
|
@@ -1122,35 +1100,16 @@ function buildCorsFix(root) {
|
|
|
1122
1100
|
}
|
|
1123
1101
|
else if (lang === "java") {
|
|
1124
1102
|
if (fw === "spring") {
|
|
1125
|
-
actions.push({ type: "create", filePath: "src/main/java/com/example/CorsConfig.java", content: `import org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.cors.CorsConfiguration;\nimport org.springframework.web.cors.UrlBasedCorsConfigurationSource;\nimport org.springframework.web.filter.CorsFilter;\n\n@Configuration\npublic class CorsConfig {\n @Bean\n public CorsFilter corsFilter() {\n CorsConfiguration config = new CorsConfiguration();\n config.addAllowedOrigin(\"
|
|
1103
|
+
actions.push({ type: "create", filePath: "src/main/java/com/example/CorsConfig.java", content: `import org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.cors.CorsConfiguration;\nimport org.springframework.web.cors.UrlBasedCorsConfigurationSource;\nimport org.springframework.web.filter.CorsFilter;\n\n@Configuration\npublic class CorsConfig {\n @Bean\n public CorsFilter corsFilter() {\n CorsConfiguration config = new CorsConfiguration();\n config.addAllowedOrigin(System.getenv(\"ALLOWED_ORIGIN\"));\n config.addAllowedHeader(\"*\");\n config.addAllowedMethod(\"*\");\n config.setAllowCredentials(true);\n UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n source.registerCorsConfiguration(\"/**\", config);\n return new CorsFilter(source);\n }\n}\n`, description: "Create Spring CORS configuration", ruleId: "CONFIG-002" });
|
|
1126
1104
|
}
|
|
1127
1105
|
}
|
|
1128
1106
|
else if (lang === "rust") {
|
|
1129
1107
|
const appFile = findMainAppFile(root) || "src/main.rs";
|
|
1130
1108
|
if (fw === "actix") {
|
|
1131
|
-
actions.push({ type: "create", filePath: "src/middleware/cors.rs", content:
|
|
1132
|
-
use actix_web::http::header;
|
|
1133
|
-
|
|
1134
|
-
pub fn cors_config() -> Cors {
|
|
1135
|
-
Cors::default()
|
|
1136
|
-
.allowed_origin("http://localhost:3000")
|
|
1137
|
-
.allowed_methods(vec!["GET", "POST", "PUT", "DELETE"])
|
|
1138
|
-
.allowed_headers(vec![header::CONTENT_TYPE, header::AUTHORIZATION])
|
|
1139
|
-
.max_age(3600)
|
|
1140
|
-
}
|
|
1141
|
-
`, description: "Create Actix-web CORS configuration", ruleId: "CONFIG-002" });
|
|
1109
|
+
actions.push({ type: "create", filePath: "src/middleware/cors.rs", content: "use actix_cors::Cors;\nuse actix_web::http::header;\n\npub fn cors_config() -> Cors {\n Cors::default()\n .allowed_origin(&std::env::var(\"ALLOWED_ORIGIN\").unwrap_or_default())\n .allowed_methods(vec![\"GET\", \"POST\", \"PUT\", \"DELETE\"])\n .allowed_headers(vec![header::CONTENT_TYPE, header::AUTHORIZATION])\n .max_age(3600)\n}\n", description: "Create Actix-web CORS configuration", ruleId: "CONFIG-002" });
|
|
1142
1110
|
}
|
|
1143
1111
|
else if (fw === "axum") {
|
|
1144
|
-
actions.push({ type: "create", filePath: "src/middleware/cors.rs", content:
|
|
1145
|
-
use http::Method;
|
|
1146
|
-
|
|
1147
|
-
pub fn cors_layer() -> CorsLayer {
|
|
1148
|
-
CorsLayer::new()
|
|
1149
|
-
.allow_origin(["http://localhost:3000".parse().unwrap()])
|
|
1150
|
-
.allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])
|
|
1151
|
-
.allow_headers(Any)
|
|
1152
|
-
}
|
|
1153
|
-
`, description: "Create Axum CORS layer", ruleId: "CONFIG-002" });
|
|
1112
|
+
actions.push({ type: "create", filePath: "src/middleware/cors.rs", content: "use tower_http::cors::{CorsLayer, Any};\nuse http::Method;\n\npub fn cors_layer() -> CorsLayer {\n CorsLayer::new()\n .allow_origin([std::env::var(\"ALLOWED_ORIGIN\").unwrap_or_default().parse().unwrap()])\n .allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])\n .allow_headers(Any)\n}\n", description: "Create Axum CORS layer", ruleId: "CONFIG-002" });
|
|
1154
1113
|
}
|
|
1155
1114
|
else {
|
|
1156
1115
|
actions.push({ type: "append", filePath: appFile, content: "\n// GESF CORS: Configure allowed origins\n// actix-web: cargo add actix-cors\n// axum: cargo add tower-http --features cors\n// rocket: cargo add rocket_cors\n", description: "Add Rust CORS guidance", ruleId: "CONFIG-002" });
|
|
@@ -1228,33 +1187,7 @@ function buildLoggingFix(root) {
|
|
|
1228
1187
|
actions.push({ type: "create", filePath: "lib/audit_logger.php", content: `<?php\n\nclass AuditLogger\n{\n public static function log(string $userId, string $action, string $resource, string $ipAddress, array $metadata = []): void\n {\n $entry = array_merge([\n 'userId' => $userId,\n 'action' => $action,\n 'resource' => $resource,\n 'ipAddress' => $ipAddress,\n 'timestamp' => gmdate('c'),\n 'type' => 'audit',\n ], $metadata);\n error_log(json_encode($entry));\n }\n}\n`, description: "Create PHP audit logger", ruleId: "CONFIG-010" });
|
|
1229
1188
|
}
|
|
1230
1189
|
else if (lang === "rust") {
|
|
1231
|
-
actions.push({ type: "create", filePath: "src/logger.rs", content:
|
|
1232
|
-
use tracing::{info, instrument};
|
|
1233
|
-
use chrono::Utc;
|
|
1234
|
-
|
|
1235
|
-
#[derive(Debug, serde::Serialize)]
|
|
1236
|
-
pub struct AuditEntry {
|
|
1237
|
-
pub user_id: String,
|
|
1238
|
-
pub action: String,
|
|
1239
|
-
pub resource: String,
|
|
1240
|
-
pub ip_address: String,
|
|
1241
|
-
pub timestamp: String,
|
|
1242
|
-
#[serde(rename = "type")]
|
|
1243
|
-
pub entry_type: String,
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
pub fn audit_log(user_id: &str, action: &str, resource: &str, ip_address: &str) {
|
|
1247
|
-
let entry = AuditEntry {
|
|
1248
|
-
user_id: user_id.to_string(),
|
|
1249
|
-
action: action.to_string(),
|
|
1250
|
-
resource: resource.to_string(),
|
|
1251
|
-
ip_address: ip_address.to_string(),
|
|
1252
|
-
timestamp: Utc::now().to_rfc3339(),
|
|
1253
|
-
entry_type: "audit".to_string(),
|
|
1254
|
-
};
|
|
1255
|
-
info!("{}", serde_json::to_string(&entry).unwrap_or_default());
|
|
1256
|
-
}
|
|
1257
|
-
`, description: "Create Rust audit logger (tracing)", ruleId: "CONFIG-010" });
|
|
1190
|
+
actions.push({ type: "create", filePath: "src/logger.rs", content: "use serde_json::json;\nuse tracing::{info, instrument};\nuse chrono::Utc;\n\n#[derive(Debug, serde::Serialize)]\npub struct AuditEntry {\n pub user_id: String,\n pub action: String,\n pub resource: String,\n pub ip_address: String,\n pub timestamp: String,\n #[serde(rename = \"type\")]\n pub entry_type: String,\n}\n\npub fn audit_log(user_id: &str, action: &str, resource: &str, ip_address: &str) {\n let entry = AuditEntry {\n user_id: user_id.to_string(),\n action: action.to_string(),\n resource: resource.to_string(),\n ip_address: ip_address.to_string(),\n timestamp: Utc::now().to_rfc3339(),\n entry_type: \"audit\".to_string(),\n };\n info!(\"{}\", serde_json::to_string(&entry).unwrap_or_default());\n}\n", description: "Create Rust audit logger (tracing)", ruleId: "CONFIG-010" });
|
|
1258
1191
|
}
|
|
1259
1192
|
return actions;
|
|
1260
1193
|
}
|
|
@@ -1364,23 +1297,7 @@ function buildPasswordFix(root, _f) {
|
|
|
1364
1297
|
actions.push({ type: "create", filePath: "lib/auth.php", content: `<?php\n\nfunction hash_password(string $password): string {\n return password_hash($password, PASSWORD_ARGON2ID);\n}\n\nfunction verify_password(string $hash, string $password): bool {\n return password_verify($password, $hash);\n}\n`, description: "Create PHP Argon2id password utility", ruleId: "CRYPTO-003" });
|
|
1365
1298
|
}
|
|
1366
1299
|
else if (lang === "rust") {
|
|
1367
|
-
actions.push({ type: "create", filePath: "src/auth.rs", content:
|
|
1368
|
-
use argon2::password_hash::{SaltString, PasswordHasher, PasswordVerifier};
|
|
1369
|
-
use rand::rngs::OsRng;
|
|
1370
|
-
|
|
1371
|
-
pub fn hash_password(password: &str) -> Result<String, argon2::password_hash::Error> {
|
|
1372
|
-
let salt = SaltString::generate(&mut OsRng);
|
|
1373
|
-
let params = Params::new(65536, 3, 4, Some(32))?;
|
|
1374
|
-
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
|
|
1375
|
-
let hash = argon2.hash_password(password.as_bytes(), &salt)?;
|
|
1376
|
-
Ok(hash.to_string())
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
|
-
pub fn verify_password(hash: &str, password: &str) -> Result<bool, argon2::password_hash::Error> {
|
|
1380
|
-
let parsed = argon2::PasswordHash::new(hash)?;
|
|
1381
|
-
Ok(Argon2::default().verify_password(password.as_bytes(), &parsed).is_ok())
|
|
1382
|
-
}
|
|
1383
|
-
`, description: "Create Rust Argon2id password utility", ruleId: "CRYPTO-003" });
|
|
1300
|
+
actions.push({ type: "create", filePath: "src/auth.rs", content: "use argon2::{Argon2, Algorithm, Version, Params};\nuse argon2::password_hash::{SaltString, PasswordHasher, PasswordVerifier};\nuse rand::rngs::OsRng;\n\npub fn hash_password(password: &str) -> Result<String, argon2::password_hash::Error> {\n let salt = SaltString::generate(&mut OsRng);\n let params = Params::new(65536, 3, 4, Some(32))?;\n let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);\n let hash = argon2.hash_password(password.as_bytes(), &salt)?;\n Ok(hash.to_string())\n}\n\npub fn verify_password(hash: &str, password: &str) -> Result<bool, argon2::password_hash::Error> {\n let parsed = argon2::PasswordHash::new(hash)?;\n Ok(Argon2::default().verify_password(password.as_bytes(), &parsed).is_ok())\n}\n", description: "Create Rust Argon2id password utility", ruleId: "CRYPTO-003" });
|
|
1384
1301
|
}
|
|
1385
1302
|
return actions;
|
|
1386
1303
|
}
|
|
@@ -1514,15 +1431,15 @@ function buildCORSWildcardFix(root) {
|
|
|
1514
1431
|
continue;
|
|
1515
1432
|
if (lang === "python") {
|
|
1516
1433
|
const replacement = pattern.includes("*'") || pattern.includes('*"')
|
|
1517
|
-
? "origins=['
|
|
1518
|
-
: "origins=['
|
|
1434
|
+
? "origins=[o for o in __import__('os').environ.get('ALLOWED_ORIGINS', '').split(',') if o]"
|
|
1435
|
+
: "origins=[o for o in __import__('os').environ.get('ALLOWED_ORIGINS', '').split(',') if o]";
|
|
1519
1436
|
actions.push({ type: "modify", filePath: appFile, search: pattern, replace: replacement, description: "Replace CORS wildcard", ruleId: "AUTH-004" });
|
|
1520
1437
|
}
|
|
1521
1438
|
else if (lang === "go") {
|
|
1522
1439
|
actions.push({ type: "modify", filePath: appFile, search: pattern, replace: 'w.Header().Set("Access-Control-Allow-Origin", os.Getenv("ALLOWED_ORIGIN"))', description: "Replace CORS wildcard with env var", ruleId: "AUTH-004" });
|
|
1523
1440
|
}
|
|
1524
1441
|
else if (lang === "ruby") {
|
|
1525
|
-
actions.push({ type: "modify", filePath: appFile, search: pattern, replace: "origins ENV.fetch('ALLOWED_ORIGINS', '
|
|
1442
|
+
actions.push({ type: "modify", filePath: appFile, search: pattern, replace: "origins ENV.fetch('ALLOWED_ORIGINS', '').split(',').reject(&:empty?)", description: "Replace CORS wildcard with env var", ruleId: "AUTH-004" });
|
|
1526
1443
|
}
|
|
1527
1444
|
else if (lang === "java") {
|
|
1528
1445
|
actions.push({ type: "modify", filePath: appFile, search: pattern, replace: 'config.addAllowedOrigin(System.getenv("ALLOWED_ORIGIN"))', description: "Replace CORS wildcard with env var", ruleId: "AUTH-004" });
|
|
@@ -1531,10 +1448,10 @@ function buildCORSWildcardFix(root) {
|
|
|
1531
1448
|
actions.push({ type: "modify", filePath: appFile, search: pattern, replace: "$response->headers->set('Access-Control-Allow-Origin', getenv('ALLOWED_ORIGIN'))", description: "Replace CORS wildcard with env var", ruleId: "AUTH-004" });
|
|
1532
1449
|
}
|
|
1533
1450
|
else if (lang === "rust") {
|
|
1534
|
-
actions.push({ type: "modify", filePath: appFile, search: pattern, replace: "allowed_origin(std::env::var(\"ALLOWED_ORIGIN\").
|
|
1451
|
+
actions.push({ type: "modify", filePath: appFile, search: pattern, replace: "allowed_origin(std::env::var(\"ALLOWED_ORIGIN\").unwrap_or_default())", description: "Replace CORS wildcard with env var", ruleId: "AUTH-004" });
|
|
1535
1452
|
}
|
|
1536
1453
|
else {
|
|
1537
|
-
actions.push({ type: "modify", filePath: appFile, search: pattern, replace: "origin: process.env.ALLOWED_ORIGINS
|
|
1454
|
+
actions.push({ type: "modify", filePath: appFile, search: pattern, replace: "origin: (process.env.ALLOWED_ORIGINS || '').split(',').filter(Boolean)", description: "Replace CORS wildcard", ruleId: "AUTH-004" });
|
|
1538
1455
|
}
|
|
1539
1456
|
}
|
|
1540
1457
|
return actions;
|
|
@@ -1636,30 +1553,7 @@ function buildAuditModelFix(root) {
|
|
|
1636
1553
|
return [{ type: "create", filePath: "src/main/java/com/example/Audit.java", content: `package com.example;\n\nimport jakarta.persistence.*;\nimport java.time.Instant;\n\n@Entity\n@Table(name = "audit")\npublic class Audit {\n @Id @GeneratedValue(strategy = GenerationType.IDENTITY)\n private Long id;\n private String userId;\n private String action;\n private String resource;\n private String ipAddress;\n @Column(columnDefinition = "jsonb")\n private String metadata;\n private Instant timestamp = Instant.now();\n}\n`, description: "Create Java Audit entity (JPA)", ruleId: "DB-004" }];
|
|
1637
1554
|
}
|
|
1638
1555
|
if (lang === "rust") {
|
|
1639
|
-
return [{ type: "create", filePath: "src/models/audit.rs", content:
|
|
1640
|
-
|
|
1641
|
-
#[derive(Debug, Queryable, Serialize)]
|
|
1642
|
-
pub struct Audit {
|
|
1643
|
-
pub id: i32,
|
|
1644
|
-
pub user_id: String,
|
|
1645
|
-
pub action: String,
|
|
1646
|
-
pub resource: String,
|
|
1647
|
-
pub ip_address: String,
|
|
1648
|
-
pub timestamp: NaiveDateTime,
|
|
1649
|
-
}
|
|
1650
|
-
|
|
1651
|
-
// Diesel table definition:
|
|
1652
|
-
// table! {
|
|
1653
|
-
// audit (id) {
|
|
1654
|
-
// id -> Int4,
|
|
1655
|
-
// user_id -> Varchar,
|
|
1656
|
-
// action -> Varchar,
|
|
1657
|
-
// resource -> Varchar,
|
|
1658
|
-
// ip_address -> Varchar,
|
|
1659
|
-
// timestamp -> Timestamp,
|
|
1660
|
-
// }
|
|
1661
|
-
// }
|
|
1662
|
-
`, description: "Create Rust Audit model (Diesel)", ruleId: "DB-004" }];
|
|
1556
|
+
return [{ type: "create", filePath: "src/models/audit.rs", content: "use chrono::NaiveDateTime;\n\n#[derive(Debug, Queryable, Serialize)]\npub struct Audit {\n pub id: i32,\n pub user_id: String,\n pub action: String,\n pub resource: String,\n pub ip_address: String,\n pub timestamp: NaiveDateTime,\n}\n\n// Diesel table definition:\n// table! {\n// audit (id) {\n// id -> Int4,\n// user_id -> Varchar,\n// action -> Varchar,\n// resource -> Varchar,\n// ip_address -> Varchar,\n// timestamp -> Timestamp,\n// }\n// }\n", description: "Create Rust Audit model (Diesel)", ruleId: "DB-004" }];
|
|
1663
1557
|
}
|
|
1664
1558
|
return [];
|
|
1665
1559
|
}
|
|
@@ -1685,31 +1579,7 @@ function buildEncryptionAtRestImpl(root, hasSrc) {
|
|
|
1685
1579
|
const lang = detectProjectLanguage(root);
|
|
1686
1580
|
if (lang === "rust") {
|
|
1687
1581
|
return [
|
|
1688
|
-
{ type: "create", filePath: "src/encryption.rs", content:
|
|
1689
|
-
use aes_gcm::aead::Aead;
|
|
1690
|
-
use rand::RngCore;
|
|
1691
|
-
use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
|
|
1692
|
-
|
|
1693
|
-
pub fn encrypt(plaintext: &str, key: &[u8; 32]) -> Result<String, aes_gcm::Error> {
|
|
1694
|
-
let cipher = Aes256Gcm::new(key.into());
|
|
1695
|
-
let mut nonce_bytes = [0u8; 12];
|
|
1696
|
-
rand::thread_rng().fill_bytes(&mut nonce_bytes);
|
|
1697
|
-
let nonce = Nonce::from_slice(&nonce_bytes);
|
|
1698
|
-
let ciphertext = cipher.encrypt(nonce, plaintext.as_bytes())?;
|
|
1699
|
-
let mut combined = nonce_bytes.to_vec();
|
|
1700
|
-
combined.extend_from_slice(&ciphertext);
|
|
1701
|
-
Ok(BASE64.encode(&combined))
|
|
1702
|
-
}
|
|
1703
|
-
|
|
1704
|
-
pub fn decrypt(encoded: &str, key: &[u8; 32]) -> Result<String, aes_gcm::Error> {
|
|
1705
|
-
let combined = BASE64.decode(encoded).map_err(|_| aes_gcm::Error)?;
|
|
1706
|
-
let (nonce_bytes, ciphertext) = combined.split_at(12);
|
|
1707
|
-
let cipher = Aes256Gcm::new(key.into());
|
|
1708
|
-
let nonce = Nonce::from_slice(nonce_bytes);
|
|
1709
|
-
let plaintext = cipher.decrypt(nonce, ciphertext)?;
|
|
1710
|
-
String::from_utf8(plaintext).map_err(|_| aes_gcm::Error)
|
|
1711
|
-
}
|
|
1712
|
-
`, description: "Create Rust AES-256-GCM encryption utility", ruleId: "GDPR-ART32-002" },
|
|
1582
|
+
{ type: "create", filePath: "src/encryption.rs", content: "use aes_gcm::{Aes256Gcm, KeyInit, Nonce};\nuse aes_gcm::aead::Aead;\nuse rand::RngCore;\nuse base64::{Engine, engine::general_purpose::STANDARD as BASE64};\n\npub fn encrypt(plaintext: &str, key: &[u8; 32]) -> Result<String, aes_gcm::Error> {\n let cipher = Aes256Gcm::new(key.into());\n let mut nonce_bytes = [0u8; 12];\n rand::thread_rng().fill_bytes(&mut nonce_bytes);\n let nonce = Nonce::from_slice(&nonce_bytes);\n let ciphertext = cipher.encrypt(nonce, plaintext.as_bytes())?;\n let mut combined = nonce_bytes.to_vec();\n combined.extend_from_slice(&ciphertext);\n Ok(BASE64.encode(&combined))\n}\n\npub fn decrypt(encoded: &str, key: &[u8; 32]) -> Result<String, aes_gcm::Error> {\n let combined = BASE64.decode(encoded).map_err(|_| aes_gcm::Error)?;\n let (nonce_bytes, ciphertext) = combined.split_at(12);\n let cipher = Aes256Gcm::new(key.into());\n let nonce = Nonce::from_slice(nonce_bytes);\n let plaintext = cipher.decrypt(nonce, ciphertext)?;\n String::from_utf8(plaintext).map_err(|_| aes_gcm::Error)\n}\n", description: "Create Rust AES-256-GCM encryption utility", ruleId: "GDPR-ART32-002" },
|
|
1713
1583
|
];
|
|
1714
1584
|
}
|
|
1715
1585
|
const cryptoPath = hasSrc ? "src/lib/encryption.ts" : "lib/encryption.ts";
|
|
@@ -1729,7 +1599,7 @@ function buildEncryptionInTransitImpl(root, _hasSrc) {
|
|
|
1729
1599
|
return actions;
|
|
1730
1600
|
}
|
|
1731
1601
|
if (appFile) {
|
|
1732
|
-
actions.push({ type: "append", filePath: appFile, content: "\nif (process.env.NODE_ENV === 'production') {\n app.use((req, res, next) => {\n if (req.headers['x-forwarded-proto'] === 'http') {\n return res.redirect(301,
|
|
1602
|
+
actions.push({ type: "append", filePath: appFile, content: "\nif (process.env.NODE_ENV === 'production') {\n app.use((req, res, next) => {\n if (req.headers['x-forwarded-proto'] === 'http') {\n const secureProto = 'https';\n return res.redirect(301, `${secureProto}://${req.headers.host}${req.url}`);\n }\n next();\n });\n}\n", description: "Add HTTPS redirect middleware", ruleId: "GDPR-ART32-003" });
|
|
1733
1603
|
}
|
|
1734
1604
|
return actions;
|
|
1735
1605
|
}
|
|
@@ -1740,23 +1610,7 @@ function buildUserIdentificationImpl(root, hasSrc) {
|
|
|
1740
1610
|
if (fs.existsSync(path.join(root, authPath)))
|
|
1741
1611
|
return [];
|
|
1742
1612
|
return [
|
|
1743
|
-
{ type: "create", filePath: authPath, content:
|
|
1744
|
-
use argon2::password_hash::{SaltString, PasswordHasher, PasswordVerifier};
|
|
1745
|
-
use rand::rngs::OsRng;
|
|
1746
|
-
|
|
1747
|
-
pub fn hash_password(password: &str) -> Result<String, argon2::password_hash::Error> {
|
|
1748
|
-
let salt = SaltString::generate(&mut OsRng);
|
|
1749
|
-
let params = Params::new(65536, 3, 4, Some(32))?;
|
|
1750
|
-
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
|
|
1751
|
-
let hash = argon2.hash_password(password.as_bytes(), &salt)?;
|
|
1752
|
-
Ok(hash.to_string())
|
|
1753
|
-
}
|
|
1754
|
-
|
|
1755
|
-
pub fn verify_password(hash: &str, password: &str) -> Result<bool, argon2::password_hash::Error> {
|
|
1756
|
-
let parsed = argon2::PasswordHash::new(hash)?;
|
|
1757
|
-
Ok(Argon2::default().verify_password(password.as_bytes(), &parsed).is_ok())
|
|
1758
|
-
}
|
|
1759
|
-
`, description: "Create Rust auth utility with Argon2id", ruleId: "GDPR-ART32-004" },
|
|
1613
|
+
{ type: "create", filePath: authPath, content: "use argon2::{Argon2, Algorithm, Version, Params};\nuse argon2::password_hash::{SaltString, PasswordHasher, PasswordVerifier};\nuse rand::rngs::OsRng;\n\npub fn hash_password(password: &str) -> Result<String, argon2::password_hash::Error> {\n let salt = SaltString::generate(&mut OsRng);\n let params = Params::new(65536, 3, 4, Some(32))?;\n let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);\n let hash = argon2.hash_password(password.as_bytes(), &salt)?;\n Ok(hash.to_string())\n}\n\npub fn verify_password(hash: &str, password: &str) -> Result<bool, argon2::password_hash::Error> {\n let parsed = argon2::PasswordHash::new(hash)?;\n Ok(Argon2::default().verify_password(password.as_bytes(), &parsed).is_ok())\n}\n", description: "Create Rust auth utility with Argon2id", ruleId: "GDPR-ART32-004" },
|
|
1760
1614
|
];
|
|
1761
1615
|
}
|
|
1762
1616
|
const authPath = hasSrc ? "src/lib/auth.ts" : "lib/auth.ts";
|
|
@@ -1771,24 +1625,7 @@ function buildIntegrityControlsImpl(root, hasSrc) {
|
|
|
1771
1625
|
const lang = detectProjectLanguage(root);
|
|
1772
1626
|
if (lang === "rust") {
|
|
1773
1627
|
return [
|
|
1774
|
-
{ type: "create", filePath: "src/integrity.rs", content:
|
|
1775
|
-
|
|
1776
|
-
pub fn hash_data(data: &str) -> String {
|
|
1777
|
-
let mut hasher = Sha256::new();
|
|
1778
|
-
hasher.update(data.as_bytes());
|
|
1779
|
-
format!("{:x}", hasher.finalize())
|
|
1780
|
-
}
|
|
1781
|
-
|
|
1782
|
-
pub fn verify_integrity(data: &str, expected_hash: &str) -> bool {
|
|
1783
|
-
hash_data(data) == expected_hash
|
|
1784
|
-
}
|
|
1785
|
-
|
|
1786
|
-
pub fn generate_checksum(content: &[u8]) -> String {
|
|
1787
|
-
let mut hasher = Sha256::new();
|
|
1788
|
-
hasher.update(content);
|
|
1789
|
-
format!("{:x}", hasher.finalize())
|
|
1790
|
-
}
|
|
1791
|
-
`, description: "Create Rust integrity verification utility", ruleId: "GDPR-ART32-007" },
|
|
1628
|
+
{ type: "create", filePath: "src/integrity.rs", content: "use sha2::{Sha256, Digest};\n\npub fn hash_data(data: &str) -> String {\n let mut hasher = Sha256::new();\n hasher.update(data.as_bytes());\n format!(\"{:x}\", hasher.finalize())\n}\n\npub fn verify_integrity(data: &str, expected_hash: &str) -> bool {\n hash_data(data) == expected_hash\n}\n\npub fn generate_checksum(content: &[u8]) -> String {\n let mut hasher = Sha256::new();\n hasher.update(content);\n format!(\"{:x}\", hasher.finalize())\n}\n", description: "Create Rust integrity verification utility", ruleId: "GDPR-ART32-007" },
|
|
1792
1629
|
];
|
|
1793
1630
|
}
|
|
1794
1631
|
const integrityPath = hasSrc ? "src/lib/integrity.ts" : "lib/integrity.ts";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@greenarmor/ges-mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "GESF MCP Server - AI Compliance Assistant for GDPR, OWASP, NIST, CIS. Check compliance, generate policies, assess risks via MCP protocol.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -26,11 +26,10 @@
|
|
|
26
26
|
"url": "git+https://github.com/greenarmor/gesf.git"
|
|
27
27
|
},
|
|
28
28
|
"bin": {
|
|
29
|
-
"ges-mcp": "
|
|
29
|
+
"ges-mcp": "dist/server.js"
|
|
30
30
|
},
|
|
31
31
|
"files": [
|
|
32
|
-
"dist"
|
|
33
|
-
"bundle"
|
|
32
|
+
"dist"
|
|
34
33
|
],
|
|
35
34
|
"type": "module",
|
|
36
35
|
"main": "./dist/index.js",
|
|
@@ -46,14 +45,14 @@
|
|
|
46
45
|
"registry": "https://registry.npmjs.org/"
|
|
47
46
|
},
|
|
48
47
|
"dependencies": {
|
|
49
|
-
"@greenarmor/ges-
|
|
50
|
-
"@greenarmor/ges-
|
|
51
|
-
"@greenarmor/ges-
|
|
52
|
-
"@greenarmor/ges-
|
|
53
|
-
"@greenarmor/ges-scoring-engine": "1.0.
|
|
54
|
-
"@greenarmor/ges-rules-engine": "1.0.
|
|
55
|
-
"@greenarmor/ges-report-generator": "1.0.
|
|
56
|
-
"@greenarmor/ges-policy-engine": "1.0.
|
|
48
|
+
"@greenarmor/ges-core": "1.0.1",
|
|
49
|
+
"@greenarmor/ges-audit-engine": "1.0.1",
|
|
50
|
+
"@greenarmor/ges-compliance-engine": "1.0.1",
|
|
51
|
+
"@greenarmor/ges-doc-generator": "1.0.1",
|
|
52
|
+
"@greenarmor/ges-scoring-engine": "1.0.1",
|
|
53
|
+
"@greenarmor/ges-rules-engine": "1.0.1",
|
|
54
|
+
"@greenarmor/ges-report-generator": "1.0.1",
|
|
55
|
+
"@greenarmor/ges-policy-engine": "1.0.1"
|
|
57
56
|
},
|
|
58
57
|
"devDependencies": {
|
|
59
58
|
"@types/node": "^22.0.0",
|
|
@@ -65,8 +64,6 @@
|
|
|
65
64
|
},
|
|
66
65
|
"scripts": {
|
|
67
66
|
"build": "tsc",
|
|
68
|
-
"build:bundle": "node scripts/build-bundle.mjs",
|
|
69
|
-
"build:all": "tsc && node scripts/build-bundle.mjs",
|
|
70
67
|
"clean": "rm -rf dist bundle tsconfig.tsbuildinfo",
|
|
71
68
|
"test": "echo \"no tests yet\""
|
|
72
69
|
}
|