Self-hosted SAST for PHP using phpcs-security-audit

phpcs-security-audit

Security Application Security Testing (SAST)

Static Application Security Testing (SAST) aims to scan source code to identify poor coding practices that could lead to security vulnerabilities. A number of vendors are now providing SAST tools as part of their offerings geared towards security-concious customers. Platforms such as GitHub, GitLab, Snyk, SonarQube offer cloud-based integration of SAST features in the CI/CD pipeline.

phpcs-security-audit

phpcs-security-audit provides a set of PHP CodeSniffer rules geared towards vulnerability scanning. GitLab uses this analyzer to scan PHP-based repos. Being an open source product, phpcs-security-audit is also provided as a Docker image on the GitLab Container Registry.

Automating local deployment and analysis

The following script setups everything needed to deploy phpcs-security-audit analyzer as a docker container on your system. The /opt/code_analysis/sast_script.sh is then set as the entrypoint for the docker container. You can add or remove more PHP repos by editing the $scan_repos variable in the sast_script.sh script. Finally, docker volumes are used to make the /opt/repos and /opt/code_analysis directories available within the container to avoid code duplication.

#!/bin/sh

# This script makes use of GitLab's phpcs-security-audit analyzer to perform
# Static Application Security Testing (SAST), also known as source code
# scanning. This script automates the docker setup and uses a script named
# 'sast_script.sh' to perform SAST analysis on all paths provided via the
# $scan_repos variable.
#
# Assumptions:
# - The /opt/repos directory holds all the code repositories
# - The /opt/code_analysis directory holds all SAST-related code
# - The SAST reports will be saved to /opt/code_analysis/sast_reports

# Pull the docker image from GitLab Container Registry
docker pull registry.gitlab.com/gitlab-org/security-products/analyzers/phpcs-security-audit:4.1.5

# Setup the required directory structure
mkdir -p /opt/code_analysis
mkdir -p /opt/code_analysis/sast_reports
mkdir -p /opt/repos
chmod -R o+w /opt/code_analysis
chmod -R o+w /opt/repos

## Clone the required repos to /opt/repos
read -p "Clone the required repos to /opt/repos/. Press enter to continue."

# We will use this script as the entry point for our docker container. You can
# configure the $scan_repos variable to specify the repos you want to be
# analyzed.
cat << 'EOF' > /opt/code_analysis/sast_script.sh
#!/bin/sh

report_dir="/opt/code_analysis/sast_reports"
scan_repos="$scan_repos /opt/repos/DVWA"
scan_repos="$scan_repos /opt/repos/OSTE-Vulnerable-Web-Application"
default_sast_report_name="gl-sast-report.json"

for repo in $scan_repos;
do
  echo $repo;
  repo_basename="$(echo $repo | cut -d\/ -f4)"
  echo $repo_basename
  sast_report_name=$repo_basename"_gl_sast-""$(date +%Y-%m-%d-%H:%M).json"
  /analyzer run --target-dir $repo
  mv $repo/$default_sast_report_name $report_dir/$sast_report_name
done
EOF

chmod +x /opt/code_analysis/sast_script.sh

# We mount /opt/repos and /opt/code_analysis in the docker container
docker run --name phpcs-security-audit \
  -v /opt/repos/:/opt/repos -v /opt/code_analysis/:/opt/code_analysis/ \
  --entrypoint /opt/code_analysis/sast_script.sh \
  registry.gitlab.com/gitlab-org/security-products/analyzers/phpcs-security-audit

# For future runs simply run `docker start phpcs-security-audit` to re-analyze
# the PHP repos. You can always edit the $scan_repos in the sast_script.sh file.

SAST scan results

As with any SAST tool, the output can be quite a handful. The reports are available at /opt/code_analysis/sast_reports/. For the Damn Vulnerable Web Application (DVWA), these results were returned:

  • 7 high severity vulnerabilities
  • 80 low severity vulnerabilities
  • 87 total vulnerabilities

The listing below shows the details of the 7 high severity vulnerabilities.


