Install
openclaw skills install php-sql-fixerDetect SQL injection risks in PHP/Yaf projects and generate parameterized query fix patches. Scans for string concatenation in SQL, unsafe superglobal interp...
openclaw skills install php-sql-fixerDetect SQL injection risks and generate parameterized query fix patches for PHP/Yaf projects.
This skill does two things:
Always prefer minimal, targeted fixes. Do not refactor surrounding code. Do not change DB abstraction patterns that already exist in the project.
bash "$SKILL_DIR/scripts/scan_sql.sh" <project-root> [output-file]
Read the output carefully. The scanner flags candidates, not confirmed vulnerabilities. Some hits may be false positives (e.g. SQL built from constants, not user input).
For each flagged file:
$_GET, $_POST, $_REQUEST, function params from controllers) reaches the SQL stringphp "$SKILL_DIR/scripts/suggest_fix.php" <file-path>
The script outputs annotated before/after for each risky SQL statement in the file.
Apply fixes manually or with targeted Edit tool calls. Rules:
// FIXED: sql injection comment on the line where the fix was appliedphp -l <file> after every edit to verify syntax# syntax check
docker compose -f /mnt/d/Users/Public/php20250819/docker-php7.3/docker-compose.yml \
exec fpm-server php -l /var/www/html/2026www/<project>/<file>
# re-scan to confirm no remaining hits
bash "$SKILL_DIR/scripts/scan_sql.sh" <project-root>
See references/fix-patterns.md for the complete catalog. Quick reference:
// BEFORE (unsafe)
$sql = "SELECT * FROM users WHERE id = " . $id;
$res = $db->query($sql);
// AFTER (PDO)
$stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
$res = $stmt->fetchAll();
// BEFORE (unsafe)
$sql = "SELECT * FROM orders WHERE status = '$status' AND uid = $uid";
// AFTER (PDO named placeholders)
$stmt = $db->prepare("SELECT * FROM orders WHERE status = :status AND uid = :uid");
$stmt->execute([':status' => $status, ':uid' => $uid]);
// BEFORE (unsafe)
$sql = sprintf("SELECT * FROM t WHERE name = '%s'", $name);
// AFTER
$stmt = $db->prepare("SELECT * FROM t WHERE name = ?");
$stmt->execute([$name]);
// BEFORE (unsafe — raw string passed to model)
$this->_model->where("user_id = $uid AND type = '$type'")->find();
// AFTER (use array condition — depends on your Model API)
$this->_model->where(['user_id' => $uid, 'type' => $type])->find();
// OR if model supports raw+bindings:
$this->_model->where("user_id = ? AND type = ?", [$uid, $type])->find();
// BEFORE (unsafe)
$ids = implode(',', $id_arr);
$sql = "SELECT * FROM t WHERE id IN ($ids)";
// AFTER (PHP 7.3 compatible)
$placeholders = implode(',', array_fill(0, count($id_arr), '?'));
$stmt = $db->prepare("SELECT * FROM t WHERE id IN ($placeholders)");
$stmt->execute($id_arr);
Before reporting a finding as confirmed SQL injection:
intval()-cast earlier?If all four are "no" → confirmed risk. If any is "yes" → suspected or false positive.
When fixing many files across a project:
scan_sql.sh on the whole project, save output to file