Adding the project for code injection and XSS vulnerability testing
This project is designed to help developers understand and mitigate code injection and XSS vulnerabilities. It includes a backend API and a frontend interface for testing various attack vectors in a controlled environment.
This commit is contained in:
3
backend/.env.example
Normal file
3
backend/.env.example
Normal file
@@ -0,0 +1,3 @@
|
||||
PORT=3000
|
||||
DB_HOST=localhost
|
||||
DB_NAME=injection_demo
|
||||
73
backend/.gitignore
vendored
Normal file
73
backend/.gitignore
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Optional npm cache
|
||||
.npm
|
||||
.eslintcache
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage/
|
||||
.nyc_output/
|
||||
|
||||
# Build output
|
||||
dist/
|
||||
build/
|
||||
out/
|
||||
public/
|
||||
|
||||
# Next.js
|
||||
.next/
|
||||
|
||||
# Nuxt
|
||||
.nuxt/
|
||||
|
||||
# Gatsby
|
||||
.cache/
|
||||
|
||||
# Parcel
|
||||
.parcel-cache/
|
||||
|
||||
# TypeScript
|
||||
*.tsbuildinfo
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.*.local
|
||||
.env.local
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.vscode/
|
||||
*.sublime-workspace
|
||||
*.sublime-project
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Misc
|
||||
*.tgz
|
||||
*.zip
|
||||
*.log.*
|
||||
tmp/
|
||||
temp/
|
||||
|
||||
# Codetyper.nvim - AI coding partner files
|
||||
*.coder.*
|
||||
.coder/
|
||||
.claude/
|
||||
1365
backend/package-lock.json
generated
Normal file
1365
backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
backend/package.json
Normal file
24
backend/package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "codeinjection",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node src/index.js",
|
||||
"dev": "nodemon src/index.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Carlos Gutierrez <ingecarlos.gutierrez@gmail.com>",
|
||||
"license": "MIT",
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"cors": "^2.8.6",
|
||||
"dotenv": "^17.2.3",
|
||||
"express": "^5.2.1",
|
||||
"pg": "^8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.11"
|
||||
}
|
||||
}
|
||||
16
backend/src/api/controller/controller.js
Normal file
16
backend/src/api/controller/controller.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const db = require("../../query/database");
|
||||
|
||||
const loginUser = async (username, password) => {
|
||||
const user = await db.loginUser(username, password);
|
||||
return user;
|
||||
};
|
||||
|
||||
const executeJS = (code) => {
|
||||
const result = db.executeCode(code);
|
||||
return result;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
loginUser,
|
||||
executeJS,
|
||||
};
|
||||
34
backend/src/api/controller/secureController.js
Normal file
34
backend/src/api/controller/secureController.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const db = require("../../query/secureDatabase");
|
||||
|
||||
const loginUser = async (username, password) => {
|
||||
const userPassword = await db.getUserPassword(username);
|
||||
|
||||
// Compare password with database password
|
||||
if (!userPassword[0] || userPassword[0].password !== password) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const user = await db.loginUser(username);
|
||||
|
||||
// Return only the first object with username and email
|
||||
if (user[0]) {
|
||||
return { username: user[0].username, email: user[0].email };
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const executeSQL = async (query) => {
|
||||
const result = await db.executeQuery(query);
|
||||
return result;
|
||||
};
|
||||
|
||||
const executeJS = (code) => {
|
||||
const result = db.executeCode(code);
|
||||
return result;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
loginUser,
|
||||
executeSQL,
|
||||
executeJS,
|
||||
};
|
||||
32
backend/src/api/network/network.js
Normal file
32
backend/src/api/network/network.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const controller = require("../controller/controller");
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
|
||||
const loginUser = async (req, res) => {
|
||||
try {
|
||||
const { username, password } = { ...req.query, ...req.body };
|
||||
const users = await controller.loginUser(username, password);
|
||||
if (users && users.length > 0) {
|
||||
res.json({ success: true, users });
|
||||
} else {
|
||||
res.status(401).json({ success: false, message: "Invalid credentials" });
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
const executeCode = async (req, res) => {
|
||||
try {
|
||||
const { code } = { ...req.query, ...req.body };
|
||||
const result = controller.executeJS(code);
|
||||
res.json({ success: true, type: "js", data: result });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
router.post("/login", loginUser);
|
||||
router.post("/execute", executeCode);
|
||||
|
||||
module.exports = router;
|
||||
47
backend/src/api/network/secureNetwork.js
Normal file
47
backend/src/api/network/secureNetwork.js
Normal file
@@ -0,0 +1,47 @@
|
||||
const controller = require("../controller/secureController");
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
|
||||
const loginUser = async (req, res) => {
|
||||
try {
|
||||
const { username, password } = { ...req.body };
|
||||
const users = await controller.loginUser(username, password);
|
||||
console.log("Login attempt for user:", users);
|
||||
if (users) {
|
||||
res.json({ success: true, users });
|
||||
} else {
|
||||
res.status(401).json({ success: false, message: "Invalid credentials" });
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
const executeCode = async (req, res) => {
|
||||
try {
|
||||
const { code, type } = { ...req.query, ...req.body };
|
||||
if (type === "sql") {
|
||||
const result = await controller.executeSQL(code);
|
||||
res.json({ success: true, type: "sql", data: result });
|
||||
} else if (type === "js") {
|
||||
const result = controller.executeJS(code);
|
||||
res.json({ success: true, type: "js", data: result });
|
||||
} else {
|
||||
// No type specified - try eval first, then SQL
|
||||
try {
|
||||
const result = controller.executeJS(code);
|
||||
res.json({ success: true, type: "js", data: result });
|
||||
} catch {
|
||||
const result = await controller.executeSQL(code);
|
||||
res.json({ success: true, type: "sql", data: result });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
router.post("/login", loginUser);
|
||||
router.post("/execute", executeCode);
|
||||
|
||||
module.exports = router;
|
||||
12
backend/src/config/config.js
Normal file
12
backend/src/config/config.js
Normal file
@@ -0,0 +1,12 @@
|
||||
require("dotenv").config();
|
||||
|
||||
module.exports = {
|
||||
port: process.env.PORT || 3000,
|
||||
db: {
|
||||
host: process.env.DB_HOST || "localhost",
|
||||
port: process.env.DB_PORT,
|
||||
database: process.env.DB_NAME,
|
||||
user: process.env.DB_USER,
|
||||
password: process.env.DB_PASSWORD || "",
|
||||
},
|
||||
};
|
||||
34
backend/src/index.js
Normal file
34
backend/src/index.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const express = require("express");
|
||||
const config = require("./config/config");
|
||||
const routes = require("./routes/routes");
|
||||
const cors = require("cors");
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
app.use("", routes(app));
|
||||
|
||||
app.get("/", (req, res) => {
|
||||
res.send("Welcome to the API server!");
|
||||
});
|
||||
|
||||
app.listen(config.port, () => {
|
||||
console.log(`Server running on http://localhost:${config.port}`);
|
||||
console.log("\nAvailable endpoints:");
|
||||
console.log(` GET http://localhost:${config.port}/`);
|
||||
console.log(
|
||||
` GET/POST http://localhost:${config.port}/api/login - SQL Injection`,
|
||||
);
|
||||
console.log(
|
||||
` GET/POST http://localhost:${config.port}/api/secure/login - Invalid SQL Injection`,
|
||||
);
|
||||
console.log(
|
||||
` GET/POST http://localhost:${config.port}/api/execute - SQL + eval() (type: sql|js)`,
|
||||
);
|
||||
console.log(
|
||||
` GET/POST http://localhost:${config.port}/api/secure/execute - Invalid SQL + eval() (type: sql|js)`,
|
||||
);
|
||||
});
|
||||
27
backend/src/query/database.js
Normal file
27
backend/src/query/database.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const { Pool } = require("pg");
|
||||
const config = require("../config/config");
|
||||
|
||||
const pool = new Pool(config.db);
|
||||
|
||||
const loginUser = async (username, password) => {
|
||||
const query = `SELECT * FROM users WHERE username = '${username}' AND password = '${password}'`;
|
||||
const result = await pool.query(query);
|
||||
return result.rows;
|
||||
};
|
||||
|
||||
const executeQuery = async (query) => {
|
||||
const result = await pool.query(query);
|
||||
return result.rows;
|
||||
};
|
||||
|
||||
const executeCode = (code) => {
|
||||
console.log("Executing code:", code);
|
||||
const result = eval(code);
|
||||
return result;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
loginUser,
|
||||
executeQuery,
|
||||
executeCode,
|
||||
};
|
||||
90
backend/src/query/secureDatabase.js
Normal file
90
backend/src/query/secureDatabase.js
Normal file
@@ -0,0 +1,90 @@
|
||||
const { Pool } = require("pg");
|
||||
const config = require("../config/config");
|
||||
|
||||
const pool = new Pool(config.db);
|
||||
|
||||
// Regex pattern matching dangerous SQL characters
|
||||
const DANGEROUS_CHARS = /['";\\`\-\-\/\*\+\=\(\)\[\]\{\}\|\&\^\%\$\#\@\!\~]/g;
|
||||
|
||||
// Sanitize input by removing dangerous characters
|
||||
const sanitizeInput = (input) => {
|
||||
if (typeof input !== "string") {
|
||||
return String(input);
|
||||
}
|
||||
return input.replace(DANGEROUS_CHARS, "");
|
||||
};
|
||||
|
||||
// Secure parameterized query - equivalent to Java PreparedStatement
|
||||
const getUserByUsername = async (username) => {
|
||||
const sql = "SELECT * FROM users WHERE username = $1";
|
||||
const result = await pool.query(sql, [username]); // Parameter is safely escaped
|
||||
return result.rows;
|
||||
};
|
||||
|
||||
const getUserPassword = async (username) => {
|
||||
let secureUsername = sanitizeInput(username);
|
||||
const sql = "SELECT password FROM users WHERE username = $1";
|
||||
const result = await pool.query(sql, [secureUsername]); // Parameter is safely escaped
|
||||
console.log("Retrieved password for user:", secureUsername);
|
||||
return result.rows;
|
||||
};
|
||||
|
||||
const loginUser = async (username) => {
|
||||
let secureUsername = sanitizeInput(username);
|
||||
return await getUserByUsername(secureUsername);
|
||||
};
|
||||
|
||||
const executeQuery = async (query) => {
|
||||
let secureQuery = sanitizeInput(query);
|
||||
const result = await pool.query(secureQuery);
|
||||
console.log("Attempting login for user:", result);
|
||||
const sanitizedRows = result.rows.map((row) => {
|
||||
const rowKeys = Object.keys(row);
|
||||
if (rowKeys.includes("username") && rowKeys.includes("email")) {
|
||||
return { username: row.username, email: row.email };
|
||||
}
|
||||
});
|
||||
return sanitizedRows;
|
||||
};
|
||||
|
||||
const DANGEROUS_JS_PATTERNS = [
|
||||
/require\s*\(/gi,
|
||||
/import\s*\(/gi,
|
||||
/process\./gi,
|
||||
/child_process/gi,
|
||||
/fs\./gi,
|
||||
/eval\s*\(/gi,
|
||||
/Function\s*\(/gi,
|
||||
/exec\s*\(/gi,
|
||||
/spawn\s*\(/gi,
|
||||
/__dirname/gi,
|
||||
/__filename/gi,
|
||||
/global\./gi,
|
||||
];
|
||||
|
||||
const sanitizeCode = (code) => {
|
||||
if (typeof code !== "string") {
|
||||
return String(code);
|
||||
}
|
||||
// Only use pattern detection for JS code - don't strip characters
|
||||
// This allows safe operations like math, string methods, and JSON
|
||||
for (const pattern of DANGEROUS_JS_PATTERNS) {
|
||||
if (pattern.test(code)) {
|
||||
throw new Error("Dangerous code pattern detected");
|
||||
}
|
||||
}
|
||||
return code;
|
||||
};
|
||||
|
||||
const executeCode = (code) => {
|
||||
let secureCode = sanitizeCode(code);
|
||||
const result = eval(secureCode);
|
||||
return result;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
loginUser,
|
||||
executeQuery,
|
||||
executeCode,
|
||||
getUserPassword,
|
||||
};
|
||||
10
backend/src/routes/routes.js
Normal file
10
backend/src/routes/routes.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const network = require("../api/network/network");
|
||||
const secureNetwork = require("../api/network/secureNetwork");
|
||||
|
||||
const routes = (app) => {
|
||||
app.use("/api", network);
|
||||
app.use("/api/secure", secureNetwork);
|
||||
return network;
|
||||
};
|
||||
|
||||
module.exports = routes;
|
||||
Reference in New Issue
Block a user