sast-sqli
Detect SQL injection vulnerabilities in a codebase using a three-phase approach: recon (find unsafe SQL construction sites), batched verify (trace user input to those sites in parallel subagents, 3 sites each), and merge (consolidate batch results).
Install / Use
npx skills add utkusen/sast-skills --skill sast-sqliInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
Data & AnalyticsSupported Platforms
Our assessment of sast-sqli
sast-sqli scores 89/100 on our quality scale, 191st of 436 Data & Analytics skills we index (top 44%).
Its SKILL.md is 24 KB long, well organised into 49 sections with 16 code examples: a thorough specification that gives an agent plenty to work with.
With 1,321 GitHub stars, it is one of the more widely adopted skills in the catalogue.
Maintenance, license and trust
- The repository was last updated about 6 months ago. That is recent enough to be usable, but agent tooling moves fast, so check the instructions against your agent's current version.
- It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
- Its trust signals score 98/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
sast-sqli compared with similar skills
All 4 of these similar skills score higher than sast-sqli; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| sast-sqli (this skill)by utkusen | 89 | 1.3k | 6mo ago | SKILL.md |
| claude-memby thedotmack | 100 | 95.0k | today | CLAUDE.md |
| algorithmic-artby anthropics | 100 | 177.9k | 8d ago | SKILL.md |
| pptxby anthropics | 100 | 177.9k | 8d ago | SKILL.md |
| designby nextlevelbuilder | 100 | 130.2k | 9d ago | SKILL.md |
Frequently asked questions
- How do I install sast-sqli?
- Run
npx skills add utkusen/sast-skills --skill sast-sqli. The install tabs above show the steps for each supported agent. - Which AI agents does sast-sqli work with?
- It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is sast-sqli safe to use?
- It is MIT-licensed and scores 98/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is sast-sqli still maintained?
- The repository was last updated about 6 months ago. That is recent enough to be usable, but agent tooling moves fast, so check the instructions against your agent's current version.
Skill content
View source on GitHubname: sast-sqli description: >- Detect SQL injection vulnerabilities in a codebase using a three-phase approach: recon (find unsafe SQL construction sites), batched verify (trace user input to those sites in parallel subagents, 3 sites each), and merge (consolidate batch results). Covers string concat, f-strings, unsafe ORM methods, and dynamic identifiers. Requires sast/architecture.md (run sast-analysis first). Outputs findings to sast/sqli-results.md. Use when asked to find SQLi or database injection bugs.
SQL Injection (SQLi) Detection
You are performing a focused security assessment to find SQL injection vulnerabilities in a codebase. This skill uses a three-phase approach with subagents: recon (find vulnerable SQL construction sites), batched verify (taint analysis in parallel batches of 3), and merge (consolidate batch reports into one file).
Prerequisites: sast/architecture.md must exist. Run the analysis skill first if it doesn't.
What is SQL Injection
SQL injection occurs when user-supplied input is incorporated into SQL queries through string concatenation or interpolation rather than parameterized binding. This allows attackers to alter query logic, bypass authentication, extract sensitive data, modify or delete records, and in some configurations execute OS commands.
The core pattern: unvalidated, unparameterized user input reaches a SQL query execution call.
What SQLi IS
- Concatenating user input directly into a SQL string:
"SELECT * FROM users WHERE name = '" + username + "'" - Using string formatting to build queries:
f"SELECT * FROM orders WHERE id = {order_id}" - Dynamic
ORDER BY/GROUP BY/ table/column names from user input with no allowlist validation - ORM raw query methods with unsanitized input:
User.objects.raw(f"SELECT * WHERE id={id}"),$queryRawUnsafe(input) - Second-order injection: input is stored in the DB and later used in a raw query without re-sanitization
What SQLi is NOT
Do not flag these as SQLi:
- IDOR: Changing
?id=1to?id=2to access another user's data — that's Insecure Direct Object Reference, a separate class - Mass assignment: Setting extra ORM model fields from user input — different vulnerability
- XSS via database: Storing a
<script>tag in the DB that's later rendered unescaped — that's XSS, not SQLi - NoSQL injection: Injecting into MongoDB operators — similar concept but a distinct vulnerability class
- Safe ORM queries: Parameterized ORM lookups like
User.objects.filter(id=user_id)orUser.find(params[:id])— do not flag these
Patterns That Prevent SQLi
When you see these patterns, the code is likely not vulnerable:
1. Parameterized queries / prepared statements (most common fix)
# Python — cursor.execute with tuple binding
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
# Node.js — mysql2 / pg placeholder binding
db.query("SELECT * FROM users WHERE id = ?", [userId])
pool.query("SELECT * FROM users WHERE id = $1", [userId])
# Java — PreparedStatement
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
ps.setInt(1, userId);
# Go — database/sql placeholder
db.QueryRow("SELECT * FROM users WHERE id = $1", userID)
# PHP — PDO with named params
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $userId]);
# C# — SqlCommand with parameters
cmd.CommandText = "SELECT * FROM users WHERE id = @id";
cmd.Parameters.AddWithValue("@id", userId);
2. ORM query builder (safe by default)
# Django ORM
User.objects.filter(id=user_id)
# ActiveRecord (Rails)
User.find(params[:id])
User.where(name: params[:name])
# Prisma (tagged template literal form of $queryRaw)
await prisma.$queryRaw`SELECT * FROM users WHERE id = ${userId}`
# Laravel Eloquent (non-raw)
User::find($id)
3. Allowlist validation for dynamic identifiers
# Dynamic ORDER BY — validate column name against a hardcoded set before interpolating
ALLOWED_COLUMNS = {'name', 'created_at', 'price'}
if sort_col not in ALLOWED_COLUMNS:
raise ValueError("Invalid column")
query = f"SELECT * FROM products ORDER BY {sort_col}" # safe only after allowlist check
Vulnerable vs. Secure Examples
Python — Django (raw SQL)
# VULNERABLE: f-string interpolation in raw()
def search_users(request):
username = request.GET.get('username')
users = User.objects.raw(f"SELECT * FROM auth_user WHERE username = '{username}'")
return JsonResponse(list(users.values()), safe=False)
# SECURE: parameterized raw()
def search_users(request):
username = request.GET.get('username')
users = User.objects.raw("SELECT * FROM auth_user WHERE username = %s", [username])
return JsonResponse(list(users.values()), safe=False)
Python — Flask / SQLAlchemy
# VULNERABLE: f-string into text()
@app.route('/search')
def search():
name = request.args.get('name')
result = db.session.execute(text(f"SELECT * FROM products WHERE name = '{name}'"))
return jsonify(result.fetchall())
# SECURE: named bound parameter
@app.route('/search')
def search():
name = request.args.get('name')
result = db.session.execute(
text("SELECT * FROM products WHERE name = :name"), {"name": name}
)
return jsonify(result.fetchall())
Python — sqlite3 / psycopg2
# VULNERABLE
def get_user(username):
cursor.execute("SELECT * FROM users WHERE username = '" + username + "'")
return cursor.fetchone()
# SECURE
def get_user(username):
cursor.execute("SELECT * FROM users WHERE username = ?", (username,))
return cursor.fetchone()
Node.js — mysql2
// VULNERABLE: template literal in query string
app.get('/user', async (req, res) => {
const { id } = req.query;
const [rows] = await db.query(`SELECT * FROM users WHERE id = ${id}`);
res.json(rows);
});
// SECURE: placeholder binding
app.get('/user', async (req, res) => {
const { id } = req.query;
const [rows] = await db.query('SELECT * FROM users WHERE id = ?', [id]);
res.json(rows);
});
Node.js — pg (PostgreSQL)
// VULNERABLE
app.get('/orders', async (req, res) => {
const status = req.query.status;
const result = await pool.query(`SELECT * FROM orders WHERE status = '${status}'`);
res.json(result.rows);
});
// SECURE
app.get('/orders', async (req, res) => {
const status = req.query.status;
const result = await pool.query('SELECT * FROM orders WHERE status = $1', [status]);
res.json(result.rows);
});
Ruby on Rails
# VULNERABLE: string interpolation in where()
def search
@users = User.where("name = '#{params[:name]}'")
end
# VULNERABLE: find_by_sql with interpolation
def find_user
@user = User.find_by_sql("SELECT * FROM users WHERE email = '#{params[:email]}'")
end
# SECURE: parameterized where()
def search
@users = User.where("name = ?", params[:name])
# or using hash form: User.where(name: params[:name])
end
Java — Spring JDBC
// VULNERABLE: string concatenation
public User findUser(String username) {
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
return jdbcTemplate.queryForObject(sql, userRowMapper);
}
// SECURE: parameterized query
public User findUser(String username) {
return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE username = ?", userRowMapper, username
);
}
Go — database/sql
// VULNERABLE: fmt.Sprintf to build query
func GetUserByName(name string) (*User, error) {
query := fmt.Sprintf("SELECT * FROM users WHERE name = '%s'", name)
row := db.QueryRow(query)
// ...
}
// SECURE: parameterized query
func GetUserByName(name string) (*User, error) {
row := db.QueryRow("SELECT * FROM users WHERE name = $1", name)
// ...
}
PHP — PDO
// VULNERABLE: string concatenation
function getUser($id) {
$stmt = $pdo->query("SELECT * FROM users WHERE id = " . $id);
return $stmt->fetch();
}
// SECURE: prepared statement
function getUser($id) {
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $id]);
return $stmt->fetch();
}
C# — ADO.NET
// VULNERABLE: string concatenation
public User GetUser(string username) {
using var cmd = new SqlCommand(
"SELECT * FROM Users WHERE Username = '" + username + "'", conn);
return ReadUser(cmd.ExecuteReader());
}
// SECURE: parameterized command
public User GetUser(string username) {
using var cmd = new SqlCommand(
"SELECT * FROM Users WHERE Username = @username", conn);
cmd.Parameters.AddWithValue("@username", username);
return ReadUser(cmd.ExecuteReader());
}
Dynamic ORDER BY / Column Names (all stacks)
# VULNERABLE: unsanitized user input as column name (parameterization can't help here)
sort_col = request.args.get('sort', 'name')
cursor.execute(f"SELECT * FROM products ORDER BY {sort_col}")
# SECURE: allowlist validation before interpolation
ALLOWED_SORT_COLS = {'name', 'price', 'created_at'}
sort_col = request.args.get('sort', 'name')
if sort_col not in ALLOWED_SORT_COLS:
return abort(400)
cursor.execute(f"SELECT * FROM products ORDER BY {sort_col}")
Execution
This skill runs in three phases using subagents. Pass the contents of sast/architecture.md to all subagents as context.
Phase 1: Recon — Find Vulnerable SQL Construction Sites
Launch a subagent with the following instructions:
Goal: Find every location in the codebase where a SQL query is constructed in a vulnerable way — using string concatenation, interpolation, or formatting with any variable (regardless of where that variable comes from). Write results to
sast/sqli-recon.md.Context: You will be given the project's architecture summary. Use it to understand the tech stack, database layer, ORM patterns, and query execution methods.
What to search for — vulnerable query construction patterns:
Look for SQL query execution calls where the query string argument is built dynamically rather than being a static string with placeholder parameters. Flag ANY dynamic variable embedded into the query — you are not yet tracing whether the variable is user-controlled; that is Phase 2's job.
- String concatenation into a SQL execution call:
cursor.execute("SELECT ... WHERE id = " + var)$pdo->query("SELECT * FROM users WHERE id = " . $var)jdbcTemplate.query("SELECT * WHERE username = '" + var + "'")- F-strings / template literals used as a query argument:
cursor.execute(f"SELECT * WHERE name = '{var}'")db.query(`SELECT * WHERE id = ${var}`)db.QueryRow(fmt.Sprintf("SELECT * WHERE id = '%s'", var))- String formatting functions used to build the query:
cursor.execute("SELECT * WHERE id = %s" % var)(note:%formatting, NOT parameterized binding)cursor.execute("SELECT * WHERE id = {}".format(var))String.format("SELECT * WHERE id = '%s'", var)(Java)sprintf("SELECT * WHERE id = %s", $var)(PHP)- ORM raw/unsafe methods called with a dynamically built string (not a static template with bound params):
- Django:
Model.objects.raw(f"..."),RawSQL(f"..."),extra(where=[f"..."])- ActiveRecord:
where("col = '#{var}'")(Ruby interpolation inside string arg)- Sequelize:
sequelize.query(`...${var}...`),literal(var)- TypeORM:
createQueryBuilder().where(`col = '${var}'`),.query("..." + var)- Prisma:
$queryRawUnsafe(...),$executeRawUnsafe(...)- Entity Framework:
FromSqlRaw("..." + var),ExecuteSqlRaw("..." + var)- Dynamic identifiers — any variable used as a column name, table name,
ORDER BY/GROUP BYvalue in a query string (parameterization can
Truncated for display — read the full file on GitHub.
Related Skills
claude-mem
95.0kPersistent Context Across Sessions for Every Agent – Captures everything your agent does during sessions, compresses it with AI, and injects relevant context back into future sessions. Works with Claude Code, OpenClaw, Codex, Gemini, Hermes, Copilot, OpenCode + More
algorithmic-art
177.9kCreating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems.
pptx
177.9kUse this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an em…
design
130.2kComprehensive design skill: brand identity, design tokens, UI styling, logo generation (55 styles, Gemini, Atlas Cloud, or MuAPI AI), corporate identity program (50 deliverables, CIP mockups), HTML presentations (Chart.js), banner design (22 styles, social/ads/web/print), icon design (15 styles, SVG…
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
