Progpilot Documentation

repository·master·Indexed 18 days ago

https://github.com/designsecurity/progpilot

Progpilot 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).

Tokens
13.2K
Snippets
45
Records
55
Agent score
63%

What's inside Progpilot

  1. How the prevent property works in sanitizers

    master

    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:

    • You can define prevent at the top level of the sanitizer object.
    • You can define prevent within specific values inside the parameters block.
    • If a function call matches a specific value condition (e.g., 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"]}
                ]
            }
        ]
    }
  2. Enforce or restrict function call definitions

    master

    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"
    }
  3. Verify call flows with MUST_VERIFY_CALL_FLOW

    master

    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"
            }]
    }
  4. How Progpilot handles chained method calls

    master

    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();
  5. Distinguish between vulnerability types in output

    master

    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:

    1. taint-style: Indicates a data-flow vulnerability (source to sink).
    2. custom: Indicates a violation of a specific rule defined in rules.json.
  6. How Progpilot handles object properties and instances

    master

    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;
  7. How the instanceof property works

    master

    The instanceof property allows you to target specific object types or method chains.

    1. Direct Class Name: Use the exact class name if known. {"name": "prepare", "instanceof": "mysql_connect", ...}

    2. Property Path: If the exact class is unknown, use the property name within an object. {"name": "isValidNumber", "instanceof": "ESAPI->validator", ...}

    3. 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.

    4. 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"}
  8. How Progpilot handles variable states and dataflow

    master

    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;
  9. Volume mounting and pathing in Progpilot Docker

    master

    Because the container is isolated, it cannot see your local filesystem unless you explicitly mount it.

    • Mounting: Use -v $(pwd):/workspace to map your current directory to /workspace inside the container.
    • Pathing: All arguments passed to the 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.
    • Runtime: The container uses PHP 8.1 (compatible with project requirements of >=8.3).
  10. Run Progpilot via Docker

    master

    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.yml