phptaint 0.1.0

Security-focused PHP lexer, parser, AST, and configurable taint analysis engine
Documentation

phptaint

Security-focused PHP lexer, parser, and taint analysis engine with configurable sink registries.

phptaint is designed for security tooling rather than full-fidelity PHP compilation. It parses the PHP constructs that matter for taint analysis, tracks flows from superglobals into sinks, supports framework presets, and now loads custom registry rules from TOML.

Features

  • PHP lexer with span tracking
  • AST and parser for security-relevant PHP constructs
  • Taint analysis across assignments, calls, methods, hooks, and user-defined functions
  • Built-in registries for php_core, wordpress, and laravel
  • TOML-backed custom sink and sanitizer registry loading
  • Support for modern constructs including match, enum, and readonly
  • Single-file and multi-file analysis

Installation

[dependencies]
phptaint = "0.1"

Quick Start

use phptaint::{analyze, TaintRegistry};

let findings = analyze(
    "sample.php",
    r#"<?php
$cmd = $_GET['cmd'];
eval($cmd);
"#,
    &TaintRegistry::php_core(),
);

assert!(findings.iter().any(|finding| finding.category == "RCE"));

Modern PHP Constructs

The parser intentionally supports a focused subset of modern PHP that commonly appears in real applications and plugins:

  • match expressions
  • enum declarations, including backed enums
  • readonly class declarations and readonly property modifiers
  • nullsafe calls, closures, arrow functions, hooks, and common control flow

Example:

use phptaint::{analyze, TaintRegistry};

let findings = analyze(
    "match.php",
    r#"<?php
$payload = match($kind) {
    "cmd" => $_GET['cmd'],
    default => "id",
};
eval($payload);
"#,
    &TaintRegistry::php_core(),
);

assert!(findings.iter().any(|finding| finding.category == "RCE"));

Built-In Registries

Use the built-in presets directly:

use phptaint::{analyze, TaintRegistry};

let source = r#"<?php
$url = $_GET['redirect'];
wp_redirect($url);
"#;

let findings = analyze("plugin.php", source, &TaintRegistry::wordpress());
assert!(findings.iter().any(|finding| finding.category == "Open Redirect"));

TOML Registry Configuration

Custom sinks and sanitizers can now be loaded from TOML with RegistryFile:

use phptaint::{analyze, RegistryFile};

let registry = RegistryFile::from_toml(
    r#"
presets = ["php_core"]
sanitizers = ["safe_template"]

[[sinks]]
function = "render_template"
dangerous_args = [0]
category = "SSTI"
severity = "high"

[[method_sinks]]
object_pattern = "renderer"
method = "render"
dangerous_args = [0]
category = "SSTI"
severity = "critical"
"#,
    "<inline>",
)?
.into_registry()?;

let findings = analyze(
    "custom.php",
    r#"<?php
$tpl = $_GET['tpl'];
render_template($tpl);
"#,
    &registry,
);

assert!(findings.iter().any(|finding| finding.category == "SSTI"));
# Ok::<(), Box<dyn std::error::Error>>(())

Multi-File Analysis

use phptaint::{analyze_multi, TaintRegistry};

let findings = analyze_multi(
    &[
        ("a.php", "<?php function run_cmd($value) { exec($value); }"),
        ("b.php", "<?php $cmd = $_GET['cmd']; run_cmd($cmd);"),
    ],
    &TaintRegistry::php_core(),
);

assert!(findings.iter().any(|finding| finding.category == "RCE"));

Examples

Run the bundled examples:

cargo run --example basic
cargo run --example toml_registry

Development

  • cargo fmt
  • cargo check --all-targets
  • cargo test

License

MIT