From harness-claude
Prevents SQL, NoSQL, and command injection using parameterized queries, input validation with Zod, and safe child process spawning. For user-param DB queries, ORMs like Prisma/TypeORM, MongoDB, and CLI wrappers.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Eliminate SQL, NoSQL, and command injection by never concatenating untrusted input into queries or commands
Prevents SQL injection attacks using prepared statements, parameterized queries, input validation, and ORM best practices in Node.js, Python, and Java database apps.
Detects SQL injection vulnerabilities by tracing user inputs through code to database queries, flagging unsafe patterns like concatenation and unparameterized ORMs. Scans frameworks including Django, Rails, Express, Go.
Explains root cause of injection vulnerabilities (SQLi, XSS, command injection) as untrusted data interpreted as code. Guides reviews of user input in queries, commands, markup; audits across tech stacks.
Share bugs, ideas, or general feedback.
Eliminate SQL, NoSQL, and command injection by never concatenating untrusted input into queries or commands
Never build queries with string concatenation. Always use parameterized queries or a query builder.
// BAD — direct interpolation
const query = `SELECT * FROM users WHERE id = ${userId}`;
// GOOD — parameterized (node-postgres)
const result = await pool.query('SELECT * FROM users WHERE id = $1', [userId]);
// GOOD — TypeORM query builder
const user = await userRepo
.createQueryBuilder('user')
.where('user.id = :id', { id: userId })
.getOne();
// GOOD — Prisma (always parameterized)
const user = await prisma.user.findUnique({ where: { id: userId } });
For raw SQL that must be dynamic (table/column names), use an allowlist — never accept these from user input:
const ALLOWED_SORT_COLUMNS = ['name', 'created_at', 'email'] as const;
type SortColumn = (typeof ALLOWED_SORT_COLUMNS)[number];
function buildSortedQuery(column: SortColumn) {
// column is validated at the type level AND runtime
if (!ALLOWED_SORT_COLUMNS.includes(column)) throw new Error('Invalid column');
return `SELECT * FROM users ORDER BY ${column}`;
}
MongoDB operators like $where, $gt, $regex can be injected through JSON body parsing. Validate and sanitize all operator keys.
// BAD — passes user object directly to find
app.post('/login', async (req, res) => {
const user = await User.findOne({ username: req.body.username, password: req.body.password });
// Attacker sends: { "username": "admin", "password": { "$gt": "" } }
});
// GOOD — extract and validate specific string fields
app.post('/login', async (req, res) => {
const username = String(req.body.username ?? '');
const password = String(req.body.password ?? '');
const user = await User.findOne({ username, password });
});
// GOOD — use express-mongo-sanitize middleware to strip $ keys
import mongoSanitize from 'express-mongo-sanitize';
app.use(mongoSanitize());
Never pass user input to exec, execSync, or shell: true spawns.
import { execFile, spawn } from 'child_process';
import { promisify } from 'util';
const execFileAsync = promisify(execFile);
// BAD
exec(`convert ${userFilename} output.png`);
// GOOD — execFile does NOT invoke a shell; args are passed directly
await execFileAsync('convert', [userFilename, 'output.png']);
// GOOD — spawn with shell: false (default)
const proc = spawn('convert', [userFilename, 'output.png']);
Validate all external input at system boundaries before it reaches any query or command:
import { z } from 'zod';
const SearchSchema = z.object({
query: z
.string()
.max(100)
.regex(/^[\w\s]+$/),
page: z.number().int().min(1).max(1000),
sortBy: z.enum(['name', 'date', 'score']),
});
app.get('/search', async (req, res) => {
const params = SearchSchema.parse(req.query); // throws ZodError on invalid input
const results = await db.search(params.query, params.page, params.sortBy);
res.json(results);
});
OWASP consistently ranks injection as a top-3 vulnerability. The root cause is always the same: user-controlled data is interpreted as code or a query operator rather than data.
Defense layers:
Common bypasses to watch for:
%27 → ', -- URL-encoded as %2D%2DORMs do NOT automatically protect you if you use raw query escape hatches (Prisma.$queryRaw without template literals, TypeORM query(), etc.).
// STILL vulnerable with Prisma if you concatenate
const bad = await prisma.$queryRaw(`SELECT * FROM users WHERE id = ${id}`);
// SAFE — template literal version uses parameterization automatically
const good = await prisma.$queryRaw`SELECT * FROM users WHERE id = ${id}`;
https://owasp.org/www-project-top-ten/