{
    "version": "15.0.7",
    "vulnerabilities": [
        {
            "id": "3ebb204839b890ee7d5259b5861a28a251f3cf527bb91ce93dcd6d6e37214862",
            "category": "sast",
            "name": "Filesystem function basename() detected with dynamic parameter directly from user input",
            "description": "Filesystem function basename() detected with dynamic parameter directly from user input",
            "cve": "opt/repos/DVWA/vulnerabilities/upload/source/high.php:PHPCS_SecurityAudit.BadFunctions.FilesystemFunctions.ErrFilesystem",
            "severity": "High",
            "scanner": {
                "id": "phpcs_security_audit",
                "name": "phpcs-security-audit v2"
            },
            "location": {
                "file": "opt/repos/DVWA/vulnerabilities/upload/source/high.php",
                "start_line": 6
            },
            "identifiers": [
                {
                    "type": "phpcs_security_audit_source",
                    "name": "PHPCS_SecurityAudit.BadFunctions.FilesystemFunctions.ErrFilesystem",
                    "value": "PHPCS_SecurityAudit.BadFunctions.FilesystemFunctions.ErrFilesystem"
                }
            ]
        },
        {
            "id": "e3e4402bfa97f19eef757a16e2dcc7b976a7f9cecb11aae537a3c8bc266257be",
            "category": "sast",
            "name": "Filesystem function move_uploaded_file() detected with dynamic parameter directly from user input",
            "description": "Filesystem function move_uploaded_file() detected with dynamic parameter directly from user input",
            "cve": "opt/repos/DVWA/vulnerabilities/upload/source/low.php:PHPCS_SecurityAudit.BadFunctions.FilesystemFunctions.ErrFilesystem",
            "severity": "High",
            "scanner": {
                "id": "phpcs_security_audit",
                "name": "phpcs-security-audit v2"
            },
            "location": {
                "file": "opt/repos/DVWA/vulnerabilities/upload/source/low.php",
                "start_line": 9
            },
            "identifiers": [
                {
                    "type": "phpcs_security_audit_source",
                    "name": "PHPCS_SecurityAudit.BadFunctions.FilesystemFunctions.ErrFilesystem",
                    "value": "PHPCS_SecurityAudit.BadFunctions.FilesystemFunctions.ErrFilesystem"
                }
            ]
        },
        {
            "id": "eb1c249548a5a9eb970fafe34d3aba706df7ca6f28d2cd1d44d4d6f0888f584f",
            "category": "sast",
            "name": "Filesystem function basename() detected with dynamic parameter directly from user input",
            "description": "Filesystem function basename() detected with dynamic parameter directly from user input",
            "cve": "opt/repos/DVWA/vulnerabilities/upload/source/low.php:PHPCS_SecurityAudit.BadFunctions.FilesystemFunctions.ErrFilesystem",
            "severity": "High",
            "scanner": {
                "id": "phpcs_security_audit",
                "name": "phpcs-security-audit v2"
            },
            "location": {
                "file": "opt/repos/DVWA/vulnerabilities/upload/source/low.php",
                "start_line": 6
            },
            "identifiers": [
                {
                    "type": "phpcs_security_audit_source",
                    "name": "PHPCS_SecurityAudit.BadFunctions.FilesystemFunctions.ErrFilesystem",
                    "value": "PHPCS_SecurityAudit.BadFunctions.FilesystemFunctions.ErrFilesystem"
                }
            ]
        },
        {
            "id": "9874685197baa59a2a5b26d6bf3b2af935bf4967b8d91a812d8f099a50a1e797",
            "category": "sast",
            "name": "Filesystem function basename() detected with dynamic parameter directly from user input",
            "description": "Filesystem function basename() detected with dynamic parameter directly from user input",
            "cve": "opt/repos/DVWA/vulnerabilities/upload/source/medium.php:PHPCS_SecurityAudit.BadFunctions.FilesystemFunctions.ErrFilesystem",
            "severity": "High",
            "scanner": {
                "id": "phpcs_security_audit",
                "name": "phpcs-security-audit v2"
            },
            "location": {
                "file": "opt/repos/DVWA/vulnerabilities/upload/source/medium.php",
                "start_line": 6
            },
            "identifiers": [
                {
                    "type": "phpcs_security_audit_source",
                    "name": "PHPCS_SecurityAudit.BadFunctions.FilesystemFunctions.ErrFilesystem",
                    "value": "PHPCS_SecurityAudit.BadFunctions.FilesystemFunctions.ErrFilesystem"
                }
            ]
        },
        {
            "id": "3102c84676fab5cf9e5a34c5d183112ae9c3b5270f44d6dcb55c0a7acc63649f",
            "category": "sast",
            "name": "Filesystem function move_uploaded_file() detected with dynamic parameter directly from user input",
            "description": "Filesystem function move_uploaded_file() detected with dynamic parameter directly from user input",
            "cve": "opt/repos/DVWA/vulnerabilities/upload/source/medium.php:PHPCS_SecurityAudit.BadFunctions.FilesystemFunctions.ErrFilesystem",
            "severity": "High",
            "scanner": {
                "id": "phpcs_security_audit",
                "name": "phpcs-security-audit v2"
            },
            "location": {
                "file": "opt/repos/DVWA/vulnerabilities/upload/source/medium.php",
                "start_line": 18
            },
            "identifiers": [
                {
                    "type": "phpcs_security_audit_source",
                    "name": "PHPCS_SecurityAudit.BadFunctions.FilesystemFunctions.ErrFilesystem",
                    "value": "PHPCS_SecurityAudit.BadFunctions.FilesystemFunctions.ErrFilesystem"
                }
            ]
        },
        {
            "id": "0f3169524cf4c57cf08947d8f06beb765e4f60168ef89811aff2d5149890ca9c",
            "category": "sast",
            "name": "Please do not use eval() functions",
            "description": "Please do not use eval() functions",
            "cve": "opt/repos/DVWA/vulnerabilities/view_help.php:PHPCS_SecurityAudit.BadFunctions.NoEvals.NoEvals",
            "severity": "High",
            "scanner": {
                "id": "phpcs_security_audit",
                "name": "phpcs-security-audit v2"
            },
            "location": {
                "file": "opt/repos/DVWA/vulnerabilities/view_help.php",
                "start_line": 22
            },
            "identifiers": [
                {
                    "type": "phpcs_security_audit_source",
                    "name": "PHPCS_SecurityAudit.BadFunctions.NoEvals.NoEvals",
                    "value": "PHPCS_SecurityAudit.BadFunctions.NoEvals.NoEvals"
                }
            ]
        },
        {
            "id": "be6fbfc48621ec0230cac67cb104da3d9629583f040d7d3b91b1670f91359364",
            "category": "sast",
            "name": "Please do not use eval() functions",
            "description": "Please do not use eval() functions",
            "cve": "opt/repos/DVWA/vulnerabilities/view_help.php:PHPCS_SecurityAudit.BadFunctions.NoEvals.NoEvals",
            "severity": "High",
            "scanner": {
                "id": "phpcs_security_audit",
                "name": "phpcs-security-audit v2"
            },
            "location": {
                "file": "opt/repos/DVWA/vulnerabilities/view_help.php",
                "start_line": 20
            },
            "identifiers": [
                {
                    "type": "phpcs_security_audit_source",
                    "name": "PHPCS_SecurityAudit.BadFunctions.NoEvals.NoEvals",
                    "value": "PHPCS_SecurityAudit.BadFunctions.NoEvals.NoEvals"
                }
            ]
        },
            ],
    "dependency_files": [],
    "scan": {
        "analyzer": {
            "id": "phpcs-security-audit",
            "name": "phpcs-security-audit v2",
            "url": "https://gitlab.com/gitlab-org/security-products/analyzers/phpcs-security-audit",
            "vendor": {
                "name": "GitLab"
            },
            "version": "4.1.5"
        },
        "scanner": {
            "id": "phpcs_security_audit",
            "name": "phpcs-security-audit v2",
            "url": "https://github.com/FloeDesignTechnologies/phpcs-security-audit",
            "vendor": {
                "name": "GitLab"
            },
            "version": "2.0.1"
        },
        "type": "sast",
        "start_time": "2024-03-27T12:06:01",
        "end_time": "2024-03-27T12:06:01",
        "status": "success"
    }
}