PHPackages                             dnoegel/pdo-inspector - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Security](/categories/security)
4. /
5. dnoegel/pdo-inspector

ActiveLibrary[Security](/categories/security)

dnoegel/pdo-inspector
=====================

Inspect PDO queries for possible SQL injections

37PHP

Since Nov 24Pushed 10y ago1 watchersCompare

[ Source](https://github.com/dnoegel/pdo-inspector)[ Packagist](https://packagist.org/packages/dnoegel/pdo-inspector)[ RSS](/packages/dnoegel-pdo-inspector/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependenciesVersions (1)Used By (0)

DatabaseInspection
==================

[](#databaseinspection)

Will decorate the PDO connection of an application and log all queries, which might have possible SQL injections in it.

How does it work?
=================

[](#how-does-it-work)

Every query is intercepted and parsed. In the parsed query, hardcoded constant values (e.g. `SELECT * FROM my_table WHERE value = 'hi'`) are found and declared as possible SQL injections.

You will find those queries with a stack trace, request infos and more details in a output folder, that you can define.

How do I use it?
================

[](#how-do-i-use-it)

In the application you want to test, find the place, where your PDO connection is created. Most PHP software uses PDO, also ORMs like Doctrine makes use of it.

Example
-------

[](#example)

Given you have an application that uses PDO like this:

```
$this->_connection = new PDO(
    $dsn,
    $username,
    $password,
    $options
);

```

with this:

```
use \\Dnoegel\\DatabaseInspection\\PDOInspectionDecorator;

try {
    $this->_connection = new PDOInspectionDecorator(
        new PDO(
            $dsn,
            $username,
            $password,
            $options
        )
    );

    $this->_connection->setProblemInspector(new \\Dnoegel\\DatabaseInspection\\SqlProblemInspector(
            new \\Dnoegel\\DatabaseInspection\\Storage\\JsonStorage('/tmp/sql_inspector_output'),
    ));

```

you can decorate your PDO connection.

So this is actually three steps:

- Include / require the autoloader of this lib. If you installed it via composer into your application, you can skip this.
- Decorate the PDO connection with the `PDOInspectionDecorator`, which acts fully transparent to the PDO connection, so your application will be fine with it.
- Inject an instance of `\Dnoegel\DatabaseInspection\SqlProblemInspector` into the `PDOInspectionDecorator`. `SqlProblemInspector` has only one mandatory requirement: `JsonStorage`, which you can configure to save your profile files to the position you like.

Whitelisting
============

[](#whitelisting)

If you checked a query and think, that its ok (e.g. values are casted to int beforehands), you can move them to the `whitelist` folder of your output folder. The script will then not bother you any more.

The whitelisting will figure out the constant parts of the query, so e.g. `SELECT * FROM my_table WHERE value = 'hi'`and `SELECT * FROM my_table WHERE value = 'another string'` will be the same query from a technical perspective.

Creating the issue inspector:
=============================

[](#creating-the-issue-inspector)

```
$parser = new \\Dnoegel\\DatabaseInspection\\SqlProblemInspector(
    new \\Dnoegel\\DatabaseInspection\\Storage\\JsonStorage(),
    new \\Dnoegel\\DatabaseInspection\\RouteProvider\\RouteProvider(),
    new \\Dnoegel\\DatabaseInspection\\Trace\\DebugTrace(),
    true
)

```

The last parameter will decide, if all findings are saved to the `problem` document or if the script should try to separate "issues" and "problems". "Issues" are findings, that only consist of scalar values, which could still be an SQL injection, so this is by no means a "all-clear" - just a "check the problems first".

Reading the "problem" file
==========================

[](#reading-the-problem-file)

Each problem file provides the following info:

- `route`: The route / request, that triggered that query
- `problems`: The list of the static values, in this case `0` - so this can probably be whitelisted
- `code`: The code of the function, that triggered the problematic query
- `sql`: The SQL being executed
- `trace`: The trace of the SQL - so you can tell, which file / which line executed it
- `normalized`: The parsed and normalized SQL query - so all constant values are removed here, to match the same query with other hardcoded values

```
{
    "route": "\/media\/checkout\/confirm",
    "problems": [
        {
            "expr_type": "const",
            "base_expr": "0",
            "sub_tree": false
        }
    ],
    "code": {
        "999": "    public function sCountBasket()",
        "1000": "    {",
        "1001": "        return $this->db->fetchOne(",
        "1002": "            'SELECT COUNT(*) FROM s_order_basket WHERE modus = 0 AND sessionID = ?',",
        "1003": "            array($this->session->get('sessionId'))",
        "!!!": "        );",
        "1005": "    }"
    },
    "sql": "SELECT COUNT(*) FROM s_order_basket WHERE modus = 0 AND sessionID = ?",
    "trace": {
        "7": "sBasket: sCountBasket:  84",
        "8": "Shopware_Controllers_Frontend_Checkout: postDispatch:  161",
        "9": "Enlight_Controller_Action: dispatch:  524",
        "10": "Enlight_Controller_Dispatcher_Default: dispatch:  227",
        "11": "Enlight_Controller_Front: dispatch:  148",
        "12": "Shopware\\Kernel: handle:  492",
        "13": "Symfony\\Component\\HttpKernel\\HttpCache\\HttpCache: forward:  255",
        "14": "Shopware\\Components\\HttpCache\\AppCache: forward:  449",
        "15": "Symfony\\Component\\HttpKernel\\HttpCache\\HttpCache: fetch:  349",
        "16": "Symfony\\Component\\HttpKernel\\HttpCache\\HttpCache: lookup:  178",
        "17": "Shopware\\Components\\HttpCache\\AppCache: lookup:  213",
        "18": "Symfony\\Component\\HttpKernel\\HttpCache\\HttpCache: handle:  114",
        "19": "Shopware\\Components\\HttpCache\\AppCache: handle:  101"
    },
    "normalized": {
        "SELECT": [
            {
                "expr_type": "aggregate_function",
                "alias": false,
                "base_expr": "COUNT",
                "sub_tree": [
                    {
                        "expr_type": "colref",
                        "base_expr": "*",
                        "sub_tree": false
                    }
                ],
                "delim": false
            }
        ],
        "FROM": [
            {
                "expr_type": "table",
                "table": "s_order_basket",
                "no_quotes": {
                    "delim": false,
                    "parts": [
                        "s_order_basket"
                    ]
                },
                "alias": false,
                "hints": false,
                "join_type": "JOIN",
                "ref_type": false,
                "ref_clause": false,
                "base_expr": "s_order_basket",
                "sub_tree": false
            }
        ],
        "WHERE": [
            {
                "expr_type": "colref",
                "base_expr": "modus",
                "no_quotes": {
                    "delim": false,
                    "parts": [
                        "modus"
                    ]
                },
                "sub_tree": false
            },
            {
                "expr_type": "operator",
                "base_expr": "=",
                "sub_tree": false
            },
            {
                "expr_type": "const",
                "base_expr": "NORMALIZED",
                "sub_tree": false
            },
            {
                "expr_type": "operator",
                "base_expr": "AND",
                "sub_tree": false
            },
            {
                "expr_type": "colref",
                "base_expr": "sessionID",
                "no_quotes": {
                    "delim": false,
                    "parts": [
                        "sessionID"
                    ]
                },
                "sub_tree": false
            },
            {
                "expr_type": "operator",
                "base_expr": "=",
                "sub_tree": false
            },
            {
                "expr_type": "colref",
                "base_expr": "?",
                "no_quotes": {
                    "delim": false,
                    "parts": [
                        "?"
                    ]
                },
                "sub_tree": false
            }
        ]
    }
}

```

Notice
======

[](#notice)

This is by no mean a security layer. **Do not** use it as such. Its a development tool and a proof of concept for parsing SQL queries in order to find constant query parts that might be replaced with prepared statements. Do not use it in production.

###  Health Score

21

—

LowBetter than 19% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity8

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/3e33c241de9be12145b80356591da2a7b875f1111d77a8f0a5474f55f02c91c1?d=identicon)[dnoegel](/maintainers/dnoegel)

---

Top Contributors

[![dnoegel](https://avatars.githubusercontent.com/u/1114384?v=4)](https://github.com/dnoegel "dnoegel (3 commits)")

### Embed Badge

![Health badge](/badges/dnoegel-pdo-inspector/health.svg)

```
[![Health](https://phpackages.com/badges/dnoegel-pdo-inspector/health.svg)](https://phpackages.com/packages/dnoegel-pdo-inspector)
```

###  Alternatives

[defuse/php-encryption

Secure PHP Encryption Library

3.9k162.4M212](/packages/defuse-php-encryption)[roave/security-advisories

Prevents installation of composer packages with known security vulnerabilities: no API, simply require it

2.9k97.3M6.4k](/packages/roave-security-advisories)[mews/purifier

Laravel 5/6/7/8/9/10 HtmlPurifier Package

2.0k16.7M112](/packages/mews-purifier)[robrichards/xmlseclibs

A PHP library for XML Security

41278.1M117](/packages/robrichards-xmlseclibs)[bjeavons/zxcvbn-php

Realistic password strength estimation PHP library based on Zxcvbn JS

86917.5M63](/packages/bjeavons-zxcvbn-php)[enlightn/security-checker

A PHP dependency vulnerabilities scanner based on the Security Advisories Database.

33732.2M110](/packages/enlightn-security-checker)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
