Check supported frameworks for Progpilot
masterProgpilot currently supports analysis for the following frameworks:
- suiteCRM
- codeIgniter
repository·master·Indexed 18 days ago
https://github.com/designsecurity/progpilotProgpilot is a Static Application Security Testing (SAST) tool for PHP that performs taint analysis to identify vulnerabilities by tracking untrusted data from sources to sinks. It can be used as a CLI tool, via Docker, or as a PHP library. The tool supports custom configuration of sources, sinks, sanitizers, and validators, and provides advanced analysis features such as call flow verification (MUST_VERIFY_CALL_FLOW) and definition enforcement (MUST_VERIFY_DEFINITION).
Progpilot currently supports analysis for the following frameworks:
The prevent property in a sanitizer tells the analyzer which attacks are no longer possible after the function is called. The value must match an attack type defined in your sinks configuration.
Overriding Prevention Rules:
prevent at the top level of the sanitizer object.prevent within specific values inside the parameters block.htmlentities($tainted, ENT_QUOTES)), the prevent rule associated with that value will overwrite the main prevent property of the sanitizer.Predefined values:
ALL: Prevents all vulnerabilities.QUOTES: Indicates that quotes are encoded.{
"name": "htmlentities",
"language": "php",
"prevent": ["xss"],
"parameters": [
{
"id": 2,
"conditions": "equals",
"values": [
{"value" : "ENT_QUOTES", "prevent" : ["xss", "command_injection"]}
]
}
]
}You can verify if specific function calls comply with defined conditions using the MUST_VERIFY_DEFINITION or MUST_NOT_VERIFY_DEFINITION actions. This allows you to audit configuration settings, such as ensuring auto-escaping is enabled in a template engine.
Key properties for these rules include:
name: The name of the function or method.is_function: Set to true to treat name as a function.instanceof: The class name the function/method belongs to.parameters: An array defining expected values for function arguments. Each parameter object includes an id (argument index) and values (an array of expected value objects).action: Either MUST_VERIFY_DEFINITION or MUST_NOT_VERIFY_DEFINITION.attack and cwe: Metadata to categorize the violation.{
"name": "__construct",
"is_function": true,
"instanceof": "Twig_Environment",
"parameters":
[
{"id": 2, "values":
[
{"value" : "false", "is_array": true, "array_index": "autoescape"}
]}
],
"description": "Twig_Environment autoescaping should be set to true",
"language": "php",
"action": "MUST_NOT_VERIFY_DEFINITION",
"attack": "security misconfiguration",
"cwe": "CWE_1004"
}Use the MUST_VERIFY_CALL_FLOW action to ensure that a specific sequence of function calls occurs within the program's call graph (starting from the main function). This is useful for verifying security protocols, such as ensuring authentication and permission checks occur before sensitive operations.
Define a sequence of objects containing the function_name and language for each step in the required flow. If any execution path in the program fails to follow this exact sequence, the rule will be raised.
{
"custom_rules": [
{
"sequence":
[
{"function_name": "dev_iam_authenticated", "language": "php"},
{"function_name": "dev_iam_rights", "language": "php"},
{"function_name": "dev_retrieve_secret", "language": "php"}
],
"description": "rule #1 not verified",
"action": "MUST_VERIFY_CALL_FLOW"
}]
}Progpilot tracks states through chained method calls by following the return values of functions. Each function call in a chain can introduce a new instance with its own definition block and state, which then flows into the next call in the chain.
$instance1 = new Object1;
// Progpilot tracks the flow through the chain:
// instance1 -> func1() (returns instance2) -> func2() (returns instance3) -> func3()
$instance1->func1()->func2()->func3();To programmatically differentiate between the types of vulnerabilities found by Progpilot, check the vuln_type field in the output object. It will contain one of two values:
taint-style: Indicates a data-flow vulnerability (source to sink).custom: Indicates a violation of a specific rule defined in rules.json.When dealing with object instances and their properties, Progpilot tracks states for the properties themselves rather than just the instance variable.
When a property is assigned a value in different execution paths, Progpilot launches a dataflow analysis for those properties. The state at a subsequent block is calculated as the merge of the states from the preceding branches. For example, if a property is tainted in one branch and set to a string in another, the resulting state at the merge point reflects the combination of those states.
$instance = new Object;
if(rand()) {
// block 2: instance->prop is tainted
$instance->prop = $_GET["p"];
}
else {
// block 3: instance->prop is "null"
$instance->prop = "null";
}
// block 4: state of instance->prop is merge(state 2, state 3)
echo $instance->prop;The instanceof property allows you to target specific object types or method chains.
Direct Class Name: Use the exact class name if known.
{"name": "prepare", "instanceof": "mysql_connect", ...}
Property Path: If the exact class is unknown, use the property name within an object.
{"name": "isValidNumber", "instanceof": "ESAPI->validator", ...}
Object Heritage: instanceof respects inheritance. If you define a parent class as an instance of a sink, all child classes will also be treated as sinks for that method.
Undefined Classes: It works even if the class is not explicitly defined in the analysis scope (e.g., using a placeholder like VulnerableClass).
// Example of targeting a method on a specific class hierarchy
{"name": "query", "instanceof": "SomeClass1", "language": "php"}Progpilot uses a state-based model to track dataflow, specifically focusing on attributes like isTainted. Each definition (variable or object) maintains multiple states corresponding to different execution blocks.
For simple variables, Progpilot uses a defaultState for the variable and tracks how its state changes across different code blocks (e.g., inside if/else branches). The visitorDataFlow mechanism is responsible for performing the actual dataflow analysis by merging these states at join points (like the end of an if/else block).
// Example of state tracking for simple variables
$foo = $_GET["p"]; // block 1: foo state is tainted
if(rand()) {
// block 2: bar state is tainted (inherits from foo)
$bar = $foo;
}
else {
// block 3: bar state is empty
$bar = null;
}
// block 4: bar state is the merge of block 2 and block 3
echo $bar;Because the container is isolated, it cannot see your local filesystem unless you explicitly mount it.
-v $(pwd):/workspace to map your current directory to /workspace inside the container.docker run command are forwarded to the progpilot command. Therefore, you must use the container-internal paths (e.g., /workspace/...) for all target files, directories, and configuration files.To use Progpilot in a container, you must mount your local files into the container using a volume so the tool can access your PHP code. The recommended pattern is to mount your current working directory to /workspace inside the container.
Important: When specifying files or directories to analyze, you must use the absolute path as it exists inside the container (e.g., starting with /workspace/).
# Analyze a single PHP file
docker run -v $(pwd):/workspace progpilot /workspace/path/to/your/file.php
# Analyze multiple files
docker run -v $(pwd):/workspace progpilot /workspace/file1.php /workspace/file2.php /workspace/file3.php
# Analyze a directory
docker run -v $(pwd):/workspace progpilot /workspace/path/to/your/php/project/
# Use with a configuration file
docker run -v $(pwd):/workspace progpilot /workspace/file.php --configuration /workspace/config.ymlTo build a local Docker image named progpilot from the source code, run the following command in the repository root. This process builds the Progpilot PHAR file inside the container.
docker build -t progpilot .