toHaveCount(1); return $sites[0]['category']; } test('prepared calls are safe regardless of the SQL text', function () { expect(category_of('db_fetch_cell_prepared("SELECT x FROM t WHERE id = ?", [$id]);'))->toBe('prepared'); }); test('fully static SQL is safe', function () { expect(category_of('db_execute("DELETE FROM settings WHERE name = \'foo\'");'))->toBe('static'); }); test('db_qstr escaped fragments are safe', function () { expect(category_of('db_fetch_assoc("SELECT * FROM host WHERE id = " . db_qstr($id));'))->toBe('qstr'); }); test('array_to_sql_or IN lists are safe', function () { expect(category_of('db_execute("DELETE FROM sites WHERE " . array_to_sql_or($items, \'id\'));'))->toBe('qstr'); }); test('a bare scalar in value position is a migration target', function () { expect(category_of('db_execute("UPDATE host SET status = $status WHERE id = $id");'))->toBe('value'); }); test('an unescaped function result in value position is a migration target', function () { expect(category_of('db_execute("DELETE FROM t WHERE id IN (" . implode(",", $ids) . ")");'))->toBe('value'); }); test('a variable table name is an identifier, not a value', function () { expect(category_of('db_execute("ALTER TABLE $table ROW_FORMAT=DYNAMIC");'))->toBe('identifier'); }); test('IF EXISTS does not hide an identifier interpolation', function () { expect(category_of('db_execute("DROP TABLE IF EXISTS $archive");'))->toBe('identifier'); }); test('SHOW COLUMNS FROM a variable table is an identifier', function () { expect(category_of('db_fetch_assoc("SHOW COLUMNS FROM $table");'))->toBe('identifier'); }); test('a prebuilt sql_where fragment is tracked separately from a scalar', function () { expect(category_of('db_fetch_assoc("SELECT * FROM colors $sql_where");'))->toBe('fragment'); }); test('a whole-query variable is opaque', function () { expect(category_of('db_execute($sql);'))->toBe('opaque'); }); test('a scalar dominates a fragment in the same statement', function () { // host_id = $device_id must still flag for binding even alongside $sql_swhere. $php = 'db_fetch_assoc("SELECT id FROM graph_local WHERE host_id = $device_id $sql_swhere");'; expect(category_of($php))->toBe('value'); }); test('multi-line calls are found and classified', function () { $php = "db_fetch_assoc(\"SELECT id\n\tFROM graph_local\n\tWHERE host_id = \$hid\");"; expect(category_of($php))->toBe('value'); }); test('non-db functions are ignored', function () { expect(audit_scan('my_execute("DELETE FROM t WHERE id = $id");'))->toBe(array()); }); test('is_fragment_name recognises Cacti fragment variables', function () { expect(is_fragment_name('sql_where'))->toBeTrue(); expect(is_fragment_name('sql_swhere'))->toBeTrue(); expect(is_fragment_name('sql_join'))->toBeTrue(); expect(is_fragment_name('sql'))->toBeTrue(); expect(is_fragment_name('device_id'))->toBeFalse(); expect(is_fragment_name('status'))->toBeFalse(); expect(is_fragment_name(null))->toBeFalse(); });