Files
Marius CiepluchandClaude Opus 5.5 7927d6e539 feat(ruleset): PHP quick-mode rules from the engine's vocabulary, measured on the development corpus
Quick mode had no PHP rules, so a PHP quick scan read the files and reported a plausible zero.
ruleset/php/rules.toml adds 13 line/statement rules built on semantic/php.toml's sinks, sources and
sanitizers (sql/wpdb, command, code_eval, unserialize, echo, file_inclusion, filesystem,
outbound_request, redirect). 9 are enabled and 4 are shadow (sql-call-composition, code-eval,
unserialize, xss-echo-input). A rule is shadow when it fired on the corpus but never at a known site.
precision_estimate is the measured lower bound.

Measured in-sample on the development corpus only (pmpro introduce/repair/benign, population-v1 PHP
cases; population-v2 was not read). Known site hit on the vulnerable pin in 4 of 5 cases (LearnPress
missed). The hit disappears on the fixed pin for pmpro and YesWiki. Numbers, per-pin files/bytes read
and the engine comparison are in benchmarks/measurements/2026-09-30-php-quick-rules/.

- fixtures php-vulnerable / php-benign plus manifests: ousast benchmark covers PHP
  (9/14 reported, 13/14 including shadow; the benign twin records 2 known false alerts)
- PHP `#` comments are comments; `#[...]` attributes are not
- the alerts task now reports PHP as quick coverage; alerts-engine keeps running the engine for PHP
  next to the quick rules (ENGINE_ALONGSIDE_QUICK)
- other languages' benchmark findings and metrics are unchanged

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
2026-09-30 15:05:47 +02:00

66 lines
1.4 KiB
PHP

<?php
// Safe equivalents. None of these lines should produce a finding.
function order_by_code_safe($code)
{
global $wpdb;
return $wpdb->get_var($wpdb->prepare("SELECT id FROM {$wpdb->prefix}orders WHERE code = %s LIMIT 1", $code));
}
function orders_in_safe($ids)
{
global $wpdb;
$ids_format = implode(', ', array_fill(0, count($ids), '%d'));
return $wpdb->prepare('SELECT * FROM orders WHERE id IN (' . $ids_format . ')', $ids);
}
function page_limit_safe($page)
{
return ' LIMIT ' . intval($page) . ', 20';
}
function quoted_escaped_safe($name)
{
return "SELECT * FROM users WHERE name = '" . esc_sql($name) . "'";
}
function ping_safe()
{
system('ping -c 1 ' . escapeshellarg($_GET['host']));
}
function version_safe()
{
exec('git describe --tags', $output);
return $output;
}
function greet_safe()
{
echo 'Hello ' . htmlspecialchars($_GET['name'], ENT_QUOTES);
}
function download_safe()
{
readfile('/var/www/files/' . basename($_GET['file']));
}
function restore_safe($raw)
{
return unserialize($raw, ['allowed_classes' => false]);
}
function query_args_safe()
{
$args = [];
$args['include'] = [(int) $_REQUEST['p']];
return $args;
}
function comment_only()
{
// system('rm -rf ' . $_GET['dir']) would be dangerous but this is a comment
# eval($_POST['code']) is a comment too
return 'ok';
}